1 /* 2 * CKEditor - The text editor for Internet - http://ckeditor.com 3 * Copyright (C) 2003-2008 Frederico Caldeira Knabben 4 * 5 * == BEGIN LICENSE == 6 * 7 * Licensed under the terms of any of the following licenses at your 8 * choice: 9 * 10 * - GNU General Public License Version 2 or later (the "GPL") 11 * http://www.gnu.org/licenses/gpl.html 12 * 13 * - GNU Lesser General Public License Version 2.1 or later (the "LGPL") 14 * http://www.gnu.org/licenses/lgpl.html 15 * 16 * - Mozilla Public License Version 1.1 or later (the "MPL") 17 * http://www.mozilla.org/MPL/MPL-1.1.html 18 * 19 * == END LICENSE == 20 */ 21 22 /** 23 * @fileOverview Defines the {@link CKEDITOR.event} class, which serves as the 24 * base for classes and objects that require event handling features. 25 */ 26 27 /** 28 * This is a base class for classes and objects that require event handling 29 * features. 30 * @constructor 31 * @example 32 */ 33 CKEDITOR.event = function() 34 { 35 ( this._ || ( this._ = {} ) ).events = {}; 36 }; 37 38 /** 39 * Implements the {@link CKEDITOR.event} features in an object. 40 * @param {Object} targetObject The object in which implement the features. 41 * @type undefined 42 * @example 43 * var myObject = { message : 'Example' }; 44 * <b>CKEDITOR.event.implementOn( myObject }</b>; 45 * myObject.on( 'testevent', function() 46 * { 47 * alert( this.message ); // "Example" 48 * }); 49 * myObject.fire( 'testevent' ); 50 */ 51 CKEDITOR.event.implementOn = function( targetObject ) 52 { 53 CKEDITOR.event.call( targetObject ); 54 55 for ( var prop in CKEDITOR.event.prototype ) 56 { 57 if ( targetObject[ prop ] == undefined ) 58 targetObject[ prop ] = CKEDITOR.event.prototype[ prop ]; 59 } 60 }; 61 62 CKEDITOR.event.prototype = (function() 63 { 64 var eventEntry = function( eventName ) 65 { 66 this.name = eventName; 67 this.listeners = []; 68 }; 69 70 eventEntry.prototype = 71 { 72 // Get the listener index for a specified function. 73 // Returns -1 if not found. 74 getListenerIndex : function( listenerFunction ) 75 { 76 for ( var i = 0, listeners = this.listeners ; i < listeners.length ; i++ ) 77 { 78 if ( listeners[i].fn == listenerFunction ) 79 return i; 80 } 81 return -1; 82 } 83 }; 84 85 return /** @lends CKEDITOR.event.prototype */ { 86 /** 87 * Registers a listener to a specific event in the current object. 88 * @param {String} eventName The event name to which listen. 89 * @param {Function} listenerFunction The function listening to the 90 * event. 91 * @param {Object} [scopeObj] The object used to scope the listener 92 * call (the this object. If omitted, the current object is used. 93 * @param {Object} [listenerData] Data to be sent as the 94 * {@link CKEDITOR.eventInfo#listenerData} when calling the 95 * listener. 96 * @param {Number} [priority] The listener priority. Lower priority 97 * listeners are called first. Listeners with the same priority 98 * value are called in registration order. Defaults to 10. 99 * @type undefined 100 * @example 101 * someObject.on( 'someevent', function() 102 * { 103 * alert( this == someObject ); // "true" 104 * }); 105 * @example 106 * someObject.on( 'someevent', function() 107 * { 108 * alert( this == anotherObject ); // "true" 109 * } 110 * , anotherObject ); 111 * @example 112 * someObject.on( 'someevent', function( event ) 113 * { 114 * alert( event.listenerData ); // "Example" 115 * } 116 * , null, 'Example' ); 117 * @example 118 * someObject.on( 'someevent', function() { ... } ); // 2nd called 119 * someObject.on( 'someevent', function() { ... }, null, null, 100 ); // 3rd called 120 * someObject.on( 'someevent', function() { ... }, null, null, 1 ); // 1st called 121 */ 122 on : function( eventName, listenerFunction, scopeObj, listenerData, priority ) 123 { 124 // Get the event entry (create it if needed). 125 var event = this._.events[ eventName ] || ( this._.events[ eventName ] = new eventEntry( eventName ) ); 126 127 if ( event.getListenerIndex( listenerFunction ) < 0 ) 128 { 129 // Get the listeners. 130 var listeners = event.listeners; 131 132 // Fill the scope. 133 if ( !scopeObj ) 134 scopeObj = this; 135 136 // Default the priority, if needed. 137 if ( isNaN( priority ) ) 138 priority = 10; 139 140 // Create the function to be fired for this listener. 141 var listenerFirer = function( editor, publisherData, stopFn, cancelFn ) 142 { 143 var ev = 144 { 145 name : eventName, 146 sender : this, 147 editor : editor, 148 data : publisherData, 149 listenerData : listenerData, 150 stop : stopFn, 151 cancel : cancelFn 152 }; 153 154 listenerFunction.call( scopeObj, ev ); 155 156 return ev.data; 157 }; 158 listenerFirer.fn = listenerFunction; 159 listenerFirer.priority = priority; 160 161 // Search for the right position for this new listener, based on its 162 // priority. 163 for ( var i = listeners.length - 1 ; i >= 0 ; i-- ) 164 { 165 // Find the item which should be before the new one. 166 if ( listeners[ i ].priority <= priority ) 167 { 168 // Insert the listener in the array. 169 listeners.splice( i + 1, 0, listenerFirer ); 170 return; 171 } 172 } 173 174 // If no position has been found (or zero length), put it in 175 // the front of list. 176 listeners.unshift( listenerFirer ); 177 } 178 }, 179 180 /** 181 * Fires an specific event in the object. All registered listeners are 182 * called at this point. 183 * @param {String} eventName The event name to fire. 184 * @param {Object} [data] Data to be sent as the 185 * {@link CKEDITOR.eventInfo#data} when calling the 186 * listeners. 187 * @param {CKEDITOR.editor} [editor] The editor instance to send as the 188 * {@link CKEDITOR.eventInfo#editor} when calling the 189 * listener. 190 * @returns {Boolean|Object} A booloan indicating that the event is to be 191 * cancelled, or data returned by one of the listeners. 192 * @example 193 * someObject.on( 'someevent', function() { ... } ); 194 * someObject.on( 'someevent', function() { ... } ); 195 * <b>someObject.fire( 'someevent' )</b>; // both listeners are called 196 * @example 197 * someObject.on( 'someevent', function( event ) 198 * { 199 * alert( event.data ); // "Example" 200 * }); 201 * <b>someObject.fire( 'someevent', 'Example' )</b>; 202 */ 203 fire : function( eventName, data, editor ) 204 { 205 // Create the function that marks the event as stopped. 206 var stopped = false; 207 var stopEvent = function() 208 { 209 stopped = true; 210 }; 211 212 // Create the function that marks the event as cancelled. 213 var cancelled = false; 214 var cancelEvent = function() 215 { 216 cancelled = true; 217 }; 218 219 // Get the event entry. 220 var event = this._.events[ eventName ]; 221 222 if ( event ) 223 { 224 // Loop through all listeners. 225 for ( var i = 0, listeners = event.listeners ; i < listeners.length ; i++ ) 226 { 227 // Call the listener, passing the event data. 228 var retData = listeners[i].call( this, editor, data, stopEvent, cancelEvent ); 229 230 if ( typeof retData != 'undefined' ) 231 data = retData; 232 233 // No further calls is stopped or cancelled. 234 if ( stopped || cancelled ) 235 break; 236 } 237 } 238 239 return cancelled || ( typeof data == 'undefined' ? false : data ); 240 }, 241 242 /** 243 * Fires an specific event in the object, releasing all listeners 244 * registered to that event. The same listeners are not called again on 245 * successive calls of it or of {@link #fire}. 246 * @param {String} eventName The event name to fire. 247 * @param {Object} [data] Data to be sent as the 248 * {@link CKEDITOR.eventInfo#data} when calling the 249 * listeners. 250 * @param {CKEDITOR.editor} [editor] The editor instance to send as the 251 * {@link CKEDITOR.eventInfo#editor} when calling the 252 * listener. 253 * @returns {Boolean|Object} A booloan indicating that the event is to be 254 * cancelled, or data returned by one of the listeners. 255 * @example 256 * someObject.on( 'someevent', function() { ... } ); 257 * someObject.fire( 'someevent' ); // above listener called 258 * <b>someObject.fireOnce( 'someevent' )</b>; // above listener called 259 * someObject.fire( 'someevent' ); // no listeners called 260 */ 261 fireOnce : function( eventName, data, editor ) 262 { 263 var ret = this.fire( eventName, data, editor ); 264 delete this._.events[ eventName ]; 265 return ret; 266 }, 267 268 /** 269 * Unregisters a listener function from being called at the specified 270 * event. No errors are thrown if the listener has not been 271 * registered previously. 272 * @param {String} eventName The event name. 273 * @param {Function} listenerFunction The listener function to unregister. 274 * @type undefined 275 * @example 276 * var myListener = function() { ... }; 277 * someObject.on( 'someevent', myListener ); 278 * someObject.fire( 'someevent' ); // myListener called 279 * <b>someObject.removeListener( 'someevent', myListener )</b>; 280 * someObject.fire( 'someevent' ); // myListener not called 281 */ 282 removeListener : function( eventName, listenerFunction ) 283 { 284 // Get the event entry. 285 var event = this._.events[ eventName ]; 286 287 if ( event ) 288 { 289 var index = event.getListenerIndex( listenerFunction ); 290 if ( index >= 0 ) 291 event.listeners.splice( index, 1 ); 292 } 293 } 294 }; 295 })(); 296