| | 102 | function createFillingChar( doc ) |
| | 103 | { |
| | 104 | removeFillingChar( doc ); |
| | 105 | |
| | 106 | var fillingChar = doc.createText( '\u200B' ); |
| | 107 | doc.setCustomData( 'cke-fillingChar', fillingChar ); |
| | 108 | |
| | 109 | return fillingChar; |
| | 110 | } |
| | 111 | |
| | 112 | function getFillingChar( doc ) |
| | 113 | { |
| | 114 | return doc && doc.getCustomData( 'cke-fillingChar' ); |
| | 115 | } |
| | 116 | |
| | 117 | // Checks if a filling char has been used, eventualy removing it (#1272). |
| | 118 | function checkFillingChar( doc ) |
| | 119 | { |
| | 120 | var fillingChar = doc && getFillingChar( doc ); |
| | 121 | if ( fillingChar ) |
| | 122 | { |
| | 123 | // Use this flag to avoid removing the filling char right after |
| | 124 | // creating it. |
| | 125 | if ( fillingChar.getCustomData( 'ready' ) ) |
| | 126 | removeFillingChar( doc ); |
| | 127 | else |
| | 128 | fillingChar.setCustomData( 'ready', 1 ); |
| | 129 | } |
| | 130 | } |
| | 131 | |
| | 132 | function removeFillingChar( doc ) |
| | 133 | { |
| | 134 | var fillingChar = doc && doc.removeCustomData( 'cke-fillingChar' ); |
| | 135 | if ( fillingChar ) |
| | 136 | { |
| | 137 | // We can't simply remove the filling node because the user |
| | 138 | // will actually enlarge it when typing, so we just remove the |
| | 139 | // invisible char from it. |
| | 140 | fillingChar.setText( fillingChar.getText().replace( /\u200B/g, '' ) ); |
| | 141 | fillingChar = 0; |
| | 142 | } |
| | 143 | } |
| | 144 | |
| | 149 | // On WebKit only, we need a special "filling" char on some situations |
| | 150 | // (#1272). Here we set the events that should invalidate that char. |
| | 151 | if ( CKEDITOR.env.webkit ) |
| | 152 | { |
| | 153 | editor.on( 'selectionChange', function() { checkFillingChar( editor.document ); } ); |
| | 154 | editor.on( 'beforeSetMode', function() { removeFillingChar( editor.document ); } ); |
| | 155 | editor.on( 'key', function( e ) |
| | 156 | { |
| | 157 | // Remove the filling char before some keys get |
| | 158 | // executed, so they'll not get blocked by it. |
| | 159 | switch ( e.data.keyCode ) |
| | 160 | { |
| | 161 | case 37 : // LEFT-ARROW |
| | 162 | case 39 : // RIGHT-ARROW |
| | 163 | case 8 : // BACKSPACE |
| | 164 | removeFillingChar( editor.document ); |
| | 165 | } |
| | 166 | }); |
| | 167 | |
| | 168 | var fillingCharBefore; |
| | 169 | function beforeData() |
| | 170 | { |
| | 171 | var fillingChar = getFillingChar( editor.document ); |
| | 172 | fillingCharBefore = fillingChar && fillingChar.getText(); |
| | 173 | fillingCharBefore && fillingChar.setText( fillingCharBefore.replace( /\u200B/g, '' ) ); |
| | 174 | } |
| | 175 | function afterData() |
| | 176 | { |
| | 177 | var fillingChar = getFillingChar( editor.document ); |
| | 178 | fillingChar && fillingChar.setText( fillingCharBefore ); |
| | 179 | } |
| | 180 | editor.on( 'beforeUndoImage', beforeData ); |
| | 181 | editor.on( 'afterUndoImage', afterData ); |
| | 182 | editor.on( 'beforeGetData', beforeData, null, null, 0 ); |
| | 183 | editor.on( 'getData', afterData ); |
| | 184 | } |
| | 185 | |
| 1191 | | nativeRange.setStart( startContainer.$, range.startOffset ); |
| | 1275 | if ( range.collapsed && CKEDITOR.env.webkit ) |
| | 1276 | { |
| | 1277 | // Append a zero-width space so WebKit will not try to |
| | 1278 | // move the selection by itself (#1272). |
| | 1279 | fillingChar = createFillingChar( this.document ); |
| | 1280 | range.insertNode( fillingChar ) ; |
| | 1281 | |
| | 1282 | var next = fillingChar.getNext(); |
| | 1283 | |
| | 1284 | // If the filling char is followed by a <br>, whithout |
| | 1285 | // having something before it, it'll not blink. |
| | 1286 | // Let's remove it in this case. |
| | 1287 | if ( next && !fillingChar.getPrevious() && next.type == CKEDITOR.NODE_ELEMENT && next.getName() == 'br' ) |
| | 1288 | { |
| | 1289 | removeFillingChar( this.document ); |
| | 1290 | range.moveToPosition( next, CKEDITOR.POSITION_BEFORE_START ); |
| | 1291 | } |
| | 1292 | else |
| | 1293 | range.moveToPosition( fillingChar, CKEDITOR.POSITION_AFTER_END ); |
| | 1294 | } |
| | 1295 | |
| | 1296 | nativeRange.setStart( range.startContainer.$, range.startOffset ); |
| 1379 | | var startContainer = this.startContainer; |
| 1380 | | |
| 1381 | | // If we have a collapsed range, inside an empty element, we must add |
| 1382 | | // something to it, otherwise the caret will not be visible. |
| 1383 | | if ( this.collapsed && startContainer.type == CKEDITOR.NODE_ELEMENT && !startContainer.getChildCount() ) |
| 1384 | | startContainer.append( new CKEDITOR.dom.text( '' ) ); |
| 1385 | | |
| 1386 | | var nativeRange = this.document.$.createRange(); |
| 1387 | | nativeRange.setStart( startContainer.$, this.startOffset ); |
| 1388 | | |
| 1389 | | try |
| 1390 | | { |
| 1391 | | nativeRange.setEnd( this.endContainer.$, this.endOffset ); |
| 1392 | | } |
| 1393 | | catch ( e ) |
| 1394 | | { |
| 1395 | | // There is a bug in Firefox implementation (it would be too easy |
| 1396 | | // otherwise). The new start can't be after the end (W3C says it can). |
| 1397 | | // So, let's create a new range and collapse it to the desired point. |
| 1398 | | if ( e.toString().indexOf( 'NS_ERROR_ILLEGAL_VALUE' ) >= 0 ) |
| 1399 | | { |
| 1400 | | this.collapse( true ); |
| 1401 | | nativeRange.setEnd( this.endContainer.$, this.endOffset ); |
| 1402 | | } |
| 1403 | | else |
| 1404 | | throw( e ); |
| 1405 | | } |
| 1406 | | |
| 1407 | | var selection = this.document.getSelection().getNative(); |
| 1408 | | // getSelection() returns null in case when iframe is "display:none" in FF. (#6577) |
| 1409 | | if ( selection ) |
| 1410 | | { |
| 1411 | | selection.removeAllRanges(); |
| 1412 | | selection.addRange( nativeRange ); |
| 1413 | | } |
| | 1484 | this.document.getSelection().selectRanges( [ this ] ); |