]>
Commit | Line | Data |
---|---|---|
7adcb81e | 1 | /** |
3b35bd27 | 2 | * @license Copyright (c) 2003-2016, CKSource - Frederico Knabben. All rights reserved. |
7adcb81e IB |
3 | * For licensing, see LICENSE.md 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 | * | |
16 | * @class | |
17 | * @mixins CKEDITOR.event | |
18 | * @constructor Creates a domObject class instance. | |
19 | * @param {Object} nativeDomObject A native DOM object. | |
20 | */ | |
21 | CKEDITOR.dom.domObject = function( nativeDomObject ) { | |
22 | if ( nativeDomObject ) { | |
23 | /** | |
24 | * The native DOM object represented by this class instance. | |
25 | * | |
26 | * var element = new CKEDITOR.dom.element( 'span' ); | |
27 | * alert( element.$.nodeType ); // '1' | |
28 | * | |
29 | * @readonly | |
30 | * @property {Object} | |
31 | */ | |
32 | this.$ = nativeDomObject; | |
33 | } | |
34 | }; | |
35 | ||
36 | CKEDITOR.dom.domObject.prototype = ( function() { | |
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 | return function( domEvent ) { | |
42 | // In FF, when reloading the page with the editor focused, it may | |
43 | // throw an error because the CKEDITOR global is not anymore | |
44 | // available. So, we check it here first. (#2923) | |
45 | if ( typeof CKEDITOR != 'undefined' ) | |
46 | domObject.fire( eventName, new CKEDITOR.dom.event( domEvent ) ); | |
47 | }; | |
48 | }; | |
49 | ||
50 | return { | |
51 | ||
52 | /** | |
53 | * Gets the private `_` object which is bound to the native | |
54 | * DOM object using {@link #getCustomData}. | |
55 | * | |
56 | * var elementA = new CKEDITOR.dom.element( nativeElement ); | |
57 | * elementA.getPrivate().value = 1; | |
58 | * ... | |
59 | * var elementB = new CKEDITOR.dom.element( nativeElement ); | |
60 | * elementB.getPrivate().value; // 1 | |
61 | * | |
62 | * @returns {Object} The private object. | |
63 | */ | |
64 | getPrivate: function() { | |
65 | var priv; | |
66 | ||
67 | // Get the main private object from the custom data. Create it if not defined. | |
68 | if ( !( priv = this.getCustomData( '_' ) ) ) | |
69 | this.setCustomData( '_', ( priv = {} ) ); | |
70 | ||
71 | return priv; | |
72 | }, | |
73 | ||
74 | // Docs inherited from event. | |
75 | on: function( eventName ) { | |
76 | // We customize the "on" function here. The basic idea is that we'll have | |
77 | // only one listener for a native event, which will then call all listeners | |
78 | // set to the event. | |
79 | ||
80 | // Get the listeners holder object. | |
81 | var nativeListeners = this.getCustomData( '_cke_nativeListeners' ); | |
82 | ||
83 | if ( !nativeListeners ) { | |
84 | nativeListeners = {}; | |
85 | this.setCustomData( '_cke_nativeListeners', nativeListeners ); | |
86 | } | |
87 | ||
88 | // Check if we have a listener for that event. | |
89 | if ( !nativeListeners[ eventName ] ) { | |
90 | var listener = nativeListeners[ eventName ] = getNativeListener( this, eventName ); | |
91 | ||
92 | if ( this.$.addEventListener ) | |
93 | this.$.addEventListener( eventName, listener, !!CKEDITOR.event.useCapture ); | |
94 | else if ( this.$.attachEvent ) | |
95 | this.$.attachEvent( 'on' + eventName, listener ); | |
96 | } | |
97 | ||
98 | // Call the original implementation. | |
99 | return CKEDITOR.event.prototype.on.apply( this, arguments ); | |
100 | }, | |
101 | ||
102 | // Docs inherited from event. | |
103 | removeListener: function( eventName ) { | |
104 | // Call the original implementation. | |
105 | CKEDITOR.event.prototype.removeListener.apply( this, arguments ); | |
106 | ||
107 | // If we don't have listeners for this event, clean the DOM up. | |
108 | if ( !this.hasListeners( eventName ) ) { | |
109 | var nativeListeners = this.getCustomData( '_cke_nativeListeners' ); | |
110 | var listener = nativeListeners && nativeListeners[ eventName ]; | |
111 | if ( listener ) { | |
112 | if ( this.$.removeEventListener ) | |
113 | this.$.removeEventListener( eventName, listener, false ); | |
114 | else if ( this.$.detachEvent ) | |
115 | this.$.detachEvent( 'on' + eventName, listener ); | |
116 | ||
117 | delete nativeListeners[ eventName ]; | |
118 | } | |
119 | } | |
120 | }, | |
121 | ||
122 | /** | |
123 | * Removes any listener set on this object. | |
124 | * | |
125 | * To avoid memory leaks we must assure that there are no | |
126 | * references left after the object is no longer needed. | |
127 | */ | |
128 | removeAllListeners: function() { | |
129 | var nativeListeners = this.getCustomData( '_cke_nativeListeners' ); | |
130 | for ( var eventName in nativeListeners ) { | |
131 | var listener = nativeListeners[ eventName ]; | |
132 | if ( this.$.detachEvent ) | |
133 | this.$.detachEvent( 'on' + eventName, listener ); | |
134 | else if ( this.$.removeEventListener ) | |
135 | this.$.removeEventListener( eventName, listener, false ); | |
136 | ||
137 | delete nativeListeners[ eventName ]; | |
138 | } | |
139 | ||
140 | // Remove events from events object so fire() method will not call | |
141 | // listeners (#11400). | |
142 | CKEDITOR.event.prototype.removeAllListeners.call( this ); | |
143 | } | |
144 | }; | |
145 | } )(); | |
146 | ||
147 | ( function( domObjectProto ) { | |
148 | var customData = {}; | |
149 | ||
150 | CKEDITOR.on( 'reset', function() { | |
151 | customData = {}; | |
152 | } ); | |
153 | ||
154 | /** | |
155 | * Determines whether the specified object is equal to the current object. | |
156 | * | |
157 | * var doc = new CKEDITOR.dom.document( document ); | |
158 | * alert( doc.equals( CKEDITOR.document ) ); // true | |
159 | * alert( doc == CKEDITOR.document ); // false | |
160 | * | |
161 | * @param {Object} object The object to compare with the current object. | |
162 | * @returns {Boolean} `true` if the object is equal. | |
163 | */ | |
164 | domObjectProto.equals = function( object ) { | |
165 | // Try/Catch to avoid IE permission error when object is from different document. | |
166 | try { | |
167 | return ( object && object.$ === this.$ ); | |
168 | } catch ( er ) { | |
169 | return false; | |
170 | } | |
171 | }; | |
172 | ||
173 | /** | |
174 | * Sets a data slot value for this object. These values are shared by all | |
175 | * instances pointing to that same DOM object. | |
176 | * | |
177 | * **Note:** The created data slot is only guaranteed to be available on this unique DOM node, | |
178 | * thus any wish to continue access to it from other element clones (either created by | |
179 | * clone node or from `innerHtml`) will fail. For such usage please use | |
180 | * {@link CKEDITOR.dom.element#setAttribute} instead. | |
181 | * | |
182 | * **Note**: This method does not work on text nodes prior to Internet Explorer 9. | |
183 | * | |
184 | * var element = new CKEDITOR.dom.element( 'span' ); | |
185 | * element.setCustomData( 'hasCustomData', true ); | |
186 | * | |
187 | * @param {String} key A key used to identify the data slot. | |
188 | * @param {Object} value The value to set to the data slot. | |
189 | * @returns {CKEDITOR.dom.domObject} This DOM object instance. | |
190 | * @chainable | |
191 | */ | |
192 | domObjectProto.setCustomData = function( key, value ) { | |
193 | var expandoNumber = this.getUniqueId(), | |
194 | dataSlot = customData[ expandoNumber ] || ( customData[ expandoNumber ] = {} ); | |
195 | ||
196 | dataSlot[ key ] = value; | |
197 | ||
198 | return this; | |
199 | }; | |
200 | ||
201 | /** | |
202 | * Gets the value set to a data slot in this object. | |
203 | * | |
204 | * var element = new CKEDITOR.dom.element( 'span' ); | |
205 | * alert( element.getCustomData( 'hasCustomData' ) ); // e.g. 'true' | |
206 | * alert( element.getCustomData( 'nonExistingKey' ) ); // null | |
207 | * | |
208 | * @param {String} key The key used to identify the data slot. | |
209 | * @returns {Object} This value set to the data slot. | |
210 | */ | |
211 | domObjectProto.getCustomData = function( key ) { | |
212 | var expandoNumber = this.$[ 'data-cke-expando' ], | |
213 | dataSlot = expandoNumber && customData[ expandoNumber ]; | |
214 | ||
215 | return ( dataSlot && key in dataSlot ) ? dataSlot[ key ] : null; | |
216 | }; | |
217 | ||
218 | /** | |
219 | * Removes the value in the data slot under the given `key`. | |
220 | * | |
221 | * @param {String} key | |
222 | * @returns {Object} Removed value or `null` if not found. | |
223 | */ | |
224 | domObjectProto.removeCustomData = function( key ) { | |
225 | var expandoNumber = this.$[ 'data-cke-expando' ], | |
226 | dataSlot = expandoNumber && customData[ expandoNumber ], | |
227 | retval, hadKey; | |
228 | ||
229 | if ( dataSlot ) { | |
230 | retval = dataSlot[ key ]; | |
231 | hadKey = key in dataSlot; | |
232 | delete dataSlot[ key ]; | |
233 | } | |
234 | ||
235 | return hadKey ? retval : null; | |
236 | }; | |
237 | ||
238 | /** | |
239 | * Removes any data stored in this object. | |
240 | * To avoid memory leaks we must assure that there are no | |
241 | * references left after the object is no longer needed. | |
242 | */ | |
243 | domObjectProto.clearCustomData = function() { | |
244 | // Clear all event listeners | |
245 | this.removeAllListeners(); | |
246 | ||
247 | var expandoNumber = this.$[ 'data-cke-expando' ]; | |
248 | expandoNumber && delete customData[ expandoNumber ]; | |
249 | }; | |
250 | ||
251 | /** | |
252 | * Gets an ID that can be used to identify this DOM object in | |
253 | * the running session. | |
254 | * | |
255 | * **Note**: This method does not work on text nodes prior to Internet Explorer 9. | |
256 | * | |
257 | * @returns {Number} A unique ID. | |
258 | */ | |
259 | domObjectProto.getUniqueId = function() { | |
260 | return this.$[ 'data-cke-expando' ] || ( this.$[ 'data-cke-expando' ] = CKEDITOR.tools.getNextNumber() ); | |
261 | }; | |
262 | ||
263 | // Implement CKEDITOR.event. | |
264 | CKEDITOR.event.implementOn( domObjectProto ); | |
265 | ||
266 | } )( CKEDITOR.dom.domObject.prototype ); |