initial commit
[namibia] / public / scripts / ckeditor / _source / core / htmlparser / element.js
1 /*
2 Copyright (c) 2003-2011, CKSource - Frederico Knabben. All rights reserved.
3 For licensing, see LICENSE.html or http://ckeditor.com/license
4 */
5
6 /**
7  * A lightweight representation of an HTML element.
8  * @param {String} name The element name.
9  * @param {Object} attributes And object holding all attributes defined for
10  *              this element.
11  * @constructor
12  * @example
13  */
14 CKEDITOR.htmlParser.element = function( name, attributes )
15 {
16         /**
17          * The element name.
18          * @type String
19          * @example
20          */
21         this.name = name;
22
23         /**
24          * Holds the attributes defined for this element.
25          * @type Object
26          * @example
27          */
28         this.attributes = attributes || ( attributes = {} );
29
30         /**
31          * The nodes that are direct children of this element.
32          * @type Array
33          * @example
34          */
35         this.children = [];
36
37         var tagName = attributes[ 'data-cke-real-element-type' ] || name || '';
38
39         // Reveal the real semantic of our internal custom tag name (#6639).
40         var internalTag = tagName.match( /^cke:(.*)/ );
41         internalTag && ( tagName = internalTag[ 1 ] );
42
43         var dtd                 = CKEDITOR.dtd,
44                 isBlockLike     = !!( dtd.$nonBodyContent[ tagName ]
45                                 || dtd.$block[ tagName ]
46                                 || dtd.$listItem[ tagName ]
47                                 || dtd.$tableContent[ tagName ]
48                                 || dtd.$nonEditable[ tagName ]
49                                 || tagName == 'br' ),
50                 isEmpty = !!dtd.$empty[ name ];
51
52         this.isEmpty    = isEmpty;
53         this.isUnknown  = !dtd[ name ];
54
55         /** @private */
56         this._ =
57         {
58                 isBlockLike : isBlockLike,
59                 hasInlineStarted : isEmpty || !isBlockLike
60         };
61 };
62
63 /**
64  *  Object presentation of  CSS style declaration text.
65  *  @param {CKEDITOR.htmlParser.element|String} elementOrStyleText A html parser element or the inline style text.
66  */
67 CKEDITOR.htmlParser.cssStyle = function()
68 {
69          var styleText,
70                 arg = arguments[ 0 ],
71                 rules = {};
72
73         styleText = arg instanceof CKEDITOR.htmlParser.element ? arg.attributes.style : arg;
74
75         // html-encoded quote might be introduced by 'font-family'
76         // from MS-Word which confused the following regexp. e.g.
77         //'font-family: "Lucida, Console"'
78         ( styleText || '' )
79                 .replace( /"/g, '"' )
80                 .replace( /\s*([^ :;]+)\s*:\s*([^;]+)\s*(?=;|$)/g,
81                         function( match, name, value )
82                         {
83                                 name == 'font-family' && ( value = value.replace( /["']/g, '' ) );
84                                 rules[ name.toLowerCase() ] = value;
85                         });
86
87         return {
88
89                 rules : rules,
90
91                 /**
92                  *  Apply the styles onto the specified element or object.
93                  * @param {CKEDITOR.htmlParser.element|CKEDITOR.dom.element|Object} obj
94                  */
95                 populate : function( obj )
96                 {
97                         var style = this.toString();
98                         if ( style )
99                         {
100                                 obj instanceof CKEDITOR.dom.element ?
101                                         obj.setAttribute( 'style', style ) :
102                                         obj instanceof CKEDITOR.htmlParser.element ?
103                                                 obj.attributes.style = style :
104                                                 obj.style = style;
105                         }
106                 },
107
108                 toString : function()
109                 {
110                         var output = [];
111                         for ( var i in rules )
112                                 rules[ i ] && output.push( i, ':', rules[ i ], ';' );
113                         return output.join( '' );
114                 }
115         };
116 };
117
118 (function()
119 {
120         // Used to sort attribute entries in an array, where the first element of
121         // each object is the attribute name.
122         var sortAttribs = function( a, b )
123         {
124                 a = a[0];
125                 b = b[0];
126                 return a < b ? -1 : a > b ? 1 : 0;
127         };
128
129         CKEDITOR.htmlParser.element.prototype =
130         {
131                 /**
132                  * The node type. This is a constant value set to {@link CKEDITOR.NODE_ELEMENT}.
133                  * @type Number
134                  * @example
135                  */
136                 type : CKEDITOR.NODE_ELEMENT,
137
138                 /**
139                  * Adds a node to the element children list.
140                  * @param {Object} node The node to be added. It can be any of of the
141                  *              following types: {@link CKEDITOR.htmlParser.element},
142                  *              {@link CKEDITOR.htmlParser.text} and
143                  *              {@link CKEDITOR.htmlParser.comment}.
144                  * @function
145                  * @example
146                  */
147                 add : CKEDITOR.htmlParser.fragment.prototype.add,
148
149                 /**
150                  * Clone this element.
151                  * @returns {CKEDITOR.htmlParser.element} The element clone.
152                  * @example
153                  */
154                 clone : function()
155                 {
156                         return new CKEDITOR.htmlParser.element( this.name, this.attributes );
157                 },
158
159                 /**
160                  * Writes the element HTML to a CKEDITOR.htmlWriter.
161                  * @param {CKEDITOR.htmlWriter} writer The writer to which write the HTML.
162                  * @example
163                  */
164                 writeHtml : function( writer, filter )
165                 {
166                         var attributes = this.attributes;
167
168                         // Ignore cke: prefixes when writing HTML.
169                         var element = this,
170                                 writeName = element.name,
171                                 a, newAttrName, value;
172
173                         var isChildrenFiltered;
174
175                         /**
176                          * Providing an option for bottom-up filtering order ( element
177                          * children to be pre-filtered before the element itself ).
178                          */
179                         element.filterChildren = function()
180                         {
181                                 if ( !isChildrenFiltered )
182                                 {
183                                         var writer = new CKEDITOR.htmlParser.basicWriter();
184                                         CKEDITOR.htmlParser.fragment.prototype.writeChildrenHtml.call( element, writer, filter );
185                                         element.children = new CKEDITOR.htmlParser.fragment.fromHtml( writer.getHtml(), 0, element.clone() ).children;
186                                         isChildrenFiltered = 1;
187                                 }
188                         };
189
190                         if ( filter )
191                         {
192                                 while ( true )
193                                 {
194                                         if ( !( writeName = filter.onElementName( writeName ) ) )
195                                                 return;
196
197                                         element.name = writeName;
198
199                                         if ( !( element = filter.onElement( element ) ) )
200                                                 return;
201
202                                         element.parent = this.parent;
203
204                                         if ( element.name == writeName )
205                                                 break;
206
207                                         // If the element has been replaced with something of a
208                                         // different type, then make the replacement write itself.
209                                         if ( element.type != CKEDITOR.NODE_ELEMENT )
210                                         {
211                                                 element.writeHtml( writer, filter );
212                                                 return;
213                                         }
214
215                                         writeName = element.name;
216
217                                         // This indicate that the element has been dropped by
218                                         // filter but not the children.
219                                         if ( !writeName )
220                                         {
221                                                 // Fix broken parent refs.
222                                                 for ( var c = 0, length = this.children.length ; c < length ; c++ )
223                                                         this.children[ c ].parent = element.parent;
224
225                                                 this.writeChildrenHtml.call( element, writer, isChildrenFiltered ? null : filter );
226                                                 return;
227                                         }
228                                 }
229
230                                 // The element may have been changed, so update the local
231                                 // references.
232                                 attributes = element.attributes;
233                         }
234
235                         // Open element tag.
236                         writer.openTag( writeName, attributes );
237
238                         // Copy all attributes to an array.
239                         var attribsArray = [];
240                         // Iterate over the attributes twice since filters may alter
241                         // other attributes.
242                         for ( var i = 0 ; i < 2; i++ )
243                         {
244                                 for ( a in attributes )
245                                 {
246                                         newAttrName = a;
247                                         value = attributes[ a ];
248                                         if ( i == 1 )
249                                                 attribsArray.push( [ a, value ] );
250                                         else if ( filter )
251                                         {
252                                                 while ( true )
253                                                 {
254                                                         if ( !( newAttrName = filter.onAttributeName( a ) ) )
255                                                         {
256                                                                 delete attributes[ a ];
257                                                                 break;
258                                                         }
259                                                         else if ( newAttrName != a )
260                                                         {
261                                                                 delete attributes[ a ];
262                                                                 a = newAttrName;
263                                                                 continue;
264                                                         }
265                                                         else
266                                                                 break;
267                                                 }
268                                                 if ( newAttrName )
269                                                 {
270                                                         if ( ( value = filter.onAttribute( element, newAttrName, value ) ) === false )
271                                                                 delete attributes[ newAttrName ];
272                                                         else
273                                                                 attributes [ newAttrName ] = value;
274                                                 }
275                                         }
276                                 }
277                         }
278                         // Sort the attributes by name.
279                         if ( writer.sortAttributes )
280                                 attribsArray.sort( sortAttribs );
281
282                         // Send the attributes.
283                         var len = attribsArray.length;
284                         for ( i = 0 ; i < len ; i++ )
285                         {
286                                 var attrib = attribsArray[ i ];
287                                 writer.attribute( attrib[0], attrib[1] );
288                         }
289
290                         // Close the tag.
291                         writer.openTagClose( writeName, element.isEmpty );
292
293                         if ( !element.isEmpty )
294                         {
295                                 this.writeChildrenHtml.call( element, writer, isChildrenFiltered ? null : filter );
296                                 // Close the element.
297                                 writer.closeTag( writeName );
298                         }
299                 },
300
301                 writeChildrenHtml : function( writer, filter )
302                 {
303                         // Send children.
304                         CKEDITOR.htmlParser.fragment.prototype.writeChildrenHtml.apply( this, arguments );
305
306                 }
307         };
308 })();