]> git.immae.eu Git - perso/Immae/Projets/packagist/connexionswing-ckeditor-component.git/blob - sources/core/event.js
Upgrade to 4.5.7 and add some plugin
[perso/Immae/Projets/packagist/connexionswing-ckeditor-component.git] / sources / core / event.js
1 /**
2 * @license Copyright (c) 2003-2016, CKSource - Frederico Knabben. All rights reserved.
3 * For licensing, see LICENSE.md or http://ckeditor.com/license
4 */
5
6 /**
7 * @fileOverview Defines the {@link CKEDITOR.event} class, which serves as the
8 * base for classes and objects that require event handling features.
9 */
10
11 if ( !CKEDITOR.event ) {
12 /**
13 * Creates an event class instance. This constructor is rarely used, being
14 * the {@link #implementOn} function used in class prototypes directly
15 * instead.
16 *
17 * This is a base class for classes and objects that require event
18 * handling features.
19 *
20 * Do not confuse this class with {@link CKEDITOR.dom.event} which is
21 * instead used for DOM events. The CKEDITOR.event class implements the
22 * internal event system used by the CKEditor to fire API related events.
23 *
24 * @class
25 * @constructor Creates an event class instance.
26 */
27 CKEDITOR.event = function() {};
28
29 /**
30 * Implements the {@link CKEDITOR.event} features in an object.
31 *
32 * var myObject = { message: 'Example' };
33 * CKEDITOR.event.implementOn( myObject );
34 *
35 * myObject.on( 'testEvent', function() {
36 * alert( this.message );
37 * } );
38 * myObject.fire( 'testEvent' ); // 'Example'
39 *
40 * @static
41 * @param {Object} targetObject The object into which implement the features.
42 */
43 CKEDITOR.event.implementOn = function( targetObject ) {
44 var eventProto = CKEDITOR.event.prototype;
45
46 for ( var prop in eventProto ) {
47 if ( targetObject[ prop ] == null )
48 targetObject[ prop ] = eventProto[ prop ];
49 }
50 };
51
52 CKEDITOR.event.prototype = ( function() {
53 // Returns the private events object for a given object.
54 var getPrivate = function( obj ) {
55 var _ = ( obj.getPrivate && obj.getPrivate() ) || obj._ || ( obj._ = {} );
56 return _.events || ( _.events = {} );
57 };
58
59 var eventEntry = function( eventName ) {
60 this.name = eventName;
61 this.listeners = [];
62 };
63
64 eventEntry.prototype = {
65 // Get the listener index for a specified function.
66 // Returns -1 if not found.
67 getListenerIndex: function( listenerFunction ) {
68 for ( var i = 0, listeners = this.listeners; i < listeners.length; i++ ) {
69 if ( listeners[ i ].fn == listenerFunction )
70 return i;
71 }
72 return -1;
73 }
74 };
75
76 // Retrieve the event entry on the event host (create it if needed).
77 function getEntry( name ) {
78 // Get the event entry (create it if needed).
79 var events = getPrivate( this );
80 return events[ name ] || ( events[ name ] = new eventEntry( name ) );
81 }
82
83 return {
84 /**
85 * Predefine some intrinsic properties on a specific event name.
86 *
87 * @param {String} name The event name
88 * @param meta
89 * @param [meta.errorProof=false] Whether the event firing should catch error thrown from a per listener call.
90 */
91 define: function( name, meta ) {
92 var entry = getEntry.call( this, name );
93 CKEDITOR.tools.extend( entry, meta, true );
94 },
95
96 /**
97 * Registers a listener to a specific event in the current object.
98 *
99 * someObject.on( 'someEvent', function() {
100 * alert( this == someObject ); // true
101 * } );
102 *
103 * someObject.on( 'someEvent', function() {
104 * alert( this == anotherObject ); // true
105 * }, anotherObject );
106 *
107 * someObject.on( 'someEvent', function( event ) {
108 * alert( event.listenerData ); // 'Example'
109 * }, null, 'Example' );
110 *
111 * someObject.on( 'someEvent', function() { ... } ); // 2nd called
112 * someObject.on( 'someEvent', function() { ... }, null, null, 100 ); // 3rd called
113 * someObject.on( 'someEvent', function() { ... }, null, null, 1 ); // 1st called
114 *
115 * @param {String} eventName The event name to which listen.
116 * @param {Function} listenerFunction The function listening to the
117 * event. A single {@link CKEDITOR.eventInfo} object instanced
118 * is passed to this function containing all the event data.
119 * @param {Object} [scopeObj] The object used to scope the listener
120 * call (the `this` object). If omitted, the current object is used.
121 * @param {Object} [listenerData] Data to be sent as the
122 * {@link CKEDITOR.eventInfo#listenerData} when calling the
123 * listener.
124 * @param {Number} [priority=10] The listener priority. Lower priority
125 * listeners are called first. Listeners with the same priority
126 * value are called in registration order.
127 * @returns {Object} An object containing the `removeListener`
128 * function, which can be used to remove the listener at any time.
129 */
130 on: function( eventName, listenerFunction, scopeObj, listenerData, priority ) {
131 // Create the function to be fired for this listener.
132 function listenerFirer( editor, publisherData, stopFn, cancelFn ) {
133 var ev = {
134 name: eventName,
135 sender: this,
136 editor: editor,
137 data: publisherData,
138 listenerData: listenerData,
139 stop: stopFn,
140 cancel: cancelFn,
141 removeListener: removeListener
142 };
143
144 var ret = listenerFunction.call( scopeObj, ev );
145
146 return ret === false ? false : ev.data;
147 }
148
149 function removeListener() {
150 me.removeListener( eventName, listenerFunction );
151 }
152
153 var event = getEntry.call( this, eventName );
154
155 if ( event.getListenerIndex( listenerFunction ) < 0 ) {
156 // Get the listeners.
157 var listeners = event.listeners;
158
159 // Fill the scope.
160 if ( !scopeObj )
161 scopeObj = this;
162
163 // Default the priority, if needed.
164 if ( isNaN( priority ) )
165 priority = 10;
166
167 var me = this;
168
169 listenerFirer.fn = listenerFunction;
170 listenerFirer.priority = priority;
171
172 // Search for the right position for this new listener, based on its
173 // priority.
174 for ( var i = listeners.length - 1; i >= 0; i-- ) {
175 // Find the item which should be before the new one.
176 if ( listeners[ i ].priority <= priority ) {
177 // Insert the listener in the array.
178 listeners.splice( i + 1, 0, listenerFirer );
179 return { removeListener: removeListener };
180 }
181 }
182
183 // If no position has been found (or zero length), put it in
184 // the front of list.
185 listeners.unshift( listenerFirer );
186 }
187
188 return { removeListener: removeListener };
189 },
190
191 /**
192 * Similiar with {@link #on} but the listener will be called only once upon the next event firing.
193 *
194 * @see CKEDITOR.event#on
195 */
196 once: function() {
197 var args = Array.prototype.slice.call( arguments ),
198 fn = args[ 1 ];
199
200 args[ 1 ] = function( evt ) {
201 evt.removeListener();
202 return fn.apply( this, arguments );
203 };
204
205 return this.on.apply( this, args );
206 },
207
208 /**
209 * @static
210 * @property {Boolean} useCapture
211 * @todo
212 */
213
214 /**
215 * Register event handler under the capturing stage on supported target.
216 */
217 capture: function() {
218 CKEDITOR.event.useCapture = 1;
219 var retval = this.on.apply( this, arguments );
220 CKEDITOR.event.useCapture = 0;
221 return retval;
222 },
223
224 /**
225 * Fires an specific event in the object. All registered listeners are
226 * called at this point.
227 *
228 * someObject.on( 'someEvent', function() { ... } );
229 * someObject.on( 'someEvent', function() { ... } );
230 * someObject.fire( 'someEvent' ); // Both listeners are called.
231 *
232 * someObject.on( 'someEvent', function( event ) {
233 * alert( event.data ); // 'Example'
234 * } );
235 * someObject.fire( 'someEvent', 'Example' );
236 *
237 * @method
238 * @param {String} eventName The event name to fire.
239 * @param {Object} [data] Data to be sent as the
240 * {@link CKEDITOR.eventInfo#data} when calling the listeners.
241 * @param {CKEDITOR.editor} [editor] The editor instance to send as the
242 * {@link CKEDITOR.eventInfo#editor} when calling the listener.
243 * @returns {Boolean/Object} A boolean indicating that the event is to be
244 * canceled, or data returned by one of the listeners.
245 */
246 fire: ( function() {
247 // Create the function that marks the event as stopped.
248 var stopped = 0;
249 var stopEvent = function() {
250 stopped = 1;
251 };
252
253 // Create the function that marks the event as canceled.
254 var canceled = 0;
255 var cancelEvent = function() {
256 canceled = 1;
257 };
258
259 return function( eventName, data, editor ) {
260 // Get the event entry.
261 var event = getPrivate( this )[ eventName ];
262
263 // Save the previous stopped and cancelled states. We may
264 // be nesting fire() calls.
265 var previousStopped = stopped,
266 previousCancelled = canceled;
267
268 // Reset the stopped and canceled flags.
269 stopped = canceled = 0;
270
271 if ( event ) {
272 var listeners = event.listeners;
273
274 if ( listeners.length ) {
275 // As some listeners may remove themselves from the
276 // event, the original array length is dinamic. So,
277 // let's make a copy of all listeners, so we are
278 // sure we'll call all of them.
279 listeners = listeners.slice( 0 );
280
281 var retData;
282 // Loop through all listeners.
283 for ( var i = 0; i < listeners.length; i++ ) {
284 // Call the listener, passing the event data.
285 if ( event.errorProof ) {
286 try {
287 retData = listeners[ i ].call( this, editor, data, stopEvent, cancelEvent );
288 } catch ( er ) {}
289 } else {
290 retData = listeners[ i ].call( this, editor, data, stopEvent, cancelEvent );
291 }
292
293 if ( retData === false )
294 canceled = 1;
295 else if ( typeof retData != 'undefined' )
296 data = retData;
297
298 // No further calls is stopped or canceled.
299 if ( stopped || canceled )
300 break;
301 }
302 }
303 }
304
305 var ret = canceled ? false : ( typeof data == 'undefined' ? true : data );
306
307 // Restore the previous stopped and canceled states.
308 stopped = previousStopped;
309 canceled = previousCancelled;
310
311 return ret;
312 };
313 } )(),
314
315 /**
316 * Fires an specific event in the object, releasing all listeners
317 * registered to that event. The same listeners are not called again on
318 * successive calls of it or of {@link #fire}.
319 *
320 * someObject.on( 'someEvent', function() { ... } );
321 * someObject.fire( 'someEvent' ); // Above listener called.
322 * someObject.fireOnce( 'someEvent' ); // Above listener called.
323 * someObject.fire( 'someEvent' ); // No listeners called.
324 *
325 * @param {String} eventName The event name to fire.
326 * @param {Object} [data] Data to be sent as the
327 * {@link CKEDITOR.eventInfo#data} when calling the listeners.
328 * @param {CKEDITOR.editor} [editor] The editor instance to send as the
329 * {@link CKEDITOR.eventInfo#editor} when calling the listener.
330 * @returns {Boolean/Object} A booloan indicating that the event is to be
331 * canceled, or data returned by one of the listeners.
332 */
333 fireOnce: function( eventName, data, editor ) {
334 var ret = this.fire( eventName, data, editor );
335 delete getPrivate( this )[ eventName ];
336 return ret;
337 },
338
339 /**
340 * Unregisters a listener function from being called at the specified
341 * event. No errors are thrown if the listener has not been registered previously.
342 *
343 * var myListener = function() { ... };
344 * someObject.on( 'someEvent', myListener );
345 * someObject.fire( 'someEvent' ); // myListener called.
346 * someObject.removeListener( 'someEvent', myListener );
347 * someObject.fire( 'someEvent' ); // myListener not called.
348 *
349 * @param {String} eventName The event name.
350 * @param {Function} listenerFunction The listener function to unregister.
351 */
352 removeListener: function( eventName, listenerFunction ) {
353 // Get the event entry.
354 var event = getPrivate( this )[ eventName ];
355
356 if ( event ) {
357 var index = event.getListenerIndex( listenerFunction );
358 if ( index >= 0 )
359 event.listeners.splice( index, 1 );
360 }
361 },
362
363 /**
364 * Remove all existing listeners on this object, for cleanup purpose.
365 */
366 removeAllListeners: function() {
367 var events = getPrivate( this );
368 for ( var i in events )
369 delete events[ i ];
370 },
371
372 /**
373 * Checks if there is any listener registered to a given event.
374 *
375 * var myListener = function() { ... };
376 * someObject.on( 'someEvent', myListener );
377 * alert( someObject.hasListeners( 'someEvent' ) ); // true
378 * alert( someObject.hasListeners( 'noEvent' ) ); // false
379 *
380 * @param {String} eventName The event name.
381 * @returns {Boolean}
382 */
383 hasListeners: function( eventName ) {
384 var event = getPrivate( this )[ eventName ];
385 return ( event && event.listeners.length > 0 );
386 }
387 };
388 } )();
389 }