/* Copyright (c) 2003-2010, CKSource - Frederico Knabben. All rights reserved. For licensing, see LICENSE.html or http://ckeditor.com/license */ /** * @fileOverview Defines the {@link CKEDITOR.editor} class, which is the base * for other classes representing DOM objects. */ /** * Represents a DOM object. This class is not intended to be used directly. It * serves as the base class for other classes representing specific DOM * objects. * @constructor * @param {Object} nativeDomObject A native DOM object. * @augments CKEDITOR.event * @example */ CKEDITOR.dom.domObject = function( nativeDomObject ) { if ( nativeDomObject ) { /** * The native DOM object represented by this class instance. * @type Object * @example * var element = new CKEDITOR.dom.element( 'span' ); * alert( element.$.nodeType ); // "1" */ this.$ = nativeDomObject; } }; CKEDITOR.dom.domObject.prototype = (function() { // Do not define other local variables here. We want to keep the native // listener closures as clean as possible. var getNativeListener = function( domObject, eventName ) { return function( domEvent ) { // In FF, when reloading the page with the editor focused, it may // throw an error because the CKEDITOR global is not anymore // available. So, we check it here first. (#2923) if ( typeof CKEDITOR != 'undefined' ) domObject.fire( eventName, new CKEDITOR.dom.event( domEvent ) ); }; }; return /** @lends CKEDITOR.dom.domObject.prototype */ { getPrivate : function() { var priv; // Get the main private function from the custom data. Create it if not // defined. if ( !( priv = this.getCustomData( '_' ) ) ) this.setCustomData( '_', ( priv = {} ) ); return priv; }, /** @ignore */ on : function( eventName ) { // We customize the "on" function here. The basic idea is that we'll have // only one listener for a native event, which will then call all listeners // set to the event. // Get the listeners holder object. var nativeListeners = this.getCustomData( '_cke_nativeListeners' ); if ( !nativeListeners ) { nativeListeners = {}; this.setCustomData( '_cke_nativeListeners', nativeListeners ); } // Check if we have a listener for that event. if ( !nativeListeners[ eventName ] ) { var listener = nativeListeners[ eventName ] = getNativeListener( this, eventName ); if ( this.$.attachEvent ) this.$.attachEvent( 'on' + eventName, listener ); else if ( this.$.addEventListener ) this.$.addEventListener( eventName, listener, !!CKEDITOR.event.useCapture ); } // Call the original implementation. return CKEDITOR.event.prototype.on.apply( this, arguments ); }, /** @ignore */ removeListener : function( eventName ) { // Call the original implementation. CKEDITOR.event.prototype.removeListener.apply( this, arguments ); // If we don't have listeners for this event, clean the DOM up. if ( !this.hasListeners( eventName ) ) { var nativeListeners = this.getCustomData( '_cke_nativeListeners' ); var listener = nativeListeners && nativeListeners[ eventName ]; if ( listener ) { if ( this.$.detachEvent ) this.$.detachEvent( 'on' + eventName, listener ); else if ( this.$.removeEventListener ) this.$.removeEventListener( eventName, listener, false ); delete nativeListeners[ eventName ]; } } }, /** * Removes any listener set on this object. * To avoid memory leaks we must assure that there are no * references left after the object is no longer needed. */ removeAllListeners : function() { var nativeListeners = this.getCustomData( '_cke_nativeListeners' ); for ( var eventName in nativeListeners ) { var listener = nativeListeners[ eventName ]; if ( this.$.detachEvent ) this.$.detachEvent( 'on' + eventName, listener ); else if ( this.$.removeEventListener ) this.$.removeEventListener( eventName, listener, false ); delete nativeListeners[ eventName ]; } } }; })(); (function( domObjectProto ) { var customData = {}; CKEDITOR.on( 'reset', function() { customData = {}; }); /** * Determines whether the specified object is equal to the current object. * @name CKEDITOR.dom.domObject.prototype.equals * @function * @param {Object} object The object to compare with the current object. * @returns {Boolean} "true" if the object is equal. * @example * var doc = new CKEDITOR.dom.document( document ); * alert( doc.equals( CKEDITOR.document ) ); // "true" * alert( doc == CKEDITOR.document ); // "false" */ domObjectProto.equals = function( object ) { return ( object && object.$ === this.$ ); }; /** * Sets a data slot value for this object. These values are shared by all * instances pointing to that same DOM object. * Note: The created data slot is only guarantied to be available on this unique dom node, * thus any wish to continue access it from other element clones (either created by clone node or from innerHtml) * will fail, for such usage, please use {@link CKEDITOR.dom.element::setAttribute} instead. * @name CKEDITOR.dom.domObject.prototype.setCustomData * @function * @param {String} key A key used to identify the data slot. * @param {Object} value The value to set to the data slot. * @returns {CKEDITOR.dom.domObject} This DOM object instance. * @see CKEDITOR.dom.domObject.prototype.getCustomData * @example * var element = new CKEDITOR.dom.element( 'span' ); * element.setCustomData( 'hasCustomData', true ); */ domObjectProto.setCustomData = function( key, value ) { var expandoNumber = this.getUniqueId(), dataSlot = customData[ expandoNumber ] || ( customData[ expandoNumber ] = {} ); dataSlot[ key ] = value; return this; }; /** * Gets the value set to a data slot in this object. * @name CKEDITOR.dom.domObject.prototype.getCustomData * @function * @param {String} key The key used to identify the data slot. * @returns {Object} This value set to the data slot. * @see CKEDITOR.dom.domObject.prototype.setCustomData * @example * var element = new CKEDITOR.dom.element( 'span' ); * alert( element.getCustomData( 'hasCustomData' ) ); // e.g. 'true' */ domObjectProto.getCustomData = function( key ) { var expandoNumber = this.$[ 'data-cke-expando' ], dataSlot = expandoNumber && customData[ expandoNumber ]; return dataSlot && dataSlot[ key ]; }; /** * @name CKEDITOR.dom.domObject.prototype.removeCustomData */ domObjectProto.removeCustomData = function( key ) { var expandoNumber = this.$[ 'data-cke-expando' ], dataSlot = expandoNumber && customData[ expandoNumber ], retval = dataSlot && dataSlot[ key ]; if ( typeof retval != 'undefined' ) delete dataSlot[ key ]; return retval || null; }; /** * Removes any data stored on this object. * To avoid memory leaks we must assure that there are no * references left after the object is no longer needed. * @name CKEDITOR.dom.domObject.prototype.clearCustomData * @function */ domObjectProto.clearCustomData = function() { // Clear all event listeners this.removeAllListeners(); var expandoNumber = this.$[ 'data-cke-expando' ]; expandoNumber && delete customData[ expandoNumber ]; }; /** * Gets an ID that can be used to identiquely identify this DOM object in * the running session. * @name CKEDITOR.dom.domObject.prototype.getUniqueId * @function * @returns {Number} A unique ID. */ domObjectProto.getUniqueId = function() { return this.$[ 'data-cke-expando' ] || ( this.$[ 'data-cke-expando' ] = CKEDITOR.tools.getNextNumber() ); }; // Implement CKEDITOR.event. CKEDITOR.event.implementOn( domObjectProto ); })( CKEDITOR.dom.domObject.prototype );