JasonWoof Got questions, comments, patches, etc.? Contact Jason Woofenden
vanilla ckeditor-3.2.2
[ckeditor.git] / _source / core / dom / domobject.js
1 /*\r
2 Copyright (c) 2003-2010, CKSource - Frederico Knabben. All rights reserved.\r
3 For licensing, see LICENSE.html or http://ckeditor.com/license\r
4 */\r
5 \r
6 /**\r
7  * @fileOverview Defines the {@link CKEDITOR.editor} class, which is the base\r
8  *              for other classes representing DOM objects.\r
9  */\r
10 \r
11 /**\r
12  * Represents a DOM object. This class is not intended to be used directly. It\r
13  * serves as the base class for other classes representing specific DOM\r
14  * objects.\r
15  * @constructor\r
16  * @param {Object} nativeDomObject A native DOM object.\r
17  * @augments CKEDITOR.event\r
18  * @example\r
19  */\r
20 CKEDITOR.dom.domObject = function( nativeDomObject )\r
21 {\r
22         if ( nativeDomObject )\r
23         {\r
24                 /**\r
25                  * The native DOM object represented by this class instance.\r
26                  * @type Object\r
27                  * @example\r
28                  * var element = new CKEDITOR.dom.element( 'span' );\r
29                  * alert( element.$.nodeType );  // "1"\r
30                  */\r
31                 this.$ = nativeDomObject;\r
32         }\r
33 };\r
34 \r
35 CKEDITOR.dom.domObject.prototype = (function()\r
36 {\r
37         // Do not define other local variables here. We want to keep the native\r
38         // listener closures as clean as possible.\r
39 \r
40         var getNativeListener = function( domObject, eventName )\r
41         {\r
42                 return function( domEvent )\r
43                 {\r
44                         // In FF, when reloading the page with the editor focused, it may\r
45                         // throw an error because the CKEDITOR global is not anymore\r
46                         // available. So, we check it here first. (#2923)\r
47                         if ( typeof CKEDITOR != 'undefined' )\r
48                                 domObject.fire( eventName, new CKEDITOR.dom.event( domEvent ) );\r
49                 };\r
50         };\r
51 \r
52         return /** @lends CKEDITOR.dom.domObject.prototype */ {\r
53 \r
54                 getPrivate : function()\r
55                 {\r
56                         var priv;\r
57 \r
58                         // Get the main private function from the custom data. Create it if not\r
59                         // defined.\r
60                         if ( !( priv = this.getCustomData( '_' ) ) )\r
61                                 this.setCustomData( '_', ( priv = {} ) );\r
62 \r
63                         return priv;\r
64                 },\r
65 \r
66                 /** @ignore */\r
67                 on  : function( eventName )\r
68                 {\r
69                         // We customize the "on" function here. The basic idea is that we'll have\r
70                         // only one listener for a native event, which will then call all listeners\r
71                         // set to the event.\r
72 \r
73                         // Get the listeners holder object.\r
74                         var nativeListeners = this.getCustomData( '_cke_nativeListeners' );\r
75 \r
76                         if ( !nativeListeners )\r
77                         {\r
78                                 nativeListeners = {};\r
79                                 this.setCustomData( '_cke_nativeListeners', nativeListeners );\r
80                         }\r
81 \r
82                         // Check if we have a listener for that event.\r
83                         if ( !nativeListeners[ eventName ] )\r
84                         {\r
85                                 var listener = nativeListeners[ eventName ] = getNativeListener( this, eventName );\r
86 \r
87                                 if ( this.$.addEventListener )\r
88                                         this.$.addEventListener( eventName, listener, !!CKEDITOR.event.useCapture );\r
89                                 else if ( this.$.attachEvent )\r
90                                         this.$.attachEvent( 'on' + eventName, listener );\r
91                         }\r
92 \r
93                         // Call the original implementation.\r
94                         return CKEDITOR.event.prototype.on.apply( this, arguments );\r
95                 },\r
96 \r
97                 /** @ignore */\r
98                 removeListener : function( eventName )\r
99                 {\r
100                         // Call the original implementation.\r
101                         CKEDITOR.event.prototype.removeListener.apply( this, arguments );\r
102 \r
103                         // If we don't have listeners for this event, clean the DOM up.\r
104                         if ( !this.hasListeners( eventName ) )\r
105                         {\r
106                                 var nativeListeners = this.getCustomData( '_cke_nativeListeners' );\r
107                                 var listener = nativeListeners && nativeListeners[ eventName ];\r
108                                 if ( listener )\r
109                                 {\r
110                                         if ( this.$.removeEventListener )\r
111                                                 this.$.removeEventListener( eventName, listener, false );\r
112                                         else if ( this.$.detachEvent )\r
113                                                 this.$.detachEvent( 'on' + eventName, listener );\r
114 \r
115                                         delete nativeListeners[ eventName ];\r
116                                 }\r
117                         }\r
118                 },\r
119 \r
120                 /**\r
121                  * Removes any listener set on this object.\r
122                  * To avoid memory leaks we must assure that there are no\r
123                  * references left after the object is no longer needed.\r
124                  */\r
125                 removeAllListeners : function()\r
126                 {\r
127                         var nativeListeners = this.getCustomData( '_cke_nativeListeners' );\r
128                         for ( var eventName in nativeListeners )\r
129                         {\r
130                                 var listener = nativeListeners[ eventName ];\r
131                                 if ( this.$.removeEventListener )\r
132                                         this.$.removeEventListener( eventName, listener, false );\r
133                                 else if ( this.$.detachEvent )\r
134                                         this.$.detachEvent( 'on' + eventName, listener );\r
135 \r
136                                 delete nativeListeners[ eventName ];\r
137                         }\r
138                 }\r
139         };\r
140 })();\r
141 \r
142 (function( domObjectProto )\r
143 {\r
144         var customData = {};\r
145 \r
146         CKEDITOR.on( 'reset', function()\r
147                 {\r
148                         customData = {};\r
149                 });\r
150 \r
151         /**\r
152          * Determines whether the specified object is equal to the current object.\r
153          * @name CKEDITOR.dom.domObject.prototype.equals\r
154          * @function\r
155          * @param {Object} object The object to compare with the current object.\r
156          * @returns {Boolean} "true" if the object is equal.\r
157          * @example\r
158          * var doc = new CKEDITOR.dom.document( document );\r
159          * alert( doc.equals( CKEDITOR.document ) );  // "true"\r
160          * alert( doc == CKEDITOR.document );         // "false"\r
161          */\r
162         domObjectProto.equals = function( object )\r
163         {\r
164                 return ( object && object.$ === this.$ );\r
165         };\r
166 \r
167         /**\r
168          * Sets a data slot value for this object. These values are shared by all\r
169          * instances pointing to that same DOM object.\r
170          * @name CKEDITOR.dom.domObject.prototype.setCustomData\r
171          * @function\r
172          * @param {String} key A key used to identify the data slot.\r
173          * @param {Object} value The value to set to the data slot.\r
174          * @returns {CKEDITOR.dom.domObject} This DOM object instance.\r
175          * @see CKEDITOR.dom.domObject.prototype.getCustomData\r
176          * @example\r
177          * var element = new CKEDITOR.dom.element( 'span' );\r
178          * element.setCustomData( 'hasCustomData', true );\r
179          */\r
180         domObjectProto.setCustomData = function( key, value )\r
181         {\r
182                 var expandoNumber = this.getUniqueId(),\r
183                         dataSlot = customData[ expandoNumber ] || ( customData[ expandoNumber ] = {} );\r
184 \r
185                 dataSlot[ key ] = value;\r
186 \r
187                 return this;\r
188         };\r
189 \r
190         /**\r
191          * Gets the value set to a data slot in this object.\r
192          * @name CKEDITOR.dom.domObject.prototype.getCustomData\r
193          * @function\r
194          * @param {String} key The key used to identify the data slot.\r
195          * @returns {Object} This value set to the data slot.\r
196          * @see CKEDITOR.dom.domObject.prototype.setCustomData\r
197          * @example\r
198          * var element = new CKEDITOR.dom.element( 'span' );\r
199          * alert( element.getCustomData( 'hasCustomData' ) );  // e.g. 'true'\r
200          */\r
201         domObjectProto.getCustomData = function( key )\r
202         {\r
203                 var expandoNumber = this.$._cke_expando,\r
204                         dataSlot = expandoNumber && customData[ expandoNumber ];\r
205 \r
206                 return dataSlot && dataSlot[ key ];\r
207         };\r
208 \r
209         /**\r
210          * @name CKEDITOR.dom.domObject.prototype.removeCustomData\r
211          */\r
212         domObjectProto.removeCustomData = function( key )\r
213         {\r
214                 var expandoNumber = this.$._cke_expando,\r
215                         dataSlot = expandoNumber && customData[ expandoNumber ],\r
216                         retval = dataSlot && dataSlot[ key ];\r
217 \r
218                 if ( typeof retval != 'undefined' )\r
219                         delete dataSlot[ key ];\r
220 \r
221                 return retval || null;\r
222         };\r
223 \r
224         /**\r
225          * Removes any data stored on this object.\r
226          * To avoid memory leaks we must assure that there are no\r
227          * references left after the object is no longer needed.\r
228          * @name CKEDITOR.dom.domObject.prototype.clearCustomData\r
229          * @function\r
230          */\r
231         domObjectProto.clearCustomData = function()\r
232         {\r
233                 // Clear all event listeners\r
234                 this.removeAllListeners();\r
235 \r
236                 var expandoNumber = this.$._cke_expando;\r
237                 expandoNumber && delete customData[ expandoNumber ];\r
238         };\r
239 \r
240         /**\r
241          * @name CKEDITOR.dom.domObject.prototype.getCustomData\r
242          */\r
243         domObjectProto.getUniqueId = function()\r
244         {\r
245                 return this.$._cke_expando || ( this.$._cke_expando = CKEDITOR.tools.getNextNumber() );\r
246         };\r
247 \r
248         // Implement CKEDITOR.event.\r
249         CKEDITOR.event.implementOn( domObjectProto );\r
250 \r
251 })( CKEDITOR.dom.domObject.prototype );\r