1 /* 2 Copyright (c) 2003-2012, 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 * Removes any listener set on this object. 122 * To avoid memory leaks we must assure that there are no 123 * references left after the object is no longer needed. 124 */ 125 removeAllListeners : function() 126 { 127 var nativeListeners = this.getCustomData( '_cke_nativeListeners' ); 128 for ( var eventName in nativeListeners ) 129 { 130 var listener = nativeListeners[ eventName ]; 131 if ( this.$.detachEvent ) 132 this.$.detachEvent( 'on' + eventName, listener ); 133 else if ( this.$.removeEventListener ) 134 this.$.removeEventListener( eventName, listener, false ); 135 136 delete nativeListeners[ eventName ]; 137 } 138 } 139 }; 140 })(); 141 142 (function( domObjectProto ) 143 { 144 var customData = {}; 145 146 CKEDITOR.on( 'reset', function() 147 { 148 customData = {}; 149 }); 150 151 /** 152 * Determines whether the specified object is equal to the current object. 153 * @name CKEDITOR.dom.domObject.prototype.equals 154 * @function 155 * @param {Object} object The object to compare with the current object. 156 * @returns {Boolean} "true" if the object is equal. 157 * @example 158 * var doc = new CKEDITOR.dom.document( document ); 159 * alert( doc.equals( CKEDITOR.document ) ); // "true" 160 * alert( doc == CKEDITOR.document ); // "false" 161 */ 162 domObjectProto.equals = function( object ) 163 { 164 return ( object && object.$ === this.$ ); 165 }; 166 167 /** 168 * Sets a data slot value for this object. These values are shared by all 169 * instances pointing to that same DOM object. 170 * <strong>Note:</strong> The created data slot is only guarantied to be available on this unique dom node, 171 * thus any wish to continue access it from other element clones (either created by clone node or from innerHtml) 172 * will fail, for such usage, please use {@link CKEDITOR.dom.element::setAttribute} instead. 173 * @name CKEDITOR.dom.domObject.prototype.setCustomData 174 * @function 175 * @param {String} key A key used to identify the data slot. 176 * @param {Object} value The value to set to the data slot. 177 * @returns {CKEDITOR.dom.domObject} This DOM object instance. 178 * @see CKEDITOR.dom.domObject.prototype.getCustomData 179 * @example 180 * var element = new CKEDITOR.dom.element( 'span' ); 181 * element.setCustomData( 'hasCustomData', true ); 182 */ 183 domObjectProto.setCustomData = function( key, value ) 184 { 185 var expandoNumber = this.getUniqueId(), 186 dataSlot = customData[ expandoNumber ] || ( customData[ expandoNumber ] = {} ); 187 188 dataSlot[ key ] = value; 189 190 return this; 191 }; 192 193 /** 194 * Gets the value set to a data slot in this object. 195 * @name CKEDITOR.dom.domObject.prototype.getCustomData 196 * @function 197 * @param {String} key The key used to identify the data slot. 198 * @returns {Object} This value set to the data slot. 199 * @see CKEDITOR.dom.domObject.prototype.setCustomData 200 * @example 201 * var element = new CKEDITOR.dom.element( 'span' ); 202 * alert( element.getCustomData( 'hasCustomData' ) ); // e.g. 'true' 203 */ 204 domObjectProto.getCustomData = function( key ) 205 { 206 var expandoNumber = this.$[ 'data-cke-expando' ], 207 dataSlot = expandoNumber && customData[ expandoNumber ]; 208 209 return dataSlot && dataSlot[ key ]; 210 }; 211 212 /** 213 * @name CKEDITOR.dom.domObject.prototype.removeCustomData 214 */ 215 domObjectProto.removeCustomData = function( key ) 216 { 217 var expandoNumber = this.$[ 'data-cke-expando' ], 218 dataSlot = expandoNumber && customData[ expandoNumber ], 219 retval = dataSlot && dataSlot[ key ]; 220 221 if ( typeof retval != 'undefined' ) 222 delete dataSlot[ key ]; 223 224 return retval || null; 225 }; 226 227 /** 228 * Removes any data stored on this object. 229 * To avoid memory leaks we must assure that there are no 230 * references left after the object is no longer needed. 231 * @name CKEDITOR.dom.domObject.prototype.clearCustomData 232 * @function 233 */ 234 domObjectProto.clearCustomData = function() 235 { 236 // Clear all event listeners 237 this.removeAllListeners(); 238 239 var expandoNumber = this.$[ 'data-cke-expando' ]; 240 expandoNumber && delete customData[ expandoNumber ]; 241 }; 242 243 /** 244 * Gets an ID that can be used to identiquely identify this DOM object in 245 * the running session. 246 * @name CKEDITOR.dom.domObject.prototype.getUniqueId 247 * @function 248 * @returns {Number} A unique ID. 249 */ 250 domObjectProto.getUniqueId = function() 251 { 252 return this.$[ 'data-cke-expando' ] || ( this.$[ 'data-cke-expando' ] = CKEDITOR.tools.getNextNumber() ); 253 }; 254 255 // Implement CKEDITOR.event. 256 CKEDITOR.event.implementOn( domObjectProto ); 257 258 })( CKEDITOR.dom.domObject.prototype ); 259