X-Git-Url: https://jasonwoof.com/gitweb/?a=blobdiff_plain;f=_source%2Fplugins%2Fstyles%2Fplugin.js;h=3af696c3eb50af9fad896c3efb7feff1ca967808;hb=055b6b0792ce7dc53d47af606b367c04b927c2ab;hp=29a2112455e674ce18213be1c7d9d582c1c3383e;hpb=c6e377a02b54abc07129d72b632763c727476a15;p=ckeditor.git diff --git a/_source/plugins/styles/plugin.js b/_source/plugins/styles/plugin.js index 29a2112..3af696c 100644 --- a/_source/plugins/styles/plugin.js +++ b/_source/plugins/styles/plugin.js @@ -60,13 +60,13 @@ CKEDITOR.editor.prototype.attachStyleStateChange = function( style, callback ) // Save the current state, so it can be compared next // time. - callback.state !== currentState; + callback.state = currentState; } } }); } - // Save the callback info, so it can be checked on the next occurence of + // Save the callback info, so it can be checked on the next occurrence of // selectionChange. styleStateChangeCallbacks.push( { style : style, fn : callback } ); }; @@ -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,25 @@ CKEDITOR.STYLE_OBJECT = 3; return false; }, + /** + * Whether this style can be applied at the element path. + * @param elementPath + */ + 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 ) @@ -199,9 +226,11 @@ CKEDITOR.STYLE_OBJECT = 3; continue; var elementAttr = element.getAttribute( attName ) || ''; - if ( attribs[ attName ] == - ( attName == 'style' ? - normalizeCssText( elementAttr, false ) : elementAttr ) ) + + // Special treatment for 'style' attribute is required. + if ( attName == 'style' ? + compareCssText( attribs[ attName ], normalizeCssText( elementAttr, false ) ) + : attribs[ attName ] == elementAttr ) { if ( !fullMatch ) return true; @@ -246,6 +275,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 +322,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 ] ).replace( semicolonFixRegex, ';' ); + { + 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 ); }; @@ -312,48 +385,14 @@ CKEDITOR.STYLE_OBJECT = 3; // Get the first node to be processed and the last, which concludes the // processing. - var boundaryNodes = range.getBoundaryNodes(); - var firstNode = boundaryNodes.startNode; - var lastNode = boundaryNodes.endNode.getNextSourceNode( true ); - - // Probably the document end is reached, we need a marker node. - if ( !lastNode ) - { - var marker; - lastNode = marker = document.createText( '' ); - lastNode.insertAfter( range.endContainer ); - } - // The detection algorithm below skips the contents inside bookmark nodes, so - // we'll need to make sure lastNode isn't the   inside a bookmark node. - var lastParent = lastNode.getParent(); - if ( lastParent && lastParent.getAttribute( '_fck_bookmark' ) ) - lastNode = lastParent; - - if ( lastNode.equals( firstNode ) ) - { - // If the last node is the same as the the first one, we must move - // it to the next one, otherwise the first one will not be - // processed. - lastNode = lastNode.getNextSourceNode( true ); - - // It may happen that there are no more nodes after it (the end of - // the document), so we must add something there to make our code - // simpler. - if ( !lastNode ) - { - lastNode = marker = document.createText( '' ); - lastNode.insertAfter( firstNode ); - } - } + var boundaryNodes = range.createBookmark(), + firstNode = boundaryNodes.startNode, + lastNode = boundaryNodes.endNode; var currentNode = firstNode; 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; @@ -424,8 +463,6 @@ CKEDITOR.STYLE_OBJECT = 3; if ( !includedNode.$.nextSibling ) applyStyle = true; - if ( !hasContents ) - hasContents = ( nodeType != CKEDITOR.NODE_TEXT || (/[^\s\ufeff]/).test( currentNode.getText() ) ); } } else @@ -439,7 +476,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 ); @@ -489,7 +526,7 @@ CKEDITOR.STYLE_OBJECT = 3; styleRange.insertNode( styleNode ); // Let's merge our new style with its neighbors, if possible. - mergeSiblings( styleNode ); + styleNode.mergeSiblings(); // As the style system breaks text nodes constantly, let's normalize // things for performance. @@ -507,9 +544,11 @@ CKEDITOR.STYLE_OBJECT = 3; } } - // Remove the temporary marking node.(#4111) - marker && marker.remove(); + firstNode.remove(); + lastNode.remove(); range.moveToBookmark( bookmark ); + // Minimize the result range to exclude empty text nodes. (#5374) + range.shrink( CKEDITOR.SHRINK_TEXT ); } function removeInlineStyle( range ) @@ -561,7 +600,7 @@ CKEDITOR.STYLE_OBJECT = 3; * no difference that they're separate entities in the DOM tree. So, merge * them before removal. */ - mergeSiblings( element ); + element.mergeSiblings(); removeFromElement( this, element ); } @@ -674,6 +713,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 @@ -683,6 +729,10 @@ CKEDITOR.STYLE_OBJECT = 3; var iterator = range.createIterator(); iterator.enforceRealBlocks = true; + // make recognize
tag as a separator in ENTER_BR mode (#5121) + if ( this._.enterMode ) + iterator.enlargeBr = ( this._.enterMode != CKEDITOR.ENTER_BR ); + var block; var doc = range.document; var previousPreBlock; @@ -724,13 +774,14 @@ CKEDITOR.STYLE_OBJECT = 3; } } + var nonWhitespaces = CKEDITOR.dom.walker.whitespaces( true ); /** * Merge a
 block with a previous sibling if available.
 	 */
 	function mergePre( preBlock )
 	{
 		var previousBlock;
-		if ( !( ( previousBlock = preBlock.getPreviousSourceNode( true, CKEDITOR.NODE_ELEMENT ) )
+		if ( !( ( previousBlock = preBlock.getPrevious( nonWhitespaces ) )
 				 && previousBlock.is
 				 && previousBlock.is( 'pre') ) )
 			return;
@@ -772,7 +823,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;
@@ -873,31 +924,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.
@@ -984,47 +1038,11 @@ CKEDITOR.STYLE_OBJECT = 3;
 			if ( firstChild )
 			{
 				// Check the cached nodes for merging.
-				mergeSiblings( firstChild );
+				firstChild.type == CKEDITOR.NODE_ELEMENT && firstChild.mergeSiblings();
 
-				if ( lastChild && !firstChild.equals( lastChild ) )
-					mergeSiblings( lastChild );
-			}
-		}
-	}
-
-	function mergeSiblings( element )
-	{
-		if ( !element || element.type != CKEDITOR.NODE_ELEMENT || !CKEDITOR.dtd.$removeEmpty[ element.getName() ] )
-			return;
-
-		mergeElements( element, element.getNext(), true );
-		mergeElements( element, element.getPrevious() );
-	}
-
-	function mergeElements( element, sibling, isNext )
-	{
-		if ( sibling && sibling.type == CKEDITOR.NODE_ELEMENT )
-		{
-			var hasBookmark = sibling.getAttribute( '_fck_bookmark' );
-
-			if ( hasBookmark )
-				sibling = isNext ? sibling.getNext() : sibling.getPrevious();
-
-			if ( sibling && sibling.type == CKEDITOR.NODE_ELEMENT && element.isIdentical( sibling ) )
-			{
-				// Save the last child to be checked too, to merge things like
-				//  => 
-				var innerSibling = isNext ? element.getLast() : element.getFirst();
-
-				if ( hasBookmark )
-					( isNext ? sibling.getPrevious() : sibling.getNext() ).move( element, !isNext );
-
-				sibling.moveChildren( element, !isNext );
-				sibling.remove();
-
-				// Now check the last inner child (see two comments above).
-				if ( innerSibling )
-					mergeSiblings( innerSibling );
+				if ( lastChild && !firstChild.equals( lastChild )
+					&& lastChild.type == CKEDITOR.NODE_ELEMENT  )
+					lastChild.mergeSiblings();
 			}
 		}
 	}
@@ -1187,6 +1205,15 @@ CKEDITOR.STYLE_OBJECT = 3;
 		return overrides;
 	}
 
+	// Make the comparison of attribute value easier by standardizing it.
+	function normalizeProperty( name, value, isStyle )
+	{
+		var temp = new CKEDITOR.dom.element( 'span' );
+		temp [ isStyle ? 'setStyle' : 'setAttribute' ]( name, value );
+		return temp[ isStyle ? 'getStyle' : 'getAttribute' ]( name );
+	}
+
+	// Make the comparison of style text easier by standardizing it.
 	function normalizeCssText( unparsedCssText, nativeNormalize )
 	{
 		var styleText;
@@ -1209,6 +1236,42 @@ CKEDITOR.STYLE_OBJECT = 3;
 							 .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;
+	}
+
+	/**
+	 * Compare two bunch of styles, with the speciality that value 'inherit'
+	 * is treated as a wildcard which will match any value.
+	 * @param {Object|String} source
+	 * @param {Object|String} target
+	 */
+	function compareCssText( source, target )
+	{
+		typeof source == 'string' && ( source = parseStyleText( source ) );
+		typeof target == 'string' && ( target = parseStyleText( target ) );
+		for( var name in source )
+		{
+			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.
@@ -1246,3 +1309,86 @@ 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
+ * @name CKEDITOR.config.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' }, ... ]; + */