/*\r
-Copyright (c) 2003-2009, CKSource - Frederico Knabben. All rights reserved.\r
+Copyright (c) 2003-2013, CKSource - Frederico Knabben. All rights reserved.\r
For licensing, see LICENSE.html or http://ckeditor.com/license\r
*/\r
\r
* @type Object\r
* @example\r
*/\r
- this.attributes = attributes;\r
+ this.attributes = attributes || {};\r
\r
/**\r
* The nodes that are direct children of this element.\r
*/\r
this.children = [];\r
\r
- var dtd = CKEDITOR.dtd,\r
- isBlockLike = !!( dtd.$block[ name ] || dtd.$listItem[ name ] || dtd.$tableContent[ name ] || dtd.$nonEditable[ name ] || name == 'br' ),\r
- isEmpty = !!dtd.$empty[ name ];\r
+ // Reveal the real semantic of our internal custom tag name (#6639),\r
+ // when resolving whether it's block like.\r
+ var realName = name || '',\r
+ prefixed = realName.match( /^cke:(.*)/ );\r
+ prefixed && ( realName = prefixed[ 1 ] );\r
\r
- this.isEmpty = isEmpty;\r
- this.isUnknown = !dtd[ name ];\r
+ var isBlockLike = !!( CKEDITOR.dtd.$nonBodyContent[ realName ]\r
+ || CKEDITOR.dtd.$block[ realName ]\r
+ || CKEDITOR.dtd.$listItem[ realName ]\r
+ || CKEDITOR.dtd.$tableContent[ realName ]\r
+ || CKEDITOR.dtd.$nonEditable[ realName ]\r
+ || realName == 'br' );\r
+\r
+ this.isEmpty = !!CKEDITOR.dtd.$empty[ name ];\r
+ this.isUnknown = !CKEDITOR.dtd[ name ];\r
\r
/** @private */\r
this._ =\r
{\r
isBlockLike : isBlockLike,\r
- hasInlineStarted : isEmpty || !isBlockLike\r
+ hasInlineStarted : this.isEmpty || !isBlockLike\r
+ };\r
+};\r
+\r
+/**\r
+ * Object presentation of CSS style declaration text.\r
+ * @param {CKEDITOR.htmlParser.element|String} elementOrStyleText A html parser element or the inline style text.\r
+ */\r
+CKEDITOR.htmlParser.cssStyle = function()\r
+{\r
+ var styleText,\r
+ arg = arguments[ 0 ],\r
+ rules = {};\r
+\r
+ styleText = arg instanceof CKEDITOR.htmlParser.element ? arg.attributes.style : arg;\r
+\r
+ // html-encoded quote might be introduced by 'font-family'\r
+ // from MS-Word which confused the following regexp. e.g.\r
+ //'font-family: "Lucida, Console"'\r
+ ( styleText || '' )\r
+ .replace( /"/g, '"' )\r
+ .replace( /\s*([^ :;]+)\s*:\s*([^;]+)\s*(?=;|$)/g,\r
+ function( match, name, value )\r
+ {\r
+ name == 'font-family' && ( value = value.replace( /["']/g, '' ) );\r
+ rules[ name.toLowerCase() ] = value;\r
+ });\r
+\r
+ return {\r
+\r
+ rules : rules,\r
+\r
+ /**\r
+ * Apply the styles onto the specified element or object.\r
+ * @param {CKEDITOR.htmlParser.element|CKEDITOR.dom.element|Object} obj\r
+ */\r
+ populate : function( obj )\r
+ {\r
+ var style = this.toString();\r
+ if ( style )\r
+ {\r
+ obj instanceof CKEDITOR.dom.element ?\r
+ obj.setAttribute( 'style', style ) :\r
+ obj instanceof CKEDITOR.htmlParser.element ?\r
+ obj.attributes.style = style :\r
+ obj.style = style;\r
+ }\r
+ },\r
+\r
+ toString : function()\r
+ {\r
+ var output = [];\r
+ for ( var i in rules )\r
+ rules[ i ] && output.push( i, ':', rules[ i ], ';' );\r
+ return output.join( '' );\r
+ }\r
};\r
};\r
\r
{\r
var attributes = this.attributes;\r
\r
- // The "_cke_replacedata" indicates that this element is replacing\r
- // a data snippet, which should be outputted as is.\r
- if ( attributes._cke_replacedata )\r
- {\r
- writer.write( attributes._cke_replacedata );\r
- return;\r
- }\r
-\r
// Ignore cke: prefixes when writing HTML.\r
var element = this,\r
writeName = element.name,\r
- a, value;\r
+ a, newAttrName, value;\r
+\r
+ var isChildrenFiltered;\r
+\r
+ /**\r
+ * Providing an option for bottom-up filtering order ( element\r
+ * children to be pre-filtered before the element itself ).\r
+ */\r
+ element.filterChildren = function()\r
+ {\r
+ if ( !isChildrenFiltered )\r
+ {\r
+ var writer = new CKEDITOR.htmlParser.basicWriter();\r
+ CKEDITOR.htmlParser.fragment.prototype.writeChildrenHtml.call( element, writer, filter );\r
+ element.children = new CKEDITOR.htmlParser.fragment.fromHtml( writer.getHtml(), 0, element.clone() ).children;\r
+ isChildrenFiltered = 1;\r
+ }\r
+ };\r
\r
if ( filter )\r
{\r
if ( !( element = filter.onElement( element ) ) )\r
return;\r
\r
+ element.parent = this.parent;\r
+\r
if ( element.name == writeName )\r
break;\r
\r
+ // If the element has been replaced with something of a\r
+ // different type, then make the replacement write itself.\r
+ if ( element.type != CKEDITOR.NODE_ELEMENT )\r
+ {\r
+ element.writeHtml( writer, filter );\r
+ return;\r
+ }\r
+\r
writeName = element.name;\r
- if ( !writeName ) // Send children.\r
+\r
+ // This indicate that the element has been dropped by\r
+ // filter but not the children.\r
+ if ( !writeName )\r
{\r
- CKEDITOR.htmlParser.fragment.prototype.writeHtml.apply( element, arguments );\r
+ // Fix broken parent refs.\r
+ for ( var c = 0, length = this.children.length ; c < length ; c++ )\r
+ this.children[ c ].parent = element.parent;\r
+\r
+ this.writeChildrenHtml.call( element, writer, isChildrenFiltered ? null : filter );\r
return;\r
}\r
}\r
// Open element tag.\r
writer.openTag( writeName, attributes );\r
\r
- if ( writer.sortAttributes )\r
+ // Copy all attributes to an array.\r
+ var attribsArray = [];\r
+ // Iterate over the attributes twice since filters may alter\r
+ // other attributes.\r
+ for ( var i = 0 ; i < 2; i++ )\r
{\r
- // Copy all attributes to an array.\r
- var attribsArray = [];\r
for ( a in attributes )\r
{\r
+ newAttrName = a;\r
value = attributes[ a ];\r
-\r
- if ( filter && ( !( a = filter.onAttributeName( a ) ) || ( value = filter.onAttribute( element, a, value ) ) === false ) )\r
- continue;\r
-\r
- attribsArray.push( [ a, value ] );\r
+ if ( i == 1 )\r
+ attribsArray.push( [ a, value ] );\r
+ else if ( filter )\r
+ {\r
+ while ( true )\r
+ {\r
+ if ( !( newAttrName = filter.onAttributeName( a ) ) )\r
+ {\r
+ delete attributes[ a ];\r
+ break;\r
+ }\r
+ else if ( newAttrName != a )\r
+ {\r
+ delete attributes[ a ];\r
+ a = newAttrName;\r
+ continue;\r
+ }\r
+ else\r
+ break;\r
+ }\r
+ if ( newAttrName )\r
+ {\r
+ if ( ( value = filter.onAttribute( element, newAttrName, value ) ) === false )\r
+ delete attributes[ newAttrName ];\r
+ else\r
+ attributes [ newAttrName ] = value;\r
+ }\r
+ }\r
}\r
-\r
- // Sort the attributes by name.\r
+ }\r
+ // Sort the attributes by name.\r
+ if ( writer.sortAttributes )\r
attribsArray.sort( sortAttribs );\r
\r
- // Send the attributes.\r
- for ( var i = 0, len = attribsArray.length ; i < len ; i++ )\r
- {\r
- var attrib = attribsArray[ i ];\r
- writer.attribute( attrib[0], attrib[1] );\r
- }\r
- }\r
- else\r
+ // Send the attributes.\r
+ var len = attribsArray.length;\r
+ for ( i = 0 ; i < len ; i++ )\r
{\r
- for ( a in attributes )\r
- {\r
- value = attributes[ a ];\r
-\r
- if ( filter && ( !( a = filter.onAttributeName( a ) ) || ( value = filter.onAttribute( element, a, value ) ) === false ) )\r
- continue;\r
-\r
- writer.attribute( a, value );\r
- }\r
+ var attrib = attribsArray[ i ];\r
+ writer.attribute( attrib[0], attrib[1] );\r
}\r
\r
// Close the tag.\r
\r
if ( !element.isEmpty )\r
{\r
- // Send children.\r
- CKEDITOR.htmlParser.fragment.prototype.writeHtml.apply( element, arguments );\r
-\r
+ this.writeChildrenHtml.call( element, writer, isChildrenFiltered ? null : filter );\r
// Close the element.\r
writer.closeTag( writeName );\r
}\r
+ },\r
+\r
+ writeChildrenHtml : function( writer, filter )\r
+ {\r
+ // Send children.\r
+ CKEDITOR.htmlParser.fragment.prototype.writeChildrenHtml.apply( this, arguments );\r
+\r
}\r
};\r
})();\r