1 /*
  2  * CKEditor - The text editor for Internet - http://ckeditor.com
  3  * Copyright (C) 2003-2008 Frederico Caldeira Knabben
  4  *
  5  * == BEGIN LICENSE ==
  6  *
  7  * Licensed under the terms of any of the following licenses at your
  8  * choice:
  9  *
 10  *  - GNU General Public License Version 2 or later (the "GPL")
 11  *    http://www.gnu.org/licenses/gpl.html
 12  *
 13  *  - GNU Lesser General Public License Version 2.1 or later (the "LGPL")
 14  *    http://www.gnu.org/licenses/lgpl.html
 15  *
 16  *  - Mozilla Public License Version 1.1 or later (the "MPL")
 17  *    http://www.mozilla.org/MPL/MPL-1.1.html
 18  *
 19  * == END LICENSE ==
 20  */
 21
 22 /**
 23  * @fileOverview Defines the {@link CKEDITOR.dom.element} class, which
 24  *		represents a DOM element.
 25  */
 26
 27 /**
 28  * Represents a DOM element.
 29  * @constructor
 30  * @augments CKEDITOR.dom.node
 31  * @param {Object|String} element A native DOM element or the element name for
 32  *		new elements.
 33  * @param {CKEDITOR.dom.document} [ownerDocument] The document that will contain
 34  *		the element in case of element creation.
 35  * @example
 36  * // Create a new <span> element.
 37  * var element = new CKEDITOR.dom.element( 'span' );
 38  * @example
 39  * // Create an element based on a native DOM element.
 40  * var element = new CKEDITOR.dom.element( document.getElementById( 'myId' ) );
 41  */
 42 CKEDITOR.dom.element = function( element, ownerDocument )
 43 {
 44 	if ( typeof element == 'string' )
 45 		element = ( ownerDocument ? ownerDocument.$ : document ).createElement( element );
 46
 47 	/**
 48 	 * The native DOM element represented by this class instance.
 49 	 * @type Object
 50 	 * @example
 51 	 * var element = new CKEDITOR.dom.element( 'span' );
 52 	 * alert( element.$.nodeType );  // "1"
 53 	 */
 54 	this.$ = element;
 55 };
 56
 57 CKEDITOR.dom.element.prototype = new CKEDITOR.dom.node();
 58
 59 /**
 60  * Creates an instance of the {@link CKEDITOR.dom.element} class based on the
 61  * HTML representation of an element.
 62  * @param {String} html The element HTML. It should define only one element in
 63  *		the "root" level. The "root" element can have child nodes, but not
 64  *		siblings.
 65  * @returns {CKEDITOR.dom.element} The element instance.
 66  * @example
 67  * var element = <b>CKEDITOR.dom.element.createFromHtml( '<strong class="anyclass">My element</strong>' )</b>;
 68  * alert( element.getName() );  // "strong"
 69  */
 70 CKEDITOR.dom.element.createFromHtml = function( html, ownerDocument )
 71 {
 72 	var temp = new CKEDITOR.dom.element( 'div', ownerDocument );
 73 	temp.setHtml( html );
 74 	return temp.getFirst();
 75 };
 76
 77 CKEDITOR.tools.extend( CKEDITOR.dom.element.prototype,
 78 	/** @lends CKEDITOR.dom.element.prototype */
 79 	{
 80 		/**
 81 		 * Adds a CSS class to the element. It appends the class to the
 82 		 * already existing names.
 83 		 * @param {String} className The name of the class to be added.
 84 		 * @example
 85 		 * var element = new CKEDITOR.dom.element( 'div' );
 86 		 * element.addClass( 'classA' );  // <div class="classA">
 87 		 * element.addClass( 'classB' );  // <div class="classA classB">
 88 		 * element.addClass( 'classA' );  // <div class="classA classB">
 89 		 */
 90 		addClass : function( className )
 91 		{
 92 			var c = this.$.className;
 93 			if ( c )
 94 			{
 95 				var regex = new RegExp( '(?:^|\\s)' + className + '(?:\\s|$)', '' );
 96 				if ( !regex.test( c ) )
 97 					c += ' ' + className;
 98 			}
 99 			this.$.className = c || className;
100 		},
101
102 		/**
103 		 * Removes a CSS class name from the elements classes. Other classes
104 		 * remain untouched.
105 		 * @param {String} className The name of the class to remove.
106 		 * @example
107 		 * var element = new CKEDITOR.dom.element( 'div' );
108 		 * element.addClass( 'classA' );  // <div class="classA">
109 		 * element.addClass( 'classB' );  // <div class="classA classB">
110 		 * element.removeClass( 'classA' );  // <div class="classB">
111 		 * element.removeClass( 'classB' );  // <div>
112 		 */
113 		removeClass : function( className )
114 		{
115 			var c = this.$.className;
116 			if ( c )
117 			{
118 				var regex = new RegExp( '(?:^|\\s+)' + className + '(?=\\s|$)', '' );
119 				if ( regex.test( c ) )
120 				{
121 					c = c.replace( regex, '' ).replace( /^\s+/, '' );
122
123 					if ( c )
124 						this.$.className = c;
125 					else
126 						this.removeAttribute( 'class' );
127 				}
128 			}
129 		},
130
131 		/**
132 		 * Append a node as a child of this element.
133 		 * @param {CKEDITOR.dom.node|String} node The node or element name to be
134 		 *		appended.
135 		 * @returns {CKEDITOR.dom.node} The appended node.
136 		 * @example
137 		 * var p = new CKEDITOR.dom.element( 'p' );
138 		 *
139 		 * var strong = new CKEDITOR.dom.element( 'strong' );
140 		 * <b>p.append( strong );</b>
141 		 *
142 		 * var em = <b>p.append( 'em' );</b>
143 		 *
144 		 * // result: "<p><strong></strong><em></em></p>"
145 		 */
146 		append : function( node )
147 		{
148 			if ( typeof node == 'string' )
149 				node = new CKEDITOR.dom.element( node );
150
151 			this.$.appendChild( node.$ );
152 			return node;
153 		},
154
155 		/**
156 		 * Append text to this element.
157 		 * @param {String} text The text to be appended.
158 		 * @returns {CKEDITOR.dom.node} The appended node.
159 		 * @example
160 		 * var p = new CKEDITOR.dom.element( 'p' );
161 		 * p.appendText( 'This is' );
162 		 * p.appendText( ' some text' );
163 		 *
164 		 * // result: "<p>This is some text</p>"
165 		 */
166 		appendText : function( text )
167 		{
168 			if ( this.$.text != undefined )
169 				this.$.text += text;
170 			else
171 				this.append( new CKEDITOR.dom.text( text ) );
172 		},
173
174 		/**
175 		 * Gets the inner HTML of this element.
176 		 * @returns {String} The inner HTML of this element.
177 		 * @example
178 		 * var element = CKEDITOR.dom.element.createFromHtml( '<div><b>Example</b></div>' );
179 		 * alert( <b>p.getHtml()</b> );  // "<b>Example</b>"
180 		 */
181 		getHtml : function()
182 		{
183 			return this.$.innerHTML;
184 		},
185
186 		/**
187 		 * Sets the inner HTML of this element.
188 		 * @param {String} html The HTML to be set for this element.
189 		 * @returns {String} The inserted HTML.
190 		 * @example
191 		 * var p = new CKEDITOR.dom.element( 'p' );
192 		 * <b>p.setHtml( '<b>Inner</b> HTML' );</b>
193 		 *
194 		 * // result: "<p><b>Inner</b> HTML</p>"
195 		 */
196 		setHtml : function( html )
197 		{
198 			return ( this.$.innerHTML = html );
199 		},
200
201 		/**
202 		 * Sets the element contents as plain text.
203 		 * @param {String} text The text to be set.
204 		 * @returns {String} The inserted text.
205 		 * @example
206 		 * var element = new CKEDITOR.dom.element( 'div' );
207 		 * element.setText( 'A > B & C < D' );
208 		 * alert( element.innerHTML );  // "A &gt; B &amp; C &lt; D"
209 		 */
210 		setText : function( text )
211 		{
212 			CKEDITOR.dom.element.prototype.setText = ( this.$.innerText != undefined ) ?
213 				function ( text )
214 				{
215 					return this.$.innerText = text;
216 				} :
217 				function ( text )
218 				{
219 					return this.$.textContent = text;
220 				};
221
222 			return this.setText( text );
223 		},
224
225 		/**
226 		 * Gets the document containing this element.
227 		 * @returns {CKEDITOR.dom.document} The document.
228 		 * @example
229 		 * var element = CKEDITOR.document.getById( 'example' );
230 		 * alert( <b>element.getDocument().equals( CKEDITOR.document )</b> );  // "true"
231 		 */
232 		getDocument : function()
233 		{
234 			var document = new CKEDITOR.dom.document( this.$.ownerDocument );
235
236 			return (
237 			/** @ignore */
238 			this.getDocument = function()
239 				{
240 					return document;
241 				})();
242 		},
243
244 		/**
245 		 * Gets the value of the "id" attribute of this element.
246 		 * @returns {String} The element id, or null if not available.
247 		 * @example
248 		 * var element = CKEDITOR.dom.element.createFromHtml( '<p id="myId"></p>' );
249 		 * alert( <b>element.getId()</b> );  // "myId"
250 		 */
251 		getId : function()
252 		{
253 			return this.$.id || null;
254 		},
255
256 		/**
257 		 * Gets the value of the "name" attribute of this element.
258 		 * @returns {String} The element name, or null if not available.
259 		 * @example
260 		 * var element = CKEDITOR.dom.element.createFromHtml( '<input name="myName"></input>' );
261 		 * alert( <b>element.getNameAtt()</b> );  // "myName"
262 		 */
263 		getNameAtt : function()
264 		{
265 			return this.$.name || null;
266 		},
267
268 		/**
269 		 * Gets the element name (tag name). The returned name is guaranteed to
270 		 * be always full lowercased.
271 		 * @returns {String} The element name.
272 		 * @example
273 		 * var element = new CKEDITOR.dom.element( 'span' );
274 		 * alert( <b>element.getName()</b> );  // "span"
275 		 */
276 		getName : function()
277 		{
278 			// Cache the lowercased name inside a closure.
279 			var nodeName = this.$.nodeName.toLowerCase();
280
281 			return (
282 			/** @ignore */
283 			this.getName = function()
284 				{
285 					return nodeName;
286 				})();
287 		},
288
289 		/**
290 		 * Gets the first child node of this element.
291 		 * @returns {CKEDITOR.dom.node} The first child node or null if not
292 		 *		available.
293 		 * @example
294 		 * var element = CKEDITOR.dom.element.createFromHtml( '<div><b>Example</b></div>' );
295 		 * var first = <b>element.getFirst()</b>;
296 		 * alert( first.getName() );  // "b"
297 		 */
298 		getFirst : function()
299 		{
300 			var $ = this.$.firstChild;
301 			return $ ? new CKEDITOR.dom.node( $ ) : null;
302 		},
303
304 		/**
305 		 * Hides this element (display:none).
306 		 * @example
307 		 * var element = CKEDITOR.dom.element.getById( 'myElement' );
308 		 * <b>element.hide()</b>;
309 		 */
310 		hide : function()
311 		{
312 			this.setStyle( 'display', 'none' );
313 		},
314
315 		/**
316 		 * Shows this element (display it).
317 		 * @example
318 		 * var element = CKEDITOR.dom.element.getById( 'myElement' );
319 		 * <b>element.show()</b>;
320 		 */
321 		show : function()
322 		{
323 			this.setStyle( 'display', '' );
324 		},
325
326 		/**
327 		 * Sets the value of an element attribute.
328 		 * @param {String} name The name of the attribute.
329 		 * @param {String} value The value to be set to the attribute.
330 		 * @function
331 		 * @example
332 		 * var element = CKEDITOR.dom.element.getById( 'myElement' );
333 		 * <b>element.setAttribute( 'class', 'myClass' )</b>;
334 		 * <b>element.setAttribute( 'title', 'This is an example' )</b>;
335 		 */
336 		setAttribute : (function()
337 		{
338 			var standard = function( name, value )
339 			{
340 				this.$.setAttribute( name, value );
341 			};
342
343 			if ( CKEDITOR.env.ie )
344 			{
345 				return function( name, value )
346 				{
347 					if ( name == 'class' )
348 						this.$.className = value;
349 					if ( name == 'style' )
350 						this.$.style.cssText = value;
351 					else
352 						standard.apply( this, arguments );
353 				};
354 			}
355 			else
356 				return standard;
357 		})(),
358
359 		/**
360 		 * Sets the value of several element attributes.
361 		 * @param {Object} attributesPairs An object containing the names and
362 		 *		values of the attributes.
363 		 * @example
364 		 * var element = CKEDITOR.dom.element.getById( 'myElement' );
365 		 * <b>element.setAttributes({
366 		 *     'class' : 'myClass',
367 		 *     'title' : 'This is an example' })</b>;
368 		 */
369 		setAttributes : function( attributesPairs )
370 		{
371 			for ( var name in attributesPairs )
372 				this.setAttribute( name, attributesPairs[ name ] );
373 		},
374
375 		/**
376 		 * Removes the element from the document DOM.
377 		 * @example
378 		 * var element = CKEDITOR.dom.element.getById( 'MyElement' );
379 		 * <b>element.remove()</b>;
380 		 */
381 		remove : function()
382 		{
383 			this.$.parentNode.removeChild( this.$ );
384 		},
385
386 		/**
387 		 * Removes an attribute from the element.
388 		 * @param {String} name The attribute name.
389 		 * @function
390 		 * @example
391 		 * var element = CKEDITOR.dom.element.createFromHtml( '<div class="classA"></div>' );
392 		 * element.removeAttribute( 'class' );
393 		 */
394 		removeAttribute : (function()
395 		{
396 			var standard = function( name )
397 			{
398 				this.$.removeAttribute( name );
399 			};
400
401 			if ( CKEDITOR.env.ie )
402 			{
403 				return function( name )
404 				{
405 					if ( name == 'class' )
406 						name = 'className';
407 					standard.call( this, name );
408 				};
409 			}
410 			else
411 				return standard;
412 		})(),
413
414 		/**
415 		 * Sets the value of an element style.
416 		 * @param {String} name The name of the style. The CSS naming notation
417 		 *		must be used (e.g. "background-color").
418 		 * @param {String} value The value to be set to the style.
419 		 * @example
420 		 * var element = CKEDITOR.dom.element.getById( 'myElement' );
421 		 * <b>element.setStyle( 'background-color', '#ff0000' )</b>;
422 		 * <b>element.setStyle( 'margin-top', '10px' )</b>;
423 		 * <b>element.setStyle( 'float', 'right' )</b>;
424 		 */
425 		setStyle : function( name, value )
426 		{
427 			this.$.style[ CKEDITOR.tools.cssStyleToDomStyle( name ) ] = value;
428 		},
429
430 		/**
431 		 * Sets the value of several element styles.
432 		 * @param {Object} stylesPairs An object containing the names and
433 		 *		values of the styles.
434 		 * @example
435 		 * var element = CKEDITOR.dom.element.getById( 'myElement' );
436 		 * <b>element.setStyles({
437 		 *     'position' : 'absolute',
438 		 *     'float' : 'right' })</b>;
439 		 */
440 		setStyles : function( stylesPairs )
441 		{
442 			for ( var name in stylesPairs )
443 				this.setStyle( name, stylesPairs[ name ] );
444 		}
445 	});
446