JasonWoof Got questions, comments, patches, etc.? Contact Jason Woofenden
vanilla ckeditor-3.6.6.1
[ckeditor.git] / _source / core / dom / element.js
1 /*\r
2 Copyright (c) 2003-2013, 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  * @fileOverview Defines the {@link CKEDITOR.dom.element} class, which\r
8  *              represents a DOM element.\r
9  */\r
10 \r
11 /**\r
12  * Represents a DOM element.\r
13  * @constructor\r
14  * @augments CKEDITOR.dom.node\r
15  * @param {Object|String} element A native DOM element or the element name for\r
16  *              new elements.\r
17  * @param {CKEDITOR.dom.document} [ownerDocument] The document that will contain\r
18  *              the element in case of element creation.\r
19  * @example\r
20  * // Create a new <span> element.\r
21  * var element = new CKEDITOR.dom.element( 'span' );\r
22  * @example\r
23  * // Create an element based on a native DOM element.\r
24  * var element = new CKEDITOR.dom.element( document.getElementById( 'myId' ) );\r
25  */\r
26 CKEDITOR.dom.element = function( element, ownerDocument )\r
27 {\r
28         if ( typeof element == 'string' )\r
29                 element = ( ownerDocument ? ownerDocument.$ : document ).createElement( element );\r
30 \r
31         // Call the base constructor (we must not call CKEDITOR.dom.node).\r
32         CKEDITOR.dom.domObject.call( this, element );\r
33 };\r
34 \r
35 // PACKAGER_RENAME( CKEDITOR.dom.element )\r
36 \r
37 /**\r
38  * The the {@link CKEDITOR.dom.element} representing and element. If the\r
39  * element is a native DOM element, it will be transformed into a valid\r
40  * CKEDITOR.dom.element object.\r
41  * @returns {CKEDITOR.dom.element} The transformed element.\r
42  * @example\r
43  * var element = new CKEDITOR.dom.element( 'span' );\r
44  * alert( element == <b>CKEDITOR.dom.element.get( element )</b> );  "true"\r
45  * @example\r
46  * var element = document.getElementById( 'myElement' );\r
47  * alert( <b>CKEDITOR.dom.element.get( element )</b>.getName() );  e.g. "p"\r
48  */\r
49 CKEDITOR.dom.element.get = function( element )\r
50 {\r
51         return element && ( element.$ ? element : new CKEDITOR.dom.element( element ) );\r
52 };\r
53 \r
54 CKEDITOR.dom.element.prototype = new CKEDITOR.dom.node();\r
55 \r
56 /**\r
57  * Creates an instance of the {@link CKEDITOR.dom.element} class based on the\r
58  * HTML representation of an element.\r
59  * @param {String} html The element HTML. It should define only one element in\r
60  *              the "root" level. The "root" element can have child nodes, but not\r
61  *              siblings.\r
62  * @returns {CKEDITOR.dom.element} The element instance.\r
63  * @example\r
64  * var element = <b>CKEDITOR.dom.element.createFromHtml( '&lt;strong class="anyclass"&gt;My element&lt;/strong&gt;' )</b>;\r
65  * alert( element.getName() );  // "strong"\r
66  */\r
67 CKEDITOR.dom.element.createFromHtml = function( html, ownerDocument )\r
68 {\r
69         var temp = new CKEDITOR.dom.element( 'div', ownerDocument );\r
70         temp.setHtml( html );\r
71 \r
72         // When returning the node, remove it from its parent to detach it.\r
73         return temp.getFirst().remove();\r
74 };\r
75 \r
76 CKEDITOR.dom.element.setMarker = function( database, element, name, value )\r
77 {\r
78         var id = element.getCustomData( 'list_marker_id' ) ||\r
79                         ( element.setCustomData( 'list_marker_id', CKEDITOR.tools.getNextNumber() ).getCustomData( 'list_marker_id' ) ),\r
80                 markerNames = element.getCustomData( 'list_marker_names' ) ||\r
81                         ( element.setCustomData( 'list_marker_names', {} ).getCustomData( 'list_marker_names' ) );\r
82         database[id] = element;\r
83         markerNames[name] = 1;\r
84 \r
85         return element.setCustomData( name, value );\r
86 };\r
87 \r
88 CKEDITOR.dom.element.clearAllMarkers = function( database )\r
89 {\r
90         for ( var i in database )\r
91                 CKEDITOR.dom.element.clearMarkers( database, database[i], 1 );\r
92 };\r
93 \r
94 CKEDITOR.dom.element.clearMarkers = function( database, element, removeFromDatabase )\r
95 {\r
96         var names = element.getCustomData( 'list_marker_names' ),\r
97                 id = element.getCustomData( 'list_marker_id' );\r
98         for ( var i in names )\r
99                 element.removeCustomData( i );\r
100         element.removeCustomData( 'list_marker_names' );\r
101         if ( removeFromDatabase )\r
102         {\r
103                 element.removeCustomData( 'list_marker_id' );\r
104                 delete database[id];\r
105         }\r
106 };\r
107 \r
108 ( function()\r
109 {\r
110 \r
111 CKEDITOR.tools.extend( CKEDITOR.dom.element.prototype,\r
112         /** @lends CKEDITOR.dom.element.prototype */\r
113         {\r
114                 /**\r
115                  * The node type. This is a constant value set to\r
116                  * {@link CKEDITOR.NODE_ELEMENT}.\r
117                  * @type Number\r
118                  * @example\r
119                  */\r
120                 type : CKEDITOR.NODE_ELEMENT,\r
121 \r
122                 /**\r
123                  * Adds a CSS class to the element. It appends the class to the\r
124                  * already existing names.\r
125                  * @param {String} className The name of the class to be added.\r
126                  * @example\r
127                  * var element = new CKEDITOR.dom.element( 'div' );\r
128                  * element.addClass( 'classA' );  // &lt;div class="classA"&gt;\r
129                  * element.addClass( 'classB' );  // &lt;div class="classA classB"&gt;\r
130                  * element.addClass( 'classA' );  // &lt;div class="classA classB"&gt;\r
131                  */\r
132                 addClass : function( className )\r
133                 {\r
134                         var c = this.$.className;\r
135                         if ( c )\r
136                         {\r
137                                 var regex = new RegExp( '(?:^|\\s)' + className + '(?:\\s|$)', '' );\r
138                                 if ( !regex.test( c ) )\r
139                                         c += ' ' + className;\r
140                         }\r
141                         this.$.className = c || className;\r
142                 },\r
143 \r
144                 /**\r
145                  * Removes a CSS class name from the elements classes. Other classes\r
146                  * remain untouched.\r
147                  * @param {String} className The name of the class to remove.\r
148                  * @example\r
149                  * var element = new CKEDITOR.dom.element( 'div' );\r
150                  * element.addClass( 'classA' );  // &lt;div class="classA"&gt;\r
151                  * element.addClass( 'classB' );  // &lt;div class="classA classB"&gt;\r
152                  * element.removeClass( 'classA' );  // &lt;div class="classB"&gt;\r
153                  * element.removeClass( 'classB' );  // &lt;div&gt;\r
154                  */\r
155                 removeClass : function( className )\r
156                 {\r
157                         var c = this.getAttribute( 'class' );\r
158                         if ( c )\r
159                         {\r
160                                 var regex = new RegExp( '(?:^|\\s+)' + className + '(?=\\s|$)', 'i' );\r
161                                 if ( regex.test( c ) )\r
162                                 {\r
163                                         c = c.replace( regex, '' ).replace( /^\s+/, '' );\r
164 \r
165                                         if ( c )\r
166                                                 this.setAttribute( 'class', c );\r
167                                         else\r
168                                                 this.removeAttribute( 'class' );\r
169                                 }\r
170                         }\r
171                 },\r
172 \r
173                 hasClass : function( className )\r
174                 {\r
175                         var regex = new RegExp( '(?:^|\\s+)' + className + '(?=\\s|$)', '' );\r
176                         return regex.test( this.getAttribute('class') );\r
177                 },\r
178 \r
179                 /**\r
180                  * Append a node as a child of this element.\r
181                  * @param {CKEDITOR.dom.node|String} node The node or element name to be\r
182                  *              appended.\r
183                  * @param {Boolean} [toStart] Indicates that the element is to be\r
184                  *              appended at the start.\r
185                  * @returns {CKEDITOR.dom.node} The appended node.\r
186                  * @example\r
187                  * var p = new CKEDITOR.dom.element( 'p' );\r
188                  *\r
189                  * var strong = new CKEDITOR.dom.element( 'strong' );\r
190                  * <b>p.append( strong );</b>\r
191                  *\r
192                  * var em = <b>p.append( 'em' );</b>\r
193                  *\r
194                  * // result: "&lt;p&gt;&lt;strong&gt;&lt;/strong&gt;&lt;em&gt;&lt;/em&gt;&lt;/p&gt;"\r
195                  */\r
196                 append : function( node, toStart )\r
197                 {\r
198                         if ( typeof node == 'string' )\r
199                                 node = this.getDocument().createElement( node );\r
200 \r
201                         if ( toStart )\r
202                                 this.$.insertBefore( node.$, this.$.firstChild );\r
203                         else\r
204                                 this.$.appendChild( node.$ );\r
205 \r
206                         return node;\r
207                 },\r
208 \r
209                 appendHtml : function( html )\r
210                 {\r
211                         if ( !this.$.childNodes.length )\r
212                                 this.setHtml( html );\r
213                         else\r
214                         {\r
215                                 var temp = new CKEDITOR.dom.element( 'div', this.getDocument() );\r
216                                 temp.setHtml( html );\r
217                                 temp.moveChildren( this );\r
218                         }\r
219                 },\r
220 \r
221                 /**\r
222                  * Append text to this element.\r
223                  * @param {String} text The text to be appended.\r
224                  * @returns {CKEDITOR.dom.node} The appended node.\r
225                  * @example\r
226                  * var p = new CKEDITOR.dom.element( 'p' );\r
227                  * p.appendText( 'This is' );\r
228                  * p.appendText( ' some text' );\r
229                  *\r
230                  * // result: "&lt;p&gt;This is some text&lt;/p&gt;"\r
231                  */\r
232                 appendText : function( text )\r
233                 {\r
234                         if ( this.$.text != undefined )\r
235                                 this.$.text += text;\r
236                         else\r
237                                 this.append( new CKEDITOR.dom.text( text ) );\r
238                 },\r
239 \r
240                 appendBogus : function()\r
241                 {\r
242                         var lastChild = this.getLast() ;\r
243 \r
244                         // Ignore empty/spaces text.\r
245                         while ( lastChild && lastChild.type == CKEDITOR.NODE_TEXT && !CKEDITOR.tools.rtrim( lastChild.getText() ) )\r
246                                 lastChild = lastChild.getPrevious();\r
247                         if ( !lastChild || !lastChild.is || !lastChild.is( 'br' ) )\r
248                         {\r
249                                 var bogus = CKEDITOR.env.opera ?\r
250                                                 this.getDocument().createText('') :\r
251                                                 this.getDocument().createElement( 'br' );\r
252 \r
253                                 CKEDITOR.env.gecko && bogus.setAttribute( 'type', '_moz' );\r
254 \r
255                                 this.append( bogus );\r
256                         }\r
257                 },\r
258 \r
259                 /**\r
260                  * Breaks one of the ancestor element in the element position, moving\r
261                  * this element between the broken parts.\r
262                  * @param {CKEDITOR.dom.element} parent The anscestor element to get broken.\r
263                  * @example\r
264                  * // Before breaking:\r
265                  * //     &lt;b&gt;This &lt;i&gt;is some&lt;span /&gt; sample&lt;/i&gt; test text&lt;/b&gt;\r
266                  * // If "element" is &lt;span /&gt; and "parent" is &lt;i&gt;:\r
267                  * //     &lt;b&gt;This &lt;i&gt;is some&lt;/i&gt;&lt;span /&gt;&lt;i&gt; sample&lt;/i&gt; test text&lt;/b&gt;\r
268                  * element.breakParent( parent );\r
269                  * @example\r
270                  * // Before breaking:\r
271                  * //     &lt;b&gt;This &lt;i&gt;is some&lt;span /&gt; sample&lt;/i&gt; test text&lt;/b&gt;\r
272                  * // If "element" is &lt;span /&gt; and "parent" is &lt;b&gt;:\r
273                  * //     &lt;b&gt;This &lt;i&gt;is some&lt;/i&gt;&lt;/b&gt;&lt;span /&gt;&lt;b&gt;&lt;i&gt; sample&lt;/i&gt; test text&lt;/b&gt;\r
274                  * element.breakParent( parent );\r
275                  */\r
276                 breakParent : function( parent )\r
277                 {\r
278                         var range = new CKEDITOR.dom.range( this.getDocument() );\r
279 \r
280                         // We'll be extracting part of this element, so let's use our\r
281                         // range to get the correct piece.\r
282                         range.setStartAfter( this );\r
283                         range.setEndAfter( parent );\r
284 \r
285                         // Extract it.\r
286                         var docFrag = range.extractContents();\r
287 \r
288                         // Move the element outside the broken element.\r
289                         range.insertNode( this.remove() );\r
290 \r
291                         // Re-insert the extracted piece after the element.\r
292                         docFrag.insertAfterNode( this );\r
293                 },\r
294 \r
295                 contains :\r
296                         CKEDITOR.env.ie || CKEDITOR.env.webkit ?\r
297                                 function( node )\r
298                                 {\r
299                                         var $ = this.$;\r
300 \r
301                                         return node.type != CKEDITOR.NODE_ELEMENT ?\r
302                                                 $.contains( node.getParent().$ ) :\r
303                                                 $ != node.$ && $.contains( node.$ );\r
304                                 }\r
305                         :\r
306                                 function( node )\r
307                                 {\r
308                                         return !!( this.$.compareDocumentPosition( node.$ ) & 16 );\r
309                                 },\r
310 \r
311                 /**\r
312                  * Moves the selection focus to this element.\r
313                  * @function\r
314                  * @param  {Boolean} defer Whether to asynchronously defer the\r
315                  *              execution by 100 ms.\r
316                  * @example\r
317                  * var element = CKEDITOR.document.getById( 'myTextarea' );\r
318                  * <b>element.focus()</b>;\r
319                  */\r
320                 focus : ( function()\r
321                 {\r
322                         function exec()\r
323                         {\r
324                         // IE throws error if the element is not visible.\r
325                         try\r
326                         {\r
327                                 this.$.focus();\r
328                         }\r
329                         catch (e)\r
330                         {}\r
331                         }\r
332 \r
333                         return function( defer )\r
334                         {\r
335                                 if ( defer )\r
336                                         CKEDITOR.tools.setTimeout( exec, 100, this );\r
337                                 else\r
338                                         exec.call( this );\r
339                         };\r
340                 })(),\r
341 \r
342                 /**\r
343                  * Gets the inner HTML of this element.\r
344                  * @returns {String} The inner HTML of this element.\r
345                  * @example\r
346                  * var element = CKEDITOR.dom.element.createFromHtml( '&lt;div&gt;&lt;b&gt;Example&lt;/b&gt;&lt;/div&gt;' );\r
347                  * alert( <b>p.getHtml()</b> );  // "&lt;b&gt;Example&lt;/b&gt;"\r
348                  */\r
349                 getHtml : function()\r
350                 {\r
351                         var retval = this.$.innerHTML;\r
352                         // Strip <?xml:namespace> tags in IE. (#3341).\r
353                         return CKEDITOR.env.ie ? retval.replace( /<\?[^>]*>/g, '' ) : retval;\r
354                 },\r
355 \r
356                 getOuterHtml : function()\r
357                 {\r
358                         if ( this.$.outerHTML )\r
359                         {\r
360                                 // IE includes the <?xml:namespace> tag in the outerHTML of\r
361                                 // namespaced element. So, we must strip it here. (#3341)\r
362                                 return this.$.outerHTML.replace( /<\?[^>]*>/, '' );\r
363                         }\r
364 \r
365                         var tmpDiv = this.$.ownerDocument.createElement( 'div' );\r
366                         tmpDiv.appendChild( this.$.cloneNode( true ) );\r
367                         return tmpDiv.innerHTML;\r
368                 },\r
369 \r
370                 /**\r
371                  * Sets the inner HTML of this element.\r
372                  * @param {String} html The HTML to be set for this element.\r
373                  * @returns {String} The inserted HTML.\r
374                  * @example\r
375                  * var p = new CKEDITOR.dom.element( 'p' );\r
376                  * <b>p.setHtml( '&lt;b&gt;Inner&lt;/b&gt; HTML' );</b>\r
377                  *\r
378                  * // result: "&lt;p&gt;&lt;b&gt;Inner&lt;/b&gt; HTML&lt;/p&gt;"\r
379                  */\r
380                 setHtml : function( html )\r
381                 {\r
382                         return ( this.$.innerHTML = html );\r
383                 },\r
384 \r
385                 /**\r
386                  * Sets the element contents as plain text.\r
387                  * @param {String} text The text to be set.\r
388                  * @returns {String} The inserted text.\r
389                  * @example\r
390                  * var element = new CKEDITOR.dom.element( 'div' );\r
391                  * element.setText( 'A > B & C < D' );\r
392                  * alert( element.innerHTML );  // "A &amp;gt; B &amp;amp; C &amp;lt; D"\r
393                  */\r
394                 setText : function( text )\r
395                 {\r
396                         CKEDITOR.dom.element.prototype.setText = ( this.$.innerText != undefined ) ?\r
397                                 function ( text )\r
398                                 {\r
399                                         return this.$.innerText = text;\r
400                                 } :\r
401                                 function ( text )\r
402                                 {\r
403                                         return this.$.textContent = text;\r
404                                 };\r
405 \r
406                         return this.setText( text );\r
407                 },\r
408 \r
409                 /**\r
410                  * Gets the value of an element attribute.\r
411                  * @function\r
412                  * @param {String} name The attribute name.\r
413                  * @returns {String} The attribute value or null if not defined.\r
414                  * @example\r
415                  * var element = CKEDITOR.dom.element.createFromHtml( '&lt;input type="text" /&gt;' );\r
416                  * alert( <b>element.getAttribute( 'type' )</b> );  // "text"\r
417                  */\r
418                 getAttribute : (function()\r
419                 {\r
420                         var standard = function( name )\r
421                         {\r
422                                 return this.$.getAttribute( name, 2 );\r
423                         };\r
424 \r
425                         if ( CKEDITOR.env.ie && ( CKEDITOR.env.ie7Compat || CKEDITOR.env.ie6Compat ) )\r
426                         {\r
427                                 return function( name )\r
428                                 {\r
429                                         switch ( name )\r
430                                         {\r
431                                                 case 'class':\r
432                                                         name = 'className';\r
433                                                         break;\r
434 \r
435                                                 case 'http-equiv':\r
436                                                         name = 'httpEquiv';\r
437                                                         break;\r
438 \r
439                                                 case 'name':\r
440                                                         return this.$.name;\r
441 \r
442                                                 case 'tabindex':\r
443                                                         var tabIndex = standard.call( this, name );\r
444 \r
445                                                         // IE returns tabIndex=0 by default for all\r
446                                                         // elements. For those elements,\r
447                                                         // getAtrribute( 'tabindex', 2 ) returns 32768\r
448                                                         // instead. So, we must make this check to give a\r
449                                                         // uniform result among all browsers.\r
450                                                         if ( tabIndex !== 0 && this.$.tabIndex === 0 )\r
451                                                                 tabIndex = null;\r
452 \r
453                                                         return tabIndex;\r
454                                                         break;\r
455 \r
456                                                 case 'checked':\r
457                                                 {\r
458                                                         var attr = this.$.attributes.getNamedItem( name ),\r
459                                                                 attrValue = attr.specified ? attr.nodeValue     // For value given by parser.\r
460                                                                                                                          : this.$.checked;  // For value created via DOM interface.\r
461 \r
462                                                         return attrValue ? 'checked' : null;\r
463                                                 }\r
464 \r
465                                                 case 'hspace':\r
466                                                 case 'value':\r
467                                                         return this.$[ name ];\r
468 \r
469                                                 case 'style':\r
470                                                         // IE does not return inline styles via getAttribute(). See #2947.\r
471                                                         return this.$.style.cssText;\r
472 \r
473                                                 case 'contenteditable':\r
474                                                 case 'contentEditable':\r
475                                                         return this.$.attributes.getNamedItem( 'contentEditable' ).specified ?\r
476                                                                         this.$.getAttribute( 'contentEditable' ) : null;\r
477                                         }\r
478 \r
479                                         return standard.call( this, name );\r
480                                 };\r
481                         }\r
482                         else\r
483                                 return standard;\r
484                 })(),\r
485 \r
486                 getChildren : function()\r
487                 {\r
488                         return new CKEDITOR.dom.nodeList( this.$.childNodes );\r
489                 },\r
490 \r
491                 /**\r
492                  * Gets the current computed value of one of the element CSS style\r
493                  * properties.\r
494                  * @function\r
495                  * @param {String} propertyName The style property name.\r
496                  * @returns {String} The property value.\r
497                  * @example\r
498                  * var element = new CKEDITOR.dom.element( 'span' );\r
499                  * alert( <b>element.getComputedStyle( 'display' )</b> );  // "inline"\r
500                  */\r
501                 getComputedStyle :\r
502                         CKEDITOR.env.ie ?\r
503                                 function( propertyName )\r
504                                 {\r
505                                         return this.$.currentStyle[ CKEDITOR.tools.cssStyleToDomStyle( propertyName ) ];\r
506                                 }\r
507                         :\r
508                                 function( propertyName )\r
509                                 {\r
510                                         var style = this.getWindow().$.getComputedStyle( this.$, null );\r
511 \r
512                                         // Firefox may return null if we call the above on a hidden iframe. (#9117)\r
513                                         return style ? style.getPropertyValue( propertyName ) : '';\r
514                                 },\r
515 \r
516                 /**\r
517                  * Gets the DTD entries for this element.\r
518                  * @returns {Object} An object containing the list of elements accepted\r
519                  *              by this element.\r
520                  */\r
521                 getDtd : function()\r
522                 {\r
523                         var dtd = CKEDITOR.dtd[ this.getName() ];\r
524 \r
525                         this.getDtd = function()\r
526                         {\r
527                                 return dtd;\r
528                         };\r
529 \r
530                         return dtd;\r
531                 },\r
532 \r
533                 getElementsByTag : CKEDITOR.dom.document.prototype.getElementsByTag,\r
534 \r
535                 /**\r
536                  * Gets the computed tabindex for this element.\r
537                  * @function\r
538                  * @returns {Number} The tabindex value.\r
539                  * @example\r
540                  * var element = CKEDITOR.document.getById( 'myDiv' );\r
541                  * alert( <b>element.getTabIndex()</b> );  // e.g. "-1"\r
542                  */\r
543                 getTabIndex :\r
544                         CKEDITOR.env.ie ?\r
545                                 function()\r
546                                 {\r
547                                         var tabIndex = this.$.tabIndex;\r
548 \r
549                                         // IE returns tabIndex=0 by default for all elements. In\r
550                                         // those cases we must check that the element really has\r
551                                         // the tabindex attribute set to zero, or it is one of\r
552                                         // those element that should have zero by default.\r
553                                         if ( tabIndex === 0 && !CKEDITOR.dtd.$tabIndex[ this.getName() ] && parseInt( this.getAttribute( 'tabindex' ), 10 ) !== 0 )\r
554                                                 tabIndex = -1;\r
555 \r
556                                                 return tabIndex;\r
557                                 }\r
558                         : CKEDITOR.env.webkit ?\r
559                                 function()\r
560                                 {\r
561                                         var tabIndex = this.$.tabIndex;\r
562 \r
563                                         // Safari returns "undefined" for elements that should not\r
564                                         // have tabindex (like a div). So, we must try to get it\r
565                                         // from the attribute.\r
566                                         // https://bugs.webkit.org/show_bug.cgi?id=20596\r
567                                         if ( tabIndex == undefined )\r
568                                         {\r
569                                                 tabIndex = parseInt( this.getAttribute( 'tabindex' ), 10 );\r
570 \r
571                                                 // If the element don't have the tabindex attribute,\r
572                                                 // then we should return -1.\r
573                                                 if ( isNaN( tabIndex ) )\r
574                                                         tabIndex = -1;\r
575                                         }\r
576 \r
577                                         return tabIndex;\r
578                                 }\r
579                         :\r
580                                 function()\r
581                                 {\r
582                                         return this.$.tabIndex;\r
583                                 },\r
584 \r
585                 /**\r
586                  * Gets the text value of this element.\r
587                  *\r
588                  * Only in IE (which uses innerText), &lt;br&gt; will cause linebreaks,\r
589                  * and sucessive whitespaces (including line breaks) will be reduced to\r
590                  * a single space. This behavior is ok for us, for now. It may change\r
591                  * in the future.\r
592                  * @returns {String} The text value.\r
593                  * @example\r
594                  * var element = CKEDITOR.dom.element.createFromHtml( '&lt;div&gt;Sample &lt;i&gt;text&lt;/i&gt;.&lt;/div&gt;' );\r
595                  * alert( <b>element.getText()</b> );  // "Sample text."\r
596                  */\r
597                 getText : function()\r
598                 {\r
599                         return this.$.textContent || this.$.innerText || '';\r
600                 },\r
601 \r
602                 /**\r
603                  * Gets the window object that contains this element.\r
604                  * @returns {CKEDITOR.dom.window} The window object.\r
605                  * @example\r
606                  */\r
607                 getWindow : function()\r
608                 {\r
609                         return this.getDocument().getWindow();\r
610                 },\r
611 \r
612                 /**\r
613                  * Gets the value of the "id" attribute of this element.\r
614                  * @returns {String} The element id, or null if not available.\r
615                  * @example\r
616                  * var element = CKEDITOR.dom.element.createFromHtml( '&lt;p id="myId"&gt;&lt;/p&gt;' );\r
617                  * alert( <b>element.getId()</b> );  // "myId"\r
618                  */\r
619                 getId : function()\r
620                 {\r
621                         return this.$.id || null;\r
622                 },\r
623 \r
624                 /**\r
625                  * Gets the value of the "name" attribute of this element.\r
626                  * @returns {String} The element name, or null if not available.\r
627                  * @example\r
628                  * var element = CKEDITOR.dom.element.createFromHtml( '&lt;input name="myName"&gt;&lt;/input&gt;' );\r
629                  * alert( <b>element.getNameAtt()</b> );  // "myName"\r
630                  */\r
631                 getNameAtt : function()\r
632                 {\r
633                         return this.$.name || null;\r
634                 },\r
635 \r
636                 /**\r
637                  * Gets the element name (tag name). The returned name is guaranteed to\r
638                  * be always full lowercased.\r
639                  * @returns {String} The element name.\r
640                  * @example\r
641                  * var element = new CKEDITOR.dom.element( 'span' );\r
642                  * alert( <b>element.getName()</b> );  // "span"\r
643                  */\r
644                 getName : function()\r
645                 {\r
646                         // Cache the lowercased name inside a closure.\r
647                         var nodeName = this.$.nodeName.toLowerCase();\r
648 \r
649                         if ( CKEDITOR.env.ie && ! ( document.documentMode > 8 ) )\r
650                         {\r
651                                 var scopeName = this.$.scopeName;\r
652                                 if ( scopeName != 'HTML' )\r
653                                         nodeName = scopeName.toLowerCase() + ':' + nodeName;\r
654                         }\r
655 \r
656                         return (\r
657                         this.getName = function()\r
658                                 {\r
659                                         return nodeName;\r
660                                 })();\r
661                 },\r
662 \r
663                 /**\r
664                  * Gets the value set to this element. This value is usually available\r
665                  * for form field elements.\r
666                  * @returns {String} The element value.\r
667                  */\r
668                 getValue : function()\r
669                 {\r
670                         return this.$.value;\r
671                 },\r
672 \r
673                 /**\r
674                  * Gets the first child node of this element.\r
675                  * @param {Function} evaluator Filtering the result node.\r
676                  * @returns {CKEDITOR.dom.node} The first child node or null if not\r
677                  *              available.\r
678                  * @example\r
679                  * var element = CKEDITOR.dom.element.createFromHtml( '&lt;div&gt;&lt;b&gt;Example&lt;/b&gt;&lt;/div&gt;' );\r
680                  * var first = <b>element.getFirst()</b>;\r
681                  * alert( first.getName() );  // "b"\r
682                  */\r
683                 getFirst : function( evaluator )\r
684                 {\r
685                         var first = this.$.firstChild,\r
686                                 retval = first && new CKEDITOR.dom.node( first );\r
687                         if ( retval && evaluator && !evaluator( retval ) )\r
688                                 retval = retval.getNext( evaluator );\r
689 \r
690                         return retval;\r
691                 },\r
692 \r
693                 /**\r
694                  * @param {Function} evaluator Filtering the result node.\r
695                  */\r
696                 getLast : function( evaluator )\r
697                 {\r
698                         var last = this.$.lastChild,\r
699                                 retval = last && new CKEDITOR.dom.node( last );\r
700                         if ( retval && evaluator && !evaluator( retval ) )\r
701                                 retval = retval.getPrevious( evaluator );\r
702 \r
703                         return retval;\r
704                 },\r
705 \r
706                 getStyle : function( name )\r
707                 {\r
708                         return this.$.style[ CKEDITOR.tools.cssStyleToDomStyle( name ) ];\r
709                 },\r
710 \r
711                 /**\r
712                  * Checks if the element name matches one or more names.\r
713                  * @param {String} name[,name[,...]] One or more names to be checked.\r
714                  * @returns {Boolean} true if the element name matches any of the names.\r
715                  * @example\r
716                  * var element = new CKEDITOR.element( 'span' );\r
717                  * alert( <b>element.is( 'span' )</b> );  "true"\r
718                  * alert( <b>element.is( 'p', 'span' )</b> );  "true"\r
719                  * alert( <b>element.is( 'p' )</b> );  "false"\r
720                  * alert( <b>element.is( 'p', 'div' )</b> );  "false"\r
721                  */\r
722                 is : function()\r
723                 {\r
724                         var name = this.getName();\r
725                         for ( var i = 0 ; i < arguments.length ; i++ )\r
726                         {\r
727                                 if ( arguments[ i ] == name )\r
728                                         return true;\r
729                         }\r
730                         return false;\r
731                 },\r
732 \r
733                 /**\r
734                  * Decide whether one element is able to receive cursor.\r
735                  * @param {Boolean} [textCursor=true] Only consider element that could receive text child.\r
736                  */\r
737                 isEditable : function( textCursor )\r
738                 {\r
739                         var name = this.getName();\r
740 \r
741                         if ( this.isReadOnly()\r
742                                         || this.getComputedStyle( 'display' ) == 'none'\r
743                                         || this.getComputedStyle( 'visibility' ) == 'hidden'\r
744                                         || this.is( 'a' ) && this.data( 'cke-saved-name' ) && !this.getChildCount()\r
745                                         || CKEDITOR.dtd.$nonEditable[ name ]\r
746                                         || CKEDITOR.dtd.$empty[ name ] )\r
747                         {\r
748                                 return false;\r
749                         }\r
750 \r
751                         if ( textCursor !== false )\r
752                         {\r
753                                 // Get the element DTD (defaults to span for unknown elements).\r
754                                 var dtd = CKEDITOR.dtd[ name ] || CKEDITOR.dtd.span;\r
755                                 // In the DTD # == text node.\r
756                                 return ( dtd && dtd[ '#'] );\r
757                         }\r
758 \r
759                         return true;\r
760                 },\r
761 \r
762                 isIdentical : function( otherElement )\r
763                 {\r
764                         if ( this.getName() != otherElement.getName() )\r
765                                 return false;\r
766 \r
767                         var thisAttribs = this.$.attributes,\r
768                                 otherAttribs = otherElement.$.attributes;\r
769 \r
770                         var thisLength = thisAttribs.length,\r
771                                 otherLength = otherAttribs.length;\r
772 \r
773                         for ( var i = 0 ; i < thisLength ; i++ )\r
774                         {\r
775                                 var attribute = thisAttribs[ i ];\r
776 \r
777                                 if ( attribute.nodeName == '_moz_dirty' )\r
778                                         continue;\r
779 \r
780                                 if ( ( !CKEDITOR.env.ie || ( attribute.specified && attribute.nodeName != 'data-cke-expando' ) ) && attribute.nodeValue != otherElement.getAttribute( attribute.nodeName ) )\r
781                                         return false;\r
782                         }\r
783 \r
784                         // For IE, we have to for both elements, because it's difficult to\r
785                         // know how the atttibutes collection is organized in its DOM.\r
786                         if ( CKEDITOR.env.ie )\r
787                         {\r
788                                 for ( i = 0 ; i < otherLength ; i++ )\r
789                                 {\r
790                                         attribute = otherAttribs[ i ];\r
791                                         if ( attribute.specified && attribute.nodeName != 'data-cke-expando'\r
792                                                         && attribute.nodeValue != this.getAttribute( attribute.nodeName ) )\r
793                                                 return false;\r
794                                 }\r
795                         }\r
796 \r
797                         return true;\r
798                 },\r
799 \r
800                 /**\r
801                  * Checks if this element is visible. May not work if the element is\r
802                  * child of an element with visibility set to "hidden", but works well\r
803                  * on the great majority of cases.\r
804                  * @return {Boolean} True if the element is visible.\r
805                  */\r
806                 isVisible : function()\r
807                 {\r
808                         var isVisible = ( this.$.offsetHeight || this.$.offsetWidth ) && this.getComputedStyle( 'visibility' ) != 'hidden',\r
809                                 elementWindow,\r
810                                 elementWindowFrame;\r
811 \r
812                         // Webkit and Opera report non-zero offsetHeight despite that\r
813                         // element is inside an invisible iframe. (#4542)\r
814                         if ( isVisible && ( CKEDITOR.env.webkit || CKEDITOR.env.opera ) )\r
815                         {\r
816                                 elementWindow = this.getWindow();\r
817 \r
818                                 if ( !elementWindow.equals( CKEDITOR.document.getWindow() )\r
819                                                 && ( elementWindowFrame = elementWindow.$.frameElement ) )\r
820                                 {\r
821                                         isVisible = new CKEDITOR.dom.element( elementWindowFrame ).isVisible();\r
822                                 }\r
823                         }\r
824 \r
825                         return !!isVisible;\r
826                 },\r
827 \r
828                 /**\r
829                  * Whether it's an empty inline elements which has no visual impact when removed.\r
830                  */\r
831                 isEmptyInlineRemoveable : function()\r
832                 {\r
833                         if ( !CKEDITOR.dtd.$removeEmpty[ this.getName() ] )\r
834                                 return false;\r
835 \r
836                         var children = this.getChildren();\r
837                         for ( var i = 0, count = children.count(); i < count; i++ )\r
838                         {\r
839                                 var child = children.getItem( i );\r
840 \r
841                                 if ( child.type == CKEDITOR.NODE_ELEMENT && child.data( 'cke-bookmark' ) )\r
842                                         continue;\r
843 \r
844                                 if ( child.type == CKEDITOR.NODE_ELEMENT && !child.isEmptyInlineRemoveable()\r
845                                         || child.type == CKEDITOR.NODE_TEXT && CKEDITOR.tools.trim( child.getText() ) )\r
846                                 {\r
847                                         return false;\r
848                                 }\r
849                         }\r
850                         return true;\r
851                 },\r
852 \r
853                 /**\r
854                  * Checks if the element has any defined attributes.\r
855                  * @function\r
856                  * @returns {Boolean} True if the element has attributes.\r
857                  * @example\r
858                  * var element = CKEDITOR.dom.element.createFromHtml( '&lt;div title="Test"&gt;Example&lt;/div&gt;' );\r
859                  * alert( <b>element.hasAttributes()</b> );  // "true"\r
860                  * @example\r
861                  * var element = CKEDITOR.dom.element.createFromHtml( '&lt;div&gt;Example&lt;/div&gt;' );\r
862                  * alert( <b>element.hasAttributes()</b> );  // "false"\r
863                  */\r
864                 hasAttributes :\r
865                         CKEDITOR.env.ie && ( CKEDITOR.env.ie7Compat || CKEDITOR.env.ie6Compat ) ?\r
866                                 function()\r
867                                 {\r
868                                         var attributes = this.$.attributes;\r
869 \r
870                                         for ( var i = 0 ; i < attributes.length ; i++ )\r
871                                         {\r
872                                                 var attribute = attributes[i];\r
873 \r
874                                                 switch ( attribute.nodeName )\r
875                                                 {\r
876                                                         case 'class' :\r
877                                                                 // IE has a strange bug. If calling removeAttribute('className'),\r
878                                                                 // the attributes collection will still contain the "class"\r
879                                                                 // attribute, which will be marked as "specified", even if the\r
880                                                                 // outerHTML of the element is not displaying the class attribute.\r
881                                                                 // Note : I was not able to reproduce it outside the editor,\r
882                                                                 // but I've faced it while working on the TC of #1391.\r
883                                                                 if ( this.getAttribute( 'class' ) )\r
884                                                                         return true;\r
885 \r
886                                                         // Attributes to be ignored.\r
887                                                         case 'data-cke-expando' :\r
888                                                                 continue;\r
889 \r
890                                                         /*jsl:fallthru*/\r
891 \r
892                                                         default :\r
893                                                                 if ( attribute.specified )\r
894                                                                         return true;\r
895                                                 }\r
896                                         }\r
897 \r
898                                         return false;\r
899                                 }\r
900                         :\r
901                                 function()\r
902                                 {\r
903                                         var attrs = this.$.attributes,\r
904                                                 attrsNum = attrs.length;\r
905 \r
906                                         // The _moz_dirty attribute might get into the element after pasting (#5455)\r
907                                         var execludeAttrs = { 'data-cke-expando' : 1, _moz_dirty : 1 };\r
908 \r
909                                         return attrsNum > 0 &&\r
910                                                 ( attrsNum > 2 ||\r
911                                                         !execludeAttrs[ attrs[0].nodeName ] ||\r
912                                                         ( attrsNum == 2 && !execludeAttrs[ attrs[1].nodeName ] ) );\r
913                                 },\r
914 \r
915                 /**\r
916                  * Checks if the specified attribute is defined for this element.\r
917                  * @returns {Boolean} True if the specified attribute is defined.\r
918                  * @param {String} name The attribute name.\r
919                  * @example\r
920                  */\r
921                 hasAttribute : (function()\r
922                 {\r
923                         function standard( name )\r
924                         {\r
925                                 var $attr = this.$.attributes.getNamedItem( name );\r
926                                 return !!( $attr && $attr.specified );\r
927                         }\r
928 \r
929                         return ( CKEDITOR.env.ie && CKEDITOR.env.version < 8 ) ?\r
930                                         function( name )\r
931                                         {\r
932                                                 // On IE < 8 the name attribute cannot be retrieved\r
933                                                 // right after the element creation and setting the\r
934                                                 // name with setAttribute.\r
935                                                 if ( name == 'name' )\r
936                                                         return !!this.$.name;\r
937 \r
938                                                 return standard.call( this, name );\r
939                                         }\r
940                                 :\r
941                                         standard;\r
942                 })(),\r
943 \r
944                 /**\r
945                  * Hides this element (display:none).\r
946                  * @example\r
947                  * var element = CKEDITOR.dom.element.getById( 'myElement' );\r
948                  * <b>element.hide()</b>;\r
949                  */\r
950                 hide : function()\r
951                 {\r
952                         this.setStyle( 'display', 'none' );\r
953                 },\r
954 \r
955                 moveChildren : function( target, toStart )\r
956                 {\r
957                         var $ = this.$;\r
958                         target = target.$;\r
959 \r
960                         if ( $ == target )\r
961                                 return;\r
962 \r
963                         var child;\r
964 \r
965                         if ( toStart )\r
966                         {\r
967                                 while ( ( child = $.lastChild ) )\r
968                                         target.insertBefore( $.removeChild( child ), target.firstChild );\r
969                         }\r
970                         else\r
971                         {\r
972                                 while ( ( child = $.firstChild ) )\r
973                                         target.appendChild( $.removeChild( child ) );\r
974                         }\r
975                 },\r
976 \r
977                 /**\r
978                  * Merges sibling elements that are identical to this one.<br>\r
979                  * <br>\r
980                  * Identical child elements are also merged. For example:<br>\r
981                  * &lt;b&gt;&lt;i&gt;&lt;/i&gt;&lt;/b&gt;&lt;b&gt;&lt;i&gt;&lt;/i&gt;&lt;/b&gt; =&gt; &lt;b&gt;&lt;i&gt;&lt;/i&gt;&lt;/b&gt;\r
982                  * @function\r
983                  * @param {Boolean} [inlineOnly] Allow only inline elements to be merged. Defaults to "true".\r
984                  */\r
985                 mergeSiblings : ( function()\r
986                 {\r
987                         function mergeElements( element, sibling, isNext )\r
988                         {\r
989                                 if ( sibling && sibling.type == CKEDITOR.NODE_ELEMENT )\r
990                                 {\r
991                                         // Jumping over bookmark nodes and empty inline elements, e.g. <b><i></i></b>,\r
992                                         // queuing them to be moved later. (#5567)\r
993                                         var pendingNodes = [];\r
994 \r
995                                         while ( sibling.data( 'cke-bookmark' )\r
996                                                 || sibling.isEmptyInlineRemoveable() )\r
997                                         {\r
998                                                 pendingNodes.push( sibling );\r
999                                                 sibling = isNext ? sibling.getNext() : sibling.getPrevious();\r
1000                                                 if ( !sibling || sibling.type != CKEDITOR.NODE_ELEMENT )\r
1001                                                         return;\r
1002                                         }\r
1003 \r
1004                                         if ( element.isIdentical( sibling ) )\r
1005                                         {\r
1006                                                 // Save the last child to be checked too, to merge things like\r
1007                                                 // <b><i></i></b><b><i></i></b> => <b><i></i></b>\r
1008                                                 var innerSibling = isNext ? element.getLast() : element.getFirst();\r
1009 \r
1010                                                 // Move pending nodes first into the target element.\r
1011                                                 while( pendingNodes.length )\r
1012                                                         pendingNodes.shift().move( element, !isNext );\r
1013 \r
1014                                                 sibling.moveChildren( element, !isNext );\r
1015                                                 sibling.remove();\r
1016 \r
1017                                                 // Now check the last inner child (see two comments above).\r
1018                                                 if ( innerSibling && innerSibling.type == CKEDITOR.NODE_ELEMENT )\r
1019                                                         innerSibling.mergeSiblings();\r
1020                                         }\r
1021                                 }\r
1022                         }\r
1023 \r
1024                         return function( inlineOnly )\r
1025                                 {\r
1026                                         if ( ! ( inlineOnly === false\r
1027                                                         || CKEDITOR.dtd.$removeEmpty[ this.getName() ]\r
1028                                                         || this.is( 'a' ) ) )   // Merge empty links and anchors also. (#5567)\r
1029                                         {\r
1030                                                 return;\r
1031                                         }\r
1032 \r
1033                                         mergeElements( this, this.getNext(), true );\r
1034                                         mergeElements( this, this.getPrevious() );\r
1035                                 };\r
1036                 } )(),\r
1037 \r
1038                 /**\r
1039                  * Shows this element (display it).\r
1040                  * @example\r
1041                  * var element = CKEDITOR.dom.element.getById( 'myElement' );\r
1042                  * <b>element.show()</b>;\r
1043                  */\r
1044                 show : function()\r
1045                 {\r
1046                         this.setStyles(\r
1047                                 {\r
1048                                         display : '',\r
1049                                         visibility : ''\r
1050                                 });\r
1051                 },\r
1052 \r
1053                 /**\r
1054                  * Sets the value of an element attribute.\r
1055                  * @param {String} name The name of the attribute.\r
1056                  * @param {String} value The value to be set to the attribute.\r
1057                  * @function\r
1058                  * @returns {CKEDITOR.dom.element} This element instance.\r
1059                  * @example\r
1060                  * var element = CKEDITOR.dom.element.getById( 'myElement' );\r
1061                  * <b>element.setAttribute( 'class', 'myClass' )</b>;\r
1062                  * <b>element.setAttribute( 'title', 'This is an example' )</b>;\r
1063                  */\r
1064                 setAttribute : (function()\r
1065                 {\r
1066                         var standard = function( name, value )\r
1067                         {\r
1068                                 this.$.setAttribute( name, value );\r
1069                                 return this;\r
1070                         };\r
1071 \r
1072                         if ( CKEDITOR.env.ie && ( CKEDITOR.env.ie7Compat || CKEDITOR.env.ie6Compat ) )\r
1073                         {\r
1074                                 return function( name, value )\r
1075                                 {\r
1076                                         if ( name == 'class' )\r
1077                                                 this.$.className = value;\r
1078                                         else if ( name == 'style' )\r
1079                                                 this.$.style.cssText = value;\r
1080                                         else if ( name == 'tabindex' )  // Case sensitive.\r
1081                                                 this.$.tabIndex = value;\r
1082                                         else if ( name == 'checked' )\r
1083                                                 this.$.checked = value;\r
1084                                         else if ( name == 'contenteditable' )\r
1085                                                 standard.call( this, 'contentEditable', value );\r
1086                                         else\r
1087                                                 standard.apply( this, arguments );\r
1088                                         return this;\r
1089                                 };\r
1090                         }\r
1091                         else if ( CKEDITOR.env.ie8Compat && CKEDITOR.env.secure )\r
1092                         {\r
1093                                 return function( name, value )\r
1094                                 {\r
1095                                         // IE8 throws error when setting src attribute to non-ssl value. (#7847)\r
1096                                         if ( name == 'src' && value.match( /^http:\/\// ) )\r
1097                                                 try { standard.apply( this, arguments ); } catch( e ){}\r
1098                                         else\r
1099                                                 standard.apply( this, arguments );\r
1100                                         return this;\r
1101                                 };\r
1102                         }\r
1103                         else\r
1104                                 return standard;\r
1105                 })(),\r
1106 \r
1107                 /**\r
1108                  * Sets the value of several element attributes.\r
1109                  * @param {Object} attributesPairs An object containing the names and\r
1110                  *              values of the attributes.\r
1111                  * @returns {CKEDITOR.dom.element} This element instance.\r
1112                  * @example\r
1113                  * var element = CKEDITOR.dom.element.getById( 'myElement' );\r
1114                  * <b>element.setAttributes({\r
1115                  *     'class' : 'myClass',\r
1116                  *     'title' : 'This is an example' })</b>;\r
1117                  */\r
1118                 setAttributes : function( attributesPairs )\r
1119                 {\r
1120                         for ( var name in attributesPairs )\r
1121                                 this.setAttribute( name, attributesPairs[ name ] );\r
1122                         return this;\r
1123                 },\r
1124 \r
1125                 /**\r
1126                  * Sets the element value. This function is usually used with form\r
1127                  * field element.\r
1128                  * @param {String} value The element value.\r
1129                  * @returns {CKEDITOR.dom.element} This element instance.\r
1130                  */\r
1131                 setValue : function( value )\r
1132                 {\r
1133                         this.$.value = value;\r
1134                         return this;\r
1135                 },\r
1136 \r
1137                 /**\r
1138                  * Removes an attribute from the element.\r
1139                  * @param {String} name The attribute name.\r
1140                  * @function\r
1141                  * @example\r
1142                  * var element = CKEDITOR.dom.element.createFromHtml( '<div class="classA"></div>' );\r
1143                  * element.removeAttribute( 'class' );\r
1144                  */\r
1145                 removeAttribute : (function()\r
1146                 {\r
1147                         var standard = function( name )\r
1148                         {\r
1149                                 this.$.removeAttribute( name );\r
1150                         };\r
1151 \r
1152                         if ( CKEDITOR.env.ie && ( CKEDITOR.env.ie7Compat || CKEDITOR.env.ie6Compat ) )\r
1153                         {\r
1154                                 return function( name )\r
1155                                 {\r
1156                                         if ( name == 'class' )\r
1157                                                 name = 'className';\r
1158                                         else if ( name == 'tabindex' )\r
1159                                                 name = 'tabIndex';\r
1160                                         else if ( name == 'contenteditable' )\r
1161                                                 name = 'contentEditable';\r
1162                                         standard.call( this, name );\r
1163                                 };\r
1164                         }\r
1165                         else\r
1166                                 return standard;\r
1167                 })(),\r
1168 \r
1169                 removeAttributes : function ( attributes )\r
1170                 {\r
1171                         if ( CKEDITOR.tools.isArray( attributes ) )\r
1172                         {\r
1173                                 for ( var i = 0 ; i < attributes.length ; i++ )\r
1174                                         this.removeAttribute( attributes[ i ] );\r
1175                         }\r
1176                         else\r
1177                         {\r
1178                                 for ( var attr in attributes )\r
1179                                         attributes.hasOwnProperty( attr ) && this.removeAttribute( attr );\r
1180                         }\r
1181                 },\r
1182 \r
1183                 /**\r
1184                  * Removes a style from the element.\r
1185                  * @param {String} name The style name.\r
1186                  * @function\r
1187                  * @example\r
1188                  * var element = CKEDITOR.dom.element.createFromHtml( '<div style="display:none"></div>' );\r
1189                  * element.removeStyle( 'display' );\r
1190                  */\r
1191                 removeStyle : function( name )\r
1192                 {\r
1193                         // Removes the specified property from the current style object.\r
1194                         var $ = this.$.style;\r
1195 \r
1196                         // "removeProperty" need to be specific on the following styles.\r
1197                         if ( !$.removeProperty && ( name == 'border' || name == 'margin' || name == 'padding' ) )\r
1198                         {\r
1199                                 var names = expandedRules( name );\r
1200                                 for ( var i = 0 ; i < names.length ; i++ )\r
1201                                         this.removeStyle( names[ i ] );\r
1202                                 return;\r
1203                         }\r
1204 \r
1205                         $.removeProperty ? $.removeProperty( name ) : $.removeAttribute( CKEDITOR.tools.cssStyleToDomStyle( name ) );\r
1206 \r
1207                         if ( !this.$.style.cssText )\r
1208                                 this.removeAttribute( 'style' );\r
1209                 },\r
1210 \r
1211                 /**\r
1212                  * Sets the value of an element style.\r
1213                  * @param {String} name The name of the style. The CSS naming notation\r
1214                  *              must be used (e.g. "background-color").\r
1215                  * @param {String} value The value to be set to the style.\r
1216                  * @returns {CKEDITOR.dom.element} This element instance.\r
1217                  * @example\r
1218                  * var element = CKEDITOR.dom.element.getById( 'myElement' );\r
1219                  * <b>element.setStyle( 'background-color', '#ff0000' )</b>;\r
1220                  * <b>element.setStyle( 'margin-top', '10px' )</b>;\r
1221                  * <b>element.setStyle( 'float', 'right' )</b>;\r
1222                  */\r
1223                 setStyle : function( name, value )\r
1224                 {\r
1225                         this.$.style[ CKEDITOR.tools.cssStyleToDomStyle( name ) ] = value;\r
1226                         return this;\r
1227                 },\r
1228 \r
1229                 /**\r
1230                  * Sets the value of several element styles.\r
1231                  * @param {Object} stylesPairs An object containing the names and\r
1232                  *              values of the styles.\r
1233                  * @returns {CKEDITOR.dom.element} This element instance.\r
1234                  * @example\r
1235                  * var element = CKEDITOR.dom.element.getById( 'myElement' );\r
1236                  * <b>element.setStyles({\r
1237                  *     'position' : 'absolute',\r
1238                  *     'float' : 'right' })</b>;\r
1239                  */\r
1240                 setStyles : function( stylesPairs )\r
1241                 {\r
1242                         for ( var name in stylesPairs )\r
1243                                 this.setStyle( name, stylesPairs[ name ] );\r
1244                         return this;\r
1245                 },\r
1246 \r
1247                 /**\r
1248                  * Sets the opacity of an element.\r
1249                  * @param {Number} opacity A number within the range [0.0, 1.0].\r
1250                  * @example\r
1251                  * var element = CKEDITOR.dom.element.getById( 'myElement' );\r
1252                  * <b>element.setOpacity( 0.75 )</b>;\r
1253                  */\r
1254                 setOpacity : function( opacity )\r
1255                 {\r
1256                         if ( CKEDITOR.env.ie && CKEDITOR.env.version < 9 )\r
1257                         {\r
1258                                 opacity = Math.round( opacity * 100 );\r
1259                                 this.setStyle( 'filter', opacity >= 100 ? '' : 'progid:DXImageTransform.Microsoft.Alpha(opacity=' + opacity + ')' );\r
1260                         }\r
1261                         else\r
1262                                 this.setStyle( 'opacity', opacity );\r
1263                 },\r
1264 \r
1265                 /**\r
1266                  * Makes the element and its children unselectable.\r
1267                  * @function\r
1268                  * @example\r
1269                  * var element = CKEDITOR.dom.element.getById( 'myElement' );\r
1270                  * element.unselectable();\r
1271                  */\r
1272                 unselectable :\r
1273                         CKEDITOR.env.gecko ?\r
1274                                 function()\r
1275                                 {\r
1276                                         this.$.style.MozUserSelect = 'none';\r
1277                                         this.on( 'dragstart', function( evt ) { evt.data.preventDefault(); } );\r
1278                                 }\r
1279                         : CKEDITOR.env.webkit ?\r
1280                                 function()\r
1281                                 {\r
1282                                         this.$.style.KhtmlUserSelect = 'none';\r
1283                                         this.on( 'dragstart', function( evt ) { evt.data.preventDefault(); } );\r
1284                                 }\r
1285                         :\r
1286                                 function()\r
1287                                 {\r
1288                                         if ( CKEDITOR.env.ie || CKEDITOR.env.opera )\r
1289                                         {\r
1290                                                 var element = this.$,\r
1291                                                         elements = element.getElementsByTagName("*"),\r
1292                                                         e,\r
1293                                                         i = 0;\r
1294 \r
1295                                                 element.unselectable = 'on';\r
1296 \r
1297                                                 while ( ( e = elements[ i++ ] ) )\r
1298                                                 {\r
1299                                                         switch ( e.tagName.toLowerCase() )\r
1300                                                         {\r
1301                                                                 case 'iframe' :\r
1302                                                                 case 'textarea' :\r
1303                                                                 case 'input' :\r
1304                                                                 case 'select' :\r
1305                                                                         /* Ignore the above tags */\r
1306                                                                         break;\r
1307                                                                 default :\r
1308                                                                         e.unselectable = 'on';\r
1309                                                         }\r
1310                                                 }\r
1311                                         }\r
1312                                 },\r
1313 \r
1314                 getPositionedAncestor : function()\r
1315                 {\r
1316                         var current = this;\r
1317                         while ( current.getName() != 'html' )\r
1318                         {\r
1319                                 if ( current.getComputedStyle( 'position' ) != 'static' )\r
1320                                         return current;\r
1321 \r
1322                                 current = current.getParent();\r
1323                         }\r
1324                         return null;\r
1325                 },\r
1326 \r
1327                 getDocumentPosition : function( refDocument )\r
1328                 {\r
1329                         var x = 0, y = 0,\r
1330                                 doc = this.getDocument(),\r
1331                                 body = doc.getBody(),\r
1332                                 quirks = doc.$.compatMode == 'BackCompat';\r
1333 \r
1334                         if ( document.documentElement[ "getBoundingClientRect" ] )\r
1335                         {\r
1336                                 var box  = this.$.getBoundingClientRect(),\r
1337                                         $doc = doc.$,\r
1338                                         $docElem = $doc.documentElement;\r
1339 \r
1340                                 var clientTop = $docElem.clientTop || body.$.clientTop || 0,\r
1341                                         clientLeft = $docElem.clientLeft || body.$.clientLeft || 0,\r
1342                                         needAdjustScrollAndBorders = true;\r
1343 \r
1344                                 /*\r
1345                                  * #3804: getBoundingClientRect() works differently on IE and non-IE\r
1346                                  * browsers, regarding scroll positions.\r
1347                                  *\r
1348                                  * On IE, the top position of the <html> element is always 0, no matter\r
1349                                  * how much you scrolled down.\r
1350                                  *\r
1351                                  * On other browsers, the top position of the <html> element is negative\r
1352                                  * scrollTop.\r
1353                                  */\r
1354                                 if ( CKEDITOR.env.ie )\r
1355                                 {\r
1356                                         var inDocElem = doc.getDocumentElement().contains( this ),\r
1357                                                 inBody = doc.getBody().contains( this );\r
1358 \r
1359                                         needAdjustScrollAndBorders = ( quirks && inBody ) || ( !quirks && inDocElem );\r
1360                                 }\r
1361 \r
1362                                 if ( needAdjustScrollAndBorders )\r
1363                                 {\r
1364                                         x = box.left + ( !quirks && $docElem.scrollLeft || body.$.scrollLeft );\r
1365                                         x -= clientLeft;\r
1366                                         y = box.top  + ( !quirks && $docElem.scrollTop || body.$.scrollTop );\r
1367                                         y -= clientTop;\r
1368                                 }\r
1369                         }\r
1370                         else\r
1371                         {\r
1372                                 var current = this, previous = null, offsetParent;\r
1373                                 while ( current && !( current.getName() == 'body' || current.getName() == 'html' ) )\r
1374                                 {\r
1375                                         x += current.$.offsetLeft - current.$.scrollLeft;\r
1376                                         y += current.$.offsetTop - current.$.scrollTop;\r
1377 \r
1378                                         // Opera includes clientTop|Left into offsetTop|Left.\r
1379                                         if ( !current.equals( this ) )\r
1380                                         {\r
1381                                                 x += ( current.$.clientLeft || 0 );\r
1382                                                 y += ( current.$.clientTop || 0 );\r
1383                                         }\r
1384 \r
1385                                         var scrollElement = previous;\r
1386                                         while ( scrollElement && !scrollElement.equals( current ) )\r
1387                                         {\r
1388                                                 x -= scrollElement.$.scrollLeft;\r
1389                                                 y -= scrollElement.$.scrollTop;\r
1390                                                 scrollElement = scrollElement.getParent();\r
1391                                         }\r
1392 \r
1393                                         previous = current;\r
1394                                         current = ( offsetParent = current.$.offsetParent ) ?\r
1395                                                   new CKEDITOR.dom.element( offsetParent ) : null;\r
1396                                 }\r
1397                         }\r
1398 \r
1399                         if ( refDocument )\r
1400                         {\r
1401                                 var currentWindow = this.getWindow(),\r
1402                                         refWindow = refDocument.getWindow();\r
1403 \r
1404                                 if ( !currentWindow.equals( refWindow ) && currentWindow.$.frameElement )\r
1405                                 {\r
1406                                         var iframePosition = ( new CKEDITOR.dom.element( currentWindow.$.frameElement ) ).getDocumentPosition( refDocument );\r
1407 \r
1408                                         x += iframePosition.x;\r
1409                                         y += iframePosition.y;\r
1410                                 }\r
1411                         }\r
1412 \r
1413                         if ( !document.documentElement[ "getBoundingClientRect" ] )\r
1414                         {\r
1415                                 // In Firefox, we'll endup one pixel before the element positions,\r
1416                                 // so we must add it here.\r
1417                                 if ( CKEDITOR.env.gecko && !quirks )\r
1418                                 {\r
1419                                         x += this.$.clientLeft ? 1 : 0;\r
1420                                         y += this.$.clientTop ? 1 : 0;\r
1421                                 }\r
1422                         }\r
1423 \r
1424                         return { x : x, y : y };\r
1425                 },\r
1426 \r
1427                 /**\r
1428                  * Make any page element visible inside the browser viewport.\r
1429                  * @param {Boolean} [alignToTop]\r
1430                  */\r
1431                 scrollIntoView : function( alignToTop )\r
1432                 {\r
1433                         var parent = this.getParent();\r
1434                         if ( !parent ) return;\r
1435 \r
1436                         // Scroll the element into parent container from the inner out.\r
1437                         do\r
1438                         {\r
1439                                 // Check ancestors that overflows.\r
1440                                 var overflowed =\r
1441                                         parent.$.clientWidth && parent.$.clientWidth < parent.$.scrollWidth\r
1442                                         || parent.$.clientHeight && parent.$.clientHeight < parent.$.scrollHeight;\r
1443 \r
1444                                 if ( overflowed )\r
1445                                         this.scrollIntoParent( parent, alignToTop, 1 );\r
1446 \r
1447                                 // Walk across the frame.\r
1448                                 if ( parent.is( 'html' ) )\r
1449                                 {\r
1450                                         var win = parent.getWindow();\r
1451 \r
1452                                         // Avoid security error.\r
1453                                         try\r
1454                                         {\r
1455                                                 var iframe = win.$.frameElement;\r
1456                                                 iframe && ( parent = new CKEDITOR.dom.element( iframe ) );\r
1457                                         }\r
1458                                         catch(er){}\r
1459                                 }\r
1460                         }\r
1461                         while ( ( parent = parent.getParent() ) );\r
1462                 },\r
1463 \r
1464                 /**\r
1465                  * Make any page element visible inside one of the ancestors by scrolling the parent.\r
1466                  * @param {CKEDITOR.dom.element|CKEDITOR.dom.window} parent The container to scroll into.\r
1467                  * @param {Boolean} [alignToTop] Align the element's top side with the container's\r
1468                  * when <code>true</code> is specified; align the bottom with viewport bottom when\r
1469                  * <code>false</code> is specified. Otherwise scroll on either side with the minimum\r
1470                  * amount to show the element.\r
1471                  * @param {Boolean} [hscroll] Whether horizontal overflow should be considered.\r
1472                  */\r
1473                 scrollIntoParent : function( parent, alignToTop, hscroll )\r
1474                 {\r
1475                         !parent && ( parent = this.getWindow() );\r
1476 \r
1477                         var doc = parent.getDocument();\r
1478                         var isQuirks = doc.$.compatMode == 'BackCompat';\r
1479 \r
1480                         // On window <html> is scrolled while quirks scrolls <body>.\r
1481                         if ( parent instanceof CKEDITOR.dom.window )\r
1482                                 parent = isQuirks ? doc.getBody() : doc.getDocumentElement();\r
1483 \r
1484                         // Scroll the parent by the specified amount.\r
1485                         function scrollBy( x, y )\r
1486                         {\r
1487                                 // Webkit doesn't support "scrollTop/scrollLeft"\r
1488                                 // on documentElement/body element.\r
1489                                 if ( /body|html/.test( parent.getName() ) )\r
1490                                         parent.getWindow().$.scrollBy( x, y );\r
1491                                 else\r
1492                                 {\r
1493                                         parent.$[ 'scrollLeft' ] += x;\r
1494                                         parent.$[ 'scrollTop' ] += y;\r
1495                                 }\r
1496                         }\r
1497 \r
1498                         // Figure out the element position relative to the specified window.\r
1499                         function screenPos( element, refWin )\r
1500                         {\r
1501                                 var pos = { x: 0, y: 0 };\r
1502 \r
1503                                 if ( !( element.is( isQuirks ? 'body' : 'html' ) ) )\r
1504                                 {\r
1505                                         var box = element.$.getBoundingClientRect();\r
1506                                         pos.x = box.left, pos.y = box.top;\r
1507                                 }\r
1508 \r
1509                                 var win = element.getWindow();\r
1510                                 if ( !win.equals( refWin ) )\r
1511                                 {\r
1512                                         var outerPos = screenPos( CKEDITOR.dom.element.get( win.$.frameElement ), refWin );\r
1513                                         pos.x += outerPos.x, pos.y += outerPos.y;\r
1514                                 }\r
1515 \r
1516                                 return pos;\r
1517                         }\r
1518 \r
1519                         // calculated margin size.\r
1520                         function margin( element, side )\r
1521                         {\r
1522                                 return parseInt( element.getComputedStyle( 'margin-' + side ) || 0, 10 ) || 0;\r
1523                         }\r
1524 \r
1525                         var win = parent.getWindow();\r
1526 \r
1527                         var thisPos = screenPos( this, win ),\r
1528                                 parentPos = screenPos( parent, win ),\r
1529                                 eh = this.$.offsetHeight,\r
1530                                 ew = this.$.offsetWidth,\r
1531                                 ch = parent.$.clientHeight,\r
1532                                 cw = parent.$.clientWidth,\r
1533                                 lt,\r
1534                                 br;\r
1535 \r
1536                         // Left-top margins.\r
1537                         lt =\r
1538                         {\r
1539                                 x : thisPos.x - margin( this, 'left' ) - parentPos.x || 0,\r
1540                                 y : thisPos.y - margin( this, 'top' ) - parentPos.y|| 0\r
1541                         };\r
1542 \r
1543                         // Bottom-right margins.\r
1544                         br =\r
1545                         {\r
1546                                 x : thisPos.x + ew + margin( this, 'right' ) - ( ( parentPos.x ) + cw ) || 0,\r
1547                                 y : thisPos.y + eh + margin( this, 'bottom' ) - ( ( parentPos.y ) + ch ) || 0\r
1548                         };\r
1549 \r
1550                         // 1. Do the specified alignment as much as possible;\r
1551                         // 2. Otherwise be smart to scroll only the minimum amount;\r
1552                         // 3. Never cut at the top;\r
1553                         // 4. DO NOT scroll when already visible.\r
1554                         if ( lt.y < 0 || br.y > 0 )\r
1555                         {\r
1556                                 scrollBy( 0,\r
1557                                                   alignToTop === true ? lt.y :\r
1558                                                   alignToTop === false ? br.y :\r
1559                                                   lt.y < 0 ? lt.y : br.y );\r
1560                         }\r
1561 \r
1562                         if ( hscroll && ( lt.x < 0 || br.x > 0 ) )\r
1563                                 scrollBy( lt.x < 0 ? lt.x : br.x, 0 );\r
1564                 },\r
1565 \r
1566                 setState : function( state )\r
1567                 {\r
1568                         switch ( state )\r
1569                         {\r
1570                                 case CKEDITOR.TRISTATE_ON :\r
1571                                         this.addClass( 'cke_on' );\r
1572                                         this.removeClass( 'cke_off' );\r
1573                                         this.removeClass( 'cke_disabled' );\r
1574                                         break;\r
1575                                 case CKEDITOR.TRISTATE_DISABLED :\r
1576                                         this.addClass( 'cke_disabled' );\r
1577                                         this.removeClass( 'cke_off' );\r
1578                                         this.removeClass( 'cke_on' );\r
1579                                         break;\r
1580                                 default :\r
1581                                         this.addClass( 'cke_off' );\r
1582                                         this.removeClass( 'cke_on' );\r
1583                                         this.removeClass( 'cke_disabled' );\r
1584                                         break;\r
1585                         }\r
1586                 },\r
1587 \r
1588                 /**\r
1589                  * Returns the inner document of this IFRAME element.\r
1590                  * @returns {CKEDITOR.dom.document} The inner document.\r
1591                  */\r
1592                 getFrameDocument : function()\r
1593                 {\r
1594                         var $ = this.$;\r
1595 \r
1596                         try\r
1597                         {\r
1598                                 // In IE, with custom document.domain, it may happen that\r
1599                                 // the iframe is not yet available, resulting in "Access\r
1600                                 // Denied" for the following property access.\r
1601                                 $.contentWindow.document;\r
1602                         }\r
1603                         catch ( e )\r
1604                         {\r
1605                                 // Trick to solve this issue, forcing the iframe to get ready\r
1606                                 // by simply setting its "src" property.\r
1607                                 $.src = $.src;\r
1608 \r
1609                                 // In IE6 though, the above is not enough, so we must pause the\r
1610                                 // execution for a while, giving it time to think.\r
1611                                 if ( CKEDITOR.env.ie && CKEDITOR.env.version < 7 )\r
1612                                 {\r
1613                                         window.showModalDialog(\r
1614                                                 'javascript:document.write("' +\r
1615                                                         '<script>' +\r
1616                                                                 'window.setTimeout(' +\r
1617                                                                         'function(){window.close();}' +\r
1618                                                                         ',50);' +\r
1619                                                         '</script>")' );\r
1620                                 }\r
1621                         }\r
1622 \r
1623                         return $ && new CKEDITOR.dom.document( $.contentWindow.document );\r
1624                 },\r
1625 \r
1626                 /**\r
1627                  * Copy all the attributes from one node to the other, kinda like a clone\r
1628                  * skipAttributes is an object with the attributes that must NOT be copied.\r
1629                  * @param {CKEDITOR.dom.element} dest The destination element.\r
1630                  * @param {Object} skipAttributes A dictionary of attributes to skip.\r
1631                  * @example\r
1632                  */\r
1633                 copyAttributes : function( dest, skipAttributes )\r
1634                 {\r
1635                         var attributes = this.$.attributes;\r
1636                         skipAttributes = skipAttributes || {};\r
1637 \r
1638                         for ( var n = 0 ; n < attributes.length ; n++ )\r
1639                         {\r
1640                                 var attribute = attributes[n];\r
1641 \r
1642                                 // Lowercase attribute name hard rule is broken for\r
1643                                 // some attribute on IE, e.g. CHECKED.\r
1644                                 var attrName = attribute.nodeName.toLowerCase(),\r
1645                                         attrValue;\r
1646 \r
1647                                 // We can set the type only once, so do it with the proper value, not copying it.\r
1648                                 if ( attrName in skipAttributes )\r
1649                                         continue;\r
1650 \r
1651                                 if ( attrName == 'checked' && ( attrValue = this.getAttribute( attrName ) ) )\r
1652                                         dest.setAttribute( attrName, attrValue );\r
1653                                 // IE BUG: value attribute is never specified even if it exists.\r
1654                                 else if ( attribute.specified ||\r
1655                                   ( CKEDITOR.env.ie && attribute.nodeValue && attrName == 'value' ) )\r
1656                                 {\r
1657                                         attrValue = this.getAttribute( attrName );\r
1658                                         if ( attrValue === null )\r
1659                                                 attrValue = attribute.nodeValue;\r
1660 \r
1661                                         dest.setAttribute( attrName, attrValue );\r
1662                                 }\r
1663                         }\r
1664 \r
1665                         // The style:\r
1666                         if ( this.$.style.cssText !== '' )\r
1667                                 dest.$.style.cssText = this.$.style.cssText;\r
1668                 },\r
1669 \r
1670                 /**\r
1671                  * Changes the tag name of the current element.\r
1672                  * @param {String} newTag The new tag for the element.\r
1673                  */\r
1674                 renameNode : function( newTag )\r
1675                 {\r
1676                         // If it's already correct exit here.\r
1677                         if ( this.getName() == newTag )\r
1678                                 return;\r
1679 \r
1680                         var doc = this.getDocument();\r
1681 \r
1682                         // Create the new node.\r
1683                         var newNode = new CKEDITOR.dom.element( newTag, doc );\r
1684 \r
1685                         // Copy all attributes.\r
1686                         this.copyAttributes( newNode );\r
1687 \r
1688                         // Move children to the new node.\r
1689                         this.moveChildren( newNode );\r
1690 \r
1691                         // Replace the node.\r
1692                         this.getParent() && this.$.parentNode.replaceChild( newNode.$, this.$ );\r
1693                         newNode.$[ 'data-cke-expando' ] = this.$[ 'data-cke-expando' ];\r
1694                         this.$ = newNode.$;\r
1695                 },\r
1696 \r
1697                 /**\r
1698                  * Gets a DOM tree descendant under the current node.\r
1699                  * @param {Array|Number} indices The child index or array of child indices under the node.\r
1700                  * @returns {CKEDITOR.dom.node} The specified DOM child under the current node. Null if child does not exist.\r
1701                  * @example\r
1702                  * var strong = p.getChild(0);\r
1703                  */\r
1704                 getChild : function( indices )\r
1705                 {\r
1706                         var rawNode = this.$;\r
1707 \r
1708                         if ( !indices.slice )\r
1709                                 rawNode = rawNode.childNodes[ indices ];\r
1710                         else\r
1711                         {\r
1712                                 while ( indices.length > 0 && rawNode )\r
1713                                         rawNode = rawNode.childNodes[ indices.shift() ];\r
1714                         }\r
1715 \r
1716                         return rawNode ? new CKEDITOR.dom.node( rawNode ) : null;\r
1717                 },\r
1718 \r
1719                 getChildCount : function()\r
1720                 {\r
1721                         return this.$.childNodes.length;\r
1722                 },\r
1723 \r
1724                 disableContextMenu : function()\r
1725                 {\r
1726                         this.on( 'contextmenu', function( event )\r
1727                                 {\r
1728                                         // Cancel the browser context menu.\r
1729                                         if ( !event.data.getTarget().hasClass( 'cke_enable_context_menu' ) )\r
1730                                                 event.data.preventDefault();\r
1731                                 } );\r
1732                 },\r
1733 \r
1734                 /**\r
1735                  * Gets element's direction. Supports both CSS 'direction' prop and 'dir' attr.\r
1736                  */\r
1737                 getDirection : function( useComputed )\r
1738                 {\r
1739                         return useComputed ?\r
1740                                 this.getComputedStyle( 'direction' )\r
1741                                         // Webkit: offline element returns empty direction (#8053).\r
1742                                         || this.getDirection()\r
1743                                         || this.getDocument().$.dir\r
1744                                         || this.getDocument().getBody().getDirection( 1 )\r
1745                                 : this.getStyle( 'direction' ) || this.getAttribute( 'dir' );\r
1746                 },\r
1747 \r
1748                 /**\r
1749                  * Gets, sets and removes custom data to be stored as HTML5 data-* attributes.\r
1750                  * @param {String} name The name of the attribute, excluding the 'data-' part.\r
1751                  * @param {String} [value] The value to set. If set to false, the attribute will be removed.\r
1752                  * @example\r
1753                  * element.data( 'extra-info', 'test' );   // appended the attribute data-extra-info="test" to the element\r
1754                  * alert( element.data( 'extra-info' ) );  // "test"\r
1755                  * element.data( 'extra-info', false );    // remove the data-extra-info attribute from the element\r
1756                  */\r
1757                 data : function ( name, value )\r
1758                 {\r
1759                         name = 'data-' + name;\r
1760                         if ( value === undefined )\r
1761                                 return this.getAttribute( name );\r
1762                         else if ( value === false )\r
1763                                 this.removeAttribute( name );\r
1764                         else\r
1765                                 this.setAttribute( name, value );\r
1766 \r
1767                         return null;\r
1768                 }\r
1769         });\r
1770 \r
1771         var sides = {\r
1772                 width : [ "border-left-width", "border-right-width","padding-left", "padding-right" ],\r
1773                 height : [ "border-top-width", "border-bottom-width", "padding-top",  "padding-bottom" ]\r
1774         };\r
1775 \r
1776         // Generate list of specific style rules, applicable to margin/padding/border.\r
1777         function expandedRules( style )\r
1778         {\r
1779                 var sides = [ 'top', 'left', 'right', 'bottom' ], components;\r
1780 \r
1781                 if ( style == 'border' )\r
1782                                 components = [ 'color', 'style', 'width' ];\r
1783 \r
1784                 var styles = [];\r
1785                 for ( var i = 0 ; i < sides.length ; i++ )\r
1786                 {\r
1787 \r
1788                         if ( components )\r
1789                         {\r
1790                                 for ( var j = 0 ; j < components.length ; j++ )\r
1791                                         styles.push( [ style, sides[ i ], components[j] ].join( '-' ) );\r
1792                         }\r
1793                         else\r
1794                                 styles.push( [ style, sides[ i ] ].join( '-' ) );\r
1795                 }\r
1796 \r
1797                 return styles;\r
1798         }\r
1799 \r
1800         function marginAndPaddingSize( type )\r
1801         {\r
1802                 var adjustment = 0;\r
1803                 for ( var i = 0, len = sides[ type ].length; i < len; i++ )\r
1804                         adjustment += parseInt( this.getComputedStyle( sides [ type ][ i ] ) || 0, 10 ) || 0;\r
1805                 return adjustment;\r
1806         }\r
1807 \r
1808         /**\r
1809          * Sets the element size considering the box model.\r
1810          * @name CKEDITOR.dom.element.prototype.setSize\r
1811          * @function\r
1812          * @param {String} type The dimension to set. It accepts "width" and "height".\r
1813          * @param {Number} size The length unit in px.\r
1814          * @param {Boolean} isBorderBox Apply the size based on the border box model.\r
1815          */\r
1816         CKEDITOR.dom.element.prototype.setSize = function( type, size, isBorderBox )\r
1817                 {\r
1818                         if ( typeof size == 'number' )\r
1819                         {\r
1820                                 if ( isBorderBox && !( CKEDITOR.env.ie && CKEDITOR.env.quirks ) )\r
1821                                         size -= marginAndPaddingSize.call( this, type );\r
1822 \r
1823                                 this.setStyle( type, size + 'px' );\r
1824                         }\r
1825                 };\r
1826 \r
1827         /**\r
1828          * Gets the element size, possibly considering the box model.\r
1829          * @name CKEDITOR.dom.element.prototype.getSize\r
1830          * @function\r
1831          * @param {String} type The dimension to get. It accepts "width" and "height".\r
1832          * @param {Boolean} isBorderBox Get the size based on the border box model.\r
1833          */\r
1834         CKEDITOR.dom.element.prototype.getSize = function( type, isBorderBox )\r
1835                 {\r
1836                         var size = Math.max( this.$[ 'offset' + CKEDITOR.tools.capitalize( type )  ],\r
1837                                 this.$[ 'client' + CKEDITOR.tools.capitalize( type )  ] ) || 0;\r
1838 \r
1839                         if ( isBorderBox )\r
1840                                 size -= marginAndPaddingSize.call( this, type );\r
1841 \r
1842                         return size;\r
1843                 };\r
1844 })();\r