--- /dev/null
+/*\r
+Copyright (c) 2003-2011, CKSource - Frederico Knabben. All rights reserved.\r
+For licensing, see LICENSE.html or http://ckeditor.com/license\r
+*/\r
+\r
+/**\r
+ * @fileOverview Defines the {@link CKEDITOR.event} class, which serves as the\r
+ * base for classes and objects that require event handling features.\r
+ */\r
+\r
+if ( !CKEDITOR.event )\r
+{\r
+ /**\r
+ * Creates an event class instance. This constructor is rearely used, being\r
+ * the {@link #.implementOn} function used in class prototypes directly\r
+ * instead.\r
+ * @class This is a base class for classes and objects that require event\r
+ * handling features.<br />\r
+ * <br />\r
+ * Do not confuse this class with {@link CKEDITOR.dom.event} which is\r
+ * instead used for DOM events. The CKEDITOR.event class implements the\r
+ * internal event system used by the CKEditor to fire API related events.\r
+ * @example\r
+ */\r
+ CKEDITOR.event = function()\r
+ {};\r
+\r
+ /**\r
+ * Implements the {@link CKEDITOR.event} features in an object.\r
+ * @param {Object} targetObject The object into which implement the features.\r
+ * @example\r
+ * var myObject = { message : 'Example' };\r
+ * <b>CKEDITOR.event.implementOn( myObject }</b>;\r
+ * myObject.on( 'testEvent', function()\r
+ * {\r
+ * alert( this.message ); // "Example"\r
+ * });\r
+ * myObject.fire( 'testEvent' );\r
+ */\r
+ CKEDITOR.event.implementOn = function( targetObject )\r
+ {\r
+ var eventProto = CKEDITOR.event.prototype;\r
+\r
+ for ( var prop in eventProto )\r
+ {\r
+ if ( targetObject[ prop ] == undefined )\r
+ targetObject[ prop ] = eventProto[ prop ];\r
+ }\r
+ };\r
+\r
+ CKEDITOR.event.prototype = (function()\r
+ {\r
+ // Returns the private events object for a given object.\r
+ var getPrivate = function( obj )\r
+ {\r
+ var _ = ( obj.getPrivate && obj.getPrivate() ) || obj._ || ( obj._ = {} );\r
+ return _.events || ( _.events = {} );\r
+ };\r
+\r
+ var eventEntry = function( eventName )\r
+ {\r
+ this.name = eventName;\r
+ this.listeners = [];\r
+ };\r
+\r
+ eventEntry.prototype =\r
+ {\r
+ // Get the listener index for a specified function.\r
+ // Returns -1 if not found.\r
+ getListenerIndex : function( listenerFunction )\r
+ {\r
+ for ( var i = 0, listeners = this.listeners ; i < listeners.length ; i++ )\r
+ {\r
+ if ( listeners[i].fn == listenerFunction )\r
+ return i;\r
+ }\r
+ return -1;\r
+ }\r
+ };\r
+\r
+ return /** @lends CKEDITOR.event.prototype */ {\r
+ /**\r
+ * Registers a listener to a specific event in the current object.\r
+ * @param {String} eventName The event name to which listen.\r
+ * @param {Function} listenerFunction The function listening to the\r
+ * event. A single {@link CKEDITOR.eventInfo} object instanced\r
+ * is passed to this function containing all the event data.\r
+ * @param {Object} [scopeObj] The object used to scope the listener\r
+ * call (the this object. If omitted, the current object is used.\r
+ * @param {Object} [listenerData] Data to be sent as the\r
+ * {@link CKEDITOR.eventInfo#listenerData} when calling the\r
+ * listener.\r
+ * @param {Number} [priority] The listener priority. Lower priority\r
+ * listeners are called first. Listeners with the same priority\r
+ * value are called in registration order. Defaults to 10.\r
+ * @example\r
+ * someObject.on( 'someEvent', function()\r
+ * {\r
+ * alert( this == someObject ); // "true"\r
+ * });\r
+ * @example\r
+ * someObject.on( 'someEvent', function()\r
+ * {\r
+ * alert( this == anotherObject ); // "true"\r
+ * }\r
+ * , anotherObject );\r
+ * @example\r
+ * someObject.on( 'someEvent', function( event )\r
+ * {\r
+ * alert( event.listenerData ); // "Example"\r
+ * }\r
+ * , null, 'Example' );\r
+ * @example\r
+ * someObject.on( 'someEvent', function() { ... } ); // 2nd called\r
+ * someObject.on( 'someEvent', function() { ... }, null, null, 100 ); // 3rd called\r
+ * someObject.on( 'someEvent', function() { ... }, null, null, 1 ); // 1st called\r
+ */\r
+ on : function( eventName, listenerFunction, scopeObj, listenerData, priority )\r
+ {\r
+ // Get the event entry (create it if needed).\r
+ var events = getPrivate( this ),\r
+ event = events[ eventName ] || ( events[ eventName ] = new eventEntry( eventName ) );\r
+\r
+ if ( event.getListenerIndex( listenerFunction ) < 0 )\r
+ {\r
+ // Get the listeners.\r
+ var listeners = event.listeners;\r
+\r
+ // Fill the scope.\r
+ if ( !scopeObj )\r
+ scopeObj = this;\r
+\r
+ // Default the priority, if needed.\r
+ if ( isNaN( priority ) )\r
+ priority = 10;\r
+\r
+ var me = this;\r
+\r
+ // Create the function to be fired for this listener.\r
+ var listenerFirer = function( editor, publisherData, stopFn, cancelFn )\r
+ {\r
+ var ev =\r
+ {\r
+ name : eventName,\r
+ sender : this,\r
+ editor : editor,\r
+ data : publisherData,\r
+ listenerData : listenerData,\r
+ stop : stopFn,\r
+ cancel : cancelFn,\r
+ removeListener : function()\r
+ {\r
+ me.removeListener( eventName, listenerFunction );\r
+ }\r
+ };\r
+\r
+ listenerFunction.call( scopeObj, ev );\r
+\r
+ return ev.data;\r
+ };\r
+ listenerFirer.fn = listenerFunction;\r
+ listenerFirer.priority = priority;\r
+\r
+ // Search for the right position for this new listener, based on its\r
+ // priority.\r
+ for ( var i = listeners.length - 1 ; i >= 0 ; i-- )\r
+ {\r
+ // Find the item which should be before the new one.\r
+ if ( listeners[ i ].priority <= priority )\r
+ {\r
+ // Insert the listener in the array.\r
+ listeners.splice( i + 1, 0, listenerFirer );\r
+ return;\r
+ }\r
+ }\r
+\r
+ // If no position has been found (or zero length), put it in\r
+ // the front of list.\r
+ listeners.unshift( listenerFirer );\r
+ }\r
+ },\r
+\r
+ /**\r
+ * Fires an specific event in the object. All registered listeners are\r
+ * called at this point.\r
+ * @function\r
+ * @param {String} eventName The event name to fire.\r
+ * @param {Object} [data] Data to be sent as the\r
+ * {@link CKEDITOR.eventInfo#data} when calling the\r
+ * listeners.\r
+ * @param {CKEDITOR.editor} [editor] The editor instance to send as the\r
+ * {@link CKEDITOR.eventInfo#editor} when calling the\r
+ * listener.\r
+ * @returns {Boolean|Object} A booloan indicating that the event is to be\r
+ * canceled, or data returned by one of the listeners.\r
+ * @example\r
+ * someObject.on( 'someEvent', function() { ... } );\r
+ * someObject.on( 'someEvent', function() { ... } );\r
+ * <b>someObject.fire( 'someEvent' )</b>; // both listeners are called\r
+ * @example\r
+ * someObject.on( 'someEvent', function( event )\r
+ * {\r
+ * alert( event.data ); // "Example"\r
+ * });\r
+ * <b>someObject.fire( 'someEvent', 'Example' )</b>;\r
+ */\r
+ fire : (function()\r
+ {\r
+ // Create the function that marks the event as stopped.\r
+ var stopped = false;\r
+ var stopEvent = function()\r
+ {\r
+ stopped = true;\r
+ };\r
+\r
+ // Create the function that marks the event as canceled.\r
+ var canceled = false;\r
+ var cancelEvent = function()\r
+ {\r
+ canceled = true;\r
+ };\r
+\r
+ return function( eventName, data, editor )\r
+ {\r
+ // Get the event entry.\r
+ var event = getPrivate( this )[ eventName ];\r
+\r
+ // Save the previous stopped and cancelled states. We may\r
+ // be nesting fire() calls.\r
+ var previousStopped = stopped,\r
+ previousCancelled = canceled;\r
+\r
+ // Reset the stopped and canceled flags.\r
+ stopped = canceled = false;\r
+\r
+ if ( event )\r
+ {\r
+ var listeners = event.listeners;\r
+\r
+ if ( listeners.length )\r
+ {\r
+ // As some listeners may remove themselves from the\r
+ // event, the original array length is dinamic. So,\r
+ // let's make a copy of all listeners, so we are\r
+ // sure we'll call all of them.\r
+ listeners = listeners.slice( 0 );\r
+\r
+ // Loop through all listeners.\r
+ for ( var i = 0 ; i < listeners.length ; i++ )\r
+ {\r
+ // Call the listener, passing the event data.\r
+ var retData = listeners[i].call( this, editor, data, stopEvent, cancelEvent );\r
+\r
+ if ( typeof retData != 'undefined' )\r
+ data = retData;\r
+\r
+ // No further calls is stopped or canceled.\r
+ if ( stopped || canceled )\r
+ break;\r
+ }\r
+ }\r
+ }\r
+\r
+ var ret = canceled || ( typeof data == 'undefined' ? false : data );\r
+\r
+ // Restore the previous stopped and canceled states.\r
+ stopped = previousStopped;\r
+ canceled = previousCancelled;\r
+\r
+ return ret;\r
+ };\r
+ })(),\r
+\r
+ /**\r
+ * Fires an specific event in the object, releasing all listeners\r
+ * registered to that event. The same listeners are not called again on\r
+ * successive calls of it or of {@link #fire}.\r
+ * @param {String} eventName The event name to fire.\r
+ * @param {Object} [data] Data to be sent as the\r
+ * {@link CKEDITOR.eventInfo#data} when calling the\r
+ * listeners.\r
+ * @param {CKEDITOR.editor} [editor] The editor instance to send as the\r
+ * {@link CKEDITOR.eventInfo#editor} when calling the\r
+ * listener.\r
+ * @returns {Boolean|Object} A booloan indicating that the event is to be\r
+ * canceled, or data returned by one of the listeners.\r
+ * @example\r
+ * someObject.on( 'someEvent', function() { ... } );\r
+ * someObject.fire( 'someEvent' ); // above listener called\r
+ * <b>someObject.fireOnce( 'someEvent' )</b>; // above listener called\r
+ * someObject.fire( 'someEvent' ); // no listeners called\r
+ */\r
+ fireOnce : function( eventName, data, editor )\r
+ {\r
+ var ret = this.fire( eventName, data, editor );\r
+ delete getPrivate( this )[ eventName ];\r
+ return ret;\r
+ },\r
+\r
+ /**\r
+ * Unregisters a listener function from being called at the specified\r
+ * event. No errors are thrown if the listener has not been\r
+ * registered previously.\r
+ * @param {String} eventName The event name.\r
+ * @param {Function} listenerFunction The listener function to unregister.\r
+ * @example\r
+ * var myListener = function() { ... };\r
+ * someObject.on( 'someEvent', myListener );\r
+ * someObject.fire( 'someEvent' ); // myListener called\r
+ * <b>someObject.removeListener( 'someEvent', myListener )</b>;\r
+ * someObject.fire( 'someEvent' ); // myListener not called\r
+ */\r
+ removeListener : function( eventName, listenerFunction )\r
+ {\r
+ // Get the event entry.\r
+ var event = getPrivate( this )[ eventName ];\r
+\r
+ if ( event )\r
+ {\r
+ var index = event.getListenerIndex( listenerFunction );\r
+ if ( index >= 0 )\r
+ event.listeners.splice( index, 1 );\r
+ }\r
+ },\r
+\r
+ /**\r
+ * Checks if there is any listener registered to a given event.\r
+ * @param {String} eventName The event name.\r
+ * @example\r
+ * var myListener = function() { ... };\r
+ * someObject.on( 'someEvent', myListener );\r
+ * alert( someObject.<b>hasListeners( 'someEvent' )</b> ); // "true"\r
+ * alert( someObject.<b>hasListeners( 'noEvent' )</b> ); // "false"\r
+ */\r
+ hasListeners : function( eventName )\r
+ {\r
+ var event = getPrivate( this )[ eventName ];\r
+ return ( event && event.listeners.length > 0 ) ;\r
+ }\r
+ };\r
+ })();\r
+}\r