      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