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 * This is a base class for classes and objects that require event handling 24 * features. 25 * @constructor 26 * @example 27 */ 28 CKEDITOR.event = function() 29 { 30 this._events = {}; 31 }; 32 33 CKEDITOR.event.implementOn = function( targetObject ) 34 { 35 CKEDITOR.event.call( targetObject ); 36 37 for ( var prop in CKEDITOR.event.prototype ) 38 { 39 if ( targetObject[ prop ] == undefined ) 40 targetObject[ prop ] = CKEDITOR.event.prototype[ prop ]; 41 } 42 }; 43 44 CKEDITOR.event.prototype = (function() 45 { 46 var eventEntry = function( eventName ) 47 { 48 this.name = eventName; 49 this.listeners = []; 50 } 51 52 eventEntry.prototype = 53 { 54 // Get the listener index for a specified function. 55 // Returns -1 if not found. 56 getListenerIndex : function( listenerFunction ) 57 { 58 for ( var i = 0, listeners = this.listeners ; i < listeners.length ; i++ ) 59 { 60 if ( listeners[i].fn == listenerFunction ) 61 return i; 62 } 63 return -1; 64 } 65 }; 66 67 return /** @lends CKEDITOR.event.prototype */ { 68 on : function( eventName, listenerFunction, scopeObj, listenerData, priority ) 69 { 70 // Get the event entry (create it if needed). 71 var event = this._events[ eventName ] || ( this._events[ eventName ] = new eventEntry( eventName ) ); 72 73 if ( event.getListenerIndex( listenerFunction ) < 0 ) 74 { 75 // Get the listeners. 76 var listeners = event.listeners; 77 78 // Fill the scope. 79 if ( !scopeObj ) 80 scopeObj = this; 81 82 // Default the priority, if needed. 83 if ( isNaN( priority ) ) 84 priority = 10; 85 86 // Create the function to be fired for this listener. 87 var listenerFirer = function( editor, publisherData, stopFn, cancelFn ) 88 { 89 var ev = 90 { 91 name : eventName, 92 sender : this, 93 editor : editor, 94 data : publisherData, 95 listenerData : listenerData, 96 stop : stopFn, 97 cancel : cancelFn 98 }; 99 100 listenerFunction.call( scopeObj, ev ); 101 102 return ev.data; 103 }; 104 listenerFirer.fn = listenerFunction; 105 listenerFirer.priority = priority; 106 107 // Search for the right position for this new listener, based on its 108 // priority. 109 for ( var i = listeners.length - 1 ; i >= 0 ; i-- ) 110 { 111 // Find the item which should be before the new one. 112 if ( listeners[ i ].priority <= priority ) 113 { 114 // Insert the listener in the array. 115 listeners.splice( i + 1, 0, listenerFirer ); 116 return; 117 } 118 } 119 120 // If no position has been found (or zero length), put it in 121 // the front of list. 122 listeners.unshift( listenerFirer ); 123 } 124 }, 125 126 fire : function( eventName, data, editor ) 127 { 128 // Create the function that marks the event as stopped. 129 var stopped = false; 130 var stopEvent = function() 131 { 132 stopped = true; 133 }; 134 135 // Create the function that marks the event as cancelled. 136 var cancelled = false; 137 var cancelEvent = function() 138 { 139 cancelled = true; 140 }; 141 142 // Get the event entry. 143 var event = this._events[ eventName ]; 144 145 if ( event ) 146 { 147 // Loop through all listeners. 148 for ( var i = 0, listeners = event.listeners ; i < listeners.length ; i++ ) 149 { 150 // Call the listener, passing the event data. 151 data = listeners[i].call( this, editor, data, stopEvent, cancelEvent ); 152 153 // No further calls is stopped or cancelled. 154 if ( stopped || cancelled ) 155 break; 156 } 157 } 158 159 return cancelled || ( typeof data == 'undefined' ? false : data ); 160 }, 161 162 fireOnce : function( eventName, data, editor ) 163 { 164 var ret = this.fire( eventName, editor, data ); 165 delete this._events[ eventName ]; 166 return ret; 167 }, 168 169 removeListener : function( eventName, listenerFunction ) 170 { 171 // Get the event entry. 172 var event = this._events[ eventName ]; 173 174 if ( event ) 175 { 176 var index = event.getListenerIndex( listenerFunction ); 177 if ( index >= 0 ) 178 event.listeners.splice( index, 1 ); 179 } 180 } 181 }; 182 })(); 183 184 // The following is for documentation purposes only. It documents a virtual 185 // CKEDITOR.eventInfo class that contains the defintions of event object passed 186 // to event listeners. 187 188 /** 189 * This class is not really part of the API. It just illustrates the features 190 * of the event object passed to event listeners by a {@link CKEDITOR.event} 191 * based object. 192 * @name CKEDITOR.eventInfo 193 * @constructor 194 */ 195 196 /** 197 * The event name. 198 * @name CKEDITOR.eventInfo.prototype.name 199 * @field 200 * @type String 201 */ 202 203 /** 204 * The object that publishes (sends) the event. 205 * @name CKEDITOR.eventInfo.prototype.sender 206 * @field 207 * @type Object 208 */ 209 210 /** 211 * The editor instance that holds the sender. May be the same as sender. May be 212 * null if the sender is not part of an editor instance, like a component 213 * running in standalone mode. 214 * @name CKEDITOR.eventInfo.prototype.editor 215 * @field 216 * @type CKEDITOR.editor 217 */ 218 219 /** 220 * Any kind of additional data. Its format and usage is event dependent. 221 * @name CKEDITOR.eventInfo.prototype.data 222 * @field 223 * @type Object 224 */ 225 226 /** 227 * Any extra data appended during the listener registration. 228 * @name CKEDITOR.eventInfo.prototype.listenerData 229 * @field 230 * @type Object 231 */ 232 233 /** 234 * Indicates that no further listeners are to be called. 235 * @name CKEDITOR.eventInfo.prototype.stop 236 * @function 237 */ 238 239 /** 240 * Indicates that the event is to be cancelled (if cancelable). 241 * @name CKEDITOR.eventInfo.prototype.cancel 242 * @function 243 */ 244