initial commit
[namibia] / public / scripts / ckeditor / _source / core / event.js
1 /*
2 Copyright (c) 2003-2011, CKSource - Frederico Knabben. All rights reserved.
3 For licensing, see LICENSE.html 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         /**
14          * Creates an event class instance. This constructor is rearely used, being
15          * the {@link #.implementOn} function used in class prototypes directly
16          * instead.
17          * @class This is a base class for classes and objects that require event
18          * handling features.<br />
19          * <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.
23          * @example
24          */
25         CKEDITOR.event = function()
26         {};
27
28         /**
29          * Implements the {@link CKEDITOR.event} features in an object.
30          * @param {Object} targetObject The object into which implement the features.
31          * @example
32          * var myObject = { message : 'Example' };
33          * <b>CKEDITOR.event.implementOn( myObject }</b>;
34          * myObject.on( 'testEvent', function()
35          *     {
36          *         alert( this.message );  // "Example"
37          *     });
38          * myObject.fire( 'testEvent' );
39          */
40         CKEDITOR.event.implementOn = function( targetObject )
41         {
42                 var eventProto = CKEDITOR.event.prototype;
43
44                 for ( var prop in eventProto )
45                 {
46                         if ( targetObject[ prop ] == undefined )
47                                 targetObject[ prop ] = eventProto[ prop ];
48                 }
49         };
50
51         CKEDITOR.event.prototype = (function()
52         {
53                 // Returns the private events object for a given object.
54                 var getPrivate = function( obj )
55                 {
56                         var _ = ( obj.getPrivate && obj.getPrivate() ) || obj._ || ( obj._ = {} );
57                         return _.events || ( _.events = {} );
58                 };
59
60                 var eventEntry = function( eventName )
61                 {
62                         this.name = eventName;
63                         this.listeners = [];
64                 };
65
66                 eventEntry.prototype =
67                 {
68                         // Get the listener index for a specified function.
69                         // Returns -1 if not found.
70                         getListenerIndex : function( listenerFunction )
71                         {
72                                 for ( var i = 0, listeners = this.listeners ; i < listeners.length ; i++ )
73                                 {
74                                         if ( listeners[i].fn == listenerFunction )
75                                                 return i;
76                                 }
77                                 return -1;
78                         }
79                 };
80
81                 return /** @lends CKEDITOR.event.prototype */ {
82                         /**
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
92                          *              listener.
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.
96                          * @example
97                          * someObject.on( 'someEvent', function()
98                          *     {
99                          *         alert( this == someObject );  // "true"
100                          *     });
101                          * @example
102                          * someObject.on( 'someEvent', function()
103                          *     {
104                          *         alert( this == anotherObject );  // "true"
105                          *     }
106                          *     , anotherObject );
107                          * @example
108                          * someObject.on( 'someEvent', function( event )
109                          *     {
110                          *         alert( event.listenerData );  // "Example"
111                          *     }
112                          *     , null, 'Example' );
113                          * @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
117                          */
118                         on : function( eventName, listenerFunction, scopeObj, listenerData, priority )
119                         {
120                                 // Get the event entry (create it if needed).
121                                 var events = getPrivate( this ),
122                                         event = events[ eventName ] || ( events[ eventName ] = new eventEntry( eventName ) );
123
124                                 if ( event.getListenerIndex( listenerFunction ) < 0 )
125                                 {
126                                         // Get the listeners.
127                                         var listeners = event.listeners;
128
129                                         // Fill the scope.
130                                         if ( !scopeObj )
131                                                 scopeObj = this;
132
133                                         // Default the priority, if needed.
134                                         if ( isNaN( priority ) )
135                                                 priority = 10;
136
137                                         var me = this;
138
139                                         // Create the function to be fired for this listener.
140                                         var listenerFirer = function( editor, publisherData, stopFn, cancelFn )
141                                         {
142                                                 var ev =
143                                                 {
144                                                         name : eventName,
145                                                         sender : this,
146                                                         editor : editor,
147                                                         data : publisherData,
148                                                         listenerData : listenerData,
149                                                         stop : stopFn,
150                                                         cancel : cancelFn,
151                                                         removeListener : function()
152                                                         {
153                                                                 me.removeListener( eventName, listenerFunction );
154                                                         }
155                                                 };
156
157                                                 listenerFunction.call( scopeObj, ev );
158
159                                                 return ev.data;
160                                         };
161                                         listenerFirer.fn = listenerFunction;
162                                         listenerFirer.priority = priority;
163
164                                         // Search for the right position for this new listener, based on its
165                                         // priority.
166                                         for ( var i = listeners.length - 1 ; i >= 0 ; i-- )
167                                         {
168                                                 // Find the item which should be before the new one.
169                                                 if ( listeners[ i ].priority <= priority )
170                                                 {
171                                                         // Insert the listener in the array.
172                                                         listeners.splice( i + 1, 0, listenerFirer );
173                                                         return;
174                                                 }
175                                         }
176
177                                         // If no position has been found (or zero length), put it in
178                                         // the front of list.
179                                         listeners.unshift( listenerFirer );
180                                 }
181                         },
182
183                         /**
184                          * Fires an specific event in the object. All registered listeners are
185                          * called at this point.
186                          * @function
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
190                          *              listeners.
191                          * @param {CKEDITOR.editor} [editor] The editor instance to send as the
192                          *              {@link CKEDITOR.eventInfo#editor} when calling the
193                          *              listener.
194                          * @returns {Boolean|Object} A booloan indicating that the event is to be
195                          *              canceled, or data returned by one of the listeners.
196                          * @example
197                          * someObject.on( 'someEvent', function() { ... } );
198                          * someObject.on( 'someEvent', function() { ... } );
199                          * <b>someObject.fire( 'someEvent' )</b>;  // both listeners are called
200                          * @example
201                          * someObject.on( 'someEvent', function( event )
202                          *     {
203                          *         alert( event.data );  // "Example"
204                          *     });
205                          * <b>someObject.fire( 'someEvent', 'Example' )</b>;
206                          */
207                         fire : (function()
208                         {
209                                 // Create the function that marks the event as stopped.
210                                 var stopped = false;
211                                 var stopEvent = function()
212                                 {
213                                         stopped = true;
214                                 };
215
216                                 // Create the function that marks the event as canceled.
217                                 var canceled = false;
218                                 var cancelEvent = function()
219                                 {
220                                         canceled = true;
221                                 };
222
223                                 return function( eventName, data, editor )
224                                 {
225                                         // Get the event entry.
226                                         var event = getPrivate( this )[ eventName ];
227
228                                         // Save the previous stopped and cancelled states. We may
229                                         // be nesting fire() calls.
230                                         var previousStopped = stopped,
231                                                 previousCancelled = canceled;
232
233                                         // Reset the stopped and canceled flags.
234                                         stopped = canceled = false;
235
236                                         if ( event )
237                                         {
238                                                 var listeners = event.listeners;
239
240                                                 if ( listeners.length )
241                                                 {
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 );
247
248                                                         // Loop through all listeners.
249                                                         for ( var i = 0 ; i < listeners.length ; i++ )
250                                                         {
251                                                                 // Call the listener, passing the event data.
252                                                                 var retData = listeners[i].call( this, editor, data, stopEvent, cancelEvent );
253
254                                                                 if ( typeof retData != 'undefined' )
255                                                                         data = retData;
256
257                                                                 // No further calls is stopped or canceled.
258                                                                 if ( stopped || canceled )
259                                                                         break;
260                                                         }
261                                                 }
262                                         }
263
264                                         var ret = canceled || ( typeof data == 'undefined' ? false : data );
265
266                                         // Restore the previous stopped and canceled states.
267                                         stopped = previousStopped;
268                                         canceled = previousCancelled;
269
270                                         return ret;
271                                 };
272                         })(),
273
274                         /**
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
281                          *              listeners.
282                          * @param {CKEDITOR.editor} [editor] The editor instance to send as the
283                          *              {@link CKEDITOR.eventInfo#editor} when calling the
284                          *              listener.
285                          * @returns {Boolean|Object} A booloan indicating that the event is to be
286                          *              canceled, or data returned by one of the listeners.
287                          * @example
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
292                          */
293                         fireOnce : function( eventName, data, editor )
294                         {
295                                 var ret = this.fire( eventName, data, editor );
296                                 delete getPrivate( this )[ eventName ];
297                                 return ret;
298                         },
299
300                         /**
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.
306                          * @example
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
312                          */
313                         removeListener : function( eventName, listenerFunction )
314                         {
315                                 // Get the event entry.
316                                 var event = getPrivate( this )[ eventName ];
317
318                                 if ( event )
319                                 {
320                                         var index = event.getListenerIndex( listenerFunction );
321                                         if ( index >= 0 )
322                                                 event.listeners.splice( index, 1 );
323                                 }
324                         },
325
326                         /**
327                          * Checks if there is any listener registered to a given event.
328                          * @param {String} eventName The event name.
329                          * @example
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"
334                          */
335                         hasListeners : function( eventName )
336                         {
337                                 var event = getPrivate( this )[ eventName ];
338                                 return ( event && event.listeners.length > 0 ) ;
339                         }
340                 };
341         })();
342 }