2 Copyright (c) 2003-2010, 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 * This is a base class for classes and objects that require event handling
\r
19 CKEDITOR.event = function()
\r
23 * Implements the {@link CKEDITOR.event} features in an object.
\r
24 * @param {Object} targetObject The object in which implement the features.
\r
26 * var myObject = { message : 'Example' };
\r
27 * <b>CKEDITOR.event.implementOn( myObject }</b>;
\r
28 * myObject.on( 'testEvent', function()
\r
30 * alert( this.message ); // "Example"
\r
32 * myObject.fire( 'testEvent' );
\r
34 CKEDITOR.event.implementOn = function( targetObject, isTargetPrototype )
\r
36 var eventProto = CKEDITOR.event.prototype;
\r
38 for ( var prop in eventProto )
\r
40 if ( targetObject[ prop ] == undefined )
\r
41 targetObject[ prop ] = eventProto[ prop ];
\r
45 CKEDITOR.event.prototype = (function()
\r
47 // Returns the private events object for a given object.
\r
48 var getPrivate = function( obj )
\r
50 var _ = ( obj.getPrivate && obj.getPrivate() ) || obj._ || ( obj._ = {} );
\r
51 return _.events || ( _.events = {} );
\r
54 var eventEntry = function( eventName )
\r
56 this.name = eventName;
\r
57 this.listeners = [];
\r
60 eventEntry.prototype =
\r
62 // Get the listener index for a specified function.
\r
63 // Returns -1 if not found.
\r
64 getListenerIndex : function( listenerFunction )
\r
66 for ( var i = 0, listeners = this.listeners ; i < listeners.length ; i++ )
\r
68 if ( listeners[i].fn == listenerFunction )
\r
75 return /** @lends CKEDITOR.event.prototype */ {
\r
77 * Registers a listener to a specific event in the current object.
\r
78 * @param {String} eventName The event name to which listen.
\r
79 * @param {Function} listenerFunction The function listening to the
\r
80 * event. A single {@link CKEDITOR.eventInfo} object instanced
\r
81 * is passed to this function containing all the event data.
\r
82 * @param {Object} [scopeObj] The object used to scope the listener
\r
83 * call (the this object. If omitted, the current object is used.
\r
84 * @param {Object} [listenerData] Data to be sent as the
\r
85 * {@link CKEDITOR.eventInfo#listenerData} when calling the
\r
87 * @param {Number} [priority] The listener priority. Lower priority
\r
88 * listeners are called first. Listeners with the same priority
\r
89 * value are called in registration order. Defaults to 10.
\r
91 * someObject.on( 'someEvent', function()
\r
93 * alert( this == someObject ); // "true"
\r
96 * someObject.on( 'someEvent', function()
\r
98 * alert( this == anotherObject ); // "true"
\r
100 * , anotherObject );
\r
102 * someObject.on( 'someEvent', function( event )
\r
104 * alert( event.listenerData ); // "Example"
\r
106 * , null, 'Example' );
\r
108 * someObject.on( 'someEvent', function() { ... } ); // 2nd called
\r
109 * someObject.on( 'someEvent', function() { ... }, null, null, 100 ); // 3rd called
\r
110 * someObject.on( 'someEvent', function() { ... }, null, null, 1 ); // 1st called
\r
112 on : function( eventName, listenerFunction, scopeObj, listenerData, priority )
\r
114 // Get the event entry (create it if needed).
\r
115 var events = getPrivate( this ),
\r
116 event = events[ eventName ] || ( events[ eventName ] = new eventEntry( eventName ) );
\r
118 if ( event.getListenerIndex( listenerFunction ) < 0 )
\r
120 // Get the listeners.
\r
121 var listeners = event.listeners;
\r
127 // Default the priority, if needed.
\r
128 if ( isNaN( priority ) )
\r
133 // Create the function to be fired for this listener.
\r
134 var listenerFirer = function( editor, publisherData, stopFn, cancelFn )
\r
141 data : publisherData,
\r
142 listenerData : listenerData,
\r
145 removeListener : function()
\r
147 me.removeListener( eventName, listenerFunction );
\r
151 listenerFunction.call( scopeObj, ev );
\r
155 listenerFirer.fn = listenerFunction;
\r
156 listenerFirer.priority = priority;
\r
158 // Search for the right position for this new listener, based on its
\r
160 for ( var i = listeners.length - 1 ; i >= 0 ; i-- )
\r
162 // Find the item which should be before the new one.
\r
163 if ( listeners[ i ].priority <= priority )
\r
165 // Insert the listener in the array.
\r
166 listeners.splice( i + 1, 0, listenerFirer );
\r
171 // If no position has been found (or zero length), put it in
\r
172 // the front of list.
\r
173 listeners.unshift( listenerFirer );
\r
178 * Fires an specific event in the object. All registered listeners are
\r
179 * called at this point.
\r
181 * @param {String} eventName The event name to fire.
\r
182 * @param {Object} [data] Data to be sent as the
\r
183 * {@link CKEDITOR.eventInfo#data} when calling the
\r
185 * @param {CKEDITOR.editor} [editor] The editor instance to send as the
\r
186 * {@link CKEDITOR.eventInfo#editor} when calling the
\r
188 * @returns {Boolean|Object} A booloan indicating that the event is to be
\r
189 * canceled, or data returned by one of the listeners.
\r
191 * someObject.on( 'someEvent', function() { ... } );
\r
192 * someObject.on( 'someEvent', function() { ... } );
\r
193 * <b>someObject.fire( 'someEvent' )</b>; // both listeners are called
\r
195 * someObject.on( 'someEvent', function( event )
\r
197 * alert( event.data ); // "Example"
\r
199 * <b>someObject.fire( 'someEvent', 'Example' )</b>;
\r
203 // Create the function that marks the event as stopped.
\r
204 var stopped = false;
\r
205 var stopEvent = function()
\r
210 // Create the function that marks the event as canceled.
\r
211 var canceled = false;
\r
212 var cancelEvent = function()
\r
217 return function( eventName, data, editor )
\r
219 // Get the event entry.
\r
220 var event = getPrivate( this )[ eventName ];
\r
222 // Save the previous stopped and cancelled states. We may
\r
223 // be nesting fire() calls.
\r
224 var previousStopped = stopped,
\r
225 previousCancelled = canceled;
\r
227 // Reset the stopped and canceled flags.
\r
228 stopped = canceled = false;
\r
232 var listeners = event.listeners;
\r
234 if ( listeners.length )
\r
236 // As some listeners may remove themselves from the
\r
237 // event, the original array length is dinamic. So,
\r
238 // let's make a copy of all listeners, so we are
\r
239 // sure we'll call all of them.
\r
240 listeners = listeners.slice( 0 );
\r
242 // Loop through all listeners.
\r
243 for ( var i = 0 ; i < listeners.length ; i++ )
\r
245 // Call the listener, passing the event data.
\r
246 var retData = listeners[i].call( this, editor, data, stopEvent, cancelEvent );
\r
248 if ( typeof retData != 'undefined' )
\r
251 // No further calls is stopped or canceled.
\r
252 if ( stopped || canceled )
\r
258 var ret = canceled || ( typeof data == 'undefined' ? false : data );
\r
260 // Restore the previous stopped and canceled states.
\r
261 stopped = previousStopped;
\r
262 canceled = previousCancelled;
\r
269 * Fires an specific event in the object, releasing all listeners
\r
270 * registered to that event. The same listeners are not called again on
\r
271 * successive calls of it or of {@link #fire}.
\r
272 * @param {String} eventName The event name to fire.
\r
273 * @param {Object} [data] Data to be sent as the
\r
274 * {@link CKEDITOR.eventInfo#data} when calling the
\r
276 * @param {CKEDITOR.editor} [editor] The editor instance to send as the
\r
277 * {@link CKEDITOR.eventInfo#editor} when calling the
\r
279 * @returns {Boolean|Object} A booloan indicating that the event is to be
\r
280 * canceled, or data returned by one of the listeners.
\r
282 * someObject.on( 'someEvent', function() { ... } );
\r
283 * someObject.fire( 'someEvent' ); // above listener called
\r
284 * <b>someObject.fireOnce( 'someEvent' )</b>; // above listener called
\r
285 * someObject.fire( 'someEvent' ); // no listeners called
\r
287 fireOnce : function( eventName, data, editor )
\r
289 var ret = this.fire( eventName, data, editor );
\r
290 delete getPrivate( this )[ eventName ];
\r
295 * Unregisters a listener function from being called at the specified
\r
296 * event. No errors are thrown if the listener has not been
\r
297 * registered previously.
\r
298 * @param {String} eventName The event name.
\r
299 * @param {Function} listenerFunction The listener function to unregister.
\r
301 * var myListener = function() { ... };
\r
302 * someObject.on( 'someEvent', myListener );
\r
303 * someObject.fire( 'someEvent' ); // myListener called
\r
304 * <b>someObject.removeListener( 'someEvent', myListener )</b>;
\r
305 * someObject.fire( 'someEvent' ); // myListener not called
\r
307 removeListener : function( eventName, listenerFunction )
\r
309 // Get the event entry.
\r
310 var event = getPrivate( this )[ eventName ];
\r
314 var index = event.getListenerIndex( listenerFunction );
\r
316 event.listeners.splice( index, 1 );
\r
321 * Checks if there is any listener registered to a given event.
\r
322 * @param {String} eventName The event name.
\r
324 * var myListener = function() { ... };
\r
325 * someObject.on( 'someEvent', myListener );
\r
326 * alert( someObject.<b>hasListeners( 'someEvent' )</b> ); // "true"
\r
327 * alert( someObject.<b>hasListeners( 'noEvent' )</b> ); // "false"
\r
329 hasListeners : function( eventName )
\r
331 var event = getPrivate( this )[ eventName ];
\r
332 return ( event && event.listeners.length > 0 ) ;
\r