X-Git-Url: https://jasonwoof.com/gitweb/?a=blobdiff_plain;f=_source%2Fplugins%2Fstyles%2Fplugin.js;h=65a36c21fa386c333a14bbc6cf91d4a0ae9a017a;hb=e7789c1ad838194d45eeee6ac2eb6e55f5cf35a1;hp=1d468ad4a4c0ccc938cfcbbfdcf3aff33caa2079;hpb=ea7e3453c7b0f023b050aca6d9f83ab372860d91;p=ckeditor.git diff --git a/_source/plugins/styles/plugin.js b/_source/plugins/styles/plugin.js index 1d468ad..65a36c2 100644 --- a/_source/plugins/styles/plugin.js +++ b/_source/plugins/styles/plugin.js @@ -1,5 +1,5 @@ /* -Copyright (c) 2003-2009, CKSource - Frederico Knabben. All rights reserved. +Copyright (c) 2003-2010, CKSource - Frederico Knabben. All rights reserved. For licensing, see LICENSE.html or http://ckeditor.com/license */ @@ -78,7 +78,7 @@ CKEDITOR.STYLE_OBJECT = 3; (function() { var blockElements = { address:1,div:1,h1:1,h2:1,h3:1,h4:1,h5:1,h6:1,p:1,pre:1 }; - var objectElements = { a:1,embed:1,hr:1,img:1,li:1,object:1,ol:1,table:1,td:1,tr:1,ul:1 }; + 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}; var semicolonFixRegex = /\s*(?:;\s*|$)/; @@ -127,6 +127,8 @@ CKEDITOR.STYLE_OBJECT = 3; applyInlineStyle : this.type == CKEDITOR.STYLE_BLOCK ? applyBlockStyle + : this.type == CKEDITOR.STYLE_OBJECT ? + applyObjectStyle : null ).call( this, range ); }, @@ -154,17 +156,23 @@ CKEDITOR.STYLE_OBJECT = 3; case CKEDITOR.STYLE_BLOCK : return this.checkElementRemovable( elementPath.block || elementPath.blockLimit, true ); + case CKEDITOR.STYLE_OBJECT : case CKEDITOR.STYLE_INLINE : var elements = elementPath.elements; for ( var i = 0, element ; i < elements.length ; i++ ) { - element = elements[i]; + element = elements[ i ]; - if ( element == elementPath.block || element == elementPath.blockLimit ) + if ( this.type == CKEDITOR.STYLE_INLINE + && ( element == elementPath.block || element == elementPath.blockLimit ) ) continue; + if( this.type == CKEDITOR.STYLE_OBJECT + && !( element.getName() in objectElements ) ) + continue; + if ( this.checkElementRemovable( element, true ) ) return true; } @@ -172,6 +180,21 @@ CKEDITOR.STYLE_OBJECT = 3; return false; }, + checkApplicable : function( elementPath ) + { + switch ( this.type ) + { + case CKEDITOR.STYLE_INLINE : + case CKEDITOR.STYLE_BLOCK : + break; + + case CKEDITOR.STYLE_OBJECT : + return elementPath.lastElement.getAscendant( this.element, true ); + } + + return true; + }, + // Checks if an element, or any of its attributes, is removable by the // current style definition. checkElementRemovable : function( element, fullMatch ) @@ -198,10 +221,10 @@ CKEDITOR.STYLE_OBJECT = 3; if ( attName == '_length' ) continue; - var elementAttr = element.getAttribute( attName ); - if ( attribs[attName] == - ( attName == 'style' ? - normalizeCssText( elementAttr, false ) : elementAttr ) ) + var elementAttr = element.getAttribute( attName ) || ''; + if ( attName == 'style' ? + compareCssText( attribs[ attName ], normalizeCssText( elementAttr, false ) ) + : attribs[ attName ] == elementAttr ) { if ( !fullMatch ) return true; @@ -209,7 +232,7 @@ CKEDITOR.STYLE_OBJECT = 3; else if ( fullMatch ) return false; } - if( fullMatch ) + if ( fullMatch ) return true; } else @@ -246,6 +269,39 @@ CKEDITOR.STYLE_OBJECT = 3; } } return false; + }, + + // Builds the preview HTML based on the styles definition. + buildPreview : function() + { + var styleDefinition = this._.definition, + html = [], + elementName = styleDefinition.element; + + // Avoid in the preview. + if ( elementName == 'bdo' ) + elementName = 'span'; + + html = [ '<', elementName ]; + + // Assign all defined attributes. + var attribs = styleDefinition.attributes; + if ( attribs ) + { + for ( var att in attribs ) + { + html.push( ' ', att, '="', attribs[ att ], '"' ); + } + } + + // Assign the style attribute. + var cssStyle = CKEDITOR.style.getStyleText( styleDefinition ); + if ( cssStyle ) + html.push( ' style="', cssStyle, '"' ); + + html.push( '>', styleDefinition.name, '' ); + + return html.join( '' ); } }; @@ -260,20 +316,31 @@ CKEDITOR.STYLE_OBJECT = 3; stylesDef = styleDefinition.styles; // Builds the StyleText. - - var stylesText = ( styleDefinition.attributes && styleDefinition.attributes[ 'style' ] ) || ''; + var stylesText = ( styleDefinition.attributes && styleDefinition.attributes[ 'style' ] ) || '', + specialStylesText = ''; if ( stylesText.length ) stylesText = stylesText.replace( semicolonFixRegex, ';' ); for ( var style in stylesDef ) - stylesText += style + ':' + stylesDef[ style ] + ';'; + { + var styleVal = stylesDef[ style ], + text = ( style + ':' + styleVal ).replace( semicolonFixRegex, ';' ); + + // Some browsers don't support 'inherit' property value, leave them intact. (#5242) + if ( styleVal == 'inherit' ) + specialStylesText += text; + else + stylesText += text; + } // Browsers make some changes to the style when applying them. So, here // we normalize it to the browser format. if ( stylesText.length ) stylesText = normalizeCssText( stylesText ); + stylesText += specialStylesText; + // Return it, saving it to the next request. return ( styleDefinition._ST = stylesText ); }; @@ -350,10 +417,6 @@ CKEDITOR.STYLE_OBJECT = 3; var styleRange; - // Indicates that that some useful inline content has been found, so - // the style should be applied. - var hasContents; - while ( currentNode ) { var applyStyle = false; @@ -375,13 +438,17 @@ CKEDITOR.STYLE_OBJECT = 3; } // Check if the current node can be a child of the style element. - if ( !nodeName || ( dtd[ nodeName ] && ( currentNode.getPosition( lastNode ) | CKEDITOR.POSITION_PRECEDING | CKEDITOR.POSITION_IDENTICAL | CKEDITOR.POSITION_IS_CONTAINED ) == ( CKEDITOR.POSITION_PRECEDING + CKEDITOR.POSITION_IDENTICAL + CKEDITOR.POSITION_IS_CONTAINED ) ) ) + if ( !nodeName || ( dtd[ nodeName ] + && ( currentNode.getPosition( lastNode ) | CKEDITOR.POSITION_PRECEDING | CKEDITOR.POSITION_IDENTICAL | CKEDITOR.POSITION_IS_CONTAINED ) == ( CKEDITOR.POSITION_PRECEDING + CKEDITOR.POSITION_IDENTICAL + CKEDITOR.POSITION_IS_CONTAINED ) + && ( !def.childRule || def.childRule( currentNode ) ) ) ) { var currentParent = currentNode.getParent(); // Check if the style element can be a child of the current // node parent or if the element is not defined in the DTD. - if ( currentParent && ( ( currentParent.getDtd() || CKEDITOR.dtd.span )[ elementName ] || isUnknownElement ) ) + if ( currentParent + && ( ( currentParent.getDtd() || CKEDITOR.dtd.span )[ elementName ] || isUnknownElement ) + && ( !def.parentRule || def.parentRule( currentParent ) ) ) { // This node will be part of our range, so if it has not // been started, place its start right before the node. @@ -406,7 +473,8 @@ CKEDITOR.STYLE_OBJECT = 3; // to the range. while ( !includedNode.$.nextSibling && ( parentNode = includedNode.getParent(), dtd[ parentNode.getName() ] ) - && ( parentNode.getPosition( firstNode ) | CKEDITOR.POSITION_FOLLOWING | CKEDITOR.POSITION_IDENTICAL | CKEDITOR.POSITION_IS_CONTAINED ) == ( CKEDITOR.POSITION_FOLLOWING + CKEDITOR.POSITION_IDENTICAL + CKEDITOR.POSITION_IS_CONTAINED ) ) + && ( parentNode.getPosition( firstNode ) | CKEDITOR.POSITION_FOLLOWING | CKEDITOR.POSITION_IDENTICAL | CKEDITOR.POSITION_IS_CONTAINED ) == ( CKEDITOR.POSITION_FOLLOWING + CKEDITOR.POSITION_IDENTICAL + CKEDITOR.POSITION_IS_CONTAINED ) + && ( !def.childRule || def.childRule( parentNode ) ) ) { includedNode = parentNode; } @@ -419,8 +487,6 @@ CKEDITOR.STYLE_OBJECT = 3; if ( !includedNode.$.nextSibling ) applyStyle = true; - if ( !hasContents ) - hasContents = ( nodeType != CKEDITOR.NODE_TEXT || (/[^\s\ufeff]/).test( currentNode.getText() ) ); } } else @@ -434,7 +500,7 @@ CKEDITOR.STYLE_OBJECT = 3; } // Apply the style if we have something to which apply it. - if ( applyStyle && hasContents && styleRange && !styleRange.collapsed ) + if ( applyStyle && styleRange && !styleRange.collapsed ) { // Build the style element, based on the style object definition. var styleNode = getElement( this, document ); @@ -505,6 +571,8 @@ CKEDITOR.STYLE_OBJECT = 3; // Remove the temporary marking node.(#4111) marker && marker.remove(); range.moveToBookmark( bookmark ); + // Minimize the result range to exclude empty text nodes. (#5374) + range.shrink( CKEDITOR.SHRINK_TEXT ); } function removeInlineStyle( range ) @@ -575,7 +643,7 @@ CKEDITOR.STYLE_OBJECT = 3; if ( newElement.equals( boundaryElement ) ) break; // Avoid copying any matched element. - else if( newElement.match ) + else if ( newElement.match ) continue; else newElement = newElement.clone(); @@ -645,7 +713,7 @@ CKEDITOR.STYLE_OBJECT = 3; if ( currentNode.type == CKEDITOR.NODE_ELEMENT && this.checkElementRemovable( currentNode ) ) { // Remove style from element or overriding element. - if( currentNode.getName() == this.element ) + if ( currentNode.getName() == this.element ) removeFromElement( this, currentNode ); else removeOverrides( currentNode, getOverrides( this )[ currentNode.getName() ] ); @@ -669,6 +737,13 @@ CKEDITOR.STYLE_OBJECT = 3; range.moveToBookmark( bookmark ); } + function applyObjectStyle( range ) + { + var root = range.getCommonAncestor( true, true ), + element = root.getAscendant( this.element, true ); + element && setupElement( element, this ); + } + function applyBlockStyle( range ) { // Serializible bookmarks is needed here since @@ -682,7 +757,7 @@ CKEDITOR.STYLE_OBJECT = 3; var doc = range.document; var previousPreBlock; - while( ( block = iterator.getNextParagraph() ) ) // Only one = + while ( ( block = iterator.getNextParagraph() ) ) // Only one = { var newBlock = getElement( this, doc ); replaceBlock( block, newBlock ); @@ -767,7 +842,7 @@ CKEDITOR.STYLE_OBJECT = 3; } ); var pres = []; - splitedHtml.replace( /
([\s\S]*?)<\/pre>/gi, function( match, preContent ){
+		splitedHtml.replace( /([\s\S]*?)<\/pre>/gi, function( match, preContent ){
 			pres.push( preContent );
 		} );
 		return pres;
@@ -868,31 +943,34 @@ CKEDITOR.STYLE_OBJECT = 3;
 	function removeFromElement( style, element )
 	{
 		var def = style._.definition,
-			attributes = def.attributes,
+			attributes = CKEDITOR.tools.extend( {}, def.attributes, getOverrides( style )[ element.getName() ] ),
 			styles = def.styles,
-			overrides = getOverrides( style );
+			// If the style is only about the element itself, we have to remove the element.
+			removeEmpty = CKEDITOR.tools.isEmpty( attributes ) && CKEDITOR.tools.isEmpty( styles );
 
-		function removeAttrs()
+		// Remove definition attributes/style from the elemnt.
+		for ( var attName in attributes )
 		{
-			for ( var attName in attributes )
-			{
-				// The 'class' element value must match (#1318).
-				if ( attName == 'class' && element.getAttribute( attName ) != attributes[ attName ] )
-					continue;
-				element.removeAttribute( attName );
-			}
+			// The 'class' element value must match (#1318).
+			if ( ( attName == 'class' || style._.definition.fullMatch )
+				&& element.getAttribute( attName ) != normalizeProperty( attName, attributes[ attName ] ) )
+				continue;
+			removeEmpty = element.hasAttribute( attName );
+			element.removeAttribute( attName );
 		}
 
-		// Remove definition attributes/style from the elemnt.
-		removeAttrs();
 		for ( var styleName in styles )
+		{
+			// Full match style insist on having fully equivalence. (#5018)
+			if ( style._.definition.fullMatch
+				&& element.getStyle( styleName ) != normalizeProperty( styleName, styles[ styleName ], true ) )
+				continue;
+
+			removeEmpty = removeEmpty || !!element.getStyle( styleName );
 			element.removeStyle( styleName );
+		}
 
-		// Now remove override styles on the element.
-		attributes = overrides[ element.getName() ];
-		if( attributes )
-			removeAttrs();
-		removeNoAttribsElement( element );
+		removeEmpty && removeNoAttribsElement( element );
 	}
 
 	// Removes a style from inside an element.
@@ -1126,7 +1204,7 @@ CKEDITOR.STYLE_OBJECT = 3;
 	 */
 	function getOverrides( style )
 	{
-		if( style._.overrides )
+		if ( style._.overrides )
 			return style._.overrides;
 
 		var overrides = ( style._.overrides = {} ),
@@ -1182,6 +1260,13 @@ CKEDITOR.STYLE_OBJECT = 3;
 		return overrides;
 	}
 
+	function normalizeProperty( name, value, isStyle )
+	{
+		var temp = new CKEDITOR.dom.element( 'span' );
+		temp [ isStyle ? 'setStyle' : 'setAttribute' ]( name, value );
+		return temp[ isStyle ? 'getStyle' : 'getAttribute' ]( name );
+	}
+
 	function normalizeCssText( unparsedCssText, nativeNormalize )
 	{
 		var styleText;
@@ -1191,7 +1276,7 @@ CKEDITOR.STYLE_OBJECT = 3;
 			// retrieving its final format.
 			var temp = new CKEDITOR.dom.element( 'span' );
 			temp.setAttribute( 'style', unparsedCssText );
-			styleText = temp.getAttribute( 'style' );
+			styleText = temp.getAttribute( 'style' ) || '';
 		}
 		else
 			styleText = unparsedCssText;
@@ -1200,9 +1285,42 @@ CKEDITOR.STYLE_OBJECT = 3;
 		// Compensate tail semi-colon.
 		return styleText.replace( /\s*([;:])\s*/, '$1' )
 							 .replace( /([^\s;])$/, '$1;')
+							 .replace( /,\s+/g, ',' ) // Trimming spaces after comma (e.g. font-family name)(#4107).
 							 .toLowerCase();
 	}
 
+	// Turn inline style text properties into one hash.
+	function parseStyleText( styleText )
+	{
+		var retval = {};
+		styleText
+		   .replace( /"/g, '"' )
+		   .replace( /\s*([^ :;]+)\s*:\s*([^;]+)\s*(?=;|$)/g, function( match, name, value )
+		{
+			retval[ name ] = value;
+		} );
+		return retval;
+	}
+
+	function compareCssText( source, target )
+	{
+		typeof source == 'string' && ( source = parseStyleText( source ) );
+		typeof target == 'string' && ( target = parseStyleText( target ) );
+		for( var name in source )
+		{
+			// Value 'inherit'  is treated as a wildcard,
+			// which will match any value.
+			if ( !( name in target &&
+					( target[ name ] == source[ name ]
+						|| source[ name ] == 'inherit'
+						|| target[ name ] == 'inherit' ) ) )
+			{
+				return false;
+			}
+		}
+		return true;
+	}
+
 	function applyStyle( document, remove )
 	{
 		// Get all ranges from the selection.
@@ -1240,3 +1358,85 @@ CKEDITOR.styleCommand.prototype.exec = function( editor )
 
 	return !!doc;
 };
+
+CKEDITOR.stylesSet = new CKEDITOR.resourceManager( '', 'stylesSet' );
+
+// Backward compatibility (#5025).
+CKEDITOR.addStylesSet = CKEDITOR.tools.bind( CKEDITOR.stylesSet.add, CKEDITOR.stylesSet );
+CKEDITOR.loadStylesSet = function( name, url, callback )
+	{
+		CKEDITOR.stylesSet.addExternal( name, url, '' );
+		CKEDITOR.stylesSet.load( name, callback );
+	};
+
+
+/**
+ * Gets the current styleSet for this instance
+ * @param {Function} The function to be called with the styles data.
+ * @example
+ * editor.getStylesSet( function( stylesDefinitions ) {} );
+ */
+CKEDITOR.editor.prototype.getStylesSet = function( callback )
+{
+	if ( !this._.stylesDefinitions )
+	{
+		var editor = this,
+			// Respect the backwards compatible definition entry
+			configStyleSet = editor.config.stylesCombo_stylesSet || editor.config.stylesSet || 'default';
+
+		// #5352 Allow to define the styles directly in the config object
+		if ( configStyleSet instanceof Array )
+		{
+			editor._.stylesDefinitions = configStyleSet;
+			callback( configStyleSet );
+			return;
+		}
+
+		var	partsStylesSet = configStyleSet.split( ':' ),
+			styleSetName = partsStylesSet[ 0 ],
+			externalPath = partsStylesSet[ 1 ],
+			pluginPath = CKEDITOR.plugins.registered.styles.path;
+
+		CKEDITOR.stylesSet.addExternal( styleSetName,
+				externalPath ?
+					partsStylesSet.slice( 1 ).join( ':' ) :
+					pluginPath + 'styles/' + styleSetName + '.js', '' );
+
+		CKEDITOR.stylesSet.load( styleSetName, function( stylesSet )
+			{
+				editor._.stylesDefinitions = stylesSet[ styleSetName ];
+				callback( editor._.stylesDefinitions );
+			} ) ;
+	}
+	else
+		callback( this._.stylesDefinitions );
+};
+
+/**
+ * The "styles definition set" to use in the editor. They will be used in the
+ * styles combo and the Style selector of the div container. 
+ * The styles may be defined in the page containing the editor, or can be + * loaded on demand from an external file. In the second case, if this setting + * contains only a name, the styles definition file will be loaded from the + * "styles" folder inside the styles plugin folder. + * Otherwise, this setting has the "name:url" syntax, making it + * possible to set the URL from which loading the styles file.
+ * Previously this setting was available as config.stylesCombo_stylesSet
+ * @type String|Array + * @default 'default' + * @since 3.3 + * @example + * // Load from the styles' styles folder (mystyles.js file). + * config.stylesSet = 'mystyles'; + * @example + * // Load from a relative URL. + * config.stylesSet = 'mystyles:/editorstyles/styles.js'; + * @example + * // Load from a full URL. + * config.stylesSet = 'mystyles:http://www.example.com/editorstyles/styles.js'; + * @example + * // Load from a list of definitions. + * config.stylesSet = [ + * { name : 'Strong Emphasis', element : 'strong' }, + * { name : 'Emphasis', element : 'em' }, ... ]; + */