Ticket #2768: 2768_3.patch

File 2768_3.patch, 50.0 KB (added by garry.yao, 6 years ago)
  • _source/core/tools.js

     
    310310                                return i; 
    311311                } 
    312312                return -1; 
     313        }, 
     314        /** 
     315         * Binds a function with an object as its this reference. 
     316         *  
     317         * @param {Function} 
     318         *            func The function to be bounded. 
     319         * @param {Object} 
     320         *            obj This this reference to bind to the function. 
     321         * @returns {Function} The bound function. 
     322         */ 
     323        bind : function ( func, obj ) 
     324        { 
     325                return function () 
     326                { 
     327                        return func.apply( obj , arguments ); 
     328                } 
    313329        } 
    314330}; 
    315331 
  • _source/core/editor.js

     
    379379                 */ 
    380380                execCommand : function( commandName, data ) 
    381381                { 
    382                         var command = this.getCommand( commandName ); 
     382                        var command = this.getCommand( commandName ), 
     383                        execResult,  
     384                        commandEvent = { 
     385                                name :commandName, 
     386                                command :command 
     387                        }; 
     388                         
    383389                        if ( command && command.state != CKEDITOR.TRISTATE_DISABLED ) 
    384                                 return command.exec( this, data ); 
     390                        { 
     391                                exeResult = command.exec( this , data ); 
     392                                this.fire( 'afterCommandExec' , commandEvent ); 
     393                                return exeResult; 
     394                        } 
    385395 
    386396                        // throw 'Unknown command name "' + commandName + '"'; 
    387397                        return false; 
  • _source/core/dom/documentFragment.js

     
    77{ 
    88        this.$ = CKEDITOR.env.ie ? ownerDocument.$.createElement( 'div' ) : ownerDocument.$.createDocumentFragment(); 
    99}; 
    10  
     10/* 
     11 * A DocumentFragment behaves like a conventional {@link CKEDITOR.dom.Node} with all of the same 
     12 * methods, except that when a DocumentFragment is inserted into a Document (or 
     13 * indeed any other Node that may take children) the children of the 
     14 * DocumentFragment and not the DocumentFragment itself are inserted into the 
     15 * Node. 
     16 */ 
     17CKEDITOR.dom.documentFragment.prototype = 
     18CKEDITOR.tools.prototypedCopy( CKEDITOR.dom.node.prototype ); 
    1119(function() 
    1220{ 
    1321        var elementPrototype = CKEDITOR.dom.element.prototype; 
     
    1220{ 
    1321        var elementPrototype = CKEDITOR.dom.element.prototype; 
    1422 
    15         CKEDITOR.dom.documentFragment.prototype = 
    16         { 
     23        CKEDITOR.tools.extend( CKEDITOR.dom.documentFragment.prototype , { 
    1724                type : CKEDITOR.NODE_DOCUMENT_FRAGMENT, 
    1825 
    1926                append : elementPrototype.append, 
     
    4047                        else 
    4148                                $parent.insertBefore( $, $node.nextSibling ); 
    4249                } 
    43         }; 
     50        }, true ); 
     51         
    4452})(); 
  • _source/core/dom/range.js

     
    457457                        }; 
    458458                }, 
    459459 
    460                 moveToBookmark : function( bookmark ) 
     460                moveToBookmark : function( bookmark, isPreserve ) 
    461461                { 
     462                        var st = bookmark.startNode , ed = bookmark.endNode; 
    462463                        // Set the range start at the bookmark start node position. 
    463                         this.setStartBefore( bookmark.startNode ); 
     464                        this.setStartBefore( typeof st === 'string' ? ( st = this.document 
     465                                        .getById( st ) ) : st ); 
    464466 
    465467                        // Remove it, because it may interfere in the setEndBefore call. 
    466                         bookmark.startNode.remove(); 
     468                        if(!isPreserve) 
     469                                st.remove(); 
    467470 
    468471                        // Set the range end at the bookmark end node position, or simply 
    469472                        // collapse it if it is not available. 
    470                         var endNode = bookmark.endNode; 
    471                         if ( endNode ) 
     473                        if ( ed ) 
    472474                        { 
    473                                 this.setEndBefore( endNode ); 
    474                                 endNode.remove(); 
     475                                this.setEndBefore( typeof ed === 'string' ?  
     476                                                ( ed = this.document.getById( ed ) ) : ed ); 
     477                                if(!isPreserve) 
     478                                        ed.remove(); 
    475479                        } 
    476480                        else 
    477481                                this.collapse( true ); 
     
    10021006 
    10031007                        var startContainer = this.startContainer; 
    10041008                        var startOffset = this.startOffset; 
     1009                        var isDomFrag = false , firstChild , childCounts = 0; 
     1010 
     1011                        // Inserted node might be {@link CKEDITOR.dom.documentFragment} 
     1012                        if ( node.type === CKEDITOR.NODE_DOCUMENT_FRAGMENT ) 
     1013                        { 
     1014                                isDomFrag = true; 
     1015                                firstChild = node.getChild( 0 ); 
     1016                                childCounts = node.getChildCount(); 
     1017                        } 
    10051018 
    10061019                        var nextNode = startContainer.getChild( startOffset ); 
    10071020 
     
    10111024                                startContainer.append( node ); 
    10121025 
    10131026                        // Check if we need to update the end boundary. 
    1014                         if ( node.getParent().equals( this.endContainer ) ) 
     1027                        if ( isDomFrag && firstChild.getParent().equals( this.endContainer ) ) 
     1028                        { 
     1029                                this.endOffset += childCounts; 
     1030                                // Expand the range to embrace the new nodes. 
     1031                                this.setStartBefore( firstChild ); 
     1032                        } 
     1033                        else if ( node.getParent().equals( this.endContainer ) ) 
    10151034                                this.endOffset++; 
    10161035 
    10171036                        // Expand the range to embrace the new node. 
  • _source/plugins/basicstyles/plugin.js

     
    1313                // duplications, let's use this tool function. 
    1414                var addButtonCommand = function( buttonName, buttonLabel, commandName, styleDefiniton ) 
    1515                { 
    16                         var style = new CKEDITOR.style( styleDefiniton ); 
     16                        var style = new CKEDITOR.style( editor, styleDefiniton ); 
    1717 
    1818                        editor.attachStyleStateChange( style, function( state ) 
    1919                                { 
  • _source/plugins/styles/plugin.js

     
    55 
    66CKEDITOR.plugins.add( 'styles', 
    77{ 
    8         requires : [ 'selection' ] 
     8        requires : [ 'selection' ], 
     9        beforeInit : function( editor, pluginPath ) 
     10        {} 
    911}); 
     12(function(){ 
     13         
     14                var blockElements   = { address:1,div:1,h1:1,h2:1,h3:1,h4:1,h5:1,h6:1,p:1,pre:1 }; 
     15                var objectElements  = { a:1,embed:1,hr:1,img:1,li:1,object:1,ol:1,table:1,td:1,tr:1,ul:1 }; 
    1016 
    11 /** 
    12  * Registers a function to be called whenever a style changes its state in the 
    13  * editing area. The current state is passed to the function. The possible 
    14  * states are {@link CKEDITOR.TRISTATE_ON} and {@link CKEDITOR.TRISTATE_OFF}. 
    15  * @param {CKEDITOR.style} The style to be watched. 
    16  * @param {Function} The function to be called when the style state changes. 
    17  * @example 
    18  * // Create a style object for the <b> element. 
    19  * var style = new CKEDITOR.style( { element : 'b' } ); 
    20  * var editor = CKEDITOR.instances.editor1; 
    21  * editor.attachStyleStateChange( style, function( state ) 
    22  *     { 
    23  *         if ( state == CKEDITOR.TRISTATE_ON ) 
    24  *             alert( 'The current state for the B element is ON' ); 
    25  *         else 
    26  *             alert( 'The current state for the B element is OFF' ); 
    27  *     }); 
    28  */ 
    29 CKEDITOR.editor.prototype.attachStyleStateChange = function( style, callback ) 
    30 { 
    31         // Try to get the list of attached callbacks. 
    32         var styleStateChangeCallbacks = this._.styleStateChangeCallbacks; 
    33  
    34         // If it doesn't exist, it means this is the first call. So, let's create 
    35         // all the structure to manage the style checks and the callback calls. 
    36         if ( !styleStateChangeCallbacks ) 
    37         { 
    38                 // Create the callbacks array. 
    39                 styleStateChangeCallbacks = this._.styleStateChangeCallbacks = []; 
    40  
    41                 // Attach to the selectionChange event, so we can check the styles at 
    42                 // that point. 
    43                 this.on( 'selectionChange', function( ev ) 
     17                CKEDITOR.style = function( editor, styleDefinition ) 
     18                { 
     19                        var element = this.element = ( styleDefinition.element || '*' ).toLowerCase(); 
     20         
     21                        this.type = 
     22                                ( element == '#' || blockElements[ element ] ) ? 
     23                                        CKEDITOR.STYLE_BLOCK 
     24                                : objectElements[ element ] ? 
     25                                        CKEDITOR.STYLE_OBJECT 
     26                                : 
     27                                        CKEDITOR.STYLE_INLINE; 
     28         
     29                        this._ = 
    4430                        { 
    45                                 // Loop throw all registered callbacks. 
    46                                 for ( var i = 0 ; i < styleStateChangeCallbacks.length ; i++ ) 
     31                                editor: editor, 
     32                                definition : styleDefinition 
     33                        }; 
     34                }; 
     35         
     36                CKEDITOR.style.prototype = 
     37                { 
     38                        /** 
     39                         * @param {CKEDITOR.dom.document} document The target document the style applies to. 
     40                         * @param {Boolean} isReverse Whether it's a reverse apply (remove). 
     41                         */ 
     42                        apply : function( isReverse ) 
     43                        { 
     44                                var doc = this._.editor.document; 
     45                                // Get all ranges from the selection. 
     46                                var selection = doc.getSelection(); 
     47                                var ranges = selection.getRanges(); 
     48                                var styleFuncName = isReverse ? 'removeFromRange' : 'applyToRange'; 
     49                                // Apply the style to the ranges. 
     50                                for (var i = 0; i < ranges.length; i++)  
    4751                                { 
    48                                         var callback = styleStateChangeCallbacks[ i ]; 
    49  
    50                                         // Check the current state for the style defined for that 
    51                                         // callback. 
    52                                         var currentState = callback.style.checkActive( ev.data.path ) ? CKEDITOR.TRISTATE_ON : CKEDITOR.TRISTATE_OFF; 
    53  
    54                                         // If the state changed since the last check. 
    55                                         if ( callback.state !== currentState ) 
     52                                        this[styleFuncName].call(this, ranges[i]); 
     53                                } 
     54                                // Select the ranges again. 
     55                                selection.selectRanges(ranges); 
     56                        }, 
     57                                         
     58                        reverse : function() { 
     59                                this.apply( true ); 
     60                        }, 
     61         
     62                        applyToRange : function( range ) 
     63                        { 
     64                                return ( this.applyToRange = 
     65                                                        this.type == CKEDITOR.STYLE_INLINE ? 
     66                                                                applyInlineStyle 
     67                                                        : this.type == CKEDITOR.STYLE_BLOCK ? 
     68                                                                applyBlockStyle 
     69                                                        : null ).call( this, range ); 
     70                        }, 
     71                                         
     72                        removeFromRange : function (range) { 
     73                         
     74                                        return (this.type == CKEDITOR.STYLE_INLINE ? removeInlineStyle : 
     75                                        this.type == CKEDITOR.STYLE_BLOCK ? removeBlockStyle :  
     76                                        null).call(this, range); 
     77                        }, 
     78                                         
     79                        /** 
     80                         * Remove all related attributes, styles on the element, or even intrinsic semantics(the element itself) which related with this style 
     81                         * @param {CKEditor.dom.element} element The target element 
     82                         */ 
     83                        removeFromElement: function(element) 
     84                        { 
     85                                if(element.$.nodeType !== 1||element.getName() !== this.element) 
     86                                        return; 
     87                                var def = this._.definition; 
     88                                var attribs = def.attributes; 
     89                                var styles = def.styles; 
     90                                 
     91                                for (var attName in attribs)  
     92                                { 
     93                                        // The 'class' element value must match (#1318). 
     94                                        if (attName == 'class' && element.getAttribute('class') != attribs[attName])  
     95                                                continue; 
     96                                         
     97                                        element.removeAttribute(attName); 
     98                                } 
     99                                 
     100                                for (var styleName in styles)  
     101                                { 
     102                                        element.removeStyle(styleName); 
     103                                } 
     104                                 
     105                                removeNoAttribsElement(element); 
     106                        }, 
     107                                         
     108                        /** 
     109                         * Remove related style from all the childs of this element 
     110                         * @param {CKEditor.dom.element} element The target element 
     111                         * @see CKEditor.style::removeFromElement 
     112                         */ 
     113                        removeFromAllChilds : function( element ) 
     114                        { 
     115                                        var innerElements = element.getElementsByTag( this.element ); 
     116                                        for ( var i = innerElements.count() ; --i >= 0 ; ) 
    56117                                        { 
    57                                                 // Call the callback function, passing the current 
    58                                                 // state to it. 
    59                                                 callback.fn.call( this, currentState ); 
    60  
    61                                                 // Save the current state, so it can be compared next 
    62                                                 // time. 
    63                                                 callback.state !== currentState; 
     118                                                var innerElement = innerElements.getItem( i ); 
     119                                                this.removeFromElement( innerElements ); 
    64120                                        } 
     121                        }, 
     122         
     123                        /** 
     124                         * Get the style state inside an element path. Returns "true" if the 
     125                         * element is active in the path. 
     126                         */ 
     127                        checkActive : function( elementPath ) 
     128                        { 
     129                                switch ( this.type ) 
     130                                { 
     131                                        case CKEDITOR.STYLE_BLOCK : 
     132                                         
     133                                                return this.checkElementRemovable( elementPath.block || elementPath.blockLimit, true ); 
     134         
     135                                        case CKEDITOR.STYLE_INLINE : 
     136         
     137                                                var elements = elementPath.elements; 
     138         
     139                                                for ( var i = 0, element ; i < elements.length ; i++ ) 
     140                                                { 
     141                                                        element = elements[i]; 
     142         
     143                                                        if ( element == elementPath.block || element == elementPath.blockLimit ) 
     144                                                                continue; 
     145         
     146                                                        if ( this.checkElementRemovable( element, true ) ) 
     147                                                                return true; 
     148                                                } 
    65149                                } 
    66                         }); 
    67         } 
    68  
    69         // Save the callback info, so it can be checked on the next occurence of 
    70         // selectionChange. 
    71         styleStateChangeCallbacks.push( { style : style, fn : callback } ); 
    72 }; 
    73  
    74 CKEDITOR.STYLE_BLOCK = 1; 
    75 CKEDITOR.STYLE_INLINE = 2; 
    76 CKEDITOR.STYLE_OBJECT = 3; 
    77  
    78 (function() 
    79 { 
    80         var blockElements       = { address:1,div:1,h1:1,h2:1,h3:1,h4:1,h5:1,h6:1,p:1,pre:1 }; 
    81         var objectElements      = { a:1,embed:1,hr:1,img:1,li:1,object:1,ol:1,table:1,td:1,tr:1,ul:1 }; 
    82  
    83         CKEDITOR.style = function( styleDefinition ) 
    84         { 
    85                 var element = this.element = ( styleDefinition.element || '*' ).toLowerCase(); 
    86  
    87                 this.type = 
    88                         ( element == '#' || blockElements[ element ] ) ? 
    89                                 CKEDITOR.STYLE_BLOCK 
    90                         : objectElements[ element ] ? 
    91                                 CKEDITOR.STYLE_OBJECT 
    92                         : 
    93                                 CKEDITOR.STYLE_INLINE; 
    94  
    95                 this._ = 
    96                 { 
    97                         definition : styleDefinition 
    98                 }; 
    99         }; 
    100  
    101         CKEDITOR.style.prototype = 
    102         { 
    103                 apply : function( document ) 
    104                 { 
    105                         // Get all ranges from the selection. 
    106                         var selection = document.getSelection(); 
    107                         var ranges = selection.getRanges(); 
    108  
    109                         // Apply the style to the ranges. 
    110                         for ( var i = 0 ; i < ranges.length ; i++ ) 
    111                                 this.applyToRange( ranges[ i ] ); 
    112  
    113                         // Select the ranges again. 
    114                         selection.selectRanges( ranges ); 
    115                 }, 
    116  
    117                 applyToRange : function( range ) 
    118                 { 
    119                         return ( this.applyToRange = 
    120                                                 this.type == CKEDITOR.STYLE_INLINE ? 
    121                                                         applyInlineStyle 
    122                                                 : this.type == CKEDITOR.STYLE_BLOCK ? 
    123                                                         applyBlockStyle 
    124                                                 : null ).call( this, range ); 
    125                 }, 
    126  
    127                 /** 
    128                  * Get the style state inside an element path. Returns "true" if the 
    129                  * element is active in the path. 
    130                  */ 
    131                 checkActive : function( elementPath ) 
    132                 { 
    133                         switch ( this.type ) 
     150                                return false; 
     151                        }, 
     152         
     153                        /** 
     154                         * @param {CKEDITOR.dom.element} element The target element to check. 
     155                         * @param {Boolean} fullMatch Whether require the attribute and styles are fully matched. 
     156                         */ 
     157                        checkElementRemovable : function( element, fullMatch ) 
    134158                        { 
    135                                 case CKEDITOR.STYLE_BLOCK : 
    136                                         return this.checkElementRemovable( elementPath.block || elementPath.blockLimit, true ); 
    137  
    138                                 case CKEDITOR.STYLE_INLINE : 
    139  
    140                                         var elements = elementPath.elements; 
    141  
    142                                         for ( var i = 0, element ; i < elements.length ; i++ ) 
     159                                if ( !element || element.getName() != this.element ) 
     160                                        return false ; 
     161         
     162                                var def = this._.definition; 
     163                                var attribs = def.attributes; 
     164                                var styles = def.styles; 
     165         
     166                                // If no attributes are defined in the element. 
     167                                if ( !fullMatch && !element.hasAttributes() ) 
     168                                        return true ; 
     169         
     170                                for ( var attName in attribs ) 
     171                                { 
     172                                        if ( element.getAttribute( attName ) == attribs[ attName ] ) 
    143173                                        { 
    144                                                 element = elements[i]; 
    145  
    146                                                 if ( element == elementPath.block || element == elementPath.blockLimit ) 
    147                                                         continue; 
    148  
    149                                                 if ( this.checkElementRemovable( element, true ) ) 
     174                                                if ( !fullMatch ) 
    150175                                                        return true; 
    151176                                        } 
    152                         } 
    153                         return false; 
    154                 }, 
    155  
    156                 // Checks if an element, or any of its attributes, is removable by the 
    157                 // current style definition. 
    158                 checkElementRemovable : function( element, fullMatch ) 
    159                 { 
    160                         if ( !element || element.getName() != this.element ) 
    161                                 return false ; 
    162  
    163                         var def = this._.definition; 
    164                         var attribs = def.attributes; 
    165                         var styles = def.styles; 
    166  
    167                         // If no attributes are defined in the element. 
    168                         if ( !fullMatch && !element.hasAttributes() ) 
    169                                 return true ; 
    170  
    171                         for ( var attName in attribs ) 
    172                         { 
    173                                 if ( element.getAttribute( attName ) == attribs[ attName ] ) 
    174                                 { 
    175                                         if ( !fullMatch ) 
    176                                                 return true; 
     177                                        else if ( fullMatch ) 
     178                                                return false; 
    177179                                } 
    178                                 else if ( fullMatch ) 
    179                                         return false; 
     180         
     181                                return true; 
     182                        }, 
     183         
     184                        /** 
     185                         * Sets the value of a variable attribute or style, to be used when 
     186                         * appliying the style. This function must be called before using any 
     187                         * other function in this object. 
     188                         */ 
     189                        setVariable : function( name, value ) 
     190                        { 
     191                                var variables = this._.variables || ( this._variables = {} ); 
     192                                variables[ name ] = value; 
    180193                        } 
    181  
    182                         return true; 
    183                 }, 
    184  
    185                 /** 
    186                  * Sets the value of a variable attribute or style, to be used when 
    187                  * appliying the style. This function must be called before using any 
    188                  * other function in this object. 
    189                  */ 
    190                 setVariable : function( name, value ) 
    191                 { 
    192                         var variables = this._.variables || ( this._variables = {} ); 
    193                         variables[ name ] = value; 
    194                 } 
    195         }; 
    196  
    197         var applyInlineStyle = function( range ) 
    198         { 
    199                 var document = range.document; 
    200  
    201                 if ( range.collapsed ) 
    202                 { 
    203                         // Create the element to be inserted in the DOM. 
    204                         var collapsedElement = getElement( this, document ); 
    205  
    206                         // Insert the empty element into the DOM at the range position. 
    207                         range.insertNode( collapsedElement ); 
    208  
    209                         // Place the selection right inside the empty element. 
    210                         range.moveToPosition( collapsedElement, CKEDITOR.POSITION_BEFORE_END ); 
    211  
    212                         return; 
    213                 } 
    214  
    215                 var elementName = this.element; 
    216                 var def = this._.definition; 
    217                 var isUnknownElement; 
    218  
    219                 // Get the DTD definition for the element. Defaults to "span". 
    220                 var dtd = CKEDITOR.dtd[ elementName ] || ( isUnknownElement = true, CKEDITOR.dtd.span ); 
    221  
    222                 // Bookmark the range so we can re-select it after processing. 
    223                 var bookmark = range.createBookmark(); 
    224  
    225                 // Expand the range. 
    226                 range.enlarge( CKEDITOR.ENLARGE_ELEMENT ); 
    227                 range.trim(); 
    228  
    229                 // Get the first node to be processed and the last, which concludes the 
    230                 // processing. 
    231                 var firstNode = range.startContainer.getChild( range.startOffset ) || range.startContainer.getNextSourceNode(); 
    232                 var lastNode = range.endContainer.getChild( range.endOffset ) || ( range.endOffset ? range.endContainer.getNextSourceNode() : range.endContainer ); 
    233  
    234                 var currentNode = firstNode; 
    235  
    236                 var styleRange; 
    237  
    238                 // Indicates that that some useful inline content has been found, so 
    239                 // the style should be applied. 
    240                 var hasContents; 
    241  
    242                 while ( currentNode ) 
     194                }; 
     195         
     196                var applyInlineStyle = function( range ) 
    243197                { 
    244                         var applyStyle = false; 
    245  
    246                         if ( currentNode.equals( lastNode ) ) 
     198                        var document = range.document; 
     199         
     200                        if ( range.collapsed ) 
    247201                        { 
    248                                 currentNode = null; 
    249                                 applyStyle = true; 
     202                                // Create the element to be inserted in the DOM. 
     203                                var collapsedElement = getElement( this, document ); 
     204         
     205                                // Insert the empty element into the DOM at the range position. 
     206                                range.insertNode( collapsedElement ); 
     207         
     208                                // Place the selection right inside the empty element. 
     209                                range.moveToPosition( collapsedElement, CKEDITOR.POSITION_BEFORE_END ); 
     210         
     211                                return; 
    250212                        } 
    251                         else 
     213         
     214                        var elementName = this.element; 
     215                        var def = this._.definition; 
     216                        var isUnknownElement; 
     217         
     218                        // Get the DTD definition for the element. Defaults to "span". 
     219                        var dtd = CKEDITOR.dtd[ elementName ] || ( isUnknownElement = true, CKEDITOR.dtd.span ); 
     220         
     221                        // Bookmark the range so we can re-select it after processing. 
     222                        var bookmark = range.createBookmark(); 
     223         
     224                        // Expand the range. 
     225                        range.enlarge( CKEDITOR.ENLARGE_ELEMENT ); 
     226                        range.trim(); 
     227         
     228                        // Get the first node to be processed and the last, which concludes the 
     229                        // processing. 
     230                        var firstNode = range.startContainer.getChild( range.startOffset ) || range.startContainer.getNextSourceNode(); 
     231                        var lastNode = range.endContainer.getChild( range.endOffset ) || ( range.endOffset ? range.endContainer.getNextSourceNode() : range.endContainer ); 
     232         
     233                        var currentNode = firstNode; 
     234         
     235                        var styleRange; 
     236         
     237                        // Indicates that that some useful inline content has been found, so 
     238                        // the style should be applied. 
     239                        var hasContents; 
     240         
     241                        while ( currentNode ) 
    252242                        { 
    253                                 var nodeType = currentNode.type; 
    254                                 var nodeName = nodeType == CKEDITOR.NODE_ELEMENT ? currentNode.getName() : null; 
    255  
    256                                 if ( nodeName && currentNode.getAttribute( '_fck_bookmark' ) ) 
     243                                var applyStyle = false; 
     244         
     245                                if ( currentNode.equals( lastNode ) ) 
    257246                                { 
    258                                         currentNode = currentNode.getNextSourceNode( true ); 
    259                                         continue; 
     247                                        currentNode = null; 
     248                                        applyStyle = true; 
    260249                                } 
    261  
    262                                 // Check if the current node can be a child of the style element. 
    263                                 if ( !nodeName || ( dtd[ nodeName ] && ( currentNode.getPosition( lastNode ) | CKEDITOR.POSITION_PRECEDING | CKEDITOR.POSITION_IDENTICAL | CKEDITOR.POSITION_IS_CONTAINED ) == ( CKEDITOR.POSITION_PRECEDING + CKEDITOR.POSITION_IDENTICAL + CKEDITOR.POSITION_IS_CONTAINED ) ) ) 
     250                                else 
    264251                                { 
    265                                         var currentParent = currentNode.getParent(); 
    266  
    267                                         // Check if the style element can be a child of the current 
    268                                         // node parent or if the element is not defined in the DTD. 
    269                                         if ( currentParent && ( ( currentParent.getDtd() || CKEDITOR.dtd.span )[ elementName ] || isUnknownElement ) ) 
     252                                        var nodeType = currentNode.type; 
     253                                        var nodeName = nodeType == CKEDITOR.NODE_ELEMENT ? currentNode.getName() : null; 
     254         
     255                                        if ( nodeName && currentNode.getAttribute( '_fck_bookmark' ) ) 
     256                                        { 
     257                                                currentNode = currentNode.getNextSourceNode( true ); 
     258                                                continue; 
     259                                        } 
     260         
     261                                        // Check if the current node can be a child of the style element. 
     262                                        if ( !nodeName || ( dtd[ nodeName ] && ( currentNode.getPosition( lastNode ) | CKEDITOR.POSITION_PRECEDING | CKEDITOR.POSITION_IDENTICAL | CKEDITOR.POSITION_IS_CONTAINED ) == ( CKEDITOR.POSITION_PRECEDING + CKEDITOR.POSITION_IDENTICAL + CKEDITOR.POSITION_IS_CONTAINED ) ) ) 
    270263                                        { 
    271                                                 // This node will be part of our range, so if it has not 
    272                                                 // been started, place its start right before the node. 
    273                                                 // In the case of an element node, it will be included 
    274                                                 // only if it is entirely inside the range. 
    275                                                 if ( !styleRange && ( !nodeName || !CKEDITOR.dtd.$removeEmpty[ nodeName ] || ( currentNode.getPosition( lastNode ) | CKEDITOR.POSITION_PRECEDING | CKEDITOR.POSITION_IDENTICAL | CKEDITOR.POSITION_IS_CONTAINED ) == ( CKEDITOR.POSITION_PRECEDING + CKEDITOR.POSITION_IDENTICAL + CKEDITOR.POSITION_IS_CONTAINED ) ) ) 
     264                                                var currentParent = currentNode.getParent(); 
     265         
     266                                                // Check if the style element can be a child of the current 
     267                                                // node parent or if the element is not defined in the DTD. 
     268                                                if ( currentParent && ( ( currentParent.getDtd() || CKEDITOR.dtd.span )[ elementName ] || isUnknownElement ) ) 
    276269                                                { 
    277                                                         styleRange = new CKEDITOR.dom.range( document ); 
    278                                                         styleRange.setStartBefore( currentNode ); 
    279                                                 } 
    280  
    281                                                 // Non element nodes, or empty elements can be added 
    282                                                 // completely to the range. 
    283                                                 if ( nodeType == CKEDITOR.NODE_TEXT || ( nodeType == CKEDITOR.NODE_ELEMENT && !currentNode.getChildCount() && currentNode.$.offsetWidth ) ) 
    284                                                 { 
    285                                                         var includedNode = currentNode; 
    286                                                         var parentNode; 
    287  
    288                                                         // This node is about to be included completelly, but, 
    289                                                         // if this is the last node in its parent, we must also 
    290                                                         // check if the parent itself can be added completelly 
    291                                                         // to the range. 
    292                                                         while ( !includedNode.$.nextSibling 
    293                                                                 && ( parentNode = includedNode.getParent(), dtd[ parentNode.getName() ] ) 
    294                                                                 && ( parentNode.getPosition( firstNode ) | CKEDITOR.POSITION_FOLLOWING | CKEDITOR.POSITION_IDENTICAL | CKEDITOR.POSITION_IS_CONTAINED ) == ( CKEDITOR.POSITION_FOLLOWING + CKEDITOR.POSITION_IDENTICAL + CKEDITOR.POSITION_IS_CONTAINED ) ) 
     270                                                        // This node will be part of our range, so if it has not 
     271                                                        // been started, place its start right before the node. 
     272                                                        // In the case of an element node, it will be included 
     273                                                        // only if it is entirely inside the range. 
     274                                                        if ( !styleRange && ( !nodeName || !CKEDITOR.dtd.$removeEmpty[ nodeName ] || ( currentNode.getPosition( lastNode ) | CKEDITOR.POSITION_PRECEDING | CKEDITOR.POSITION_IDENTICAL | CKEDITOR.POSITION_IS_CONTAINED ) == ( CKEDITOR.POSITION_PRECEDING + CKEDITOR.POSITION_IDENTICAL + CKEDITOR.POSITION_IS_CONTAINED ) ) ) 
    295275                                                        { 
    296                                                                 includedNode = parentNode; 
     276                                                                styleRange = new CKEDITOR.dom.range( document ); 
     277                                                                styleRange.setStartBefore( currentNode ); 
    297278                                                        } 
    298  
    299                                                         styleRange.setEndAfter( includedNode ); 
    300  
    301                                                         // If the included node still is the last node in its 
    302                                                         // parent, it means that the parent can't be included 
    303                                                         // in this style DTD, so apply the style immediately. 
    304                                                         if ( !includedNode.$.nextSibling ) 
    305                                                                 applyStyle = true; 
    306  
    307                                                         if ( !hasContents ) 
    308                                                                 hasContents = ( nodeType != CKEDITOR.NODE_TEXT || (/[^\s\ufeff]/).test( currentNode.getText() ) ); 
     279         
     280                                                        // Non element nodes, or empty elements can be added 
     281                                                        // completely to the range. 
     282                                                        if ( nodeType == CKEDITOR.NODE_TEXT || ( nodeType == CKEDITOR.NODE_ELEMENT && !currentNode.getChildCount() && currentNode.$.offsetWidth ) ) 
     283                                                        { 
     284                                                                var includedNode = currentNode; 
     285                                                                var parentNode; 
     286         
     287                                                                // This node is about to be included completelly, but, 
     288                                                                // if this is the last node in its parent, we must also 
     289                                                                // check if the parent itself can be added completelly 
     290                                                                // to the range. 
     291                                                                while ( !includedNode.$.nextSibling 
     292                                                                        && ( parentNode = includedNode.getParent(), dtd[ parentNode.getName() ] ) 
     293                                                                        && ( parentNode.getPosition( firstNode ) | CKEDITOR.POSITION_FOLLOWING | CKEDITOR.POSITION_IDENTICAL | CKEDITOR.POSITION_IS_CONTAINED ) == ( CKEDITOR.POSITION_FOLLOWING + CKEDITOR.POSITION_IDENTICAL + CKEDITOR.POSITION_IS_CONTAINED ) ) 
     294                                                                { 
     295                                                                        includedNode = parentNode; 
     296                                                                } 
     297         
     298                                                                styleRange.setEndAfter( includedNode ); 
     299         
     300                                                                // If the included node still is the last node in its 
     301                                                                // parent, it means that the parent can't be included 
     302                                                                // in this style DTD, so apply the style immediately. 
     303                                                                if ( !includedNode.$.nextSibling ) 
     304                                                                        applyStyle = true; 
     305         
     306                                                                if ( !hasContents ) 
     307                                                                        hasContents = ( nodeType != CKEDITOR.NODE_TEXT || (/[^\s\ufeff]/).test( currentNode.getText() ) ); 
     308                                                        } 
    309309                                                } 
     310                                                else 
     311                                                        applyStyle = true; 
    310312                                        } 
    311313                                        else 
    312314                                                applyStyle = true; 
     315         
     316                                        // Get the next node to be processed. 
     317                                        currentNode = currentNode.getNextSourceNode(); 
    313318                                } 
    314                                 else 
    315                                         applyStyle = true; 
    316  
    317                                 // Get the next node to be processed. 
    318                                 currentNode = currentNode.getNextSourceNode(); 
    319                         } 
    320  
    321                         // Apply the style if we have something to which apply it. 
    322                         if ( applyStyle && hasContents && styleRange && !styleRange.collapsed ) 
    323                         { 
    324                                 // Build the style element, based on the style object definition. 
    325                                 var styleNode = getElement( this, document ); 
    326  
    327                                 var parent = styleRange.getCommonAncestor(); 
    328  
    329                                 while ( styleNode && parent ) 
     319         
     320                                // Apply the style if we have something to which apply it. 
     321                                if ( applyStyle && hasContents && styleRange && !styleRange.collapsed ) 
    330322                                { 
    331                                         if ( parent.getName() == elementName ) 
     323                                        // Build the style element, based on the style object definition. 
     324                                        var styleNode = getElement( this, document ); 
     325         
     326                                        var parent = styleRange.getCommonAncestor(); 
     327         
     328                                        while ( styleNode && parent ) 
    332329                                        { 
    333                                                 for ( var attName in def.attribs ) 
     330                                                if ( parent.getName() == elementName ) 
    334331                                                { 
    335                                                         if ( styleNode.getAttribute( attName ) == parent.getAttribute( attName ) ) 
    336                                                                 styleNode.removeAttribute( attName ); 
    337                                                 } 
    338  
    339                                                 for ( var styleName in def.styles ) 
    340                                                 { 
    341                                                         if ( styleNode.getStyle( styleName ) == parent.getStyle( styleName ) ) 
    342                                                                 styleNode.removeStyle( styleName ); 
    343                                                 } 
    344  
    345                                                 if ( !styleNode.hasAttributes() ) 
    346                                                 { 
    347                                                         styleNode = null; 
    348                                                         break; 
     332                                                        for ( var attName in def.attribs ) 
     333                                                        { 
     334                                                                if ( styleNode.getAttribute( attName ) == parent.getAttribute( attName ) ) 
     335                                                                        styleNode.removeAttribute( attName ); 
     336                                                        } 
     337         
     338                                                        for ( var styleName in def.styles ) 
     339                                                        { 
     340                                                                if ( styleNode.getStyle( styleName ) == parent.getStyle( styleName ) ) 
     341                                                                        styleNode.removeStyle( styleName ); 
     342                                                        } 
     343         
     344                                                        if ( !styleNode.hasAttributes() ) 
     345                                                        { 
     346                                                                styleNode = null; 
     347                                                                break; 
     348                                                        } 
    349349                                                } 
     350         
     351                                                parent = parent.getParent(); 
    350352                                        } 
    351  
    352                                         parent = parent.getParent(); 
     353         
     354                                        if ( styleNode ) 
     355                                        { 
     356                                                // Move the contents of the range to the style element. 
     357                                                styleRange.extractContents().appendTo( styleNode ); 
     358         
     359                                                // Here we do some cleanup, removing all duplicated 
     360                                                // elements from the style element. 
     361                                                                                        this.removeFromAllChilds( styleNode ); 
     362         
     363                                                // Insert it into the range position (it is collapsed after 
     364                                                // extractContents. 
     365                                                styleRange.insertNode( styleNode ); 
     366         
     367                                                // Let's merge our new style with its neighbors, if possible. 
     368                                                mergeSiblings( styleNode ); 
     369         
     370                                                // As the style system breaks text nodes constantly, let's normalize 
     371                                                // things for performance. 
     372                                                // With IE, some paragraphs get broken when calling normalize() 
     373                                                // repeatedly. Also, for IE, we must normalize body, not documentElement. 
     374                                                // IE is also known for having a "crash effect" with normalize(). 
     375                                                // We should try to normalize with IE too in some way, somewhere. 
     376                                                if ( !CKEDITOR.env.ie ) 
     377                                                        styleNode.$.normalize(); 
     378                                        } 
     379         
     380                                        // Style applied, let's release the range, so it gets 
     381                                        // re-initialization in the next loop. 
     382                                        styleRange = null; 
    353383                                } 
    354  
    355                                 if ( styleNode ) 
     384                        } 
     385         
     386        //              this._FixBookmarkStart( startNode ); 
     387         
     388                        range.moveToBookmark( bookmark ); 
     389                }; 
     390                         
     391                /** 
     392                 * Remove the style from the specified {@param range}.  
     393                 * @param {CKEDITOR.dom.range} range    The range to be processed.  
     394                 */ 
     395                function removeInlineStyle(range) { 
     396                 
     397                        var doc = range.document; 
     398                        var self = this, //bookmark start and end boundaries  
     399                        bookmark, startNode, endNode, walker, //the topmost parent node which conflicts with this style removing 
     400                        matchedTopParent; 
     401         
     402                        var isCollapsed = range.collapsed, // A placeholder for cheat with collapsed range 
     403                        marker; 
     404                 
     405                         
     406                        // Expand the range, if inside inline element boundaries. 
     407                        if ( !isCollapsed )  
     408                                range.enlarge( CKEDITOR.ENLARGE_ELEMENT ); 
     409                 
     410                        //Cheat by insert a placeholder node which make the range no longer collapsed 
     411                        else          
     412                        { 
     413                                marker = doc.createText( '&nbsp;' ); 
     414                                range.insertNode( marker ); 
     415                                range.setStartBefore( marker ); 
     416                                range.setEndAfter( marker ); 
     417                        } 
     418         
     419                        // Bookmark the range, bookmark nodes are used to establish boundaries. 
     420                        bookmark = range.createBookmark(), 
     421                        startNode   = bookmark.startNode, 
     422                        endNode = bookmark.endNode; 
     423         
     424                        if ( matchedTopParent = checkPath( startNode , CKEDITOR.tools.bind( 
     425                                                        this.checkElementRemovable , this ) ) ) 
     426                                branch( startNode , matchedTopParent , true ); 
     427                        if ( matchedTopParent = checkPath( endNode , CKEDITOR.tools.bind( 
     428                                                        this.checkElementRemovable , this ) ) ) 
     429                                branch( endNode , matchedTopParent , true ); 
     430                 
     431                        walker = new CKEDITOR.dom.domWalker( startNode ); 
     432                        walker.forward( function ( walkerEvt ) 
     433                        { 
     434                                var currentNode = walkerEvt.data.from; 
     435                                if ( currentNode.equals( startNode ) ) 
     436                                                return; 
     437                                else if ( currentNode.equals( endNode ) ) 
     438                                                this.stop(); 
     439                                else 
     440                                        { 
     441                                                self.removeFromElement( currentNode ); 
     442                                        }        
     443                        } ); 
     444                         
     445                        if ( isCollapsed ) // remove the marker node 
     446                        { 
     447                                marker.remove(); 
     448                        } 
     449                        else 
     450                        { 
     451                                if ( !CKEDITOR.env.ie ) // normalize text nodes for non-IE 
    356452                                { 
    357                                         // Move the contents of the range to the style element. 
    358                                         styleRange.extractContents().appendTo( styleNode ); 
    359  
    360                                         // Here we do some cleanup, removing all duplicated 
    361                                         // elements from the style element. 
    362                                         removeFromElement( this, styleNode ); 
    363  
    364                                         // Insert it into the range position (it is collapsed after 
    365                                         // extractContents. 
    366                                         styleRange.insertNode( styleNode ); 
    367  
    368                                         // Let's merge our new style with its neighbors, if possible. 
    369                                         mergeSiblings( styleNode ); 
    370  
    371                                         // As the style system breaks text nodes constantly, let's normalize 
    372                                         // things for performance. 
    373                                         // With IE, some paragraphs get broken when calling normalize() 
    374                                         // repeatedly. Also, for IE, we must normalize body, not documentElement. 
    375                                         // IE is also known for having a "crash effect" with normalize(). 
    376                                         // We should try to normalize with IE too in some way, somewhere. 
    377                                         if ( !CKEDITOR.env.ie ) 
    378                                                 styleNode.$.normalize(); 
     453                                        var frag; 
     454                                        range.moveToBookmark( bookmark , true ); 
     455                                        frag = range.extractContents(); 
     456                                         
     457                                        var boundaryNode = endNode.getPrevious(); 
     458                                         
     459                                        frag.$.normalize(); 
     460                                        range.insertNode( frag ); 
     461                                        //Merge nodes which splited by the extract operation if possible. 
     462                                        mergeSiblings(boundaryNode); 
    379463                                } 
    380  
    381                                 // Style applied, let's release the range, so it gets 
    382                                 // re-initialization in the next loop. 
    383                                 styleRange = null; 
    384464                        } 
     465                                 
     466                        //TODO: Anchor range boundaries inside element if possible 
     467                         
     468                        range.moveToBookmark(bookmark); 
     469                }; 
     470                 
     471                var applyBlockStyle = function( range ) 
     472                { 
     473                }; 
     474                         
     475                /** 
     476                 * Create a new branch of the dom tree which rooted by {@param parentNode} and start from the {@param boundaryNode}, after then the {@param boundaryNode} would be placed between these two branches. 
     477                 * For example in the following dom tree, if we specified '<span/>' as the boundaryNode, 
     478                 * <pre>  
     479                 *      <b>This <i>is some<span /> sample</i> test text</b> 
     480                 *      </pre> 
     481                 *The dom tree will end up with: 
     482                 *<pre>       
     483                 *      <b>This <i>is some</i><span /><i> sample</i> test text</b>          <!-- (If parent = <i>) --> 
     484                 *      <b>This <i>is some</i></b><span /><b><i> sample</i> test text</b>   <!-- (If parent = <b>) --> 
     485                 *<pre>       
     486                 * @param {CKEDITOR.dom.node} boundaryNode The left-most node which used as boundary of this branch. 
     487                 * @param {CKEDITOR.dom.node} parentNode The first node which will be the root of this branch. 
     488                 * @see FCKDomTools.BreakParent The v2 similiar method. 
     489                 */ 
     490                function branch ( boundaryNode, parentNode ) 
     491                { 
     492                        var doc = new CKEDITOR.dom.document(( boundaryNode || parentNode ).$.ownerDocument); 
     493                        var range = new CKEDITOR.dom.range( doc ); 
     494         
     495                        // We'll be extracting part of this boundaryNode, so let's use our 
     496                        // range to get the correct piece. 
     497                        range.setStartAfter( boundaryNode ); 
     498                        range.setEndAfter( parentNode ); 
     499         
     500                        // Extract it, range should be collapsed after the first branch 
     501                        var docFrag = range.extractContents(); 
     502         
     503                        // Place the boundary node right after first branch  
     504                        range.insertNode( boundaryNode.remove() ); 
     505         
     506                        // Insert the second branch in following 
     507                        docFrag.insertAfterNode( boundaryNode ); 
    385508                } 
    386  
    387 //              this._FixBookmarkStart( startNode ); 
    388  
    389                 range.moveToBookmark( bookmark ); 
    390         }; 
    391  
    392         var applyBlockStyle = function( range ) 
    393         { 
    394         }; 
    395  
    396         // Removes a style from inside an element. 
    397         var removeFromElement = function( style, element ) 
    398         { 
    399                 var def = style._.definition; 
    400                 var attribs = def.attributes; 
    401                 var styles = def.styles; 
    402  
    403                 var innerElements = element.getElementsByTag( style.element ); 
    404  
    405                 for ( var i = innerElements.count() ; --i >= 0 ; ) 
     509                 
     510                 
     511                /** 
     512                 * Reversely seaching on the element path which started by {@param pathStartNode}, return the first path element which satisfy {@param testFunction}. 
     513                 * @param {CKEditor.dom.node} pathStartNode 
     514                 * @param {Functoin} testFunction The function to test whether the element is the root of branch. 
     515                 * @param {Boolean} skipBlock Whether ignore all the block level elements included in this path. 
     516                 */ 
     517                function checkPath(pathStartNode, testFunction, skipBlock) 
    406518                { 
    407                         var innerElement = innerElements.getItem( i ); 
    408  
    409                         for ( var attName in attribs ) 
     519                        // Let's start checking the start boundary. 
     520                        var path = new CKEDITOR.dom.elementPath( pathStartNode ), currentElements = path.elements; 
     521                        var currentElement; 
     522                        var i = skipBlock ? CKEDITOR.tools.indexOf( currentElements , path.block ) - 1 
     523                                                        : currentElements.length - 1; 
     524                         
     525                        for (; i > 0 ; i--) // skip the beginning node 
    410526                        { 
    411                                 // The 'class' element value must match (#1318). 
    412                                 if ( attName == 'class' && innerElement.getAttribute( 'class' ) != attribs[ attName ] ) 
    413                                         continue; 
    414  
    415                                 innerElement.removeAttribute( attName ); 
     527                                currentElement = currentElements[ i ]; 
     528                                 
     529                                if (testFunction( currentElement ))  
     530                                { 
     531                                        return currentElement; 
     532                                } 
    416533                        } 
    417  
    418                         for ( var styleName in styles ) 
     534                }; 
     535                 
     536                /** 
     537                 * If no more attributes remained in the element, remove it, but leaving its 
     538                 * children. 
     539                 *  
     540                 * @param {CKEDITOR.dom.element} 
     541                 *            The target element to remove. 
     542                 */ 
     543                function removeNoAttribsElement( element ) 
     544                { 
     545                        // If no more attributes remained in the element, remove it, 
     546                        // leaving its children. 
     547                        if ( !element.hasAttributes() ) 
    419548                        { 
    420                                 innerElement.removeStyle( styleName ); 
     549                                //Merge sibling on remove element if possible 
     550                                var parent = element.getParent(), 
     551                                                previous = element.getPrevious(); 
     552                                                 
     553                                element.remove( true ); 
     554                                mergeSiblings( previous ); 
     555                                mergeSiblings( parent ); 
    421556                        } 
    422  
    423                         removeNoAttribsElement( innerElement ); 
    424                 } 
    425         }; 
    426  
    427         // If the element has no more attributes, remove it. 
    428         var removeNoAttribsElement = function( element ) 
    429         { 
    430                 // If no more attributes remained in the element, remove it, 
    431                 // leaving its children. 
    432                 if ( !element.hasAttributes() ) 
     557                }; 
     558         
     559                // Get the the collection used to compare the attributes defined in this 
     560                // style with attributes in an element. All information in it is lowercased. 
     561                // V2 
     562        //      var getAttribsForComparison = function( style ) 
     563        //      { 
     564        //              // If we have already computed it, just return it. 
     565        //              var attribs = style._.attribsForComparison; 
     566        //              if ( attribs ) 
     567        //                      return attribs; 
     568         
     569        //              attribs = {}; 
     570         
     571        //              var def = style._.definition; 
     572         
     573        //              // Loop through all defined attributes. 
     574        //              var styleAttribs = def.attributes; 
     575        //              if ( styleAttribs ) 
     576        //              { 
     577        //                      for ( var styleAtt in styleAttribs ) 
     578        //                      { 
     579        //                              attribs[ styleAtt.toLowerCase() ] = styleAttribs[ styleAtt ].toLowerCase(); 
     580        //                      } 
     581        //              } 
     582         
     583        //              // Includes the style definitions. 
     584        //              if ( this._GetStyleText().length > 0 ) 
     585        //              { 
     586        //                      attribs['style'] = this._GetStyleText().toLowerCase(); 
     587        //              } 
     588         
     589        //              // Appends the "length" information to the object. 
     590        //              FCKTools.AppendLengthProperty( attribs, '_length' ); 
     591         
     592        //              // Return it, saving it to the next request. 
     593        //              return ( this._GetAttribsForComparison_$ = attribs ); 
     594        //      }, 
     595         
     596                var mergeSiblings = function( element ) 
     597                { 
     598                        if ( !element || element.type != CKEDITOR.NODE_ELEMENT || !CKEDITOR.dtd.$removeEmpty[ element.getName() ] ) 
     599                                return; 
     600         
     601                        mergeElements( element, element.getNext(), true ); 
     602                        mergeElements( element, element.getPrevious() ); 
     603                }; 
     604         
     605                var mergeElements = function( element, sibling, isNext ) 
    433606                { 
    434                         // Removing elements may open points where merging is possible, 
    435                         // so let's cache the first and last nodes for later checking. 
    436                         var firstChild  = element.getFirst(); 
    437                         var lastChild   = element.getLast(); 
    438  
    439                         element.remove( true ); 
    440  
    441                         if ( firstChild ) 
     607                        if ( sibling && sibling.type == CKEDITOR.NODE_ELEMENT ) 
    442608                        { 
    443                                 // Check the cached nodes for merging. 
    444                                 mergeSiblings( firstChild ); 
    445  
    446                                 if ( lastChild && !firstChild.equals( lastChild ) ) 
    447                                         mergeSiblings( lastChild ); 
     609                                var hasBookmark = sibling.getAttribute( '_fck_bookmark' ); 
     610         
     611                                if ( hasBookmark ) 
     612                                        sibling = isNext ? sibling.getNext() : sibling.getPrevious(); 
     613         
     614                                if ( sibling && sibling.type == CKEDITOR.NODE_ELEMENT && sibling.getName() == element.getName() ) 
     615                                { 
     616                                        // Save the last child to be checked too, to merge things like 
     617                                        // <b><i></i></b><b><i></i></b> => <b><i></i></b> 
     618                                        var innerSibling = isNext ? element.getLast() : element.getFirst(); 
     619         
     620                                        if ( hasBookmark ) 
     621                                                ( isNext ? sibling.getPrevious() : sibling.getNext() ).move( element, !isNext ); 
     622         
     623                                        sibling.moveChildren( element, !isNext ); 
     624                                        sibling.remove(); 
     625         
     626                                        // Now check the last inner child (see two comments above). 
     627                                        if ( innerSibling ) 
     628                                                mergeSiblings( innerSibling ); 
     629                                } 
    448630                        } 
    449                 } 
    450         }; 
    451  
    452         // Get the the collection used to compare the attributes defined in this 
    453         // style with attributes in an element. All information in it is lowercased. 
    454         // V2 
    455 //      var getAttribsForComparison = function( style ) 
    456 //      { 
    457 //              // If we have already computed it, just return it. 
    458 //              var attribs = style._.attribsForComparison; 
    459 //              if ( attribs ) 
    460 //                      return attribs; 
    461  
    462 //              attribs = {}; 
    463  
    464 //              var def = style._.definition; 
    465  
    466 //              // Loop through all defined attributes. 
    467 //              var styleAttribs = def.attributes; 
    468 //              if ( styleAttribs ) 
    469 //              { 
    470 //                      for ( var styleAtt in styleAttribs ) 
    471 //                      { 
    472 //                              attribs[ styleAtt.toLowerCase() ] = styleAttribs[ styleAtt ].toLowerCase(); 
    473 //                      } 
    474 //              } 
    475  
    476 //              // Includes the style definitions. 
    477 //              if ( this._GetStyleText().length > 0 ) 
    478 //              { 
    479 //                      attribs['style'] = this._GetStyleText().toLowerCase(); 
    480 //              } 
    481  
    482 //              // Appends the "length" information to the object. 
    483 //              FCKTools.AppendLengthProperty( attribs, '_length' ); 
    484  
    485 //              // Return it, saving it to the next request. 
    486 //              return ( this._GetAttribsForComparison_$ = attribs ); 
    487 //      }, 
    488  
    489         var mergeSiblings = function( element ) 
    490         { 
    491                 if ( !element || element.type != CKEDITOR.NODE_ELEMENT || !CKEDITOR.dtd.$removeEmpty[ element.getName() ] ) 
    492                         return; 
    493  
    494                 mergeElements( element, element.getNext(), true ); 
    495                 mergeElements( element, element.getPrevious() ); 
    496         }; 
    497  
    498         var mergeElements = function( element, sibling, isNext ) 
    499         { 
    500                 if ( sibling && sibling.type == CKEDITOR.NODE_ELEMENT ) 
     631                }; 
     632         
     633                // Regex used to match all variables defined in an attribute or style 
     634                // value. The variable name is returned with $2. 
     635                var styleVariableAttNameRegex = /#\(\s*("|')(.+?)\1[^\)]*\s*\)/g; 
     636         
     637                var getElement = function( style, targetDocument ) 
    501638                { 
    502                         var hasBookmark = sibling.getAttribute( '_fck_bookmark' ); 
    503  
    504                         if ( hasBookmark ) 
    505                                 sibling = isNext ? sibling.getNext() : sibling.getPrevious(); 
    506  
    507                         if ( sibling && sibling.type == CKEDITOR.NODE_ELEMENT && sibling.getName() == element.getName() ) 
     639                        var el = style._.element; 
     640         
     641                        if ( el ) 
     642                                return el.clone(); 
     643         
     644                        var def = style._.definition; 
     645                        var variables = style._.variables; 
     646         
     647                        var elementName = style.element; 
     648                        var attributes = def.attributes; 
     649                        var styles = def.styles; 
     650         
     651                        // The "*" element name will always be a span for this function. 
     652                        if ( elementName == '*' ) 
     653                                elementName = 'span'; 
     654         
     655                        // Create the element. 
     656                        el = new CKEDITOR.dom.element( elementName, targetDocument ); 
     657         
     658                        // Assign all defined attributes. 
     659                        if ( attributes ) 
    508660                        { 
    509                                 // Save the last child to be checked too, to merge things like 
    510                                 // <b><i></i></b><b><i></i></b> => <b><i></i></b> 
    511                                 var innerSibling = isNext ? element.getLast() : element.getFirst(); 
    512  
    513                                 if ( hasBookmark ) 
    514                                         ( isNext ? sibling.getPrevious() : sibling.getNext() ).move( element, !isNext ); 
    515  
    516                                 sibling.moveChildren( element, !isNext ); 
    517                                 sibling.remove(); 
    518  
    519                                 // Now check the last inner child (see two comments above). 
    520                                 if ( innerSibling ) 
    521                                         mergeSiblings( innerSibling ); 
     661                                for ( var att in attributes ) 
     662                                { 
     663                                        var attValue = attributes[ att ]; 
     664                                        if ( attValue && variables ) 
     665                                        { 
     666                                                attValue = attValue.replace( styleVariableAttNameRegex, function() 
     667                                                        { 
     668                                                                // The second group in the regex is the variable name. 
     669                                                                return variables[ arguments[2] ] || arguments[0]; 
     670                                                        }); 
     671                                        } 
     672                                        el.setAttribute( att, attValue ); 
     673                                } 
    522674                        } 
    523                 } 
    524         }; 
    525  
    526         // Regex used to match all variables defined in an attribute or style 
    527         // value. The variable name is returned with $2. 
    528         var styleVariableAttNameRegex = /#\(\s*("|')(.+?)\1[^\)]*\s*\)/g; 
    529  
    530         var getElement = function( style, targetDocument ) 
    531         { 
    532                 var el = style._.element; 
    533  
    534                 if ( el ) 
    535                         return el.clone(); 
    536  
    537                 var def = style._.definition; 
    538                 var variables = style._.variables; 
    539  
    540                 var elementName = style.element; 
    541                 var attributes = def.attributes; 
    542                 var styles = def.styles; 
    543  
    544                 // The "*" element name will always be a span for this function. 
    545                 if ( elementName == '*' ) 
    546                         elementName = 'span'; 
    547  
    548                 // Create the element. 
    549                 el = new CKEDITOR.dom.element( elementName, targetDocument ); 
    550  
    551                 // Assign all defined attributes. 
    552                 if ( attributes ) 
    553                 { 
    554                         for ( var att in attributes ) 
     675         
     676                        // Assign all defined styles. 
     677                        if ( styles ) 
    555678                        { 
    556                                 var attValue = attributes[ att ]; 
    557                                 if ( attValue && variables ) 
     679                                for ( var styleName in styles ) 
     680                                        el.setStyle( styleName, styles[ styleName ] ); 
     681         
     682                                if ( variables ) 
    558683                                { 
    559                                         attValue = attValue.replace( styleVariableAttNameRegex, function() 
     684                                        attValue = el.getAttribute( 'style' ).replace( styleVariableAttNameRegex, function() 
    560685                                                { 
    561686                                                        // The second group in the regex is the variable name. 
    562687                                                        return variables[ arguments[2] ] || arguments[0]; 
     
    561686                                                        // The second group in the regex is the variable name. 
    562687                                                        return variables[ arguments[2] ] || arguments[0]; 
    563688                                                }); 
     689                                        el.setAttribute( 'style', attValue ); 
    564690                                } 
    565                                 el.setAttribute( att, attValue ); 
    566691                        } 
    567                 } 
     692         
     693                        // Save the created element. It will be reused on future calls. 
     694                        return ( style._.element = el ); 
     695                }; 
     696         
     697})(); 
     698/** 
     699 * Registers a function to be called whenever a style changes its state in the 
     700 * editing area. The current state is passed to the function. The possible 
     701 * states are {@link CKEDITOR.TRISTATE_ON} and {@link CKEDITOR.TRISTATE_OFF}. 
     702 * @param {CKEDITOR.style} The style to be watched. 
     703 * @param {Function} The function to be called when the style state changes. 
     704 * @example 
     705 * // Create a style object for the <b> element. 
     706 * var style = new CKEDITOR.style( { element : 'b' } ); 
     707 * var editor = CKEDITOR.instances.editor1; 
     708 * editor.attachStyleStateChange( style, function( state ) 
     709 *     { 
     710 *         if ( state == CKEDITOR.TRISTATE_ON ) 
     711 *             alert( 'The current state for the B element is ON' ); 
     712 *         else 
     713 *             alert( 'The current state for the B element is OFF' ); 
     714 *     }); 
     715 */ 
     716CKEDITOR.editor.prototype.attachStyleStateChange = function( style, callback ) 
     717{ 
     718                // Try to get the list of attached callbacks. 
     719                var styleStateChangeCallbacks = this._.styleStateChangeCallbacks; 
    568720 
    569                 // Assign all defined styles. 
    570                 if ( styles ) 
     721                // If it doesn't exist, it means this is the first call. So, let's create 
     722                // all the structure to manage the style checks and the callback calls. 
     723                if ( !styleStateChangeCallbacks ) 
    571724                { 
    572                         for ( var styleName in styles ) 
    573                                 el.setStyle( styleName, styles[ styleName ] ); 
     725                                // Create the callbacks array. 
     726                                styleStateChangeCallbacks = this._.styleStateChangeCallbacks = []; 
    574727 
    575                         if ( variables ) 
    576                         { 
    577                                 attValue = el.getAttribute( 'style' ).replace( styleVariableAttNameRegex, function() 
    578                                         { 
    579                                                 // The second group in the regex is the variable name. 
    580                                                 return variables[ arguments[2] ] || arguments[0]; 
    581                                         }); 
    582                                 el.setAttribute( 'style', attValue ); 
    583                         } 
     728                                // Attach to the selectionChange event, so we can check the styles at 
     729                                // that point. 
     730                                this.on( 'selectionChange', function( ev ) 
     731                                                { 
     732                                                                // Loop throw all registered callbacks. 
     733                                                                for ( var i = 0 ; i < styleStateChangeCallbacks.length ; i++ ) 
     734                                                                { 
     735                                                                                var callback = styleStateChangeCallbacks[ i ]; 
     736 
     737                                                                                // Check the current state for the style defined for that 
     738                                                                                // callback. 
     739                                                                                var currentState = callback.style.checkActive( ev.data.path ) ? CKEDITOR.TRISTATE_ON : CKEDITOR.TRISTATE_OFF; 
     740 
     741                                                                                // If the state changed since the last check. 
     742                                                                                if ( callback.state !== currentState ) 
     743                                                                                { 
     744                                                                                                // Call the callback function, passing the current 
     745                                                                                                // state to it. 
     746                                                                                                callback.fn.call( this, currentState ); 
     747 
     748                                                                                                // Save the current state, so it can be compared next 
     749                                                                                                // time. 
     750                                                                                                callback.state !== currentState; 
     751                                                                                } 
     752                                                                } 
     753                                                }); 
    584754                } 
    585755 
    586                 // Save the created element. It will be reused on future calls. 
    587                 return ( style._.element = el ); 
    588         }; 
    589 })(); 
     756                // Save the callback info, so it can be checked on the next occurence of 
     757                // selectionChange. 
     758                styleStateChangeCallbacks.push( { style : style, fn : callback } ); 
     759}; 
     760 
     761CKEDITOR.STYLE_BLOCK = 1; 
     762CKEDITOR.STYLE_INLINE = 2; 
     763CKEDITOR.STYLE_OBJECT = 3; 
     764 
    590765 
    591766CKEDITOR.styleCommand = function( style ) 
    592767{ 
     
    595770 
    596771CKEDITOR.styleCommand.prototype.exec = function( editor ) 
    597772{ 
    598         editor.focus(); 
    599  
    600         var doc = editor.document; 
    601  
    602         if ( doc ) 
    603                 this.style.apply( doc ); 
    604  
    605         return !!doc; 
    606 }; 
     773        var editor = this.style._.editor; 
     774        if(this.state === CKEDITOR.TRISTATE_OFF)         
     775        { 
     776                this.style.apply(); 
     777        } 
     778        else if(this.state === CKEDITOR.TRISTATE_ON) 
     779        { 
     780                this.style.reverse(); 
     781        } 
     782}; 
     783 No newline at end of file 
  • _source/plugins/selection/plugin.js

     
    9999 
    100100                                                editor.document.on( 'mouseup', checkSelectionChangeTimeout, editor ); 
    101101                                                editor.document.on( 'keyup', checkSelectionChangeTimeout, editor ); 
     102                                                // Check selection after commands applied. 
     103                                                editor.on( 'afterCommandExec', checkSelectionChangeTimeout, editor ); 
     104                                         
    102105                                        } 
    103106                                }); 
    104107 
© 2003 – 2012 CKSource – Frederico Knabben. All rights reserved. | Terms of use | Privacy policy