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