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