X-Git-Url: https://git.immae.eu/?p=perso%2FImmae%2FProjets%2Fpackagist%2Fconnexionswing-ckeditor-component.git;a=blobdiff_plain;f=sources%2Fcore%2Fselection.js;fp=sources%2Fcore%2Fselection.js;h=573b890e18d89bd42f58550f4ccd7e99f456bfe4;hp=fec3b7fc166bba1e57c5c764c1a683feb334df70;hb=3b35bd273a79f6b01fda7a246aed64aca147ea7a;hpb=7adcb81e4f83f98c468889aaa5a85558ba88c770 diff --git a/sources/core/selection.js b/sources/core/selection.js index fec3b7fc..573b890e 100644 --- a/sources/core/selection.js +++ b/sources/core/selection.js @@ -1,5 +1,5 @@ -/** - * @license Copyright (c) 2003-2015, CKSource - Frederico Knabben. All rights reserved. +/** + * @license Copyright (c) 2003-2016, CKSource - Frederico Knabben. All rights reserved. * For licensing, see LICENSE.md or http://ckeditor.com/license */ @@ -138,72 +138,81 @@ return false; } - function createFillingChar( element ) { - removeFillingChar( element, false ); + function createFillingCharSequenceNode( editable ) { + removeFillingCharSequenceNode( editable, false ); - var fillingChar = element.getDocument().createText( '\u200B' ); - element.setCustomData( 'cke-fillingChar', fillingChar ); + var fillingChar = editable.getDocument().createText( fillingCharSequence ); + editable.setCustomData( 'cke-fillingChar', fillingChar ); return fillingChar; } - function getFillingChar( element ) { - return element.getCustomData( 'cke-fillingChar' ); - } - // Checks if a filling char has been used, eventualy removing it (#1272). - function checkFillingChar( element ) { - var fillingChar = getFillingChar( element ); + function checkFillingCharSequenceNodeReady( editable ) { + var fillingChar = editable.getCustomData( 'cke-fillingChar' ); + if ( fillingChar ) { // Use this flag to avoid removing the filling char right after // creating it. - if ( fillingChar.getCustomData( 'ready' ) ) - removeFillingChar( element ); - else + if ( fillingChar.getCustomData( 'ready' ) ) { + removeFillingCharSequenceNode( editable ); + } else { fillingChar.setCustomData( 'ready', 1 ); + } } } - function removeFillingChar( element, keepSelection ) { - var fillingChar = element && element.removeCustomData( 'cke-fillingChar' ); - if ( fillingChar ) { + function removeFillingCharSequenceNode( editable, keepSelection ) { + var fillingChar = editable && editable.removeCustomData( 'cke-fillingChar' ); + if ( fillingChar ) { // Text selection position might get mangled by // subsequent dom modification, save it now for restoring. (#8617) if ( keepSelection !== false ) { - var bm, - sel = element.getDocument().getSelection().getNative(), + var sel = editable.getDocument().getSelection().getNative(), // Be error proof. - range = sel && sel.type != 'None' && sel.getRangeAt( 0 ); + range = sel && sel.type != 'None' && sel.getRangeAt( 0 ), + fillingCharSeqLength = fillingCharSequence.length; - if ( fillingChar.getLength() > 1 && range && range.intersectsNode( fillingChar.$ ) ) { - bm = createNativeSelectionBookmark( sel ); + // If there's some text other than the sequence in the FC text node and the range + // intersects with that node... + if ( fillingChar.getLength() > fillingCharSeqLength && range && range.intersectsNode( fillingChar.$ ) ) { + var bm = createNativeSelectionBookmark( sel ); + + // Correct start offset anticipating the removal of FC. + if ( sel.anchorNode == fillingChar.$ && sel.anchorOffset > fillingCharSeqLength ) { + bm[ 0 ].offset -= fillingCharSeqLength; + } - // Anticipate the offset change brought by the removed char. - var startAffected = sel.anchorNode == fillingChar.$ && sel.anchorOffset > 0, - endAffected = sel.focusNode == fillingChar.$ && sel.focusOffset > 0; - startAffected && bm[ 0 ].offset--; - endAffected && bm[ 1 ].offset--; + // Correct end offset anticipating the removal of FC. + if ( sel.focusNode == fillingChar.$ && sel.focusOffset > fillingCharSeqLength ) { + bm[ 1 ].offset -= fillingCharSeqLength; + } } } // We can't simply remove the filling node because the user // will actually enlarge it when typing, so we just remove the // invisible char from it. - fillingChar.setText( replaceFillingChar( fillingChar.getText() ) ); + fillingChar.setText( removeFillingCharSequenceString( fillingChar.getText(), 1 ) ); // Restore the bookmark preserving selection's direction. if ( bm ) { - moveNativeSelectionToBookmark( element.getDocument().$, bm ); + moveNativeSelectionToBookmark( editable.getDocument().$, bm ); } } } - function replaceFillingChar( html ) { - return html.replace( /\u200B( )?/g, function( match ) { - // #10291 if filling char is followed by a space replace it with nbsp. - return match[ 1 ] ? '\xa0' : ''; - } ); + // #13816 + function removeFillingCharSequenceString( str, nbspAware ) { + if ( nbspAware ) { + return str.replace( fillingCharSequenceRegExp, function( m, p ) { + // #10291 if filling char is followed by a space replace it with NBSP. + return p ? '\xa0' : ''; + } ); + } else { + return str.replace( fillingCharSequence, '' ); + } } function createNativeSelectionBookmark( sel ) { @@ -724,7 +733,7 @@ case 8: // BACKSPACE case 45: // INS case 46: // DEl - removeFillingChar( editable ); + removeFillingCharSequenceNode( editable ); } }, null, null, -1 ); @@ -839,65 +848,39 @@ } } ); - CKEDITOR.on( 'instanceReady', function( evt ) { - var editor = evt.editor, - fillingCharBefore, - selectionBookmark; + // On WebKit only, we need a special "filling" char on some situations + // (#1272). Here we set the events that should invalidate that char. + if ( CKEDITOR.env.webkit ) { + CKEDITOR.on( 'instanceReady', function( evt ) { + var editor = evt.editor; - // On WebKit only, we need a special "filling" char on some situations - // (#1272). Here we set the events that should invalidate that char. - if ( CKEDITOR.env.webkit ) { editor.on( 'selectionChange', function() { - checkFillingChar( editor.editable() ); + checkFillingCharSequenceNodeReady( editor.editable() ); }, null, null, -1 ); + editor.on( 'beforeSetMode', function() { - removeFillingChar( editor.editable() ); + removeFillingCharSequenceNode( editor.editable() ); }, null, null, -1 ); - editor.on( 'beforeUndoImage', beforeData ); - editor.on( 'afterUndoImage', afterData ); - editor.on( 'beforeGetData', beforeData, null, null, 0 ); - editor.on( 'getData', afterData ); - } - - function beforeData() { - var editable = editor.editable(); - if ( !editable ) - return; - - var fillingChar = getFillingChar( editable ); - - if ( fillingChar ) { - // If the selection's focus or anchor is located in the filling char's text node, - // we need to restore the selection in afterData, because it will be lost - // when setting text. Selection's direction must be preserved. - // (#7437, #12489, #12491 comment:3) - var sel = editor.document.$.getSelection(); - if ( sel.type != 'None' && ( sel.anchorNode == fillingChar.$ || sel.focusNode == fillingChar.$ ) ) - selectionBookmark = createNativeSelectionBookmark( sel ); - - fillingCharBefore = fillingChar.getText(); - fillingChar.setText( replaceFillingChar( fillingCharBefore ) ); - } - } - - function afterData() { - var editable = editor.editable(); - if ( !editable ) - return; - - var fillingChar = getFillingChar( editable ); - - if ( fillingChar ) { - fillingChar.setText( fillingCharBefore ); - - if ( selectionBookmark ) { - moveNativeSelectionToBookmark( editor.document.$, selectionBookmark ); - selectionBookmark = null; + // Filter Undo snapshot's HTML to get rid of Filling Char Sequence. + // Note: CKEDITOR.dom.range.createBookmark2() normalizes snapshot's + // bookmarks to anticipate the removal of FCSeq from the snapshot's HTML (#13816). + editor.on( 'getSnapshot', function( evt ) { + if ( evt.data ) { + evt.data = removeFillingCharSequenceString( evt.data ); } - } - } - } ); + }, editor, null, 20 ); + + // Filter data to get rid of Filling Char Sequence. Filter on #toDataFormat + // instead of #getData because once removed, FCSeq may leave an empty element, + // which should be pruned by the dataProcessor (#13816). + // Note: Used low priority to filter when dataProcessor works on strings, + // not pseudo–DOM. + editor.on( 'toDataFormat', function( evt ) { + evt.data.dataValue = removeFillingCharSequenceString( evt.data.dataValue ); + }, null, null, 0 ); + } ); + } /** * Check the selection change in editor and potentially fires @@ -1159,6 +1142,25 @@ var styleObjectElements = { img: 1, hr: 1, li: 1, table: 1, tr: 1, td: 1, th: 1, embed: 1, object: 1, ol: 1, ul: 1, a: 1, input: 1, form: 1, select: 1, textarea: 1, button: 1, fieldset: 1, thead: 1, tfoot: 1 }; + // #13816 + var fillingCharSequence = CKEDITOR.tools.repeat( '\u200b', 7 ), + fillingCharSequenceRegExp = new RegExp( fillingCharSequence + '( )?', 'g' ); + + CKEDITOR.tools.extend( CKEDITOR.dom.selection, { + _removeFillingCharSequenceString: removeFillingCharSequenceString, + _createFillingCharSequenceNode: createFillingCharSequenceNode, + + /** + * The sequence used in a WebKit-based browser to create a Filling Character. By default it is + * a string of 7 zero-width space characters (U+200B). + * + * @since 4.5.7 + * @readonly + * @property {String} + */ + FILLING_CHAR_SEQUENCE: fillingCharSequence + } ); + CKEDITOR.dom.selection.prototype = { /** * Gets the native selection object from the browser. @@ -1899,7 +1901,7 @@ if ( range.collapsed && CKEDITOR.env.webkit && rangeRequiresFix( range ) ) { // Append a zero-width space so WebKit will not try to // move the selection by itself (#1272). - var fillingChar = createFillingChar( this.root ); + var fillingChar = createFillingCharSequenceNode( this.root ); range.insertNode( fillingChar ); next = fillingChar.getNext(); @@ -1908,7 +1910,7 @@ // having something before it, it'll not blink. // Let's remove it in this case. if ( next && !fillingChar.getPrevious() && next.type == CKEDITOR.NODE_ELEMENT && next.getName() == 'br' ) { - removeFillingChar( this.root ); + removeFillingCharSequenceNode( this.root ); range.moveToPosition( next, CKEDITOR.POSITION_BEFORE_START ); } else { range.moveToPosition( fillingChar, CKEDITOR.POSITION_AFTER_END );