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