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