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