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