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