      1 ﻿/*
      2 Copyright (c) 2003-2009, CKSource - Frederico Knabben. All rights reserved.
      3 For licensing, see LICENSE.html or http://ckeditor.com/license
      4 */
      5
      6 /**
      7  * @fileOverview Defines the {@link CKEDITOR.dom.element} class, which
      8  *		represents a DOM element.
      9  */
     10
     11 /**
     12  * Represents a DOM element.
     13  * @constructor
     14  * @augments CKEDITOR.dom.node
     15  * @param {Object|String} element A native DOM element or the element name for
     16  *		new elements.
     17  * @param {CKEDITOR.dom.document} [ownerDocument] The document that will contain
     18  *		the element in case of element creation.
     19  * @example
     20  * // Create a new <span> element.
     21  * var element = new CKEDITOR.dom.element( 'span' );
     22  * @example
     23  * // Create an element based on a native DOM element.
     24  * var element = new CKEDITOR.dom.element( document.getElementById( 'myId' ) );
     25  */
     26 CKEDITOR.dom.element = function( element, ownerDocument )
     27 {
     28 	if ( typeof element == 'string' )
     29 		element = ( ownerDocument ? ownerDocument.$ : document ).createElement( element );
     30
     31 	// Call the base constructor (we must not call CKEDITOR.dom.node).
     32 	CKEDITOR.dom.domObject.call( this, element );
     33 };
     34
     35 // PACKAGER_RENAME( CKEDITOR.dom.element )
     36
     37 /**
     38  * The the {@link CKEDITOR.dom.element} representing and element. If the
     39  * element is a native DOM element, it will be transformed into a valid
     40  * CKEDITOR.dom.element object.
     41  * @returns {CKEDITOR.dom.element} The transformed element.
     42  * @example
     43  * var element = new CKEDITOR.dom.element( 'span' );
     44  * alert( element == <b>CKEDITOR.dom.element.get( element )</b> );  "true"
     45  * @example
     46  * var element = document.getElementById( 'myElement' );
     47  * alert( <b>CKEDITOR.dom.element.get( element )</b>.getName() );  e.g. "p"
     48  */
     49 CKEDITOR.dom.element.get = function( element )
     50 {
     51 	return element && ( element.$ ? element : new CKEDITOR.dom.element( element ) );
     52 };
     53
     54 CKEDITOR.dom.element.prototype = new CKEDITOR.dom.node();
     55
     56 /**
     57  * Creates an instance of the {@link CKEDITOR.dom.element} class based on the
     58  * HTML representation of an element.
     59  * @param {String} html The element HTML. It should define only one element in
     60  *		the "root" level. The "root" element can have child nodes, but not
     61  *		siblings.
     62  * @returns {CKEDITOR.dom.element} The element instance.
     63  * @example
     64  * var element = <b>CKEDITOR.dom.element.createFromHtml( '<strong class="anyclass">My element</strong>' )</b>;
     65  * alert( element.getName() );  // "strong"
     66  */
     67 CKEDITOR.dom.element.createFromHtml = function( html, ownerDocument )
     68 {
     69 	var temp = new CKEDITOR.dom.element( 'div', ownerDocument );
     70 	temp.setHtml( html );
     71
     72 	// When returning the node, remove it from its parent to detach it.
     73 	return temp.getFirst().remove();
     74 };
     75
     76 CKEDITOR.dom.element.setMarker = function( database, element, name, value )
     77 {
     78 	var id = element.getCustomData( 'list_marker_id' ) ||
     79 			( element.setCustomData( 'list_marker_id', CKEDITOR.tools.getNextNumber() ).getCustomData( 'list_marker_id' ) ),
     80 		markerNames = element.getCustomData( 'list_marker_names' ) ||
     81 			( element.setCustomData( 'list_marker_names', {} ).getCustomData( 'list_marker_names' ) );
     82 	database[id] = element;
     83 	markerNames[name] = 1;
     84
     85 	return element.setCustomData( name, value );
     86 };
     87
     88 CKEDITOR.dom.element.clearAllMarkers = function( database )
     89 {
     90 	for ( var i in database )
     91 		CKEDITOR.dom.element.clearMarkers( database, database[i], true );
     92 };
     93
     94 CKEDITOR.dom.element.clearMarkers = function( database, element, removeFromDatabase )
     95 {
     96 	var names = element.getCustomData( 'list_marker_names' ),
     97 		id = element.getCustomData( 'list_marker_id' );
     98 	for ( var i in names )
     99 		element.removeCustomData( i );
    100 	element.removeCustomData( 'list_marker_names' );
    101 	if ( removeFromDatabase )
    102 	{
    103 		element.removeCustomData( 'list_marker_id' );
    104 		delete database[id];
    105 	}
    106 };
    107
    108 CKEDITOR.tools.extend( CKEDITOR.dom.element.prototype,
    109 	/** @lends CKEDITOR.dom.element.prototype */
    110 	{
    111 		/**
    112 		 * The node type. This is a constant value set to
    113 		 * {@link CKEDITOR.NODE_ELEMENT}.
    114 		 * @type Number
    115 		 * @example
    116 		 */
    117 		type : CKEDITOR.NODE_ELEMENT,
    118
    119 		/**
    120 		 * Adds a CSS class to the element. It appends the class to the
    121 		 * already existing names.
    122 		 * @param {String} className The name of the class to be added.
    123 		 * @example
    124 		 * var element = new CKEDITOR.dom.element( 'div' );
    125 		 * element.addClass( 'classA' );  // <div class="classA">
    126 		 * element.addClass( 'classB' );  // <div class="classA classB">
    127 		 * element.addClass( 'classA' );  // <div class="classA classB">
    128 		 */
    129 		addClass : function( className )
    130 		{
    131 			var c = this.$.className;
    132 			if ( c )
    133 			{
    134 				var regex = new RegExp( '(?:^|\\s)' + className + '(?:\\s|$)', '' );
    135 				if ( !regex.test( c ) )
    136 					c += ' ' + className;
    137 			}
    138 			this.$.className = c || className;
    139 		},
    140
    141 		/**
    142 		 * Removes a CSS class name from the elements classes. Other classes
    143 		 * remain untouched.
    144 		 * @param {String} className The name of the class to remove.
    145 		 * @example
    146 		 * var element = new CKEDITOR.dom.element( 'div' );
    147 		 * element.addClass( 'classA' );  // <div class="classA">
    148 		 * element.addClass( 'classB' );  // <div class="classA classB">
    149 		 * element.removeClass( 'classA' );  // <div class="classB">
    150 		 * element.removeClass( 'classB' );  // <div>
    151 		 */
    152 		removeClass : function( className )
    153 		{
    154 			var c = this.getAttribute( 'class' );
    155 			if ( c )
    156 			{
    157 				var regex = new RegExp( '(?:^|\\s+)' + className + '(?=\\s|$)', 'i' );
    158 				if ( regex.test( c ) )
    159 				{
    160 					c = c.replace( regex, '' ).replace( /^\s+/, '' );
    161
    162 					if ( c )
    163 						this.setAttribute( 'class', c );
    164 					else
    165 						this.removeAttribute( 'class' );
    166 				}
    167 			}
    168 		},
    169
    170 		hasClass : function( className )
    171 		{
    172 			var regex = new RegExp( '(?:^|\\s+)' + className + '(?=\\s|$)', '' );
    173 			return regex.test( this.getAttribute('class') );
    174 		},
    175
    176 		/**
    177 		 * Append a node as a child of this element.
    178 		 * @param {CKEDITOR.dom.node|String} node The node or element name to be
    179 		 *		appended.
    180 		 * @param {Boolean} [toStart] Indicates that the element is to be
    181 		 *		appended at the start.
    182 		 * @returns {CKEDITOR.dom.node} The appended node.
    183 		 * @example
    184 		 * var p = new CKEDITOR.dom.element( 'p' );
    185 		 *
    186 		 * var strong = new CKEDITOR.dom.element( 'strong' );
    187 		 * <b>p.append( strong );</b>
    188 		 *
    189 		 * var em = <b>p.append( 'em' );</b>
    190 		 *
    191 		 * // result: "<p><strong></strong><em></em></p>"
    192 		 */
    193 		append : function( node, toStart )
    194 		{
    195 			if ( typeof node == 'string' )
    196 				node = this.getDocument().createElement( node );
    197
    198 			if ( toStart )
    199 				this.$.insertBefore( node.$, this.$.firstChild );
    200 			else
    201 				this.$.appendChild( node.$ );
    202
    203 			return node;
    204 		},
    205
    206 		appendHtml : function( html )
    207 		{
    208 			if ( !this.$.childNodes.length )
    209 				this.setHtml( html );
    210 			else
    211 			{
    212 				var temp = new CKEDITOR.dom.element( 'div', this.getDocument() );
    213 				temp.setHtml( html );
    214 				temp.moveChildren( this );
    215 			}
    216 		},
    217
    218 		/**
    219 		 * Append text to this element.
    220 		 * @param {String} text The text to be appended.
    221 		 * @returns {CKEDITOR.dom.node} The appended node.
    222 		 * @example
    223 		 * var p = new CKEDITOR.dom.element( 'p' );
    224 		 * p.appendText( 'This is' );
    225 		 * p.appendText( ' some text' );
    226 		 *
    227 		 * // result: "<p>This is some text</p>"
    228 		 */
    229 		appendText : function( text )
    230 		{
    231 			if ( this.$.text != undefined )
    232 				this.$.text += text;
    233 			else
    234 				this.append( new CKEDITOR.dom.text( text ) );
    235 		},
    236
    237 		appendBogus : function()
    238 		{
    239 			var lastChild = this.getLast() ;
    240
    241 			// Ignore empty/spaces text.
    242 			while ( lastChild && lastChild.type == CKEDITOR.NODE_TEXT && !CKEDITOR.tools.rtrim( lastChild.getText() ) )
    243 				lastChild = lastChild.getPrevious();
    244 			if ( !lastChild || !lastChild.is || !lastChild.is( 'br' ) )
    245 			{
    246 				this.append(
    247 					CKEDITOR.env.opera ?
    248 						this.getDocument().createText('') :
    249 						this.getDocument().createElement( 'br' ) );
    250 			}
    251 		},
    252
    253 		/**
    254 		 * Breaks one of the ancestor element in the element position, moving
    255 		 * this element between the broken parts.
    256 		 * @param {CKEDITOR.dom.element} parent The anscestor element to get broken.
    257 		 * @example
    258 		 * // Before breaking:
    259 		 * //     <b>This <i>is some<span /> sample</i> test text</b>
    260 		 * // If "element" is <span /> and "parent" is <i>:
    261 		 * //     <b>This <i>is some</i><span /><i> sample</i> test text</b>
    262 		 * element.breakParent( parent );
    263 		 * @example
    264 		 * // Before breaking:
    265 		 * //     <b>This <i>is some<span /> sample</i> test text</b>
    266 		 * // If "element" is <span /> and "parent" is <b>:
    267 		 * //     <b>This <i>is some</i></b><span /><b><i> sample</i> test text</b>
    268 		 * element.breakParent( parent );
    269 		 */
    270 		breakParent : function( parent )
    271 		{
    272 			var range = new CKEDITOR.dom.range( this.getDocument() );
    273
    274 			// We'll be extracting part of this element, so let's use our
    275 			// range to get the correct piece.
    276 			range.setStartAfter( this );
    277 			range.setEndAfter( parent );
    278
    279 			// Extract it.
    280 			var docFrag = range.extractContents();
    281
    282 			// Move the element outside the broken element.
    283 			range.insertNode( this.remove() );
    284
    285 			// Re-insert the extracted piece after the element.
    286 			docFrag.insertAfterNode( this );
    287 		},
    288
    289 		contains :
    290 			CKEDITOR.env.ie || CKEDITOR.env.webkit ?
    291 				function( node )
    292 				{
    293 					var $ = this.$;
    294
    295 					return node.type != CKEDITOR.NODE_ELEMENT ?
    296 						$.contains( node.getParent().$ ) :
    297 						$ != node.$ && $.contains( node.$ );
    298 				}
    299 			:
    300 				function( node )
    301 				{
    302 					return !!( this.$.compareDocumentPosition( node.$ ) & 16 );
    303 				},
    304
    305 		/**
    306 		 * Moves the selection focus to this element.
    307 		 * @example
    308 		 * var element = CKEDITOR.document.getById( 'myTextarea' );
    309 		 * <b>element.focus()</b>;
    310 		 */
    311 		focus : function()
    312 		{
    313 			// IE throws error if the element is not visible.
    314 			try
    315 			{
    316 				this.$.focus();
    317 			}
    318 			catch (e)
    319 			{}
    320 		},
    321
    322 		/**
    323 		 * Gets the inner HTML of this element.
    324 		 * @returns {String} The inner HTML of this element.
    325 		 * @example
    326 		 * var element = CKEDITOR.dom.element.createFromHtml( '<div><b>Example</b></div>' );
    327 		 * alert( <b>p.getHtml()</b> );  // "<b>Example</b>"
    328 		 */
    329 		getHtml : function()
    330 		{
    331 			return this.$.innerHTML;
    332 		},
    333
    334 		getOuterHtml : function()
    335 		{
    336 			if ( this.$.outerHTML )
    337 			{
    338 				// IE includes the <?xml:namespace> tag in the outerHTML of
    339 				// namespaced element. So, we must strip it here. (#3341)
    340 				return this.$.outerHTML.replace( /<\?[^>]*>/, '' );
    341 			}
    342
    343 			var tmpDiv = this.$.ownerDocument.createElement( 'div' );
    344 			tmpDiv.appendChild( this.$.cloneNode( true ) );
    345 			return tmpDiv.innerHTML;
    346 		},
    347
    348 		/**
    349 		 * Sets the inner HTML of this element.
    350 		 * @param {String} html The HTML to be set for this element.
    351 		 * @returns {String} The inserted HTML.
    352 		 * @example
    353 		 * var p = new CKEDITOR.dom.element( 'p' );
    354 		 * <b>p.setHtml( '<b>Inner</b> HTML' );</b>
    355 		 *
    356 		 * // result: "<p><b>Inner</b> HTML</p>"
    357 		 */
    358 		setHtml : function( html )
    359 		{
    360 			return ( this.$.innerHTML = html );
    361 		},
    362
    363 		/**
    364 		 * Sets the element contents as plain text.
    365 		 * @param {String} text The text to be set.
    366 		 * @returns {String} The inserted text.
    367 		 * @example
    368 		 * var element = new CKEDITOR.dom.element( 'div' );
    369 		 * element.setText( 'A > B & C < D' );
    370 		 * alert( element.innerHTML );  // "A &gt; B &amp; C &lt; D"
    371 		 */
    372 		setText : function( text )
    373 		{
    374 			CKEDITOR.dom.element.prototype.setText = ( this.$.innerText != undefined ) ?
    375 				function ( text )
    376 				{
    377 					return this.$.innerText = text;
    378 				} :
    379 				function ( text )
    380 				{
    381 					return this.$.textContent = text;
    382 				};
    383
    384 			return this.setText( text );
    385 		},
    386
    387 		/**
    388 		 * Gets the value of an element attribute.
    389 		 * @function
    390 		 * @param {String} name The attribute name.
    391 		 * @returns {String} The attribute value or null if not defined.
    392 		 * @example
    393 		 * var element = CKEDITOR.dom.element.createFromHtml( '<input type="text" />' );
    394 		 * alert( <b>element.getAttribute( 'type' )</b> );  // "text"
    395 		 */
    396 		getAttribute : (function()
    397 		{
    398 			var standard = function( name )
    399 			{
    400 				return this.$.getAttribute( name, 2 );
    401 			};
    402
    403 			if ( CKEDITOR.env.ie && ( CKEDITOR.env.ie7Compat || CKEDITOR.env.ie6Compat ) )
    404 			{
    405 				return function( name )
    406 				{
    407 					switch ( name )
    408 					{
    409 						case 'class':
    410 							name = 'className';
    411 							break;
    412
    413 						case 'tabindex':
    414 							var tabIndex = standard.call( this, name );
    415
    416 							// IE returns tabIndex=0 by default for all
    417 							// elements. For those elements,
    418 							// getAtrribute( 'tabindex', 2 ) returns 32768
    419 							// instead. So, we must make this check to give a
    420 							// uniform result among all browsers.
    421 							if ( tabIndex !== 0 && this.$.tabIndex === 0 )
    422 								tabIndex = null;
    423
    424 							return tabIndex;
    425 							break;
    426
    427 						case 'checked':
    428 							return this.$.checked;
    429 							break;
    430
    431 						case 'style':
    432 							// IE does not return inline styles via getAttribute(). See #2947.
    433 							var styleText = this.$.style.cssText;
    434 							return styleText.toLowerCase().replace(
    435 								/\s*(?:;\s*|$)/, ';').replace(
    436 									/([^;])$/, '$1;');
    437 					}
    438
    439 					return standard.call( this, name );
    440 				};
    441 			}
    442 			else
    443 				return standard;
    444 		})(),
    445
    446 		getChildren : function()
    447 		{
    448 			return new CKEDITOR.dom.nodeList( this.$.childNodes );
    449 		},
    450
    451 		/**
    452 		 * Gets the current computed value of one of the element CSS style
    453 		 * properties.
    454 		 * @function
    455 		 * @param {String} propertyName The style property name.
    456 		 * @returns {String} The property value.
    457 		 * @example
    458 		 * var element = new CKEDITOR.dom.element( 'span' );
    459 		 * alert( <b>element.getComputedStyle( 'display' )</b> );  // "inline"
    460 		 */
    461 		getComputedStyle :
    462 			CKEDITOR.env.ie ?
    463 				function( propertyName )
    464 				{
    465 					return this.$.currentStyle[ CKEDITOR.tools.cssStyleToDomStyle( propertyName ) ];
    466 				}
    467 			:
    468 				function( propertyName )
    469 				{
    470 					return this.getWindow().$.getComputedStyle( this.$, '' ).getPropertyValue( propertyName );
    471 				},
    472
    473 		/**
    474 		 * Gets the DTD entries for this element.
    475 		 * @returns {Object} An object containing the list of elements accepted
    476 		 *		by this element.
    477 		 */
    478 		getDtd : function()
    479 		{
    480 			var dtd = CKEDITOR.dtd[ this.getName() ];
    481
    482 			this.getDtd = function()
    483 			{
    484 				return dtd;
    485 			};
    486
    487 			return dtd;
    488 		},
    489
    490 		getElementsByTag : CKEDITOR.dom.document.prototype.getElementsByTag,
    491
    492 		/**
    493 		 * Gets the computed tabindex for this element.
    494 		 * @function
    495 		 * @returns {Number} The tabindex value.
    496 		 * @example
    497 		 * var element = CKEDITOR.document.getById( 'myDiv' );
    498 		 * alert( <b>element.getTabIndex()</b> );  // e.g. "-1"
    499 		 */
    500 		getTabIndex :
    501 			CKEDITOR.env.ie ?
    502 				function()
    503 				{
    504 					var tabIndex = this.$.tabIndex;
    505
    506 					// IE returns tabIndex=0 by default for all elements. In
    507 					// those cases we must check that the element really has
    508 					// the tabindex attribute set to zero, or it is one of
    509 					// those element that should have zero by default.
    510 					if ( tabIndex === 0 && !CKEDITOR.dtd.$tabIndex[ this.getName() ] && parseInt( this.getAttribute( 'tabindex' ), 10 ) !== 0 )
    511 						tabIndex = -1;
    512
    513 						return tabIndex;
    514 				}
    515 			: CKEDITOR.env.webkit ?
    516 				function()
    517 				{
    518 					var tabIndex = this.$.tabIndex;
    519
    520 					// Safari returns "undefined" for elements that should not
    521 					// have tabindex (like a div). So, we must try to get it
    522 					// from the attribute.
    523 					// https://bugs.webkit.org/show_bug.cgi?id=20596
    524 					if ( tabIndex == undefined )
    525 					{
    526 						tabIndex = parseInt( this.getAttribute( 'tabindex' ), 10 );
    527
    528 						// If the element don't have the tabindex attribute,
    529 						// then we should return -1.
    530 						if ( isNaN( tabIndex ) )
    531 							tabIndex = -1;
    532 					}
    533
    534 					return tabIndex;
    535 				}
    536 			:
    537 				function()
    538 				{
    539 					return this.$.tabIndex;
    540 				},
    541
    542 		/**
    543 		 * Gets the text value of this element.
    544 		 *
    545 		 * Only in IE (which uses innerText), <br> will cause linebreaks,
    546 		 * and sucessive whitespaces (including line breaks) will be reduced to
    547 		 * a single space. This behavior is ok for us, for now. It may change
    548 		 * in the future.
    549 		 * @returns {String} The text value.
    550 		 * @example
    551 		 * var element = CKEDITOR.dom.element.createFromHtml( '<div>Same <i>text</i>.</div>' );
    552 		 * alert( <b>element.getText()</b> );  // "Sample text."
    553 		 */
    554 		getText : function()
    555 		{
    556 			return this.$.textContent || this.$.innerText || '';
    557 		},
    558
    559 		/**
    560 		 * Gets the window object that contains this element.
    561 		 * @returns {CKEDITOR.dom.window} The window object.
    562 		 * @example
    563 		 */
    564 		getWindow : function()
    565 		{
    566 			return this.getDocument().getWindow();
    567 		},
    568
    569 		/**
    570 		 * Gets the value of the "id" attribute of this element.
    571 		 * @returns {String} The element id, or null if not available.
    572 		 * @example
    573 		 * var element = CKEDITOR.dom.element.createFromHtml( '<p id="myId"></p>' );
    574 		 * alert( <b>element.getId()</b> );  // "myId"
    575 		 */
    576 		getId : function()
    577 		{
    578 			return this.$.id || null;
    579 		},
    580
    581 		/**
    582 		 * Gets the value of the "name" attribute of this element.
    583 		 * @returns {String} The element name, or null if not available.
    584 		 * @example
    585 		 * var element = CKEDITOR.dom.element.createFromHtml( '<input name="myName"></input>' );
    586 		 * alert( <b>element.getNameAtt()</b> );  // "myName"
    587 		 */
    588 		getNameAtt : function()
    589 		{
    590 			return this.$.name || null;
    591 		},
    592
    593 		/**
    594 		 * Gets the element name (tag name). The returned name is guaranteed to
    595 		 * be always full lowercased.
    596 		 * @returns {String} The element name.
    597 		 * @example
    598 		 * var element = new CKEDITOR.dom.element( 'span' );
    599 		 * alert( <b>element.getName()</b> );  // "span"
    600 		 */
    601 		getName : function()
    602 		{
    603 			// Cache the lowercased name inside a closure.
    604 			var nodeName = this.$.nodeName.toLowerCase();
    605
    606 			if ( CKEDITOR.env.ie )
    607 			{
    608 				var scopeName = this.$.scopeName;
    609 				if ( scopeName != 'HTML' )
    610 					nodeName = scopeName.toLowerCase() + ':' + nodeName;
    611 			}
    612
    613 			return (
    614 			/** @ignore */
    615 			this.getName = function()
    616 				{
    617 					return nodeName;
    618 				})();
    619 		},
    620
    621 		/**
    622 		 * Gets the value set to this element. This value is usually available
    623 		 * for form field elements.
    624 		 * @returns {String} The element value.
    625 		 */
    626 		getValue : function()
    627 		{
    628 			return this.$.value;
    629 		},
    630
    631 		/**
    632 		 * Gets the first child node of this element.
    633 		 * @returns {CKEDITOR.dom.node} The first child node or null if not
    634 		 *		available.
    635 		 * @example
    636 		 * var element = CKEDITOR.dom.element.createFromHtml( '<div><b>Example</b></div>' );
    637 		 * var first = <b>element.getFirst()</b>;
    638 		 * alert( first.getName() );  // "b"
    639 		 */
    640 		getFirst : function()
    641 		{
    642 			var $ = this.$.firstChild;
    643 			return $ ? new CKEDITOR.dom.node( $ ) : null;
    644 		},
    645
    646 		/**
    647 		 * @param ignoreEmpty Skip empty text nodes.
    648 		 */
    649 		getLast : function( ignoreEmpty )
    650 		{
    651 			var $ = this.$.lastChild;
    652 			if ( ignoreEmpty && $ && ( $.nodeType == CKEDITOR.NODE_TEXT )
    653 					&& !CKEDITOR.tools.trim( $.nodeValue ) )
    654 				return new CKEDITOR.dom.node( $ ).getPrevious( true );
    655 			else
    656 				return $ ? new CKEDITOR.dom.node( $ ) : null;
    657 		},
    658
    659 		getStyle : function( name )
    660 		{
    661 			return this.$.style[ CKEDITOR.tools.cssStyleToDomStyle( name ) ];
    662 		},
    663
    664 		/**
    665 		 * Checks if the element name matches one or more names.
    666 		 * @param {String} name[,name[,...]] One or more names to be checked.
    667 		 * @returns {Boolean} true if the element name matches any of the names.
    668 		 * @example
    669 		 * var element = new CKEDITOR.element( 'span' );
    670 		 * alert( <b>element.is( 'span' )</b> );  "true"
    671 		 * alert( <b>element.is( 'p', 'span' )</b> );  "true"
    672 		 * alert( <b>element.is( 'p' )</b> );  "false"
    673 		 * alert( <b>element.is( 'p', 'div' )</b> );  "false"
    674 		 */
    675 		is : function()
    676 		{
    677 			var name = this.getName();
    678 			for ( var i = 0 ; i < arguments.length ; i++ )
    679 			{
    680 				if ( arguments[ i ] == name )
    681 					return true;
    682 			}
    683 			return false;
    684 		},
    685
    686 		isEditable : function()
    687 		{
    688 			// Get the element name.
    689 			var name = this.getName();
    690
    691 			// Get the element DTD (defaults to span for unknown elements).
    692 			var dtd = !CKEDITOR.dtd.$nonEditable[ name ]
    693 						&& ( CKEDITOR.dtd[ name ] || CKEDITOR.dtd.span );
    694
    695 			// In the DTD # == text node.
    696 			return ( dtd && dtd['#'] );
    697 		},
    698
    699 		isIdentical : function( otherElement )
    700 		{
    701 			if ( this.getName() != otherElement.getName() )
    702 				return false;
    703
    704 			var thisAttribs = this.$.attributes,
    705 				otherAttribs = otherElement.$.attributes;
    706
    707 			var thisLength = thisAttribs.length,
    708 				otherLength = otherAttribs.length;
    709
    710 			if ( !CKEDITOR.env.ie && thisLength != otherLength )
    711 				return false;
    712
    713 			for ( var i = 0 ; i < thisLength ; i++ )
    714 			{
    715 				var attribute = thisAttribs[ i ];
    716
    717 				if ( ( !CKEDITOR.env.ie || ( attribute.specified && attribute.nodeName != '_cke_expando' ) ) && attribute.nodeValue != otherElement.getAttribute( attribute.nodeName ) )
    718 					return false;
    719 			}
    720
    721 			// For IE, we have to for both elements, because it's difficult to
    722 			// know how the atttibutes collection is organized in its DOM.
    723 			if ( CKEDITOR.env.ie )
    724 			{
    725 				for ( i = 0 ; i < otherLength ; i++ )
    726 				{
    727 					attribute = otherAttribs[ i ];
    728
    729 					if ( ( !CKEDITOR.env.ie || ( attribute.specified && attribute.nodeName != '_cke_expando' ) ) && attribute.nodeValue != thisAttribs.getAttribute( attribute.nodeName ) )
    730 						return false;
    731 				}
    732 			}
    733
    734 			return true;
    735 		},
    736
    737 		/**
    738 		 * Checks if this element is visible. May not work if the element is
    739 		 * child of an element with visibility set to "hidden", but works well
    740 		 * on the great majority of cases.
    741 		 * @return {Boolean} True if the element is visible.
    742 		 */
    743 		isVisible : function()
    744 		{
    745 			return this.$.offsetWidth && ( this.$.style.visibility != 'hidden' );
    746 		},
    747
    748 		/**
    749 		 * Indicates that the element has defined attributes.
    750 		 * @returns {Boolean} True if the element has attributes.
    751 		 * @example
    752 		 * var element = CKEDITOR.dom.element.createFromHtml( '<div title="Test">Example</div>' );
    753 		 * alert( <b>element.hasAttributes()</b> );  "true"
    754 		 * @example
    755 		 * var element = CKEDITOR.dom.element.createFromHtml( '<div>Example</div>' );
    756 		 * alert( <b>element.hasAttributes()</b> );  "false"
    757 		 */
    758 		hasAttributes :
    759 			CKEDITOR.env.ie && ( CKEDITOR.env.ie7Compat || CKEDITOR.env.ie6Compat ) ?
    760 				function()
    761 				{
    762 					var attributes = this.$.attributes;
    763
    764 					for ( var i = 0 ; i < attributes.length ; i++ )
    765 					{
    766 						var attribute = attributes[i];
    767
    768 						switch ( attribute.nodeName )
    769 						{
    770 							case 'class' :
    771 								// IE has a strange bug. If calling removeAttribute('className'),
    772 								// the attributes collection will still contain the "class"
    773 								// attribute, which will be marked as "specified", even if the
    774 								// outerHTML of the element is not displaying the class attribute.
    775 								// Note : I was not able to reproduce it outside the editor,
    776 								// but I've faced it while working on the TC of #1391.
    777 								if ( this.getAttribute( 'class' ) > 0 )
    778 									return true;
    779
    780 							// Attributes to be ignored.
    781 							case '_cke_expando' :
    782 								continue;
    783
    784 							/*jsl:fallthru*/
    785
    786 							default :
    787 								if ( attribute.specified )
    788 									return true;
    789 						}
    790 					}
    791
    792 					return false;
    793 				}
    794 			:
    795 				function()
    796 				{
    797 					var attributes = this.$.attributes;
    798 					return ( attributes.length > 1 || ( attributes.length == 1 && attributes[0].nodeName != '_cke_expando' ) );
    799 				},
    800
    801 		/**
    802 		 * Indicates whether a specified attribute is defined for this element.
    803 		 * @returns {Boolean} True if the specified attribute is defined.
    804 		 * @param (String) name The attribute name.
    805 		 * @example
    806 		 */
    807 		hasAttribute : function( name )
    808 		{
    809 			var $attr = this.$.attributes.getNamedItem( name );
    810 			return !!( $attr && $attr.specified );
    811 		},
    812
    813 		/**
    814 		 * Hides this element (display:none).
    815 		 * @example
    816 		 * var element = CKEDITOR.dom.element.getById( 'myElement' );
    817 		 * <b>element.hide()</b>;
    818 		 */
    819 		hide : function()
    820 		{
    821 			this.setStyle( 'display', 'none' );
    822 		},
    823
    824 		moveChildren : function( target, toStart )
    825 		{
    826 			var $ = this.$;
    827 			target = target.$;
    828
    829 			if ( $ == target )
    830 				return;
    831
    832 			var child;
    833
    834 			if ( toStart )
    835 			{
    836 				while ( ( child = $.lastChild ) )
    837 					target.insertBefore( $.removeChild( child ), target.firstChild );
    838 			}
    839 			else
    840 			{
    841 				while ( ( child = $.firstChild ) )
    842 					target.appendChild( $.removeChild( child ) );
    843 			}
    844 		},
    845
    846 		/**
    847 		 * Shows this element (display it).
    848 		 * @example
    849 		 * var element = CKEDITOR.dom.element.getById( 'myElement' );
    850 		 * <b>element.show()</b>;
    851 		 */
    852 		show : function()
    853 		{
    854 			this.setStyles(
    855 				{
    856 					display : '',
    857 					visibility : ''
    858 				});
    859 		},
    860
    861 		/**
    862 		 * Sets the value of an element attribute.
    863 		 * @param {String} name The name of the attribute.
    864 		 * @param {String} value The value to be set to the attribute.
    865 		 * @function
    866 		 * @returns {CKEDITOR.dom.element} This element instance.
    867 		 * @example
    868 		 * var element = CKEDITOR.dom.element.getById( 'myElement' );
    869 		 * <b>element.setAttribute( 'class', 'myClass' )</b>;
    870 		 * <b>element.setAttribute( 'title', 'This is an example' )</b>;
    871 		 */
    872 		setAttribute : (function()
    873 		{
    874 			var standard = function( name, value )
    875 			{
    876 				this.$.setAttribute( name, value );
    877 				return this;
    878 			};
    879
    880 			if ( CKEDITOR.env.ie && ( CKEDITOR.env.ie7Compat || CKEDITOR.env.ie6Compat ) )
    881 			{
    882 				return function( name, value )
    883 				{
    884 					if ( name == 'class' )
    885 						this.$.className = value;
    886 					else if ( name == 'style' )
    887 						this.$.style.cssText = value;
    888 					else if ( name == 'tabindex' )	// Case sensitive.
    889 						this.$.tabIndex = value;
    890 					else if ( name == 'checked' )
    891 						this.$.checked = value;
    892 					else
    893 						standard.apply( this, arguments );
    894 					return this;
    895 				};
    896 			}
    897 			else
    898 				return standard;
    899 		})(),
    900
    901 		/**
    902 		 * Sets the value of several element attributes.
    903 		 * @param {Object} attributesPairs An object containing the names and
    904 		 *		values of the attributes.
    905 		 * @returns {CKEDITOR.dom.element} This element instance.
    906 		 * @example
    907 		 * var element = CKEDITOR.dom.element.getById( 'myElement' );
    908 		 * <b>element.setAttributes({
    909 		 *     'class' : 'myClass',
    910 		 *     'title' : 'This is an example' })</b>;
    911 		 */
    912 		setAttributes : function( attributesPairs )
    913 		{
    914 			for ( var name in attributesPairs )
    915 				this.setAttribute( name, attributesPairs[ name ] );
    916 			return this;
    917 		},
    918
    919 		/**
    920 		 * Sets the element value. This function is usually used with form
    921 		 * field element.
    922 		 * @param {String} value The element value.
    923 		 * @returns {CKEDITOR.dom.element} This element instance.
    924 		 */
    925 		setValue : function( value )
    926 		{
    927 			this.$.value = value;
    928 			return this;
    929 		},
    930
    931 		/**
    932 		 * Removes an attribute from the element.
    933 		 * @param {String} name The attribute name.
    934 		 * @function
    935 		 * @example
    936 		 * var element = CKEDITOR.dom.element.createFromHtml( '<div class="classA"></div>' );
    937 		 * element.removeAttribute( 'class' );
    938 		 */
    939 		removeAttribute : (function()
    940 		{
    941 			var standard = function( name )
    942 			{
    943 				this.$.removeAttribute( name );
    944 			};
    945
    946 			if ( CKEDITOR.env.ie && ( CKEDITOR.env.ie7Compat || CKEDITOR.env.ie6Compat ) )
    947 			{
    948 				return function( name )
    949 				{
    950 					if ( name == 'class' )
    951 						name = 'className';
    952 					else if ( name == 'tabindex' )
    953 						name = 'tabIndex';
    954 					standard.call( this, name );
    955 				};
    956 			}
    957 			else
    958 				return standard;
    959 		})(),
    960
    961 		removeAttributes : function ( attributes )
    962 		{
    963 			for ( var i = 0 ; i < attributes.length ; i++ )
    964 				this.removeAttribute( attributes[ i ] );
    965 		},
    966
    967 		/**
    968 		 * Removes a style from the element.
    969 		 * @param {String} name The style name.
    970 		 * @function
    971 		 * @example
    972 		 * var element = CKEDITOR.dom.element.createFromHtml( '<div style="display:none"></div>' );
    973 		 * element.removeStyle( 'display' );
    974 		 */
    975 		removeStyle : function( name )
    976 		{
    977 			if ( this.$.style.removeAttribute )
    978 				this.$.style.removeAttribute( CKEDITOR.tools.cssStyleToDomStyle( name ) );
    979 			else
    980 				this.setStyle( name, '' );
    981
    982 			if ( !this.$.style.cssText )
    983 				this.removeAttribute( 'style' );
    984 		},
    985
    986 		/**
    987 		 * Sets the value of an element style.
    988 		 * @param {String} name The name of the style. The CSS naming notation
    989 		 *		must be used (e.g. "background-color").
    990 		 * @param {String} value The value to be set to the style.
    991 		 * @returns {CKEDITOR.dom.element} This element instance.
    992 		 * @example
    993 		 * var element = CKEDITOR.dom.element.getById( 'myElement' );
    994 		 * <b>element.setStyle( 'background-color', '#ff0000' )</b>;
    995 		 * <b>element.setStyle( 'margin-top', '10px' )</b>;
    996 		 * <b>element.setStyle( 'float', 'right' )</b>;
    997 		 */
    998 		setStyle : function( name, value )
    999 		{
    1000 			this.$.style[ CKEDITOR.tools.cssStyleToDomStyle( name ) ] = value;
    1001 			return this;
    1002 		},
    1003
    1004 		/**
    1005 		 * Sets the value of several element styles.
    1006 		 * @param {Object} stylesPairs An object containing the names and
    1007 		 *		values of the styles.
    1008 		 * @returns {CKEDITOR.dom.element} This element instance.
    1009 		 * @example
    1010 		 * var element = CKEDITOR.dom.element.getById( 'myElement' );
    1011 		 * <b>element.setStyles({
    1012 		 *     'position' : 'absolute',
    1013 		 *     'float' : 'right' })</b>;
    1014 		 */
    1015 		setStyles : function( stylesPairs )
    1016 		{
    1017 			for ( var name in stylesPairs )
    1018 				this.setStyle( name, stylesPairs[ name ] );
    1019 			return this;
    1020 		},
    1021
    1022 		/**
    1023 		 * Sets the opacity of an element.
    1024 		 * @param {Number} opacity A number within the range [0.0, 1.0].
    1025 		 * @example
    1026 		 * var element = CKEDITOR.dom.element.getById( 'myElement' );
    1027 		 * <b>element.setOpacity( 0.75 )</b>;
    1028 		 */
    1029 		setOpacity : function( opacity )
    1030 		{
    1031 			if ( CKEDITOR.env.ie )
    1032 			{
    1033 				opacity = Math.round( opacity * 100 );
    1034 				this.setStyle( 'filter', opacity >= 100 ? '' : 'progid:DXImageTransform.Microsoft.Alpha(opacity=' + opacity + ')' );
    1035 			}
    1036 			else
    1037 				this.setStyle( 'opacity', opacity );
    1038 		},
    1039
    1040 		/**
    1041 		 * Makes the element and its children unselectable.
    1042 		 * @function
    1043 		 * @example
    1044 		 * var element = CKEDITOR.dom.element.getById( 'myElement' );
    1045 		 * element.unselectable();
    1046 		 */
    1047 		unselectable :
    1048 			CKEDITOR.env.gecko ?
    1049 				function()
    1050 				{
    1051 					this.$.style.MozUserSelect = 'none';
    1052 				}
    1053 			: CKEDITOR.env.webkit ?
    1054 				function()
    1055 				{
    1056 					this.$.style.KhtmlUserSelect = 'none';
    1057 				}
    1058 			:
    1059 				function()
    1060 				{
    1061 					if ( CKEDITOR.env.ie || CKEDITOR.env.opera )
    1062 					{
    1063 						var element = this.$,
    1064 							e,
    1065 							i = 0;
    1066
    1067 						element.unselectable = 'on';
    1068
    1069 						while ( ( e = element.all[ i++ ] ) )
    1070 						{
    1071 							switch ( e.tagName.toLowerCase() )
    1072 							{
    1073 								case 'iframe' :
    1074 								case 'textarea' :
    1075 								case 'input' :
    1076 								case 'select' :
    1077 									/* Ignore the above tags */
    1078 									break;
    1079 								default :
    1080 									e.unselectable = 'on';
    1081 							}
    1082 						}
    1083 					}
    1084 				},
    1085
    1086 		getPositionedAncestor : function()
    1087 		{
    1088 			var current = this;
    1089 			while ( current.getName() != 'html' )
    1090 			{
    1091 				if ( current.getComputedStyle( 'position' ) != 'static' )
    1092 					return current;
    1093
    1094 				current = current.getParent();
    1095 			}
    1096 			return null;
    1097 		},
    1098
    1099 		getDocumentPosition : function( refDocument )
    1100 		{
    1101 			var x = 0, y = 0,
    1102 				body = this.getDocument().getBody(),
    1103 				quirks = this.getDocument().$.compatMode == 'BackCompat';
    1104
    1105 			var doc = this.getDocument();
    1106
    1107 			if ( document.documentElement[ "getBoundingClientRect" ] )
    1108 			{
    1109 				var box  = this.$.getBoundingClientRect(),
    1110 					$doc = doc.$,
    1111 					$docElem = $doc.documentElement;
    1112
    1113 				var clientTop = $docElem.clientTop || body.$.clientTop || 0,
    1114 					clientLeft = $docElem.clientLeft || body.$.clientLeft || 0,
    1115 					needAdjustScrollAndBorders = true;
    1116
    1117 				/*
    1118 				 * #3804: getBoundingClientRect() works differently on IE and non-IE
    1119 				 * browsers, regarding scroll positions.
    1120 				 *
    1121 				 * On IE, the top position of the <html> element is always 0, no matter
    1122 				 * how much you scrolled down.
    1123 				 *
    1124 				 * On other browsers, the top position of the <html> element is negative
    1125 				 * scrollTop.
    1126 				 */
    1127 				if ( CKEDITOR.env.ie )
    1128 				{
    1129 					var inDocElem = doc.getDocumentElement().contains( this ),
    1130 						inBody = doc.getBody().contains( this );
    1131
    1132 					needAdjustScrollAndBorders = ( quirks && inBody ) || ( !quirks && inDocElem );
    1133 				}
    1134
    1135 				if ( needAdjustScrollAndBorders )
    1136 				{
    1137 					x = box.left + ( !quirks && $docElem.scrollLeft || body.$.scrollLeft );
    1138 					x -= clientLeft;
    1139 					y = box.top  + ( !quirks && $docElem.scrollTop || body.$.scrollTop );
    1140 					y -= clientTop;
    1141 				}
    1142 			}
    1143 			else
    1144  			{
    1145 				var current = this, previous = null, offsetParent;
    1146 				while ( current && !( current.getName() == 'body' || current.getName() == 'html' ) )
    1147 				{
    1148 					x += current.$.offsetLeft - current.$.scrollLeft;
    1149 					y += current.$.offsetTop - current.$.scrollTop;
    1150
    1151 					// Opera includes clientTop|Left into offsetTop|Left.
    1152 					if ( !current.equals( this ) )
    1153 					{
    1154 						x += ( current.$.clientLeft || 0 );
    1155 						y += ( current.$.clientTop || 0 );
    1156 					}
    1157
    1158 					var scrollElement = previous;
    1159 					while ( scrollElement && !scrollElement.equals( current ) )
    1160 					{
    1161 						x -= scrollElement.$.scrollLeft;
    1162 						y -= scrollElement.$.scrollTop;
    1163 						scrollElement = scrollElement.getParent();
    1164 					}
    1165
    1166 					previous = current;
    1167 					current = ( offsetParent = current.$.offsetParent ) ?
    1168 					          new CKEDITOR.dom.element( offsetParent ) : null;
    1169 				}
    1170 			}
    1171
    1172 			if ( refDocument )
    1173 			{
    1174 				var currentWindow = this.getWindow(),
    1175 					refWindow = refDocument.getWindow();
    1176
    1177 				if ( !currentWindow.equals( refWindow ) && currentWindow.$.frameElement )
    1178 				{
    1179 					var iframePosition = ( new CKEDITOR.dom.element( currentWindow.$.frameElement ) ).getDocumentPosition( refDocument );
    1180
    1181 					x += iframePosition.x;
    1182 					y += iframePosition.y;
    1183 				}
    1184 			}
    1185
    1186 			if ( !document.documentElement[ "getBoundingClientRect" ] )
    1187 			{
    1188 				// In Firefox, we'll endup one pixel before the element positions,
    1189 				// so we must add it here.
    1190 				if ( CKEDITOR.env.gecko && !quirks )
    1191 				{
    1192 					x += this.$.clientLeft ? 1 : 0;
    1193 					y += this.$.clientTop ? 1 : 0;
    1194 				}
    1195 			}
    1196
    1197 			return { x : x, y : y };
    1198 		},
    1199
    1200 		scrollIntoView : function( alignTop )
    1201 		{
    1202 			// Get the element window.
    1203 			var win = this.getWindow(),
    1204 				winHeight = win.getViewPaneSize().height;
    1205
    1206 			// Starts from the offset that will be scrolled with the negative value of
    1207 			// the visible window height.
    1208 			var offset = winHeight * -1;
    1209
    1210 			// Append the view pane's height if align to top.
    1211 			// Append element height if we are aligning to the bottom.
    1212 			if ( alignTop )
    1213 				offset += winHeight;
    1214 			else
    1215 			{
    1216 				offset += this.$.offsetHeight || 0;
    1217
    1218 				// Consider the margin in the scroll, which is ok for our current needs, but
    1219 				// needs investigation if we will be using this function in other places.
    1220 				offset += parseInt( this.getComputedStyle( 'marginBottom' ) || 0, 10 ) || 0;
    1221 			}
    1222
    1223 			// Append the offsets for the entire element hierarchy.
    1224 			var elementPosition = this.getDocumentPosition();
    1225 			offset += elementPosition.y;
    1226 			// Scroll the window to the desired position, if not already visible.
    1227 			var currentScroll = win.getScrollPosition().y;
    1228
    1229 			// Though the computed offset value maybe out of range ( e.g.
    1230 			// a negative value ), browser will try to scroll as much as possible. (#3692)
    1231 			win.$.scrollTo( 0, offset > 0 ? offset : 0 );
    1232 		},
    1233
    1234 		setState : function( state )
    1235 		{
    1236 			switch ( state )
    1237 			{
    1238 				case CKEDITOR.TRISTATE_ON :
    1239 					this.addClass( 'cke_on' );
    1240 					this.removeClass( 'cke_off' );
    1241 					this.removeClass( 'cke_disabled' );
    1242 					break;
    1243 				case CKEDITOR.TRISTATE_DISABLED :
    1244 					this.addClass( 'cke_disabled' );
    1245 					this.removeClass( 'cke_off' );
    1246 					this.removeClass( 'cke_on' );
    1247 					break;
    1248 				default :
    1249 					this.addClass( 'cke_off' );
    1250 					this.removeClass( 'cke_on' );
    1251 					this.removeClass( 'cke_disabled' );
    1252 					break;
    1253 			}
    1254 		},
    1255
    1256 		/**
    1257 		 * Returns the inner document of this IFRAME element.
    1258 		 * @returns {CKEDITOR.dom.document} The inner document.
    1259 		 */
    1260 		getFrameDocument : function()
    1261 		{
    1262 			var $ = this.$;
    1263
    1264 			try
    1265 			{
    1266 				// In IE, with custom document.domain, it may happen that
    1267 				// the iframe is not yet available, resulting in "Access
    1268 				// Denied" for the following property access.
    1269 				$.contentWindow.document;
    1270 			}
    1271 			catch ( e )
    1272 			{
    1273 				// Trick to solve this issue, forcing the iframe to get ready
    1274 				// by simply setting its "src" property.
    1275 				$.src = $.src;
    1276
    1277 				// In IE6 though, the above is not enough, so we must pause the
    1278 				// execution for a while, giving it time to think.
    1279 				if ( CKEDITOR.env.ie && CKEDITOR.env.version < 7 )
    1280 				{
    1281 					window.showModalDialog(
    1282 						'javascript:document.write("' +
    1283 							'<script>' +
    1284 								'window.setTimeout(' +
    1285 									'function(){window.close();}' +
    1286 									',50);' +
    1287 							'</script>")' );
    1288 				}
    1289 			}
    1290
    1291 			return $ && new CKEDITOR.dom.document( $.contentWindow.document );
    1292 		},
    1293
    1294 		/**
    1295 		 * Copy all the attributes from one node to the other, kinda like a clone
    1296 		 * skipAttributes is an object with the attributes that must NOT be copied.
    1297 		 * @param {CKEDITOR.dom.element} dest The destination element.
    1298 		 * @param {Object} skipAttributes A dictionary of attributes to skip.
    1299 		 * @example
    1300 		 */
    1301 		copyAttributes : function( dest, skipAttributes )
    1302 		{
    1303 			var attributes = this.$.attributes;
    1304 			skipAttributes = skipAttributes || {};
    1305
    1306 			for ( var n = 0 ; n < attributes.length ; n++ )
    1307 			{
    1308 				var attribute = attributes[n];
    1309
    1310 				// IE BUG: value attribute is never specified even if it exists.
    1311 				if ( attribute.specified ||
    1312 				  ( CKEDITOR.env.ie && attribute.nodeValue && attribute.nodeName.toLowerCase() == 'value' ) )
    1313 				{
    1314 					var attrName = attribute.nodeName;
    1315 					// We can set the type only once, so do it with the proper value, not copying it.
    1316 					if ( attrName in skipAttributes )
    1317 						continue;
    1318
    1319 					var attrValue = this.getAttribute( attrName );
    1320 					if ( attrValue === null )
    1321 						attrValue = attribute.nodeValue;
    1322
    1323 					dest.setAttribute( attrName, attrValue );
    1324 				}
    1325 			}
    1326
    1327 			// The style:
    1328 			if ( this.$.style.cssText !== '' )
    1329 				dest.$.style.cssText = this.$.style.cssText;
    1330 		},
    1331
    1332 		/**
    1333 		 * Changes the tag name of the current element.
    1334 		 * @param {String} newTag The new tag for the element.
    1335 		 */
    1336 		renameNode : function( newTag )
    1337 		{
    1338 			// If it's already correct exit here.
    1339 			if ( this.getName() == newTag )
    1340 				return;
    1341
    1342 			var doc = this.getDocument();
    1343
    1344 			// Create the new node.
    1345 			var newNode = new CKEDITOR.dom.element( newTag, doc );
    1346
    1347 			// Copy all attributes.
    1348 			this.copyAttributes( newNode );
    1349
    1350 			// Move children to the new node.
    1351 			this.moveChildren( newNode );
    1352
    1353 			// Replace the node.
    1354 			this.$.parentNode.replaceChild( newNode.$, this.$ );
    1355 			newNode.$._cke_expando = this.$._cke_expando;
    1356 			this.$ = newNode.$;
    1357 		},
    1358
    1359 		/**
    1360 		 * Gets a DOM tree descendant under the current node.
    1361 		 * @param {Array|Number} indices The child index or array of child indices under the node.
    1362 		 * @returns {CKEDITOR.dom.node} The specified DOM child under the current node. Null if child does not exist.
    1363 		 * @example
    1364 		 * var strong = p.getChild(0);
    1365 		 */
    1366 		getChild : function( indices )
    1367 		{
    1368 			var rawNode = this.$;
    1369
    1370 			if ( !indices.slice )
    1371 				rawNode = rawNode.childNodes[ indices ];
    1372 			else
    1373 			{
    1374 				while ( indices.length > 0 && rawNode )
    1375 					rawNode = rawNode.childNodes[ indices.shift() ];
    1376 			}
    1377
    1378 			return rawNode ? new CKEDITOR.dom.node( rawNode ) : null;
    1379 		},
    1380
    1381 		getChildCount : function()
    1382 		{
    1383 			return this.$.childNodes.length;
    1384  		}
    1385 	});
    1386