JasonWoof Got questions, comments, patches, etc.? Contact Jason Woofenden
vanilla ckeditor-3.1
[ckeditor.git] / _source / core / htmlparser / element.js
1 /*\r
2 Copyright (c) 2003-2010, CKSource - Frederico Knabben. All rights reserved.\r
3 For licensing, see LICENSE.html or http://ckeditor.com/license\r
4 */\r
5 \r
6 /**\r
7  * A lightweight representation of an HTML element.\r
8  * @param {String} name The element name.\r
9  * @param {Object} attributes And object holding all attributes defined for\r
10  *              this element.\r
11  * @constructor\r
12  * @example\r
13  */\r
14 CKEDITOR.htmlParser.element = function( name, attributes )\r
15 {\r
16         /**\r
17          * The element name.\r
18          * @type String\r
19          * @example\r
20          */\r
21         this.name = name;\r
22 \r
23         /**\r
24          * Holds the attributes defined for this element.\r
25          * @type Object\r
26          * @example\r
27          */\r
28         this.attributes = attributes || ( attributes = {} );\r
29 \r
30         /**\r
31          * The nodes that are direct children of this element.\r
32          * @type Array\r
33          * @example\r
34          */\r
35         this.children = [];\r
36 \r
37         var tagName = attributes._cke_real_element_type || name;\r
38 \r
39         var dtd                 = CKEDITOR.dtd,\r
40                 isBlockLike     = !!( dtd.$nonBodyContent[ tagName ] || dtd.$block[ tagName ] || dtd.$listItem[ tagName ] || dtd.$tableContent[ tagName ] || dtd.$nonEditable[ tagName ] || tagName == 'br' ),\r
41                 isEmpty         = !!dtd.$empty[ name ];\r
42 \r
43         this.isEmpty    = isEmpty;\r
44         this.isUnknown  = !dtd[ name ];\r
45 \r
46         /** @private */\r
47         this._ =\r
48         {\r
49                 isBlockLike : isBlockLike,\r
50                 hasInlineStarted : isEmpty || !isBlockLike\r
51         };\r
52 };\r
53 \r
54 (function()\r
55 {\r
56         // Used to sort attribute entries in an array, where the first element of\r
57         // each object is the attribute name.\r
58         var sortAttribs = function( a, b )\r
59         {\r
60                 a = a[0];\r
61                 b = b[0];\r
62                 return a < b ? -1 : a > b ? 1 : 0;\r
63         };\r
64 \r
65         CKEDITOR.htmlParser.element.prototype =\r
66         {\r
67                 /**\r
68                  * The node type. This is a constant value set to {@link CKEDITOR.NODE_ELEMENT}.\r
69                  * @type Number\r
70                  * @example\r
71                  */\r
72                 type : CKEDITOR.NODE_ELEMENT,\r
73 \r
74                 /**\r
75                  * Adds a node to the element children list.\r
76                  * @param {Object} node The node to be added. It can be any of of the\r
77                  *              following types: {@link CKEDITOR.htmlParser.element},\r
78                  *              {@link CKEDITOR.htmlParser.text} and\r
79                  *              {@link CKEDITOR.htmlParser.comment}.\r
80                  * @function\r
81                  * @example\r
82                  */\r
83                 add : CKEDITOR.htmlParser.fragment.prototype.add,\r
84 \r
85                 /**\r
86                  * Clone this element.\r
87                  * @returns {CKEDITOR.htmlParser.element} The element clone.\r
88                  * @example\r
89                  */\r
90                 clone : function()\r
91                 {\r
92                         return new CKEDITOR.htmlParser.element( this.name, this.attributes );\r
93                 },\r
94 \r
95                 /**\r
96                  * Writes the element HTML to a CKEDITOR.htmlWriter.\r
97                  * @param {CKEDITOR.htmlWriter} writer The writer to which write the HTML.\r
98                  * @example\r
99                  */\r
100                 writeHtml : function( writer, filter )\r
101                 {\r
102                         var attributes = this.attributes;\r
103 \r
104                         // Ignore cke: prefixes when writing HTML.\r
105                         var element = this,\r
106                                 writeName = element.name,\r
107                                 a, newAttrName, value;\r
108 \r
109                         var isChildrenFiltered;\r
110 \r
111                         /**\r
112                          * Providing an option for bottom-up filtering order ( element\r
113                          * children to be pre-filtered before the element itself ).\r
114                          */\r
115                         element.filterChildren = function()\r
116                         {\r
117                                 if( !isChildrenFiltered )\r
118                                 {\r
119                                         var writer = new CKEDITOR.htmlParser.basicWriter();\r
120                                         CKEDITOR.htmlParser.fragment.prototype.writeChildrenHtml.call( element, writer, filter );\r
121                                         element.children = new CKEDITOR.htmlParser.fragment.fromHtml( writer.getHtml() ).children;\r
122                                         isChildrenFiltered = 1;\r
123                                 }\r
124                         };\r
125 \r
126                         if ( filter )\r
127                         {\r
128                                 while ( true )\r
129                                 {\r
130                                         if ( !( writeName = filter.onElementName( writeName ) ) )\r
131                                                 return;\r
132 \r
133                                         element.name = writeName;\r
134 \r
135                                         if ( !( element = filter.onElement( element ) ) )\r
136                                                 return;\r
137 \r
138                                         element.parent = this.parent;\r
139 \r
140                                         if ( element.name == writeName )\r
141                                                 break;\r
142 \r
143                                         // If the element has been replaced with something of a\r
144                                         // different type, then make the replacement write itself.\r
145                                         if ( element.type != CKEDITOR.NODE_ELEMENT )\r
146                                         {\r
147                                                 element.writeHtml( writer, filter );\r
148                                                 return;\r
149                                         }\r
150 \r
151                                         writeName = element.name;\r
152 \r
153                                         // This indicate that the element has been dropped by\r
154                                         // filter but not the children.\r
155                                         if ( !writeName )\r
156                                         {\r
157                                                 this.writeChildrenHtml.call( element, writer, isChildrenFiltered ? null : filter );\r
158                                                 return;\r
159                                         }\r
160                                 }\r
161 \r
162                                 // The element may have been changed, so update the local\r
163                                 // references.\r
164                                 attributes = element.attributes;\r
165                         }\r
166 \r
167                         // Open element tag.\r
168                         writer.openTag( writeName, attributes );\r
169 \r
170                         // Copy all attributes to an array.\r
171                         var attribsArray = [];\r
172                         // Iterate over the attributes twice since filters may alter\r
173                         // other attributes.\r
174                         for( var i = 0 ; i < 2; i++ )\r
175                         {\r
176                                 for ( a in attributes )\r
177                                 {\r
178                                         newAttrName = a;\r
179                                         value = attributes[ a ];\r
180                                         if( i == 1 )\r
181                                                 attribsArray.push( [ a, value ] );\r
182                                         else if ( filter )\r
183                                         {\r
184                                                 while ( true )\r
185                                                 {\r
186                                                         if ( !( newAttrName = filter.onAttributeName( a ) ) )\r
187                                                         {\r
188                                                                 delete attributes[ a ];\r
189                                                                 break;\r
190                                                         }\r
191                                                         else if( newAttrName != a )\r
192                                                         {\r
193                                                                 delete attributes[ a ];\r
194                                                                 a = newAttrName;\r
195                                                                 continue;\r
196                                                         }\r
197                                                         else\r
198                                                                 break;\r
199                                                 }\r
200                                                 if( newAttrName )\r
201                                                 {\r
202                                                         if( ( value = filter.onAttribute( element, newAttrName, value ) ) === false )\r
203                                                                 delete attributes[ newAttrName ];\r
204                                                         else\r
205                                                                 attributes [ newAttrName ] = value;\r
206                                                 }\r
207                                         }\r
208                                 }\r
209                         }\r
210                         // Sort the attributes by name.\r
211                         if ( writer.sortAttributes )\r
212                                 attribsArray.sort( sortAttribs );\r
213 \r
214                         // Send the attributes.\r
215                         var len = attribsArray.length;\r
216                         for ( i = 0 ; i < len ; i++ )\r
217                         {\r
218                                 var attrib = attribsArray[ i ];\r
219                                 writer.attribute( attrib[0], attrib[1] );\r
220                         }\r
221 \r
222                         // Close the tag.\r
223                         writer.openTagClose( writeName, element.isEmpty );\r
224 \r
225                         if ( !element.isEmpty )\r
226                         {\r
227                                 this.writeChildrenHtml.call( element, writer, isChildrenFiltered ? null : filter );\r
228                                 // Close the element.\r
229                                 writer.closeTag( writeName );\r
230                         }\r
231                 },\r
232 \r
233                 writeChildrenHtml : function( writer, filter )\r
234                 {\r
235                         // Send children.\r
236                         CKEDITOR.htmlParser.fragment.prototype.writeChildrenHtml.apply( this, arguments );\r
237 \r
238                 }\r
239         };\r
240 })();\r