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 		 * Moves the selection focus to this element.
176 		 * @example
177 		 * var element = CKEDITOR.document.getById( 'myTextarea' );
178 		 * <b>element.focus()</b>;
179 		 */
180 		focus : function()
181 		{
182 			this.$.focus();
183 		},
184
185 		/**
186 		 * Gets the inner HTML of this element.
187 		 * @returns {String} The inner HTML of this element.
188 		 * @example
189 		 * var element = CKEDITOR.dom.element.createFromHtml( '<div><b>Example</b></div>' );
190 		 * alert( <b>p.getHtml()</b> );  // "<b>Example</b>"
191 		 */
192 		getHtml : function()
193 		{
194 			return this.$.innerHTML;
195 		},
196
197 		/**
198 		 * Sets the inner HTML of this element.
199 		 * @param {String} html The HTML to be set for this element.
200 		 * @returns {String} The inserted HTML.
201 		 * @example
202 		 * var p = new CKEDITOR.dom.element( 'p' );
203 		 * <b>p.setHtml( '<b>Inner</b> HTML' );</b>
204 		 *
205 		 * // result: "<p><b>Inner</b> HTML</p>"
206 		 */
207 		setHtml : function( html )
208 		{
209 			return ( this.$.innerHTML = html );
210 		},
211
212 		/**
213 		 * Sets the element contents as plain text.
214 		 * @param {String} text The text to be set.
215 		 * @returns {String} The inserted text.
216 		 * @example
217 		 * var element = new CKEDITOR.dom.element( 'div' );
218 		 * element.setText( 'A > B & C < D' );
219 		 * alert( element.innerHTML );  // "A &gt; B &amp; C &lt; D"
220 		 */
221 		setText : function( text )
222 		{
223 			CKEDITOR.dom.element.prototype.setText = ( this.$.innerText != undefined ) ?
224 				function ( text )
225 				{
226 					return this.$.innerText = text;
227 				} :
228 				function ( text )
229 				{
230 					return this.$.textContent = text;
231 				};
232
233 			return this.setText( text );
234 		},
235
236 		/**
237 		 * Gets the document containing this element.
238 		 * @returns {CKEDITOR.dom.document} The document.
239 		 * @example
240 		 * var element = CKEDITOR.document.getById( 'example' );
241 		 * alert( <b>element.getDocument().equals( CKEDITOR.document )</b> );  // "true"
242 		 */
243 		getDocument : function()
244 		{
245 			var document = new CKEDITOR.dom.document( this.$.ownerDocument );
246
247 			return (
248 			/** @ignore */
249 			this.getDocument = function()
250 				{
251 					return document;
252 				})();
253 		},
254
255 		/**
256 		 * Gets the window object that contains this element.
257 		 * @returns {CKEDITOR.dom.window} The window object.
258 		 * @example
259 		 */
260 		getWindow : function()
261 		{
262 			return this.getDocument().getWindow();
263 		},
264
265 		/**
266 		 * Gets the value of the "id" attribute of this element.
267 		 * @returns {String} The element id, or null if not available.
268 		 * @example
269 		 * var element = CKEDITOR.dom.element.createFromHtml( '<p id="myId"></p>' );
270 		 * alert( <b>element.getId()</b> );  // "myId"
271 		 */
272 		getId : function()
273 		{
274 			return this.$.id || null;
275 		},
276
277 		/**
278 		 * Gets the value of the "name" attribute of this element.
279 		 * @returns {String} The element name, or null if not available.
280 		 * @example
281 		 * var element = CKEDITOR.dom.element.createFromHtml( '<input name="myName"></input>' );
282 		 * alert( <b>element.getNameAtt()</b> );  // "myName"
283 		 */
284 		getNameAtt : function()
285 		{
286 			return this.$.name || null;
287 		},
288
289 		/**
290 		 * Gets the element name (tag name). The returned name is guaranteed to
291 		 * be always full lowercased.
292 		 * @returns {String} The element name.
293 		 * @example
294 		 * var element = new CKEDITOR.dom.element( 'span' );
295 		 * alert( <b>element.getName()</b> );  // "span"
296 		 */
297 		getName : function()
298 		{
299 			// Cache the lowercased name inside a closure.
300 			var nodeName = this.$.nodeName.toLowerCase();
301
302 			return (
303 			/** @ignore */
304 			this.getName = function()
305 				{
306 					return nodeName;
307 				})();
308 		},
309
310 		/**
311 		 * Gets the value set to this element. This value is usually available
312 		 * for form field elements.
313 		 * @returns {String} The element value.
314 		 */
315 		getValue : function()
316 		{
317 			return this.$.value;
318 		},
319
320 		/**
321 		 * Gets the first child node of this element.
322 		 * @returns {CKEDITOR.dom.node} The first child node or null if not
323 		 *		available.
324 		 * @example
325 		 * var element = CKEDITOR.dom.element.createFromHtml( '<div><b>Example</b></div>' );
326 		 * var first = <b>element.getFirst()</b>;
327 		 * alert( first.getName() );  // "b"
328 		 */
329 		getFirst : function()
330 		{
331 			var $ = this.$.firstChild;
332 			return $ ? new CKEDITOR.dom.node( $ ) : null;
333 		},
334
335 		/**
336 		 * Hides this element (display:none).
337 		 * @example
338 		 * var element = CKEDITOR.dom.element.getById( 'myElement' );
339 		 * <b>element.hide()</b>;
340 		 */
341 		hide : function()
342 		{
343 			this.setStyle( 'display', 'none' );
344 		},
345
346 		/**
347 		 * Shows this element (display it).
348 		 * @example
349 		 * var element = CKEDITOR.dom.element.getById( 'myElement' );
350 		 * <b>element.show()</b>;
351 		 */
352 		show : function()
353 		{
354 			this.setStyle( 'display', '' );
355 		},
356
357 		/**
358 		 * Sets the value of an element attribute.
359 		 * @param {String} name The name of the attribute.
360 		 * @param {String} value The value to be set to the attribute.
361 		 * @function
362 		 * @returns {CKEDITOR.dom.element} This element instance.
363 		 * @example
364 		 * var element = CKEDITOR.dom.element.getById( 'myElement' );
365 		 * <b>element.setAttribute( 'class', 'myClass' )</b>;
366 		 * <b>element.setAttribute( 'title', 'This is an example' )</b>;
367 		 */
368 		setAttribute : (function()
369 		{
370 			var standard = function( name, value )
371 			{
372 				this.$.setAttribute( name, value );
373 				return this;
374 			};
375
376 			if ( CKEDITOR.env.ie )
377 			{
378 				return function( name, value )
379 				{
380 					if ( name == 'class' )
381 						this.$.className = value;
382 					if ( name == 'style' )
383 						this.$.style.cssText = value;
384 					else
385 						standard.apply( this, arguments );
386 					return this;
387 				};
388 			}
389 			else
390 				return standard;
391 		})(),
392
393 		/**
394 		 * Sets the value of several element attributes.
395 		 * @param {Object} attributesPairs An object containing the names and
396 		 *		values of the attributes.
397 		 * @returns {CKEDITOR.dom.element} This element instance.
398 		 * @example
399 		 * var element = CKEDITOR.dom.element.getById( 'myElement' );
400 		 * <b>element.setAttributes({
401 		 *     'class' : 'myClass',
402 		 *     'title' : 'This is an example' })</b>;
403 		 */
404 		setAttributes : function( attributesPairs )
405 		{
406 			for ( var name in attributesPairs )
407 				this.setAttribute( name, attributesPairs[ name ] );
408 			return this;
409 		},
410
411 		/**
412 		 * Sets the element value. This function is usually used with form
413 		 * field element.
414 		 * @param {String} value The element value.
415 		 * @returns {CKEDITOR.dom.element} This element instance.
416 		 */
417 		setValue : function( value )
418 		{
419 			this.$.value = value;
420 			return this;
421 		},
422
423 		/**
424 		 * Removes the element from the document DOM.
425 		 * @example
426 		 * var element = CKEDITOR.dom.element.getById( 'MyElement' );
427 		 * <b>element.remove()</b>;
428 		 */
429 		remove : function()
430 		{
431 			this.$.parentNode.removeChild( this.$ );
432 		},
433
434 		/**
435 		 * Removes an attribute from the element.
436 		 * @param {String} name The attribute name.
437 		 * @function
438 		 * @example
439 		 * var element = CKEDITOR.dom.element.createFromHtml( '<div class="classA"></div>' );
440 		 * element.removeAttribute( 'class' );
441 		 */
442 		removeAttribute : (function()
443 		{
444 			var standard = function( name )
445 			{
446 				this.$.removeAttribute( name );
447 			};
448
449 			if ( CKEDITOR.env.ie )
450 			{
451 				return function( name )
452 				{
453 					if ( name == 'class' )
454 						name = 'className';
455 					standard.call( this, name );
456 				};
457 			}
458 			else
459 				return standard;
460 		})(),
461
462 		/**
463 		 * Sets the value of an element style.
464 		 * @param {String} name The name of the style. The CSS naming notation
465 		 *		must be used (e.g. "background-color").
466 		 * @param {String} value The value to be set to the style.
467 		 * @returns {CKEDITOR.dom.element} This element instance.
468 		 * @example
469 		 * var element = CKEDITOR.dom.element.getById( 'myElement' );
470 		 * <b>element.setStyle( 'background-color', '#ff0000' )</b>;
471 		 * <b>element.setStyle( 'margin-top', '10px' )</b>;
472 		 * <b>element.setStyle( 'float', 'right' )</b>;
473 		 */
474 		setStyle : function( name, value )
475 		{
476 			this.$.style[ CKEDITOR.tools.cssStyleToDomStyle( name ) ] = value;
477 			return this;
478 		},
479
480 		/**
481 		 * Sets the value of several element styles.
482 		 * @param {Object} stylesPairs An object containing the names and
483 		 *		values of the styles.
484 		 * @returns {CKEDITOR.dom.element} This element instance.
485 		 * @example
486 		 * var element = CKEDITOR.dom.element.getById( 'myElement' );
487 		 * <b>element.setStyles({
488 		 *     'position' : 'absolute',
489 		 *     'float' : 'right' })</b>;
490 		 */
491 		setStyles : function( stylesPairs )
492 		{
493 			for ( var name in stylesPairs )
494 				this.setStyle( name, stylesPairs[ name ] );
495 			return this;
496 		},
497
498 		/**
499 		 * Makes the element unselectable.
500 		 * @function
501 		 * @example
502 		 * var element = CKEDITOR.dom.element.getById( 'myElement' );
503 		 * element.unselectable();
504 		 */
505 		unselectable :
506 			CKEDITOR.env.gecko ?
507 				function()
508 				{
509 					this.$.style.MozUserSelect = 'none';
510 				}
511 			: CKEDITOR.env.webkit ?
512 				function()
513 				{
514 					this.$.style.KhtmlUserSelect = 'none';
515 				}
516 			:
517 				function()
518 				{
519 					if ( CKEDITOR.env.ie || CKEDITOR.env.opera )
520 					{
521 						var element = this.$,
522 							e,
523 							i = 0;
524
525 						element.unselectable = 'on';
526
527 						while ( ( e = element.all[ i++ ] ) )
528 						{
529 							switch ( e.tagName.toLowerCase() )
530 							{
531 								case 'iframe' :
532 								case 'textarea' :
533 								case 'input' :
534 								case 'select' :
535 									/* Ignore the above tags */
536 									break;
537 								default :
538 									e.unselectable = 'on';
539 							}
540 						}
541 					}
542 				}
543 	});
544