JasonWoof Got questions, comments, patches, etc.? Contact Jason Woofenden
vanilla ckeditor-3.2.1
[ckeditor.git] / _source / plugins / styles / plugin.js
index 85cda5f..65a36c2 100644 (file)
@@ -78,7 +78,7 @@ CKEDITOR.STYLE_OBJECT = 3;
 (function()\r
 {\r
        var blockElements       = { address:1,div:1,h1:1,h2:1,h3:1,h4:1,h5:1,h6:1,p:1,pre:1 };\r
-       var objectElements      = { a:1,embed:1,hr:1,img:1,li:1,object:1,ol:1,table:1,td:1,tr:1,ul:1 };\r
+       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};\r
 \r
        var semicolonFixRegex = /\s*(?:;\s*|$)/;\r
 \r
@@ -127,6 +127,8 @@ CKEDITOR.STYLE_OBJECT = 3;
                                                        applyInlineStyle\r
                                                : this.type == CKEDITOR.STYLE_BLOCK ?\r
                                                        applyBlockStyle\r
+                                               : this.type == CKEDITOR.STYLE_OBJECT ?\r
+                                                       applyObjectStyle\r
                                                : null ).call( this, range );\r
                },\r
 \r
@@ -154,17 +156,23 @@ CKEDITOR.STYLE_OBJECT = 3;
                                case CKEDITOR.STYLE_BLOCK :\r
                                        return this.checkElementRemovable( elementPath.block || elementPath.blockLimit, true );\r
 \r
+                               case CKEDITOR.STYLE_OBJECT :\r
                                case CKEDITOR.STYLE_INLINE :\r
 \r
                                        var elements = elementPath.elements;\r
 \r
                                        for ( var i = 0, element ; i < elements.length ; i++ )\r
                                        {\r
-                                               element = elements[i];\r
+                                               element = elements[ i ];\r
 \r
-                                               if ( element == elementPath.block || element == elementPath.blockLimit )\r
+                                               if ( this.type == CKEDITOR.STYLE_INLINE\r
+                                                         && ( element == elementPath.block || element == elementPath.blockLimit ) )\r
                                                        continue;\r
 \r
+                                               if( this.type == CKEDITOR.STYLE_OBJECT\r
+                                                        && !( element.getName() in objectElements ) )\r
+                                                               continue;\r
+\r
                                                if ( this.checkElementRemovable( element, true ) )\r
                                                        return true;\r
                                        }\r
@@ -172,6 +180,21 @@ CKEDITOR.STYLE_OBJECT = 3;
                        return false;\r
                },\r
 \r
+               checkApplicable : function( elementPath )\r
+               {\r
+                       switch ( this.type )\r
+                       {\r
+                               case CKEDITOR.STYLE_INLINE :\r
+                               case CKEDITOR.STYLE_BLOCK :\r
+                                       break;\r
+\r
+                               case CKEDITOR.STYLE_OBJECT :\r
+                                       return elementPath.lastElement.getAscendant( this.element, true );\r
+                       }\r
+\r
+                       return true;\r
+               },\r
+\r
                // Checks if an element, or any of its attributes, is removable by the\r
                // current style definition.\r
                checkElementRemovable : function( element, fullMatch )\r
@@ -199,9 +222,9 @@ CKEDITOR.STYLE_OBJECT = 3;
                                                        continue;\r
 \r
                                                var elementAttr = element.getAttribute( attName ) || '';\r
-                                               if ( attribs[ attName ] ==\r
-                                                        ( attName == 'style' ?\r
-                                                          normalizeCssText( elementAttr, false ) : elementAttr  ) )\r
+                                               if ( attName == 'style' ?\r
+                                                       compareCssText( attribs[ attName ], normalizeCssText( elementAttr, false ) )\r
+                                                       : attribs[ attName ] == elementAttr  )\r
                                                {\r
                                                        if ( !fullMatch )\r
                                                                return true;\r
@@ -246,6 +269,39 @@ CKEDITOR.STYLE_OBJECT = 3;
                                }\r
                        }\r
                        return false;\r
+               },\r
+\r
+               // Builds the preview HTML based on the styles definition.\r
+               buildPreview : function()\r
+               {\r
+                       var styleDefinition = this._.definition,\r
+                               html = [],\r
+                               elementName = styleDefinition.element;\r
+\r
+                       // Avoid <bdo> in the preview.\r
+                       if ( elementName == 'bdo' )\r
+                               elementName = 'span';\r
+\r
+                       html = [ '<', elementName ];\r
+\r
+                       // Assign all defined attributes.\r
+                       var attribs     = styleDefinition.attributes;\r
+                       if ( attribs )\r
+                       {\r
+                               for ( var att in attribs )\r
+                               {\r
+                                       html.push( ' ', att, '="', attribs[ att ], '"' );\r
+                               }\r
+                       }\r
+\r
+                       // Assign the style attribute.\r
+                       var cssStyle = CKEDITOR.style.getStyleText( styleDefinition );\r
+                       if ( cssStyle )\r
+                               html.push( ' style="', cssStyle, '"' );\r
+\r
+                       html.push( '>', styleDefinition.name, '</', elementName, '>' );\r
+\r
+                       return html.join( '' );\r
                }\r
        };\r
 \r
@@ -260,20 +316,31 @@ CKEDITOR.STYLE_OBJECT = 3;
                stylesDef = styleDefinition.styles;\r
 \r
                // Builds the StyleText.\r
-\r
-               var stylesText = ( styleDefinition.attributes && styleDefinition.attributes[ 'style' ] ) || '';\r
+               var stylesText = ( styleDefinition.attributes && styleDefinition.attributes[ 'style' ] ) || '',\r
+                               specialStylesText = '';\r
 \r
                if ( stylesText.length )\r
                        stylesText = stylesText.replace( semicolonFixRegex, ';' );\r
 \r
                for ( var style in stylesDef )\r
-                       stylesText += ( style + ':' + stylesDef[ style ] ).replace( semicolonFixRegex, ';' );\r
+               {\r
+                       var styleVal = stylesDef[ style ],\r
+                                       text = ( style + ':' + styleVal ).replace( semicolonFixRegex, ';' );\r
+\r
+                       // Some browsers don't support 'inherit' property value, leave them intact. (#5242)\r
+                       if ( styleVal == 'inherit' )\r
+                               specialStylesText += text;\r
+                       else\r
+                               stylesText += text;\r
+               }\r
 \r
                // Browsers make some changes to the style when applying them. So, here\r
                // we normalize it to the browser format.\r
                if ( stylesText.length )\r
                        stylesText = normalizeCssText( stylesText );\r
 \r
+               stylesText += specialStylesText;\r
+\r
                // Return it, saving it to the next request.\r
                return ( styleDefinition._ST = stylesText );\r
        };\r
@@ -350,10 +417,6 @@ CKEDITOR.STYLE_OBJECT = 3;
 \r
                var styleRange;\r
 \r
-               // Indicates that that some useful inline content has been found, so\r
-               // the style should be applied.\r
-               var hasContents;\r
-\r
                while ( currentNode )\r
                {\r
                        var applyStyle = false;\r
@@ -424,8 +487,6 @@ CKEDITOR.STYLE_OBJECT = 3;
                                                        if ( !includedNode.$.nextSibling )\r
                                                                applyStyle = true;\r
 \r
-                                                       if ( !hasContents )\r
-                                                               hasContents = ( nodeType != CKEDITOR.NODE_TEXT || (/[^\s\ufeff]/).test( currentNode.getText() ) );\r
                                                }\r
                                        }\r
                                        else\r
@@ -439,7 +500,7 @@ CKEDITOR.STYLE_OBJECT = 3;
                        }\r
 \r
                        // Apply the style if we have something to which apply it.\r
-                       if ( applyStyle && hasContents && styleRange && !styleRange.collapsed )\r
+                       if ( applyStyle && styleRange && !styleRange.collapsed )\r
                        {\r
                                // Build the style element, based on the style object definition.\r
                                var styleNode = getElement( this, document );\r
@@ -510,6 +571,8 @@ CKEDITOR.STYLE_OBJECT = 3;
                // Remove the temporary marking node.(#4111)\r
                marker && marker.remove();\r
                range.moveToBookmark( bookmark );\r
+               // Minimize the result range to exclude empty text nodes. (#5374)\r
+               range.shrink( CKEDITOR.SHRINK_TEXT );\r
        }\r
 \r
        function removeInlineStyle( range )\r
@@ -674,6 +737,13 @@ CKEDITOR.STYLE_OBJECT = 3;
                range.moveToBookmark( bookmark );\r
 }\r
 \r
+       function applyObjectStyle( range )\r
+       {\r
+               var root = range.getCommonAncestor( true, true ),\r
+                               element = root.getAscendant( this.element, true );\r
+               element && setupElement( element, this );\r
+       }\r
+\r
        function applyBlockStyle( range )\r
        {\r
                // Serializible bookmarks is needed here since\r
@@ -772,7 +842,7 @@ CKEDITOR.STYLE_OBJECT = 3;
                                } );\r
 \r
                var pres = [];\r
-               splitedHtml.replace( /<pre>([\s\S]*?)<\/pre>/gi, function( match, preContent ){\r
+               splitedHtml.replace( /<pre\b.*?>([\s\S]*?)<\/pre>/gi, function( match, preContent ){\r
                        pres.push( preContent );\r
                } );\r
                return pres;\r
@@ -873,31 +943,34 @@ CKEDITOR.STYLE_OBJECT = 3;
        function removeFromElement( style, element )\r
        {\r
                var def = style._.definition,\r
-                       attributes = def.attributes,\r
+                       attributes = CKEDITOR.tools.extend( {}, def.attributes, getOverrides( style )[ element.getName() ] ),\r
                        styles = def.styles,\r
-                       overrides = getOverrides( style );\r
+                       // If the style is only about the element itself, we have to remove the element.\r
+                       removeEmpty = CKEDITOR.tools.isEmpty( attributes ) && CKEDITOR.tools.isEmpty( styles );\r
 \r
-               function removeAttrs()\r
+               // Remove definition attributes/style from the elemnt.\r
+               for ( var attName in attributes )\r
                {\r
-                       for ( var attName in attributes )\r
-                       {\r
-                               // The 'class' element value must match (#1318).\r
-                               if ( attName == 'class' && element.getAttribute( attName ) != attributes[ attName ] )\r
-                                       continue;\r
-                               element.removeAttribute( attName );\r
-                       }\r
+                       // The 'class' element value must match (#1318).\r
+                       if ( ( attName == 'class' || style._.definition.fullMatch )\r
+                               && element.getAttribute( attName ) != normalizeProperty( attName, attributes[ attName ] ) )\r
+                               continue;\r
+                       removeEmpty = element.hasAttribute( attName );\r
+                       element.removeAttribute( attName );\r
                }\r
 \r
-               // Remove definition attributes/style from the elemnt.\r
-               removeAttrs();\r
                for ( var styleName in styles )\r
+               {\r
+                       // Full match style insist on having fully equivalence. (#5018)\r
+                       if ( style._.definition.fullMatch\r
+                               && element.getStyle( styleName ) != normalizeProperty( styleName, styles[ styleName ], true ) )\r
+                               continue;\r
+\r
+                       removeEmpty = removeEmpty || !!element.getStyle( styleName );\r
                        element.removeStyle( styleName );\r
+               }\r
 \r
-               // Now remove override styles on the element.\r
-               attributes = overrides[ element.getName() ];\r
-               if ( attributes )\r
-                       removeAttrs();\r
-               removeNoAttribsElement( element );\r
+               removeEmpty && removeNoAttribsElement( element );\r
        }\r
 \r
        // Removes a style from inside an element.\r
@@ -1187,6 +1260,13 @@ CKEDITOR.STYLE_OBJECT = 3;
                return overrides;\r
        }\r
 \r
+       function normalizeProperty( name, value, isStyle )\r
+       {\r
+               var temp = new CKEDITOR.dom.element( 'span' );\r
+               temp [ isStyle ? 'setStyle' : 'setAttribute' ]( name, value );\r
+               return temp[ isStyle ? 'getStyle' : 'getAttribute' ]( name );\r
+       }\r
+\r
        function normalizeCssText( unparsedCssText, nativeNormalize )\r
        {\r
                var styleText;\r
@@ -1209,6 +1289,38 @@ CKEDITOR.STYLE_OBJECT = 3;
                                                         .toLowerCase();\r
        }\r
 \r
+       // Turn inline style text properties into one hash.\r
+       function parseStyleText( styleText )\r
+       {\r
+               var retval = {};\r
+               styleText\r
+                  .replace( /&quot;/g, '"' )\r
+                  .replace( /\s*([^ :;]+)\s*:\s*([^;]+)\s*(?=;|$)/g, function( match, name, value )\r
+               {\r
+                       retval[ name ] = value;\r
+               } );\r
+               return retval;\r
+       }\r
+\r
+       function compareCssText( source, target )\r
+       {\r
+               typeof source == 'string' && ( source = parseStyleText( source ) );\r
+               typeof target == 'string' && ( target = parseStyleText( target ) );\r
+               for( var name in source )\r
+               {\r
+                       // Value 'inherit'  is treated as a wildcard,\r
+                       // which will match any value.\r
+                       if ( !( name in target &&\r
+                                       ( target[ name ] == source[ name ]\r
+                                               || source[ name ] == 'inherit'\r
+                                               || target[ name ] == 'inherit' ) ) )\r
+                       {\r
+                               return false;\r
+                       }\r
+               }\r
+               return true;\r
+       }\r
+\r
        function applyStyle( document, remove )\r
        {\r
                // Get all ranges from the selection.\r
@@ -1256,3 +1368,75 @@ CKEDITOR.loadStylesSet = function( name, url, callback )
                CKEDITOR.stylesSet.addExternal( name, url, '' );\r
                CKEDITOR.stylesSet.load( name, callback );\r
        };\r
+\r
+\r
+/**\r
+ * Gets the current styleSet for this instance\r
+ * @param {Function} The function to be called with the styles data.\r
+ * @example\r
+ * editor.getStylesSet( function( stylesDefinitions ) {} );\r
+ */\r
+CKEDITOR.editor.prototype.getStylesSet = function( callback )\r
+{\r
+       if ( !this._.stylesDefinitions )\r
+       {\r
+               var editor = this,\r
+                       // Respect the backwards compatible definition entry\r
+                       configStyleSet = editor.config.stylesCombo_stylesSet || editor.config.stylesSet || 'default';\r
+\r
+               // #5352 Allow to define the styles directly in the config object\r
+               if ( configStyleSet instanceof Array )\r
+               {\r
+                       editor._.stylesDefinitions = configStyleSet;\r
+                       callback( configStyleSet );\r
+                       return;\r
+               }\r
+\r
+               var     partsStylesSet = configStyleSet.split( ':' ),\r
+                       styleSetName = partsStylesSet[ 0 ],\r
+                       externalPath = partsStylesSet[ 1 ],\r
+                       pluginPath = CKEDITOR.plugins.registered.styles.path;\r
+\r
+               CKEDITOR.stylesSet.addExternal( styleSetName,\r
+                               externalPath ?\r
+                                       partsStylesSet.slice( 1 ).join( ':' ) :\r
+                                       pluginPath + 'styles/' + styleSetName + '.js', '' );\r
+\r
+               CKEDITOR.stylesSet.load( styleSetName, function( stylesSet )\r
+                       {\r
+                               editor._.stylesDefinitions = stylesSet[ styleSetName ];\r
+                               callback( editor._.stylesDefinitions );\r
+                       } ) ;\r
+       }\r
+       else\r
+               callback( this._.stylesDefinitions );\r
+};\r
+\r
+/**\r
+ * The "styles definition set" to use in the editor. They will be used in the\r
+ * styles combo and the Style selector of the div container. <br>\r
+ * The styles may be defined in the page containing the editor, or can be\r
+ * loaded on demand from an external file. In the second case, if this setting\r
+ * contains only a name, the styles definition file will be loaded from the\r
+ * "styles" folder inside the styles plugin folder.\r
+ * Otherwise, this setting has the "name:url" syntax, making it\r
+ * possible to set the URL from which loading the styles file.<br>\r
+ * Previously this setting was available as config.stylesCombo_stylesSet<br>\r
+ * @type String|Array\r
+ * @default 'default'\r
+ * @since 3.3\r
+ * @example\r
+ * // Load from the styles' styles folder (mystyles.js file).\r
+ * config.stylesSet = 'mystyles';\r
+ * @example\r
+ * // Load from a relative URL.\r
+ * config.stylesSet = 'mystyles:/editorstyles/styles.js';\r
+ * @example\r
+ * // Load from a full URL.\r
+ * config.stylesSet = 'mystyles:http://www.example.com/editorstyles/styles.js';\r
+ * @example\r
+ * // Load from a list of definitions.\r
+ * config.stylesSet = [\r
+ *  { name : 'Strong Emphasis', element : 'strong' },\r
+ * { name : 'Emphasis', element : 'em' }, ... ];\r
+ */\r