2 Copyright (c) 2003-2011, CKSource - Frederico Knabben. All rights reserved.
3 For licensing, see LICENSE.html or http://ckeditor.com/license
7 * @fileOverview Defines the {@link CKEDITOR.event} class, which serves as the
8 * base for classes and objects that require event handling features.
11 if ( !CKEDITOR.event )
14 * Creates an event class instance. This constructor is rearely used, being
15 * the {@link #.implementOn} function used in class prototypes directly
17 * @class This is a base class for classes and objects that require event
18 * handling features.<br />
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.
25 CKEDITOR.event = function()
29 * Implements the {@link CKEDITOR.event} features in an object.
30 * @param {Object} targetObject The object into which implement the features.
32 * var myObject = { message : 'Example' };
33 * <b>CKEDITOR.event.implementOn( myObject }</b>;
34 * myObject.on( 'testEvent', function()
36 * alert( this.message ); // "Example"
38 * myObject.fire( 'testEvent' );
40 CKEDITOR.event.implementOn = function( targetObject )
42 var eventProto = CKEDITOR.event.prototype;
44 for ( var prop in eventProto )
46 if ( targetObject[ prop ] == undefined )
47 targetObject[ prop ] = eventProto[ prop ];
51 CKEDITOR.event.prototype = (function()
53 // Returns the private events object for a given object.
54 var getPrivate = function( obj )
56 var _ = ( obj.getPrivate && obj.getPrivate() ) || obj._ || ( obj._ = {} );
57 return _.events || ( _.events = {} );
60 var eventEntry = function( eventName )
62 this.name = eventName;
66 eventEntry.prototype =
68 // Get the listener index for a specified function.
69 // Returns -1 if not found.
70 getListenerIndex : function( listenerFunction )
72 for ( var i = 0, listeners = this.listeners ; i < listeners.length ; i++ )
74 if ( listeners[i].fn == listenerFunction )
81 return /** @lends CKEDITOR.event.prototype */ {
83 * Registers a listener to a specific event in the current object.
84 * @param {String} eventName The event name to which listen.
85 * @param {Function} listenerFunction The function listening to the
86 * event. A single {@link CKEDITOR.eventInfo} object instanced
87 * is passed to this function containing all the event data.
88 * @param {Object} [scopeObj] The object used to scope the listener
89 * call (the this object. If omitted, the current object is used.
90 * @param {Object} [listenerData] Data to be sent as the
91 * {@link CKEDITOR.eventInfo#listenerData} when calling the
93 * @param {Number} [priority] The listener priority. Lower priority
94 * listeners are called first. Listeners with the same priority
95 * value are called in registration order. Defaults to 10.
97 * someObject.on( 'someEvent', function()
99 * alert( this == someObject ); // "true"
102 * someObject.on( 'someEvent', function()
104 * alert( this == anotherObject ); // "true"
108 * someObject.on( 'someEvent', function( event )
110 * alert( event.listenerData ); // "Example"
112 * , null, 'Example' );
114 * someObject.on( 'someEvent', function() { ... } ); // 2nd called
115 * someObject.on( 'someEvent', function() { ... }, null, null, 100 ); // 3rd called
116 * someObject.on( 'someEvent', function() { ... }, null, null, 1 ); // 1st called
118 on : function( eventName, listenerFunction, scopeObj, listenerData, priority )
120 // Get the event entry (create it if needed).
121 var events = getPrivate( this ),
122 event = events[ eventName ] || ( events[ eventName ] = new eventEntry( eventName ) );
124 if ( event.getListenerIndex( listenerFunction ) < 0 )
126 // Get the listeners.
127 var listeners = event.listeners;
133 // Default the priority, if needed.
134 if ( isNaN( priority ) )
139 // Create the function to be fired for this listener.
140 var listenerFirer = function( editor, publisherData, stopFn, cancelFn )
147 data : publisherData,
148 listenerData : listenerData,
151 removeListener : function()
153 me.removeListener( eventName, listenerFunction );
157 listenerFunction.call( scopeObj, ev );
161 listenerFirer.fn = listenerFunction;
162 listenerFirer.priority = priority;
164 // Search for the right position for this new listener, based on its
166 for ( var i = listeners.length - 1 ; i >= 0 ; i-- )
168 // Find the item which should be before the new one.
169 if ( listeners[ i ].priority <= priority )
171 // Insert the listener in the array.
172 listeners.splice( i + 1, 0, listenerFirer );
177 // If no position has been found (or zero length), put it in
178 // the front of list.
179 listeners.unshift( listenerFirer );
184 * Fires an specific event in the object. All registered listeners are
185 * called at this point.
187 * @param {String} eventName The event name to fire.
188 * @param {Object} [data] Data to be sent as the
189 * {@link CKEDITOR.eventInfo#data} when calling the
191 * @param {CKEDITOR.editor} [editor] The editor instance to send as the
192 * {@link CKEDITOR.eventInfo#editor} when calling the
194 * @returns {Boolean|Object} A booloan indicating that the event is to be
195 * canceled, or data returned by one of the listeners.
197 * someObject.on( 'someEvent', function() { ... } );
198 * someObject.on( 'someEvent', function() { ... } );
199 * <b>someObject.fire( 'someEvent' )</b>; // both listeners are called
201 * someObject.on( 'someEvent', function( event )
203 * alert( event.data ); // "Example"
205 * <b>someObject.fire( 'someEvent', 'Example' )</b>;
209 // Create the function that marks the event as stopped.
211 var stopEvent = function()
216 // Create the function that marks the event as canceled.
217 var canceled = false;
218 var cancelEvent = function()
223 return function( eventName, data, editor )
225 // Get the event entry.
226 var event = getPrivate( this )[ eventName ];
228 // Save the previous stopped and cancelled states. We may
229 // be nesting fire() calls.
230 var previousStopped = stopped,
231 previousCancelled = canceled;
233 // Reset the stopped and canceled flags.
234 stopped = canceled = false;
238 var listeners = event.listeners;
240 if ( listeners.length )
242 // As some listeners may remove themselves from the
243 // event, the original array length is dinamic. So,
244 // let's make a copy of all listeners, so we are
245 // sure we'll call all of them.
246 listeners = listeners.slice( 0 );
248 // Loop through all listeners.
249 for ( var i = 0 ; i < listeners.length ; i++ )
251 // Call the listener, passing the event data.
252 var retData = listeners[i].call( this, editor, data, stopEvent, cancelEvent );
254 if ( typeof retData != 'undefined' )
257 // No further calls is stopped or canceled.
258 if ( stopped || canceled )
264 var ret = canceled || ( typeof data == 'undefined' ? false : data );
266 // Restore the previous stopped and canceled states.
267 stopped = previousStopped;
268 canceled = previousCancelled;
275 * Fires an specific event in the object, releasing all listeners
276 * registered to that event. The same listeners are not called again on
277 * successive calls of it or of {@link #fire}.
278 * @param {String} eventName The event name to fire.
279 * @param {Object} [data] Data to be sent as the
280 * {@link CKEDITOR.eventInfo#data} when calling the
282 * @param {CKEDITOR.editor} [editor] The editor instance to send as the
283 * {@link CKEDITOR.eventInfo#editor} when calling the
285 * @returns {Boolean|Object} A booloan indicating that the event is to be
286 * canceled, or data returned by one of the listeners.
288 * someObject.on( 'someEvent', function() { ... } );
289 * someObject.fire( 'someEvent' ); // above listener called
290 * <b>someObject.fireOnce( 'someEvent' )</b>; // above listener called
291 * someObject.fire( 'someEvent' ); // no listeners called
293 fireOnce : function( eventName, data, editor )
295 var ret = this.fire( eventName, data, editor );
296 delete getPrivate( this )[ eventName ];
301 * Unregisters a listener function from being called at the specified
302 * event. No errors are thrown if the listener has not been
303 * registered previously.
304 * @param {String} eventName The event name.
305 * @param {Function} listenerFunction The listener function to unregister.
307 * var myListener = function() { ... };
308 * someObject.on( 'someEvent', myListener );
309 * someObject.fire( 'someEvent' ); // myListener called
310 * <b>someObject.removeListener( 'someEvent', myListener )</b>;
311 * someObject.fire( 'someEvent' ); // myListener not called
313 removeListener : function( eventName, listenerFunction )
315 // Get the event entry.
316 var event = getPrivate( this )[ eventName ];
320 var index = event.getListenerIndex( listenerFunction );
322 event.listeners.splice( index, 1 );
327 * Checks if there is any listener registered to a given event.
328 * @param {String} eventName The event name.
330 * var myListener = function() { ... };
331 * someObject.on( 'someEvent', myListener );
332 * alert( someObject.<b>hasListeners( 'someEvent' )</b> ); // "true"
333 * alert( someObject.<b>hasListeners( 'noEvent' )</b> ); // "false"
335 hasListeners : function( eventName )
337 var event = getPrivate( this )[ eventName ];
338 return ( event && event.listeners.length > 0 ) ;