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.editor} class, which is the base
 24  *		for other classes representing DOM objects.
 25  */
 26
 27 /**
 28  * Represents a DOM object. This class is not intended to be used directly. It
 29  * serves as the base class for other classes representing specific DOM
 30  * objects.
 31  * @constructor
 32  * @param {Object} nativeDomObject A native DOM object.
 33  * @augments CKEDITOR.event
 34  * @example
 35  */
 36 CKEDITOR.dom.domObject = function( nativeDomObject )
 37 {
 38 	if ( nativeDomObject )
 39 	{
 40 		// Call the base event constructor.
 41 		CKEDITOR.event.call( this );
 42
 43 		/**
 44 		 * The native DOM object represented by this class instance.
 45 		 * @type Object
 46 		 * @example
 47 		 * var element = new CKEDITOR.dom.element( 'span' );
 48 		 * alert( element.$.nodeType );  // "1"
 49 		 */
 50 		this.$ = nativeDomObject;
 51 	}
 52 };
 53
 54 CKEDITOR.dom.domObject.prototype = (function()
 55 {
 56 	// Do not define other local variables here. We want to keep the native
 57 	// listener closures as clean as possible.
 58
 59 	var getNativeListener = function( domObject, eventName )
 60 	{
 61 		return function( domEvent )
 62 		{
 63 			domObject.fire( eventName, new CKEDITOR.dom.event( domEvent ) );
 64 		};
 65 	};
 66
 67 	return /** @lends CKEDITOR.dom.domObject.prototype */ {
 68
 69 		/** @ignore */
 70 		on  : function( eventName )
 71 		{
 72 			// We customize the "on" function here. The basic idea is that we'll have
 73 			// only one listener for a native event, which will then call all listeners
 74 			// set to the event.
 75
 76 			// Get the listeners holder object.
 77 			var nativeListeners = this.getCustomData( '_cke_nativeListeners' ) || this.setCustomData( '_cke_nativeListeners', {} );
 78
 79 			// Check if we have a listener for that event.
 80 			if ( !nativeListeners[ eventName ] )
 81 			{
 82 				var listener = nativeListeners[ eventName ] = getNativeListener( this, eventName );
 83
 84 				if ( this.$.addEventListener )
 85 					this.$.addEventListener( eventName, listener, false );
 86 				else if ( this.$.attachEvent )
 87 					this.$.attachEvent( 'on' + eventName, listener );
 88 			}
 89
 90 			// Call the original implementation.
 91 			return CKEDITOR.event.prototype.on.apply( this, arguments );
 92 		},
 93
 94 		/** @ignore */
 95 		removeListener : function( eventName )
 96 		{
 97 			// Call the original implementation.
 98 			CKEDITOR.event.prototype.fire.removeListener.apply( this, arguments );
 99
100 			// If we don't have listeners for this event, clean the DOM up.
101 			if ( !this.hasListeners( eventName ) )
102 			{
103 				var nativeListeners = this.getCustomData( '_cke_nativeListeners' );
104 				var listener = nativeListeners && nativeListeners[ eventName ];
105 				if ( listener )
106 				{
107 					if ( this.$.removeEventListener )
108 						this.$.removeEventListener( eventName, listener );
109 					else if ( this.$.dettachEvent )
110 						this.$.dettachEvent( eventName, listener );
111
112 					delete nativeListeners[ eventName ];
113 				}
114 			}
115 		}
116 	};
117 })();
118
119 (function( domObjectProto )
120 {
121 	var customData = {};
122
123 	/**
124 	 * Sets a data slot value for this object. These values are shared by all
125 	 * instances pointing to that same DOM object.
126 	 * @name CKEDITOR.dom.domObject.prototype.setCustomData
127 	 * @param {String} key A key used to identify the data slot.
128 	 * @param {Object} value The value to set to the data slot.
129 	 * @returns {CKEDITOR.dom.domObject} This DOM object instance.
130 	 * @see CKEDITOR.dom.domObject.prototype.getCustomData
131 	 * @example
132 	 * var element = new CKEDITOR.dom.element( 'span' );
133 	 * element.setCustomData( 'hasCustomData', true );
134 	 */
135 	domObjectProto.setCustomData = function( key, value )
136 	{
137 		var expandoNumber = this.$._cke_expando || ( this.$._cke_expando = CKEDITOR.tools.getNextNumber() ),
138 			dataSlot = customData[ expandoNumber ] || ( customData[ expandoNumber ] = {} );
139
140 		dataSlot[ key ] = value;
141
142 		return this;
143 	};
144
145 	/**
146 	 * Gets the value set to a data slot in this object.
147 	 * @name CKEDITOR.dom.domObject.prototype.getCustomData
148 	 * @param {String} key The key used to identify the data slot.
149 	 * @returns {Object} This value set to the data slot.
150 	 * @see CKEDITOR.dom.domObject.prototype.setCustomData
151 	 * @example
152 	 * var element = new CKEDITOR.dom.element( 'span' );
153 	 * alert( element.getCustomData( 'hasCustomData' ) );  // e.g. 'true'
154 	 */
155 	domObjectProto.getCustomData = function( key )
156 	{
157 		var expandoNumber = this.$._cke_expando,
158 			dataSlot = expandoNumber && customData[ expandoNumber ];
159
160 		return ( dataSlot && dataSlot[ key ] ) || null;
161 	};
162
163 	// Implement CKEDITOR.event.
164 	CKEDITOR.event.implementOn( domObjectProto );
165
166 })( CKEDITOR.dom.domObject.prototype );
167