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.editor} class, which is the base
  8  *		for other classes representing DOM objects.
  9  */
 10
 11 /**
 12  * Represents a DOM object. This class is not intended to be used directly. It
 13  * serves as the base class for other classes representing specific DOM
 14  * objects.
 15  * @constructor
 16  * @param {Object} nativeDomObject A native DOM object.
 17  * @augments CKEDITOR.event
 18  * @example
 19  */
 20 CKEDITOR.dom.domObject = function( nativeDomObject )
 21 {
 22 	if ( nativeDomObject )
 23 	{
 24 		/**
 25 		 * The native DOM object represented by this class instance.
 26 		 * @type Object
 27 		 * @example
 28 		 * var element = new CKEDITOR.dom.element( 'span' );
 29 		 * alert( element.$.nodeType );  // "1"
 30 		 */
 31 		this.$ = nativeDomObject;
 32 	}
 33 };
 34
 35 CKEDITOR.dom.domObject.prototype = (function()
 36 {
 37 	// Do not define other local variables here. We want to keep the native
 38 	// listener closures as clean as possible.
 39
 40 	var getNativeListener = function( domObject, eventName )
 41 	{
 42 		return function( domEvent )
 43 		{
 44 			// In FF, when reloading the page with the editor focused, it may
 45 			// throw an error because the CKEDITOR global is not anymore
 46 			// available. So, we check it here first. (#2923)
 47 			if ( typeof CKEDITOR != 'undefined' )
 48 				domObject.fire( eventName, new CKEDITOR.dom.event( domEvent ) );
 49 		};
 50 	};
 51
 52 	return /** @lends CKEDITOR.dom.domObject.prototype */ {
 53
 54 		getPrivate : function()
 55 		{
 56 			var priv;
 57
 58 			// Get the main private function from the custom data. Create it if not
 59 			// defined.
 60 			if ( !( priv = this.getCustomData( '_' ) ) )
 61 				this.setCustomData( '_', ( priv = {} ) );
 62
 63 			return priv;
 64 		},
 65
 66 		/** @ignore */
 67 		on  : function( eventName )
 68 		{
 69 			// We customize the "on" function here. The basic idea is that we'll have
 70 			// only one listener for a native event, which will then call all listeners
 71 			// set to the event.
 72
 73 			// Get the listeners holder object.
 74 			var nativeListeners = this.getCustomData( '_cke_nativeListeners' );
 75
 76 			if ( !nativeListeners )
 77 			{
 78 				nativeListeners = {};
 79 				this.setCustomData( '_cke_nativeListeners', nativeListeners );
 80 			}
 81
 82 			// Check if we have a listener for that event.
 83 			if ( !nativeListeners[ eventName ] )
 84 			{
 85 				var listener = nativeListeners[ eventName ] = getNativeListener( this, eventName );
 86
 87 				if ( this.$.addEventListener )
 88 					this.$.addEventListener( eventName, listener, !!CKEDITOR.event.useCapture );
 89 				else if ( this.$.attachEvent )
 90 					this.$.attachEvent( 'on' + eventName, listener );
 91 			}
 92
 93 			// Call the original implementation.
 94 			return CKEDITOR.event.prototype.on.apply( this, arguments );
 95 		},
 96
 97 		/** @ignore */
 98 		removeListener : function( eventName )
 99 		{
100 			// Call the original implementation.
101 			CKEDITOR.event.prototype.removeListener.apply( this, arguments );
102
103 			// If we don't have listeners for this event, clean the DOM up.
104 			if ( !this.hasListeners( eventName ) )
105 			{
106 				var nativeListeners = this.getCustomData( '_cke_nativeListeners' );
107 				var listener = nativeListeners && nativeListeners[ eventName ];
108 				if ( listener )
109 				{
110 					if ( this.$.removeEventListener )
111 						this.$.removeEventListener( eventName, listener, false );
112 					else if ( this.$.detachEvent )
113 						this.$.detachEvent( 'on' + eventName, listener );
114
115 					delete nativeListeners[ eventName ];
116 				}
117 			}
118 		}
119 	};
120 })();
121
122 (function( domObjectProto )
123 {
124 	var customData = {};
125
126 	/**
127 	 * Determines whether the specified object is equal to the current object.
128 	 * @name CKEDITOR.dom.domObject.prototype.equals
129 	 * @function
130 	 * @param {Object} object The object to compare with the current object.
131 	 * @returns {Boolean} "true" if the object is equal.
132 	 * @example
133 	 * var doc = new CKEDITOR.dom.document( document );
134 	 * alert( doc.equals( CKEDITOR.document ) );  // "true"
135 	 * alert( doc == CKEDITOR.document );         // "false"
136 	 */
137 	domObjectProto.equals = function( object )
138 	{
139 		return ( object && object.$ === this.$ );
140 	};
141
142 	/**
143 	 * Sets a data slot value for this object. These values are shared by all
144 	 * instances pointing to that same DOM object.
145 	 * @name CKEDITOR.dom.domObject.prototype.setCustomData
146 	 * @function
147 	 * @param {String} key A key used to identify the data slot.
148 	 * @param {Object} value The value to set to the data slot.
149 	 * @returns {CKEDITOR.dom.domObject} This DOM object instance.
150 	 * @see CKEDITOR.dom.domObject.prototype.getCustomData
151 	 * @example
152 	 * var element = new CKEDITOR.dom.element( 'span' );
153 	 * element.setCustomData( 'hasCustomData', true );
154 	 */
155 	domObjectProto.setCustomData = function( key, value )
156 	{
157 		var expandoNumber = this.getUniqueId(),
158 			dataSlot = customData[ expandoNumber ] || ( customData[ expandoNumber ] = {} );
159
160 		dataSlot[ key ] = value;
161
162 		return this;
163 	};
164
165 	/**
166 	 * Gets the value set to a data slot in this object.
167 	 * @name CKEDITOR.dom.domObject.prototype.getCustomData
168 	 * @function
169 	 * @param {String} key The key used to identify the data slot.
170 	 * @returns {Object} This value set to the data slot.
171 	 * @see CKEDITOR.dom.domObject.prototype.setCustomData
172 	 * @example
173 	 * var element = new CKEDITOR.dom.element( 'span' );
174 	 * alert( element.getCustomData( 'hasCustomData' ) );  // e.g. 'true'
175 	 */
176 	domObjectProto.getCustomData = function( key )
177 	{
178 		var expandoNumber = this.$._cke_expando,
179 			dataSlot = expandoNumber && customData[ expandoNumber ];
180
181 		return dataSlot && dataSlot[ key ];
182 	};
183
184 	domObjectProto.removeCustomData = function( key )
185 	{
186 		var expandoNumber = this.$._cke_expando,
187 			dataSlot = expandoNumber && customData[ expandoNumber ],
188 			retval = dataSlot && dataSlot[ key ];
189
190 		if ( typeof retval != 'undefined' )
191 			delete dataSlot[ key ];
192
193 		return retval || null;
194 	};
195
196 	domObjectProto.getUniqueId = function()
197 	{
198 		return this.$._cke_expando || ( this.$._cke_expando = CKEDITOR.tools.getNextNumber() );
199 	};
200
201 	// Implement CKEDITOR.event.
202 	CKEDITOR.event.implementOn( domObjectProto );
203
204 })( CKEDITOR.dom.domObject.prototype );
205