source: sandbox/3.0/phpgwapi/js/ckeditor/_source/plugins/styles/plugin.js @ 2862

Revision 2862, 44.5 KB checked in by rodsouza, 14 years ago (diff)

Ticket #663 - Atualizando e centralizando o CKEditor (v. 3.2.1)

Line 
1/*
2Copyright (c) 2003-2010, CKSource - Frederico Knabben. All rights reserved.
3For licensing, see LICENSE.html or http://ckeditor.com/license
4*/
5
6CKEDITOR.plugins.add( 'styles',
7{
8        requires : [ 'selection' ]
9});
10
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 */
29CKEDITOR.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 )
44                        {
45                                // Loop throw all registered callbacks.
46                                for ( var i = 0 ; i < styleStateChangeCallbacks.length ; i++ )
47                                {
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 )
56                                        {
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;
64                                        }
65                                }
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
74CKEDITOR.STYLE_BLOCK = 1;
75CKEDITOR.STYLE_INLINE = 2;
76CKEDITOR.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,th:1,ul:1,dl:1,dt:1,dd:1,form:1};
82
83        var semicolonFixRegex = /\s*(?:;\s*|$)/;
84
85        CKEDITOR.style = function( styleDefinition, variablesValues )
86        {
87                if ( variablesValues )
88                {
89                        styleDefinition = CKEDITOR.tools.clone( styleDefinition );
90
91                        replaceVariables( styleDefinition.attributes, variablesValues );
92                        replaceVariables( styleDefinition.styles, variablesValues );
93                }
94
95                var element = this.element = ( styleDefinition.element || '*' ).toLowerCase();
96
97                this.type =
98                        ( element == '#' || blockElements[ element ] ) ?
99                                CKEDITOR.STYLE_BLOCK
100                        : objectElements[ element ] ?
101                                CKEDITOR.STYLE_OBJECT
102                        :
103                                CKEDITOR.STYLE_INLINE;
104
105                this._ =
106                {
107                        definition : styleDefinition
108                };
109        };
110
111        CKEDITOR.style.prototype =
112        {
113                apply : function( document )
114                {
115                        applyStyle.call( this, document, false );
116                },
117
118                remove : function( document )
119                {
120                        applyStyle.call( this, document, true );
121                },
122
123                applyToRange : function( range )
124                {
125                        return ( this.applyToRange =
126                                                this.type == CKEDITOR.STYLE_INLINE ?
127                                                        applyInlineStyle
128                                                : this.type == CKEDITOR.STYLE_BLOCK ?
129                                                        applyBlockStyle
130                                                : this.type == CKEDITOR.STYLE_OBJECT ?
131                                                        applyObjectStyle
132                                                : null ).call( this, range );
133                },
134
135                removeFromRange : function( range )
136                {
137                        return ( this.removeFromRange =
138                                                this.type == CKEDITOR.STYLE_INLINE ?
139                                                        removeInlineStyle
140                                                : null ).call( this, range );
141                },
142
143                applyToObject : function( element )
144                {
145                        setupElement( element, this );
146                },
147
148                /**
149                 * Get the style state inside an element path. Returns "true" if the
150                 * element is active in the path.
151                 */
152                checkActive : function( elementPath )
153                {
154                        switch ( this.type )
155                        {
156                                case CKEDITOR.STYLE_BLOCK :
157                                        return this.checkElementRemovable( elementPath.block || elementPath.blockLimit, true );
158
159                                case CKEDITOR.STYLE_OBJECT :
160                                case CKEDITOR.STYLE_INLINE :
161
162                                        var elements = elementPath.elements;
163
164                                        for ( var i = 0, element ; i < elements.length ; i++ )
165                                        {
166                                                element = elements[ i ];
167
168                                                if ( this.type == CKEDITOR.STYLE_INLINE
169                                                          && ( element == elementPath.block || element == elementPath.blockLimit ) )
170                                                        continue;
171
172                                                if( this.type == CKEDITOR.STYLE_OBJECT
173                                                         && !( element.getName() in objectElements ) )
174                                                                continue;
175
176                                                if ( this.checkElementRemovable( element, true ) )
177                                                        return true;
178                                        }
179                        }
180                        return false;
181                },
182
183                checkApplicable : function( elementPath )
184                {
185                        switch ( this.type )
186                        {
187                                case CKEDITOR.STYLE_INLINE :
188                                case CKEDITOR.STYLE_BLOCK :
189                                        break;
190
191                                case CKEDITOR.STYLE_OBJECT :
192                                        return elementPath.lastElement.getAscendant( this.element, true );
193                        }
194
195                        return true;
196                },
197
198                // Checks if an element, or any of its attributes, is removable by the
199                // current style definition.
200                checkElementRemovable : function( element, fullMatch )
201                {
202                        if ( !element )
203                                return false;
204
205                        var def = this._.definition,
206                                attribs;
207
208                        // If the element name is the same as the style name.
209                        if ( element.getName() == this.element )
210                        {
211                                // If no attributes are defined in the element.
212                                if ( !fullMatch && !element.hasAttributes() )
213                                        return true;
214
215                                attribs = getAttributesForComparison( def );
216
217                                if ( attribs._length )
218                                {
219                                        for ( var attName in attribs )
220                                                if ( ! ( attName in Object.prototype ) )
221                                                {
222                                                        if ( attName == '_length' )
223                                                                continue;
224
225                                                        var elementAttr = element.getAttribute( attName ) || '';
226                                                        if ( attName == 'style' ?
227                                                                compareCssText( attribs[ attName ], normalizeCssText( elementAttr, false ) )
228                                                                : attribs[ attName ] == elementAttr  )
229                                                        {
230                                                                if ( !fullMatch )
231                                                                        return true;
232                                                        }
233                                                        else if ( fullMatch )
234                                                                        return false;
235                                                }
236                                        if ( fullMatch )
237                                                return true;
238                                }
239                                else
240                                        return true;
241                        }
242
243                        // Check if the element can be somehow overriden.
244                        var override = getOverrides( this )[ element.getName() ] ;
245                        if ( override )
246                        {
247                                // If no attributes have been defined, remove the element.
248                                if ( !( attribs = override.attributes ) )
249                                        return true;
250
251                                for ( var i = 0 ; i < attribs.length ; i++ )
252                                {
253                                        attName = attribs[i][0];
254                                        var actualAttrValue = element.getAttribute( attName );
255                                        if ( actualAttrValue )
256                                        {
257                                                var attValue = attribs[i][1];
258
259                                                // Remove the attribute if:
260                                                //    - The override definition value is null;
261                                                //    - The override definition value is a string that
262                                                //      matches the attribute value exactly.
263                                                //    - The override definition value is a regex that
264                                                //      has matches in the attribute value.
265                                                if ( attValue === null ||
266                                                                ( typeof attValue == 'string' && actualAttrValue == attValue ) ||
267                                                                attValue.test( actualAttrValue ) )
268                                                        return true;
269                                        }
270                                }
271                        }
272                        return false;
273                },
274
275                // Builds the preview HTML based on the styles definition.
276                buildPreview : function()
277                {
278                        var styleDefinition = this._.definition,
279                                html = [],
280                                elementName = styleDefinition.element;
281
282                        // Avoid <bdo> in the preview.
283                        if ( elementName == 'bdo' )
284                                elementName = 'span';
285
286                        html = [ '<', elementName ];
287
288                        // Assign all defined attributes.
289                        var attribs     = styleDefinition.attributes;
290                        if ( attribs )
291                        {
292                                for ( var att in attribs )
293                                        if ( ! ( att in Object.prototype ) )
294                                        {
295                                                html.push( ' ', att, '="', attribs[ att ], '"' );
296                                        }
297                        }
298
299                        // Assign the style attribute.
300                        var cssStyle = CKEDITOR.style.getStyleText( styleDefinition );
301                        if ( cssStyle )
302                                html.push( ' style="', cssStyle, '"' );
303
304                        html.push( '>', styleDefinition.name, '</', elementName, '>' );
305
306                        return html.join( '' );
307                }
308        };
309
310        // Build the cssText based on the styles definition.
311        CKEDITOR.style.getStyleText = function( styleDefinition )
312        {
313                // If we have already computed it, just return it.
314                var stylesDef = styleDefinition._ST;
315                if ( stylesDef )
316                        return stylesDef;
317
318                stylesDef = styleDefinition.styles;
319
320                // Builds the StyleText.
321                var stylesText = ( styleDefinition.attributes && styleDefinition.attributes[ 'style' ] ) || '',
322                                specialStylesText = '';
323
324                if ( stylesText.length )
325                        stylesText = stylesText.replace( semicolonFixRegex, ';' );
326
327                for ( var style in stylesDef )
328                        if ( ! ( style in Object.prototype ) )
329                        {
330                                var styleVal = stylesDef[ style ],
331                                                text = ( style + ':' + styleVal ).replace( semicolonFixRegex, ';' );
332
333                                // Some browsers don't support 'inherit' property value, leave them intact. (#5242)
334                                if ( styleVal == 'inherit' )
335                                        specialStylesText += text;
336                                else
337                                        stylesText += text;
338                        }
339
340                // Browsers make some changes to the style when applying them. So, here
341                // we normalize it to the browser format.
342                if ( stylesText.length )
343                        stylesText = normalizeCssText( stylesText );
344
345                stylesText += specialStylesText;
346
347                // Return it, saving it to the next request.
348                return ( styleDefinition._ST = stylesText );
349        };
350
351        function applyInlineStyle( range )
352        {
353                var document = range.document;
354
355                if ( range.collapsed )
356                {
357                        // Create the element to be inserted in the DOM.
358                        var collapsedElement = getElement( this, document );
359
360                        // Insert the empty element into the DOM at the range position.
361                        range.insertNode( collapsedElement );
362
363                        // Place the selection right inside the empty element.
364                        range.moveToPosition( collapsedElement, CKEDITOR.POSITION_BEFORE_END );
365
366                        return;
367                }
368
369                var elementName = this.element;
370                var def = this._.definition;
371                var isUnknownElement;
372
373                // Get the DTD definition for the element. Defaults to "span".
374                var dtd = CKEDITOR.dtd[ elementName ] || ( isUnknownElement = true, CKEDITOR.dtd.span );
375
376                // Bookmark the range so we can re-select it after processing.
377                var bookmark = range.createBookmark();
378
379                // Expand the range.
380                range.enlarge( CKEDITOR.ENLARGE_ELEMENT );
381                range.trim();
382
383                // Get the first node to be processed and the last, which concludes the
384                // processing.
385                var boundaryNodes = range.getBoundaryNodes();
386                var firstNode = boundaryNodes.startNode;
387                var lastNode = boundaryNodes.endNode.getNextSourceNode( true );
388
389                // Probably the document end is reached, we need a marker node.
390                if ( !lastNode )
391                {
392                                var marker;
393                                lastNode = marker = document.createText( '' );
394                                lastNode.insertAfter( range.endContainer );
395                }
396                // The detection algorithm below skips the contents inside bookmark nodes, so
397                // we'll need to make sure lastNode isn't the &nbsp; inside a bookmark node.
398                var lastParent = lastNode.getParent();
399                if ( lastParent && lastParent.getAttribute( '_fck_bookmark' ) )
400                        lastNode = lastParent;
401
402                if ( lastNode.equals( firstNode ) )
403                {
404                        // If the last node is the same as the the first one, we must move
405                        // it to the next one, otherwise the first one will not be
406                        // processed.
407                        lastNode = lastNode.getNextSourceNode( true );
408
409                        // It may happen that there are no more nodes after it (the end of
410                        // the document), so we must add something there to make our code
411                        // simpler.
412                        if ( !lastNode )
413                        {
414                                lastNode = marker = document.createText( '' );
415                                lastNode.insertAfter( firstNode );
416                        }
417                }
418
419                var currentNode = firstNode;
420
421                var styleRange;
422
423                while ( currentNode )
424                {
425                        var applyStyle = false;
426
427                        if ( currentNode.equals( lastNode ) )
428                        {
429                                currentNode = null;
430                                applyStyle = true;
431                        }
432                        else
433                        {
434                                var nodeType = currentNode.type;
435                                var nodeName = nodeType == CKEDITOR.NODE_ELEMENT ? currentNode.getName() : null;
436
437                                if ( nodeName && currentNode.getAttribute( '_fck_bookmark' ) )
438                                {
439                                        currentNode = currentNode.getNextSourceNode( true );
440                                        continue;
441                                }
442
443                                // Check if the current node can be a child of the style element.
444                                if ( !nodeName || ( dtd[ nodeName ]
445                                        && ( currentNode.getPosition( lastNode ) | CKEDITOR.POSITION_PRECEDING | CKEDITOR.POSITION_IDENTICAL | CKEDITOR.POSITION_IS_CONTAINED ) == ( CKEDITOR.POSITION_PRECEDING + CKEDITOR.POSITION_IDENTICAL + CKEDITOR.POSITION_IS_CONTAINED )
446                                        && ( !def.childRule || def.childRule( currentNode ) ) ) )
447                                {
448                                        var currentParent = currentNode.getParent();
449
450                                        // Check if the style element can be a child of the current
451                                        // node parent or if the element is not defined in the DTD.
452                                        if ( currentParent
453                                                && ( ( currentParent.getDtd() || CKEDITOR.dtd.span )[ elementName ] || isUnknownElement )
454                                                && ( !def.parentRule || def.parentRule( currentParent ) ) )
455                                        {
456                                                // This node will be part of our range, so if it has not
457                                                // been started, place its start right before the node.
458                                                // In the case of an element node, it will be included
459                                                // only if it is entirely inside the range.
460                                                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 ) ) )
461                                                {
462                                                        styleRange = new CKEDITOR.dom.range( document );
463                                                        styleRange.setStartBefore( currentNode );
464                                                }
465
466                                                // Non element nodes, or empty elements can be added
467                                                // completely to the range.
468                                                if ( nodeType == CKEDITOR.NODE_TEXT || ( nodeType == CKEDITOR.NODE_ELEMENT && !currentNode.getChildCount() ) )
469                                                {
470                                                        var includedNode = currentNode;
471                                                        var parentNode;
472
473                                                        // This node is about to be included completelly, but,
474                                                        // if this is the last node in its parent, we must also
475                                                        // check if the parent itself can be added completelly
476                                                        // to the range.
477                                                        while ( !includedNode.$.nextSibling
478                                                                && ( parentNode = includedNode.getParent(), dtd[ parentNode.getName() ] )
479                                                                && ( parentNode.getPosition( firstNode ) | CKEDITOR.POSITION_FOLLOWING | CKEDITOR.POSITION_IDENTICAL | CKEDITOR.POSITION_IS_CONTAINED ) == ( CKEDITOR.POSITION_FOLLOWING + CKEDITOR.POSITION_IDENTICAL + CKEDITOR.POSITION_IS_CONTAINED )
480                                                                && ( !def.childRule || def.childRule( parentNode ) ) )
481                                                        {
482                                                                includedNode = parentNode;
483                                                        }
484
485                                                        styleRange.setEndAfter( includedNode );
486
487                                                        // If the included node still is the last node in its
488                                                        // parent, it means that the parent can't be included
489                                                        // in this style DTD, so apply the style immediately.
490                                                        if ( !includedNode.$.nextSibling )
491                                                                applyStyle = true;
492
493                                                }
494                                        }
495                                        else
496                                                applyStyle = true;
497                                }
498                                else
499                                        applyStyle = true;
500
501                                // Get the next node to be processed.
502                                currentNode = currentNode.getNextSourceNode();
503                        }
504
505                        // Apply the style if we have something to which apply it.
506                        if ( applyStyle && styleRange && !styleRange.collapsed )
507                        {
508                                // Build the style element, based on the style object definition.
509                                var styleNode = getElement( this, document );
510
511                                // Get the element that holds the entire range.
512                                var parent = styleRange.getCommonAncestor();
513
514                                // Loop through the parents, removing the redundant attributes
515                                // from the element to be applied.
516                                while ( styleNode && parent )
517                                {
518                                        if ( parent.getName() == elementName )
519                                        {
520                                                for ( var attName in def.attributes )
521                                                        if ( ! ( attName in Object.prototype ) )
522                                                        {
523                                                                if ( styleNode.getAttribute( attName ) == parent.getAttribute( attName ) )
524                                                                        styleNode.removeAttribute( attName );
525                                                        }
526
527                                                for ( var styleName in def.styles )
528                                                        if ( ! ( styleName in Object.prototype ) )
529                                                        {
530                                                                if ( styleNode.getStyle( styleName ) == parent.getStyle( styleName ) )
531                                                                        styleNode.removeStyle( styleName );
532                                                        }
533
534                                                if ( !styleNode.hasAttributes() )
535                                                {
536                                                        styleNode = null;
537                                                        break;
538                                                }
539                                        }
540
541                                        parent = parent.getParent();
542                                }
543
544                                if ( styleNode )
545                                {
546                                        // Move the contents of the range to the style element.
547                                        styleRange.extractContents().appendTo( styleNode );
548
549                                        // Here we do some cleanup, removing all duplicated
550                                        // elements from the style element.
551                                        removeFromInsideElement( this, styleNode );
552
553                                        // Insert it into the range position (it is collapsed after
554                                        // extractContents.
555                                        styleRange.insertNode( styleNode );
556
557                                        // Let's merge our new style with its neighbors, if possible.
558                                        mergeSiblings( styleNode );
559
560                                        // As the style system breaks text nodes constantly, let's normalize
561                                        // things for performance.
562                                        // With IE, some paragraphs get broken when calling normalize()
563                                        // repeatedly. Also, for IE, we must normalize body, not documentElement.
564                                        // IE is also known for having a "crash effect" with normalize().
565                                        // We should try to normalize with IE too in some way, somewhere.
566                                        if ( !CKEDITOR.env.ie )
567                                                styleNode.$.normalize();
568                                }
569
570                                // Style applied, let's release the range, so it gets
571                                // re-initialization in the next loop.
572                                styleRange = null;
573                        }
574                }
575
576                // Remove the temporary marking node.(#4111)
577                marker && marker.remove();
578                range.moveToBookmark( bookmark );
579                // Minimize the result range to exclude empty text nodes. (#5374)
580                range.shrink( CKEDITOR.SHRINK_TEXT );
581        }
582
583        function removeInlineStyle( range )
584        {
585                /*
586                 * Make sure our range has included all "collpased" parent inline nodes so
587                 * that our operation logic can be simpler.
588                 */
589                range.enlarge( CKEDITOR.ENLARGE_ELEMENT );
590
591                var bookmark = range.createBookmark(),
592                        startNode = bookmark.startNode;
593
594                if ( range.collapsed )
595                {
596
597                        var startPath = new CKEDITOR.dom.elementPath( startNode.getParent() ),
598                                // The topmost element in elementspatch which we should jump out of.
599                                boundaryElement;
600
601
602                        for ( var i = 0, element ; i < startPath.elements.length
603                                        && ( element = startPath.elements[i] ) ; i++ )
604                        {
605                                /*
606                                 * 1. If it's collaped inside text nodes, try to remove the style from the whole element.
607                                 *
608                                 * 2. Otherwise if it's collapsed on element boundaries, moving the selection
609                                 *  outside the styles instead of removing the whole tag,
610                                 *  also make sure other inner styles were well preserverd.(#3309)
611                                 */
612                                if ( element == startPath.block || element == startPath.blockLimit )
613                                        break;
614
615                                if ( this.checkElementRemovable( element ) )
616                                {
617                                        var endOfElement = range.checkBoundaryOfElement( element, CKEDITOR.END ),
618                                                        startOfElement = !endOfElement && range.checkBoundaryOfElement( element, CKEDITOR.START );
619                                        if ( startOfElement || endOfElement )
620                                        {
621                                                boundaryElement = element;
622                                                boundaryElement.match = startOfElement ? 'start' : 'end';
623                                        }
624                                        else
625                                        {
626                                                /*
627                                                 * Before removing the style node, there may be a sibling to the style node
628                                                 * that's exactly the same to the one to be removed. To the user, it makes
629                                                 * no difference that they're separate entities in the DOM tree. So, merge
630                                                 * them before removal.
631                                                 */
632                                                mergeSiblings( element );
633                                                removeFromElement( this, element );
634
635                                        }
636                                }
637                        }
638
639                        // Re-create the style tree after/before the boundary element,
640                        // the replication start from bookmark start node to define the
641                        // new range.
642                        if ( boundaryElement )
643                        {
644                                var clonedElement = startNode;
645                                for ( i = 0 ;; i++ )
646                                {
647                                        var newElement = startPath.elements[ i ];
648                                        if ( newElement.equals( boundaryElement ) )
649                                                break;
650                                        // Avoid copying any matched element.
651                                        else if ( newElement.match )
652                                                continue;
653                                        else
654                                                newElement = newElement.clone();
655                                        newElement.append( clonedElement );
656                                        clonedElement = newElement;
657                                }
658                                clonedElement[ boundaryElement.match == 'start' ?
659                                                        'insertBefore' : 'insertAfter' ]( boundaryElement );
660                        }
661                }
662                else
663                {
664                        /*
665                         * Now our range isn't collapsed. Lets walk from the start node to the end
666                         * node via DFS and remove the styles one-by-one.
667                         */
668                        var endNode = bookmark.endNode,
669                                me = this;
670
671                        /*
672                         * Find out the style ancestor that needs to be broken down at startNode
673                         * and endNode.
674                         */
675                        function breakNodes()
676                        {
677                                var startPath = new CKEDITOR.dom.elementPath( startNode.getParent() ),
678                                        endPath = new CKEDITOR.dom.elementPath( endNode.getParent() ),
679                                        breakStart = null,
680                                        breakEnd = null;
681                                for ( var i = 0 ; i < startPath.elements.length ; i++ )
682                                {
683                                        var element = startPath.elements[ i ];
684
685                                        if ( element == startPath.block || element == startPath.blockLimit )
686                                                break;
687
688                                        if ( me.checkElementRemovable( element ) )
689                                                breakStart = element;
690                                }
691                                for ( i = 0 ; i < endPath.elements.length ; i++ )
692                                {
693                                        element = endPath.elements[ i ];
694
695                                        if ( element == endPath.block || element == endPath.blockLimit )
696                                                break;
697
698                                        if ( me.checkElementRemovable( element ) )
699                                                breakEnd = element;
700                                }
701
702                                if ( breakEnd )
703                                        endNode.breakParent( breakEnd );
704                                if ( breakStart )
705                                        startNode.breakParent( breakStart );
706                        }
707                        breakNodes();
708
709                        // Now, do the DFS walk.
710                        var currentNode = startNode.getNext();
711                        while ( !currentNode.equals( endNode ) )
712                        {
713                                /*
714                                 * Need to get the next node first because removeFromElement() can remove
715                                 * the current node from DOM tree.
716                                 */
717                                var nextNode = currentNode.getNextSourceNode();
718                                if ( currentNode.type == CKEDITOR.NODE_ELEMENT && this.checkElementRemovable( currentNode ) )
719                                {
720                                        // Remove style from element or overriding element.
721                                        if ( currentNode.getName() == this.element )
722                                                removeFromElement( this, currentNode );
723                                        else
724                                                removeOverrides( currentNode, getOverrides( this )[ currentNode.getName() ] );
725
726                                        /*
727                                         * removeFromElement() may have merged the next node with something before
728                                         * the startNode via mergeSiblings(). In that case, the nextNode would
729                                         * contain startNode and we'll have to call breakNodes() again and also
730                                         * reassign the nextNode to something after startNode.
731                                         */
732                                        if ( nextNode.type == CKEDITOR.NODE_ELEMENT && nextNode.contains( startNode ) )
733                                        {
734                                                breakNodes();
735                                                nextNode = startNode.getNext();
736                                        }
737                                }
738                                currentNode = nextNode;
739                        }
740                }
741
742                range.moveToBookmark( bookmark );
743}
744
745        function applyObjectStyle( range )
746        {
747                var root = range.getCommonAncestor( true, true ),
748                                element = root.getAscendant( this.element, true );
749                element && setupElement( element, this );
750        }
751
752        function applyBlockStyle( range )
753        {
754                // Serializible bookmarks is needed here since
755                // elements may be merged.
756                var bookmark = range.createBookmark( true );
757
758                var iterator = range.createIterator();
759                iterator.enforceRealBlocks = true;
760
761                var block;
762                var doc = range.document;
763                var previousPreBlock;
764
765                while ( ( block = iterator.getNextParagraph() ) )               // Only one =
766                {
767                        var newBlock = getElement( this, doc );
768                        replaceBlock( block, newBlock );
769                }
770
771                range.moveToBookmark( bookmark );
772        }
773
774        // Replace the original block with new one, with special treatment
775        // for <pre> blocks to make sure content format is well preserved, and merging/splitting adjacent
776        // when necessary.(#3188)
777        function replaceBlock( block, newBlock )
778        {
779                var newBlockIsPre       = newBlock.is( 'pre' );
780                var blockIsPre          = block.is( 'pre' );
781
782                var isToPre     = newBlockIsPre && !blockIsPre;
783                var isFromPre   = !newBlockIsPre && blockIsPre;
784
785                if ( isToPre )
786                        newBlock = toPre( block, newBlock );
787                else if ( isFromPre )
788                        // Split big <pre> into pieces before start to convert.
789                        newBlock = fromPres( splitIntoPres( block ), newBlock );
790                else
791                        block.moveChildren( newBlock );
792
793                newBlock.replace( block );
794
795                if ( newBlockIsPre )
796                {
797                        // Merge previous <pre> blocks.
798                        mergePre( newBlock );
799                }
800        }
801
802        /**
803         * Merge a <pre> block with a previous sibling if available.
804         */
805        function mergePre( preBlock )
806        {
807                var previousBlock;
808                if ( !( ( previousBlock = preBlock.getPreviousSourceNode( true, CKEDITOR.NODE_ELEMENT ) )
809                                 && previousBlock.is
810                                 && previousBlock.is( 'pre') ) )
811                        return;
812
813                // Merge the previous <pre> block contents into the current <pre>
814                // block.
815                //
816                // Another thing to be careful here is that currentBlock might contain
817                // a '\n' at the beginning, and previousBlock might contain a '\n'
818                // towards the end. These new lines are not normally displayed but they
819                // become visible after merging.
820                var mergedHtml = replace( previousBlock.getHtml(), /\n$/, '' ) + '\n\n' +
821                                replace( preBlock.getHtml(), /^\n/, '' ) ;
822
823                // Krugle: IE normalizes innerHTML from <pre>, breaking whitespaces.
824                if ( CKEDITOR.env.ie )
825                        preBlock.$.outerHTML = '<pre>' + mergedHtml + '</pre>';
826                else
827                        preBlock.setHtml( mergedHtml );
828
829                previousBlock.remove();
830        }
831
832        /**
833         * Split into multiple <pre> blocks separated by double line-break.
834         * @param preBlock
835         */
836        function splitIntoPres( preBlock )
837        {
838                // Exclude the ones at header OR at tail,
839                // and ignore bookmark content between them.
840                var duoBrRegex = /(\S\s*)\n(?:\s|(<span[^>]+_fck_bookmark.*?\/span>))*\n(?!$)/gi,
841                        blockName = preBlock.getName(),
842                        splitedHtml = replace( preBlock.getOuterHtml(),
843                                duoBrRegex,
844                                function( match, charBefore, bookmark )
845                                {
846                                  return charBefore + '</pre>' + bookmark + '<pre>';
847                                } );
848
849                var pres = [];
850                splitedHtml.replace( /<pre\b.*?>([\s\S]*?)<\/pre>/gi, function( match, preContent ){
851                        pres.push( preContent );
852                } );
853                return pres;
854        }
855
856        // Wrapper function of String::replace without considering of head/tail bookmarks nodes.
857        function replace( str, regexp, replacement )
858        {
859                var headBookmark = '',
860                        tailBookmark = '';
861
862                str = str.replace( /(^<span[^>]+_fck_bookmark.*?\/span>)|(<span[^>]+_fck_bookmark.*?\/span>$)/gi,
863                        function( str, m1, m2 ){
864                                        m1 && ( headBookmark = m1 );
865                                        m2 && ( tailBookmark = m2 );
866                                return '';
867                        } );
868                return headBookmark + str.replace( regexp, replacement ) + tailBookmark;
869        }
870        /**
871         * Converting a list of <pre> into blocks with format well preserved.
872         */
873        function fromPres( preHtmls, newBlock )
874        {
875                var docFrag = new CKEDITOR.dom.documentFragment( newBlock.getDocument() );
876                for ( var i = 0 ; i < preHtmls.length ; i++ )
877                {
878                        var blockHtml = preHtmls[ i ];
879
880                        // 1. Trim the first and last line-breaks immediately after and before <pre>,
881                        // they're not visible.
882                         blockHtml =  blockHtml.replace( /(\r\n|\r)/g, '\n' ) ;
883                         blockHtml = replace(  blockHtml, /^[ \t]*\n/, '' ) ;
884                         blockHtml = replace(  blockHtml, /\n$/, '' ) ;
885                        // 2. Convert spaces or tabs at the beginning or at the end to &nbsp;
886                         blockHtml = replace(  blockHtml, /^[ \t]+|[ \t]+$/g, function( match, offset, s )
887                                        {
888                                                if ( match.length == 1 )        // one space, preserve it
889                                                        return '&nbsp;' ;
890                                                else if ( !offset )             // beginning of block
891                                                        return CKEDITOR.tools.repeat( '&nbsp;', match.length - 1 ) + ' ';
892                                                else                            // end of block
893                                                        return ' ' + CKEDITOR.tools.repeat( '&nbsp;', match.length - 1 );
894                                        } ) ;
895
896                        // 3. Convert \n to <BR>.
897                        // 4. Convert contiguous (i.e. non-singular) spaces or tabs to &nbsp;
898                         blockHtml =  blockHtml.replace( /\n/g, '<br>' ) ;
899                         blockHtml =  blockHtml.replace( /[ \t]{2,}/g,
900                                        function ( match )
901                                        {
902                                                return CKEDITOR.tools.repeat( '&nbsp;', match.length - 1 ) + ' ' ;
903                                        } ) ;
904
905                        var newBlockClone = newBlock.clone();
906                        newBlockClone.setHtml(  blockHtml );
907                        docFrag.append( newBlockClone );
908                }
909                return docFrag;
910        }
911
912        /**
913         * Converting from a non-PRE block to a PRE block in formatting operations.
914         */
915        function toPre( block, newBlock )
916        {
917                // First trim the block content.
918                var preHtml = block.getHtml();
919
920                // 1. Trim head/tail spaces, they're not visible.
921                preHtml = replace( preHtml, /(?:^[ \t\n\r]+)|(?:[ \t\n\r]+$)/g, '' );
922                // 2. Delete ANSI whitespaces immediately before and after <BR> because
923                //    they are not visible.
924                preHtml = preHtml.replace( /[ \t\r\n]*(<br[^>]*>)[ \t\r\n]*/gi, '$1' );
925                // 3. Compress other ANSI whitespaces since they're only visible as one
926                //    single space previously.
927                // 4. Convert &nbsp; to spaces since &nbsp; is no longer needed in <PRE>.
928                preHtml = preHtml.replace( /([ \t\n\r]+|&nbsp;)/g, ' ' );
929                // 5. Convert any <BR /> to \n. This must not be done earlier because
930                //    the \n would then get compressed.
931                preHtml = preHtml.replace( /<br\b[^>]*>/gi, '\n' );
932
933                // Krugle: IE normalizes innerHTML to <pre>, breaking whitespaces.
934                if ( CKEDITOR.env.ie )
935                {
936                        var temp = block.getDocument().createElement( 'div' );
937                        temp.append( newBlock );
938                        newBlock.$.outerHTML =  '<pre>' + preHtml + '</pre>';
939                        newBlock = temp.getFirst().remove();
940                }
941                else
942                        newBlock.setHtml( preHtml );
943
944                return newBlock;
945        }
946
947        // Removes a style from an element itself, don't care about its subtree.
948        function removeFromElement( style, element )
949        {
950                var def = style._.definition,
951                        attributes = CKEDITOR.tools.extend( {}, def.attributes, getOverrides( style )[ element.getName() ] ),
952                        styles = def.styles,
953                        // If the style is only about the element itself, we have to remove the element.
954                        removeEmpty = CKEDITOR.tools.isEmpty( attributes ) && CKEDITOR.tools.isEmpty( styles );
955
956                // Remove definition attributes/style from the elemnt.
957                for ( var attName in attributes )
958                        if ( ! ( attName in Object.prototype ) )
959                        {
960                                // The 'class' element value must match (#1318).
961                                if ( ( attName == 'class' || style._.definition.fullMatch )
962                                        && element.getAttribute( attName ) != normalizeProperty( attName, attributes[ attName ] ) )
963                                        continue;
964                                removeEmpty = element.hasAttribute( attName );
965                                element.removeAttribute( attName );
966                        }
967
968                for ( var styleName in styles )
969                        if ( ! ( styleName in Object.prototype ) )
970                        {
971                                // Full match style insist on having fully equivalence. (#5018)
972                                if ( style._.definition.fullMatch
973                                        && element.getStyle( styleName ) != normalizeProperty( styleName, styles[ styleName ], true ) )
974                                        continue;
975
976                                removeEmpty = removeEmpty || !!element.getStyle( styleName );
977                                element.removeStyle( styleName );
978                        }
979
980                removeEmpty && removeNoAttribsElement( element );
981        }
982
983        // Removes a style from inside an element.
984        function removeFromInsideElement( style, element )
985        {
986                var def = style._.definition,
987                        attribs = def.attributes,
988                        styles = def.styles,
989                        overrides = getOverrides( style );
990
991                var innerElements = element.getElementsByTag( style.element );
992
993                for ( var i = innerElements.count(); --i >= 0 ; )
994                        removeFromElement( style,  innerElements.getItem( i ) );
995
996                // Now remove any other element with different name that is
997                // defined to be overriden.
998                for ( var overrideElement in overrides )
999                        if ( ! ( overrideElement in Object.prototype ) )
1000                        {
1001                                if ( overrideElement != style.element )
1002                                {
1003                                        innerElements = element.getElementsByTag( overrideElement ) ;
1004                                        for ( i = innerElements.count() - 1 ; i >= 0 ; i-- )
1005                                        {
1006                                                var innerElement = innerElements.getItem( i );
1007                                                removeOverrides( innerElement, overrides[ overrideElement ] ) ;
1008                                        }
1009                                }
1010                        }
1011
1012        }
1013
1014        /**
1015         *  Remove overriding styles/attributes from the specific element.
1016         *  Note: Remove the element if no attributes remain.
1017         * @param {Object} element
1018         * @param {Object} overrides
1019         */
1020        function removeOverrides( element, overrides )
1021        {
1022                var attributes = overrides && overrides.attributes ;
1023
1024                if ( attributes )
1025                {
1026                        for ( var i = 0 ; i < attributes.length ; i++ )
1027                        {
1028                                var attName = attributes[i][0], actualAttrValue ;
1029
1030                                if ( ( actualAttrValue = element.getAttribute( attName ) ) )
1031                                {
1032                                        var attValue = attributes[i][1] ;
1033
1034                                        // Remove the attribute if:
1035                                        //    - The override definition value is null ;
1036                                        //    - The override definition valie is a string that
1037                                        //      matches the attribute value exactly.
1038                                        //    - The override definition value is a regex that
1039                                        //      has matches in the attribute value.
1040                                        if ( attValue === null ||
1041                                                        ( attValue.test && attValue.test( actualAttrValue ) ) ||
1042                                                        ( typeof attValue == 'string' && actualAttrValue == attValue ) )
1043                                                element.removeAttribute( attName ) ;
1044                                }
1045                        }
1046                }
1047
1048                removeNoAttribsElement( element );
1049        }
1050
1051        // If the element has no more attributes, remove it.
1052        function removeNoAttribsElement( element )
1053        {
1054                // If no more attributes remained in the element, remove it,
1055                // leaving its children.
1056                if ( !element.hasAttributes() )
1057                {
1058                        // Removing elements may open points where merging is possible,
1059                        // so let's cache the first and last nodes for later checking.
1060                        var firstChild  = element.getFirst();
1061                        var lastChild   = element.getLast();
1062
1063                        element.remove( true );
1064
1065                        if ( firstChild )
1066                        {
1067                                // Check the cached nodes for merging.
1068                                mergeSiblings( firstChild );
1069
1070                                if ( lastChild && !firstChild.equals( lastChild ) )
1071                                        mergeSiblings( lastChild );
1072                        }
1073                }
1074        }
1075
1076        function mergeSiblings( element )
1077        {
1078                if ( !element || element.type != CKEDITOR.NODE_ELEMENT || !CKEDITOR.dtd.$removeEmpty[ element.getName() ] )
1079                        return;
1080
1081                mergeElements( element, element.getNext(), true );
1082                mergeElements( element, element.getPrevious() );
1083        }
1084
1085        function mergeElements( element, sibling, isNext )
1086        {
1087                if ( sibling && sibling.type == CKEDITOR.NODE_ELEMENT )
1088                {
1089                        var hasBookmark = sibling.getAttribute( '_fck_bookmark' );
1090
1091                        if ( hasBookmark )
1092                                sibling = isNext ? sibling.getNext() : sibling.getPrevious();
1093
1094                        if ( sibling && sibling.type == CKEDITOR.NODE_ELEMENT && element.isIdentical( sibling ) )
1095                        {
1096                                // Save the last child to be checked too, to merge things like
1097                                // <b><i></i></b><b><i></i></b> => <b><i></i></b>
1098                                var innerSibling = isNext ? element.getLast() : element.getFirst();
1099
1100                                if ( hasBookmark )
1101                                        ( isNext ? sibling.getPrevious() : sibling.getNext() ).move( element, !isNext );
1102
1103                                sibling.moveChildren( element, !isNext );
1104                                sibling.remove();
1105
1106                                // Now check the last inner child (see two comments above).
1107                                if ( innerSibling )
1108                                        mergeSiblings( innerSibling );
1109                        }
1110                }
1111        }
1112
1113        function getElement( style, targetDocument )
1114        {
1115                var el;
1116
1117                var def = style._.definition;
1118
1119                var elementName = style.element;
1120
1121                // The "*" element name will always be a span for this function.
1122                if ( elementName == '*' )
1123                        elementName = 'span';
1124
1125                // Create the element.
1126                el = new CKEDITOR.dom.element( elementName, targetDocument );
1127
1128                return setupElement( el, style );
1129        }
1130
1131        function setupElement( el, style )
1132        {
1133                var def = style._.definition;
1134                var attributes = def.attributes;
1135                var styles = CKEDITOR.style.getStyleText( def );
1136
1137                // Assign all defined attributes.
1138                if ( attributes )
1139                {
1140                        for ( var att in attributes )
1141                                if ( ! ( att in Object.prototype ) )
1142                                {
1143                                        el.setAttribute( att, attributes[ att ] );
1144                                }
1145                }
1146
1147                // Assign all defined styles.
1148                if ( styles )
1149                        el.setAttribute( 'style', styles );
1150
1151                return el;
1152        }
1153
1154        var varRegex = /#\((.+?)\)/g;
1155        function replaceVariables( list, variablesValues )
1156        {
1157                for ( var item in list )
1158                        if ( ! ( item in Object.prototype ) )
1159                        {
1160                                list[ item ] = list[ item ].replace( varRegex, function( match, varName )
1161                                        {
1162                                                return variablesValues[ varName ];
1163                                        });
1164                        }
1165        }
1166
1167
1168        // Returns an object that can be used for style matching comparison.
1169        // Attributes names and values are all lowercased, and the styles get
1170        // merged with the style attribute.
1171        function getAttributesForComparison( styleDefinition )
1172        {
1173                // If we have already computed it, just return it.
1174                var attribs = styleDefinition._AC;
1175                if ( attribs )
1176                        return attribs;
1177
1178                attribs = {};
1179
1180                var length = 0;
1181
1182                // Loop through all defined attributes.
1183                var styleAttribs = styleDefinition.attributes;
1184                if ( styleAttribs )
1185                {
1186                        for ( var styleAtt in styleAttribs )
1187                                if ( ! ( styleAtt in Object.prototype ) )
1188                                {
1189                                        length++;
1190                                        attribs[ styleAtt ] = styleAttribs[ styleAtt ];
1191                                }
1192                }
1193
1194                // Includes the style definitions.
1195                var styleText = CKEDITOR.style.getStyleText( styleDefinition );
1196                if ( styleText )
1197                {
1198                        if ( !attribs[ 'style' ] )
1199                                length++;
1200                        attribs[ 'style' ] = styleText;
1201                }
1202
1203                // Appends the "length" information to the object.
1204                attribs._length = length;
1205
1206                // Return it, saving it to the next request.
1207                return ( styleDefinition._AC = attribs );
1208        }
1209
1210        /**
1211         * Get the the collection used to compare the elements and attributes,
1212         * defined in this style overrides, with other element. All information in
1213         * it is lowercased.
1214         * @param {CKEDITOR.style} style
1215         */
1216        function getOverrides( style )
1217        {
1218                if ( style._.overrides )
1219                        return style._.overrides;
1220
1221                var overrides = ( style._.overrides = {} ),
1222                        definition = style._.definition.overrides;
1223
1224                if ( definition )
1225                {
1226                        // The override description can be a string, object or array.
1227                        // Internally, well handle arrays only, so transform it if needed.
1228                        if ( !CKEDITOR.tools.isArray( definition ) )
1229                                definition = [ definition ];
1230
1231                        // Loop through all override definitions.
1232                        for ( var i = 0 ; i < definition.length ; i++ )
1233                        {
1234                                var override = definition[i];
1235                                var elementName;
1236                                var overrideEl;
1237                                var attrs;
1238
1239                                // If can be a string with the element name.
1240                                if ( typeof override == 'string' )
1241                                        elementName = override.toLowerCase();
1242                                // Or an object.
1243                                else
1244                                {
1245                                        elementName = override.element ? override.element.toLowerCase() : style.element;
1246                                        attrs = override.attributes;
1247                                }
1248
1249                                // We can have more than one override definition for the same
1250                                // element name, so we attempt to simply append information to
1251                                // it if it already exists.
1252                                overrideEl = overrides[ elementName ] || ( overrides[ elementName ] = {} );
1253
1254                                if ( attrs )
1255                                {
1256                                        // The returning attributes list is an array, because we
1257                                        // could have different override definitions for the same
1258                                        // attribute name.
1259                                        var overrideAttrs = ( overrideEl.attributes = overrideEl.attributes || new Array() );
1260                                        for ( var attName in attrs )
1261                                                if ( ! ( attName in Object.prototype ) )
1262                                                {
1263                                                        // Each item in the attributes array is also an array,
1264                                                        // where [0] is the attribute name and [1] is the
1265                                                        // override value.
1266                                                        overrideAttrs.push( [ attName.toLowerCase(), attrs[ attName ] ] );
1267                                                }
1268                                }
1269                        }
1270                }
1271
1272                return overrides;
1273        }
1274
1275        function normalizeProperty( name, value, isStyle )
1276        {
1277                var temp = new CKEDITOR.dom.element( 'span' );
1278                temp [ isStyle ? 'setStyle' : 'setAttribute' ]( name, value );
1279                return temp[ isStyle ? 'getStyle' : 'getAttribute' ]( name );
1280        }
1281
1282        function normalizeCssText( unparsedCssText, nativeNormalize )
1283        {
1284                var styleText;
1285                if ( nativeNormalize !== false )
1286                {
1287                        // Injects the style in a temporary span object, so the browser parses it,
1288                        // retrieving its final format.
1289                        var temp = new CKEDITOR.dom.element( 'span' );
1290                        temp.setAttribute( 'style', unparsedCssText );
1291                        styleText = temp.getAttribute( 'style' ) || '';
1292                }
1293                else
1294                        styleText = unparsedCssText;
1295
1296                // Shrinking white-spaces around colon and semi-colon (#4147).
1297                // Compensate tail semi-colon.
1298                return styleText.replace( /\s*([;:])\s*/, '$1' )
1299                                                         .replace( /([^\s;])$/, '$1;')
1300                                                         .replace( /,\s+/g, ',' ) // Trimming spaces after comma (e.g. font-family name)(#4107).
1301                                                         .toLowerCase();
1302        }
1303
1304        // Turn inline style text properties into one hash.
1305        function parseStyleText( styleText )
1306        {
1307                var retval = {};
1308                styleText
1309                   .replace( /&quot;/g, '"' )
1310                   .replace( /\s*([^ :;]+)\s*:\s*([^;]+)\s*(?=;|$)/g, function( match, name, value )
1311                {
1312                        retval[ name ] = value;
1313                } );
1314                return retval;
1315        }
1316
1317        function compareCssText( source, target )
1318        {
1319                typeof source == 'string' && ( source = parseStyleText( source ) );
1320                typeof target == 'string' && ( target = parseStyleText( target ) );
1321                for( var name in source )
1322                        if ( ! ( name in Object.prototype ) )
1323                        {
1324                                // Value 'inherit'  is treated as a wildcard,
1325                                // which will match any value.
1326                                if ( !( name in target &&
1327                                                ( target[ name ] == source[ name ]
1328                                                        || source[ name ] == 'inherit'
1329                                                        || target[ name ] == 'inherit' ) ) )
1330                                {
1331                                        return false;
1332                                }
1333                        }
1334                return true;
1335        }
1336
1337        function applyStyle( document, remove )
1338        {
1339                // Get all ranges from the selection.
1340                var selection = document.getSelection();
1341                var ranges = selection.getRanges();
1342                var func = remove ? this.removeFromRange : this.applyToRange;
1343
1344                // Apply the style to the ranges.
1345                for ( var i = 0 ; i < ranges.length ; i++ )
1346                        func.call( this, ranges[ i ] );
1347
1348                // Select the ranges again.
1349                selection.selectRanges( ranges );
1350        }
1351})();
1352
1353CKEDITOR.styleCommand = function( style )
1354{
1355        this.style = style;
1356};
1357
1358CKEDITOR.styleCommand.prototype.exec = function( editor )
1359{
1360        editor.focus();
1361
1362        var doc = editor.document;
1363
1364        if ( doc )
1365        {
1366                if ( this.state == CKEDITOR.TRISTATE_OFF )
1367                        this.style.apply( doc );
1368                else if ( this.state == CKEDITOR.TRISTATE_ON )
1369                        this.style.remove( doc );
1370        }
1371
1372        return !!doc;
1373};
1374
1375CKEDITOR.stylesSet = new CKEDITOR.resourceManager( '', 'stylesSet' );
1376
1377// Backward compatibility (#5025).
1378CKEDITOR.addStylesSet = CKEDITOR.tools.bind( CKEDITOR.stylesSet.add, CKEDITOR.stylesSet );
1379CKEDITOR.loadStylesSet = function( name, url, callback )
1380        {
1381                CKEDITOR.stylesSet.addExternal( name, url, '' );
1382                CKEDITOR.stylesSet.load( name, callback );
1383        };
1384
1385
1386/**
1387 * Gets the current styleSet for this instance
1388 * @param {Function} The function to be called with the styles data.
1389 * @example
1390 * editor.getStylesSet( function( stylesDefinitions ) {} );
1391 */
1392CKEDITOR.editor.prototype.getStylesSet = function( callback )
1393{
1394        if ( !this._.stylesDefinitions )
1395        {
1396                var editor = this,
1397                        // Respect the backwards compatible definition entry
1398                        configStyleSet = editor.config.stylesCombo_stylesSet || editor.config.stylesSet || 'default';
1399
1400                // #5352 Allow to define the styles directly in the config object
1401                if ( configStyleSet instanceof Array )
1402                {
1403                        editor._.stylesDefinitions = configStyleSet;
1404                        callback( configStyleSet );
1405                        return;
1406                }
1407
1408                var     partsStylesSet = configStyleSet.split( ':' ),
1409                        styleSetName = partsStylesSet[ 0 ],
1410                        externalPath = partsStylesSet[ 1 ],
1411                        pluginPath = CKEDITOR.plugins.registered.styles.path;
1412
1413                CKEDITOR.stylesSet.addExternal( styleSetName,
1414                                externalPath ?
1415                                        partsStylesSet.slice( 1 ).join( ':' ) :
1416                                        pluginPath + 'styles/' + styleSetName + '.js', '' );
1417
1418                CKEDITOR.stylesSet.load( styleSetName, function( stylesSet )
1419                        {
1420                                editor._.stylesDefinitions = stylesSet[ styleSetName ];
1421                                callback( editor._.stylesDefinitions );
1422                        } ) ;
1423        }
1424        else
1425                callback( this._.stylesDefinitions );
1426};
1427
1428/**
1429 * The "styles definition set" to use in the editor. They will be used in the
1430 * styles combo and the Style selector of the div container. <br>
1431 * The styles may be defined in the page containing the editor, or can be
1432 * loaded on demand from an external file. In the second case, if this setting
1433 * contains only a name, the styles definition file will be loaded from the
1434 * "styles" folder inside the styles plugin folder.
1435 * Otherwise, this setting has the "name:url" syntax, making it
1436 * possible to set the URL from which loading the styles file.<br>
1437 * Previously this setting was available as config.stylesCombo_stylesSet<br>
1438 * @type String|Array
1439 * @default 'default'
1440 * @since 3.3
1441 * @example
1442 * // Load from the styles' styles folder (mystyles.js file).
1443 * config.stylesSet = 'mystyles';
1444 * @example
1445 * // Load from a relative URL.
1446 * config.stylesSet = 'mystyles:/editorstyles/styles.js';
1447 * @example
1448 * // Load from a full URL.
1449 * config.stylesSet = 'mystyles:http://www.example.com/editorstyles/styles.js';
1450 * @example
1451 * // Load from a list of definitions.
1452 * config.stylesSet = [
1453 *  { name : 'Strong Emphasis', element : 'strong' },
1454 * { name : 'Emphasis', element : 'em' }, ... ];
1455 */
Note: See TracBrowser for help on using the repository browser.