JasonWoof Got questions, comments, patches, etc.? Contact Jason Woofenden
vanilla ckeditor-3.6.3
[ckeditor.git] / _source / core / dom / domobject.js
1 /*\r
2 Copyright (c) 2003-2012, 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.$.detachEvent )\r
132                                         this.$.detachEvent( 'on' + eventName, listener );\r
133                                 else if ( this.$.removeEventListener )\r
134                                         this.$.removeEventListener( eventName, listener, false );\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          * <strong>Note:</strong> The created data slot is only guarantied to be available on this unique dom node,\r
171          * thus any wish to continue access it from other element clones (either created by clone node or from innerHtml)\r
172          * will fail, for such usage, please use {@link CKEDITOR.dom.element::setAttribute} instead.\r
173          * @name CKEDITOR.dom.domObject.prototype.setCustomData\r
174          * @function\r
175          * @param {String} key A key used to identify the data slot.\r
176          * @param {Object} value The value to set to the data slot.\r
177          * @returns {CKEDITOR.dom.domObject} This DOM object instance.\r
178          * @see CKEDITOR.dom.domObject.prototype.getCustomData\r
179          * @example\r
180          * var element = new CKEDITOR.dom.element( 'span' );\r
181          * element.setCustomData( 'hasCustomData', true );\r
182          */\r
183         domObjectProto.setCustomData = function( key, value )\r
184         {\r
185                 var expandoNumber = this.getUniqueId(),\r
186                         dataSlot = customData[ expandoNumber ] || ( customData[ expandoNumber ] = {} );\r
187 \r
188                 dataSlot[ key ] = value;\r
189 \r
190                 return this;\r
191         };\r
192 \r
193         /**\r
194          * Gets the value set to a data slot in this object.\r
195          * @name CKEDITOR.dom.domObject.prototype.getCustomData\r
196          * @function\r
197          * @param {String} key The key used to identify the data slot.\r
198          * @returns {Object} This value set to the data slot.\r
199          * @see CKEDITOR.dom.domObject.prototype.setCustomData\r
200          * @example\r
201          * var element = new CKEDITOR.dom.element( 'span' );\r
202          * alert( element.getCustomData( 'hasCustomData' ) );  // e.g. 'true'\r
203          */\r
204         domObjectProto.getCustomData = function( key )\r
205         {\r
206                 var expandoNumber = this.$[ 'data-cke-expando' ],\r
207                         dataSlot = expandoNumber && customData[ expandoNumber ];\r
208 \r
209                 return dataSlot && dataSlot[ key ];\r
210         };\r
211 \r
212         /**\r
213          * @name CKEDITOR.dom.domObject.prototype.removeCustomData\r
214          */\r
215         domObjectProto.removeCustomData = function( key )\r
216         {\r
217                 var expandoNumber = this.$[ 'data-cke-expando' ],\r
218                         dataSlot = expandoNumber && customData[ expandoNumber ],\r
219                         retval = dataSlot && dataSlot[ key ];\r
220 \r
221                 if ( typeof retval != 'undefined' )\r
222                         delete dataSlot[ key ];\r
223 \r
224                 return retval || null;\r
225         };\r
226 \r
227         /**\r
228          * Removes any data stored on this object.\r
229          * To avoid memory leaks we must assure that there are no\r
230          * references left after the object is no longer needed.\r
231          * @name CKEDITOR.dom.domObject.prototype.clearCustomData\r
232          * @function\r
233          */\r
234         domObjectProto.clearCustomData = function()\r
235         {\r
236                 // Clear all event listeners\r
237                 this.removeAllListeners();\r
238 \r
239                 var expandoNumber = this.$[ 'data-cke-expando' ];\r
240                 expandoNumber && delete customData[ expandoNumber ];\r
241         };\r
242 \r
243         /**\r
244          * Gets an ID that can be used to identiquely identify this DOM object in\r
245          * the running session.\r
246          * @name CKEDITOR.dom.domObject.prototype.getUniqueId\r
247          * @function\r
248          * @returns {Number} A unique ID.\r
249          */\r
250         domObjectProto.getUniqueId = function()\r
251         {\r
252                 return this.$[ 'data-cke-expando' ] || ( this.$[ 'data-cke-expando' ] = CKEDITOR.tools.getNextNumber() );\r
253         };\r
254 \r
255         // Implement CKEDITOR.event.\r
256         CKEDITOR.event.implementOn( domObjectProto );\r
257 \r
258 })( CKEDITOR.dom.domObject.prototype );\r