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