Changeset 6461


Ignore:
Timestamp:
02/18/11 20:10:24 (3 years ago)
Author:
fredck
Message:

#1272 : It's now possible to apply styles to collapsed selections on Safari and Chrome (WebKit).

Location:
CKEditor/trunk
Files:
7 edited

Legend:

Unmodified
Added
Removed
  • CKEditor/trunk/CHANGES.html

    r6459 r6461  
    1 <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> 
     1<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> 
    22<!-- 
    33Copyright (c) 2003-2011, CKSource - Frederico Knabben. All rights reserved. 
     
    4545                        Fixed issues:</p> 
    4646        <ul> 
     47                <li><a href="http://dev.ckeditor.com/ticket/1272">#1272</a> : It's now possible to apply styles to collapsed selections on Safari and Chrome (WebKit).</li> 
    4748                <li><a href="http://dev.ckeditor.com/ticket/7054">#7054</a> : Special characters' tooltips are now lowercased, making them more readable.</li> 
    4849                <li><a href="http://dev.ckeditor.com/ticket/7102">#7102</a> : "Replacing Div" sample didn't work when double clicking inside of formatted text.</li> 
  • CKEditor/trunk/_source/core/dom/node.js

    r6419 r6461  
    205205                        { 
    206206                                var parentNode = node.parentNode; 
    207                                 var currentIndex = -1; 
    208207 
    209208                                if ( parentNode ) 
    210209                                { 
    211                                         for ( var i = 0 ; i < parentNode.childNodes.length ; i++ ) 
    212                                         { 
    213                                                 var candidate = parentNode.childNodes[i]; 
    214  
    215                                                 if ( normalized && 
    216                                                                 candidate.nodeType == 3 && 
    217                                                                 candidate.previousSibling && 
    218                                                                 candidate.previousSibling.nodeType == 3 ) 
    219                                                 { 
    220                                                         continue; 
    221                                                 } 
    222  
    223                                                 currentIndex++; 
    224  
    225                                                 if ( candidate == node ) 
    226                                                         break; 
    227                                         } 
    228  
    229                                         address.unshift( currentIndex ); 
     210                                        // Get the node index. For performance, call getIndex 
     211                                        // directly, instead of creating a new node object. 
     212                                        address.unshift( this.getIndex.call( { $ : node }, normalized ) ); 
    230213                                } 
    231214 
     
    248231                }, 
    249232 
    250                 getIndex : function() 
    251                 { 
    252                         var $ = this.$; 
    253  
    254                         var currentNode = $.parentNode && $.parentNode.firstChild; 
    255                         var currentIndex = -1; 
    256  
    257                         while ( currentNode ) 
    258                         { 
    259                                 currentIndex++; 
    260  
    261                                 if ( currentNode == $ ) 
    262                                         return currentIndex; 
    263  
    264                                 currentNode = currentNode.nextSibling; 
    265                         } 
    266  
    267                         return -1; 
     233                getIndex : function( normalized ) 
     234                { 
     235                        // Attention: getAddress depends on this.$ 
     236 
     237                        var current = this.$, 
     238                                index = 0; 
     239 
     240                        while ( ( current = current.previousSibling ) ) 
     241                        { 
     242                                // When normalizing, do not count it if this is an 
     243                                // empty text node or if it's a text node following another one. 
     244                                if ( normalized && current.nodeType == 3 && 
     245                                         ( !current.nodeValue.length || 
     246                                           ( current.previousSibling && current.previousSibling.nodeType == 3 ) ) ) 
     247                                { 
     248                                        continue; 
     249                                } 
     250 
     251                                index++; 
     252                        } 
     253 
     254                        return index; 
    268255                }, 
    269256 
  • CKEditor/trunk/_source/core/dom/range.js

    r6418 r6461  
    590590                                                startOffset = 0; 
    591591                                        } 
     592 
     593                                        // Get the normalized offset. 
     594                                        if ( child && child.type == CKEDITOR.NODE_ELEMENT ) 
     595                                                startOffset = child.getIndex( 1 ); 
    592596                                } 
    593597 
     
    618622                                                        endOffset = 0; 
    619623                                                } 
     624 
     625                                                // Get the normalized offset. 
     626                                                if ( child && child.type == CKEDITOR.NODE_ELEMENT ) 
     627                                                        endOffset = child.getIndex( 1 ); 
    620628                                        } 
    621629 
  • CKEditor/trunk/_source/core/dom/text.js

    r6348 r6461  
    6868                }, 
    6969 
     70                setText : function( text ) 
     71                { 
     72                        this.$.nodeValue = text; 
     73                }, 
     74 
    7075                /** 
    7176                 * Breaks this text node into two nodes at the specified offset, 
  • CKEditor/trunk/_source/plugins/editingblock/plugin.js

    r6435 r6461  
    154154        CKEDITOR.editor.prototype.setMode = function( mode ) 
    155155        { 
     156                this.fire( 'beforeSetMode', { newMode : mode } ); 
     157 
    156158                var data, 
    157159                        holderElement = this.getThemeSpace( 'contents' ), 
     
    243245 
    244246/** 
    245  * Fired before changing the editing mode 
     247 * Fired before changing the editing mode. 
    246248 * @name CKEDITOR.editor#beforeModeUnload 
    247249 * @event 
    248250 */ 
     251 
     252 /** 
     253 * Fired before the editor mode is set. 
     254 * @name CKEDITOR.editor#beforeSetMode 
     255 * @event 
     256 * @since 3.5.3 
     257 * @param {String} newMode The name of the mode which is about to be set. 
     258 */ 
  • CKEditor/trunk/_source/plugins/selection/plugin.js

    r6459 r6461  
    100100        }; 
    101101 
     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 
    102145        CKEDITOR.plugins.add( 'selection', 
    103146        { 
    104147                init : function( editor ) 
    105148                { 
     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 
    106186                        editor.on( 'contentDom', function() 
    107187                                { 
     
    11461226                                var sel = this.getNative(); 
    11471227 
     1228                                // getNative() returns null if iframe is "display:none" in FF. (#6577) 
     1229                                if ( !sel ) 
     1230                                        return; 
     1231 
    11481232                                if ( ranges.length ) 
     1233                                { 
    11491234                                        sel.removeAllRanges(); 
     1235                                        // Remove any existing filling char first. 
     1236                                        CKEDITOR.env.webkit && removeFillingChar( this.document ); 
     1237                                } 
    11501238 
    11511239                                for ( var i = 0 ; i < ranges.length ; i++ ) 
     
    11971285                                        } 
    11981286 
    1199                                         nativeRange.setStart( startContainer.$, range.startOffset ); 
    1200                                         nativeRange.setEnd( range.endContainer.$, range.endOffset ); 
     1287                                        if ( range.collapsed && CKEDITOR.env.webkit ) 
     1288                                        { 
     1289                                                // Append a zero-width space so WebKit will not try to 
     1290                                                // move the selection by itself (#1272). 
     1291                                                var fillingChar = createFillingChar( this.document ); 
     1292                                                range.insertNode( fillingChar ) ; 
     1293 
     1294                                                var next = fillingChar.getNext(); 
     1295 
     1296                                                // If the filling char is followed by a <br>, whithout 
     1297                                                // having something before it, it'll not blink. 
     1298                                                // Let's remove it in this case. 
     1299                                                if ( next && !fillingChar.getPrevious() && next.type == CKEDITOR.NODE_ELEMENT && next.getName() == 'br' ) 
     1300                                                { 
     1301                                                        removeFillingChar( this.document ); 
     1302                                                        range.moveToPosition( next, CKEDITOR.POSITION_BEFORE_START ); 
     1303                                                } 
     1304                                                else 
     1305                                                        range.moveToPosition( fillingChar, CKEDITOR.POSITION_AFTER_END ); 
     1306                                        } 
     1307 
     1308                                        nativeRange.setStart( range.startContainer.$, range.startOffset ); 
     1309 
     1310                                        try 
     1311                                        { 
     1312                                                nativeRange.setEnd( range.endContainer.$, range.endOffset ); 
     1313                                        } 
     1314                                        catch ( e ) 
     1315                                        { 
     1316                                                // There is a bug in Firefox implementation (it would be too easy 
     1317                                                // otherwise). The new start can't be after the end (W3C says it can). 
     1318                                                // So, let's create a new range and collapse it to the desired point. 
     1319                                                if ( e.toString().indexOf( 'NS_ERROR_ILLEGAL_VALUE' ) >= 0 ) 
     1320                                                { 
     1321                                                        range.collapse( 1 ); 
     1322                                                        nativeRange.setEnd( range.endContainer.$, range.endOffset ); 
     1323                                                } 
     1324                                                else 
     1325                                                        throw e; 
     1326                                        } 
    12011327 
    12021328                                        // Select the range. 
     
    13851511                        function() 
    13861512                        { 
    1387                                 var startContainer = this.startContainer; 
    1388  
    1389                                 // If we have a collapsed range, inside an empty element, we must add 
    1390                                 // something to it, otherwise the caret will not be visible. 
    1391                                 if ( this.collapsed && startContainer.type == CKEDITOR.NODE_ELEMENT && !startContainer.getChildCount() ) 
    1392                                         startContainer.append( new CKEDITOR.dom.text( '' ) ); 
    1393  
    1394                                 var nativeRange = this.document.$.createRange(); 
    1395                                 nativeRange.setStart( startContainer.$, this.startOffset ); 
    1396  
    1397                                 try 
    1398                                 { 
    1399                                         nativeRange.setEnd( this.endContainer.$, this.endOffset ); 
    1400                                 } 
    1401                                 catch ( e ) 
    1402                                 { 
    1403                                         // There is a bug in Firefox implementation (it would be too easy 
    1404                                         // otherwise). The new start can't be after the end (W3C says it can). 
    1405                                         // So, let's create a new range and collapse it to the desired point. 
    1406                                         if ( e.toString().indexOf( 'NS_ERROR_ILLEGAL_VALUE' ) >= 0 ) 
    1407                                         { 
    1408                                                 this.collapse( true ); 
    1409                                                 nativeRange.setEnd( this.endContainer.$, this.endOffset ); 
    1410                                         } 
    1411                                         else 
    1412                                                 throw( e ); 
    1413                                 } 
    1414  
    1415                                 var selection = this.document.getSelection().getNative(); 
    1416                                 // getSelection() returns null in case when iframe is "display:none" in FF. (#6577) 
    1417                                 if ( selection ) 
    1418                                 { 
    1419                                         selection.removeAllRanges(); 
    1420                                         selection.addRange( nativeRange ); 
    1421                                 } 
     1513                                this.document.getSelection().selectRanges( [ this ] ); 
    14221514                        }; 
    14231515} )(); 
  • CKEditor/trunk/_source/plugins/undo/plugin.js

    r6348 r6461  
    149149        { 
    150150                this.editor = editor; 
     151 
     152                editor.fire( 'beforeUndoImage' ); 
     153 
    151154                var contents = editor.getSnapshot(), 
    152155                        selection       = contents && editor.getSelection(); 
     
    157160                this.contents   = contents; 
    158161                this.bookmarks  = selection && selection.createBookmarks2( true ); 
     162 
     163                editor.fire( 'afterUndoImage' ); 
    159164        }; 
    160165 
     
    553558 * @event 
    554559 */ 
     560 
     561/** 
     562 * Fired before an undo image is to be taken. An undo image represents the 
     563 * editor state at some point. It's saved into an undo store, so the editor is 
     564 * able to recover the editor state on undo and redo operations. 
     565 * @name CKEDITOR.editor#beforeUndoImage 
     566 * @since 3.5.3 
     567 * @see CKEDITOR.editor#afterUndoImage 
     568 * @event 
     569 */ 
     570 
     571/** 
     572 * Fired after an undo image is taken. An undo image represents the 
     573 * editor state at some point. It's saved into an undo store, so the editor is 
     574 * able to recover the editor state on undo and redo operations. 
     575 * @name CKEDITOR.editor#afterUndoImage 
     576 * @since 3.5.3 
     577 * @see CKEDITOR.editor#beforeUndoImage 
     578 * @event 
     579 */ 
Note: See TracChangeset for help on using the changeset viewer.
© 2003 – 2012 CKSource – Frederico Knabben. All rights reserved. | Terms of use | Privacy policy