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 // In FF, when reloading the page with the editor focused, it may 54 // throw an error because the CKEDITOR global is not anymore 55 // available. So, we check it here first. (#2923) 56 if ( typeof CKEDITOR != 'undefined' ) 57 domObject.fire( eventName, new CKEDITOR.dom.event( domEvent ) ); 58 }; 59 }; 60 61 return /** @lends CKEDITOR.dom.domObject.prototype */ { 62 63 /** @ignore */ 64 on : function( eventName ) 65 { 66 // We customize the "on" function here. The basic idea is that we'll have 67 // only one listener for a native event, which will then call all listeners 68 // set to the event. 69 70 // Get the listeners holder object. 71 var nativeListeners = this.getCustomData( '_cke_nativeListeners' ); 72 73 if ( !nativeListeners ) 74 { 75 nativeListeners = {}; 76 this.setCustomData( '_cke_nativeListeners', nativeListeners ); 77 } 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.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, false ); 109 else if ( this.$.detachEvent ) 110 this.$.detachEvent( 'on' + eventName, listener ); 111 112 delete nativeListeners[ eventName ]; 113 } 114 } 115 } 116 }; 117 })(); 118 119 (function( domObjectProto ) 120 { 121 var customData = {}; 122 123 /** 124 * Determines whether the specified object is equal to the current object. 125 * @name CKEDITOR.dom.domObject.prototype.equals 126 * @function 127 * @param {Object} object The object to compare with the current object. 128 * @returns {Boolean} "true" if the object is equal. 129 * @example 130 * var doc = new CKEDITOR.dom.document( document ); 131 * alert( doc.equals( CKEDITOR.document ) ); // "true" 132 * alert( doc == CKEDITOR.document ); // "false" 133 */ 134 domObjectProto.equals = function( object ) 135 { 136 return ( object && object.$ === this.$ ); 137 }; 138 139 /** 140 * Sets a data slot value for this object. These values are shared by all 141 * instances pointing to that same DOM object. 142 * @name CKEDITOR.dom.domObject.prototype.setCustomData 143 * @function 144 * @param {String} key A key used to identify the data slot. 145 * @param {Object} value The value to set to the data slot. 146 * @returns {CKEDITOR.dom.domObject} This DOM object instance. 147 * @see CKEDITOR.dom.domObject.prototype.getCustomData 148 * @example 149 * var element = new CKEDITOR.dom.element( 'span' ); 150 * element.setCustomData( 'hasCustomData', true ); 151 */ 152 domObjectProto.setCustomData = function( key, value ) 153 { 154 var expandoNumber = this.getUniqueId(), 155 dataSlot = customData[ expandoNumber ] || ( customData[ expandoNumber ] = {} ); 156 157 dataSlot[ key ] = value; 158 159 return this; 160 }; 161 162 /** 163 * Gets the value set to a data slot in this object. 164 * @name CKEDITOR.dom.domObject.prototype.getCustomData 165 * @function 166 * @param {String} key The key used to identify the data slot. 167 * @returns {Object} This value set to the data slot. 168 * @see CKEDITOR.dom.domObject.prototype.setCustomData 169 * @example 170 * var element = new CKEDITOR.dom.element( 'span' ); 171 * alert( element.getCustomData( 'hasCustomData' ) ); // e.g. 'true' 172 */ 173 domObjectProto.getCustomData = function( key ) 174 { 175 var expandoNumber = this.$._cke_expando, 176 dataSlot = expandoNumber && customData[ expandoNumber ]; 177 178 if ( dataSlot && dataSlot[ key ] !== undefined ) 179 return dataSlot[ key ]; 180 return null; 181 }; 182 183 domObjectProto.removeCustomData = function( key ) 184 { 185 var expandoNumber = this.$._cke_expando, 186 dataSlot = expandoNumber && customData[ expandoNumber ], 187 retval = dataSlot[ key ]; 188 189 delete dataSlot[ key ]; 190 return retval || null; 191 }; 192 193 domObjectProto.getUniqueId = function() 194 { 195 return this.$._cke_expando || ( this.$._cke_expando = CKEDITOR.tools.getNextNumber() ); 196 }; 197 198 // Implement CKEDITOR.event. 199 CKEDITOR.event.implementOn( domObjectProto, true ); 200 201 })( CKEDITOR.dom.domObject.prototype ); 202