1 /**
  2  * A base support class for emitting events, based on the
  3  * <a href="http://nodejs.org/docs/latest/api/events.html">Event Emitter</a> class from node.js.
  4  * It supports registering event listeners for any type of event and later emitting events to
  5  * those listeners that registered for a specific type of event.
  6  * Typically, users of Content Studio would only use the instances of a class that are
  7  * provided by Content Studio, such as an action or the Content Studio instance.  And the
  8  * most common thing to do would be to register (or deregister) event listeners.
  9  * @constructor
 10  * @example
 11  * CS.on('some-event', function(args) {
 12  *   // do something.
 13  * });
 14  */
 15 function EventEmitter() {
 16   var listeners = [];
 17   var instance = this;
 18 
 19   /**
 20    * @ignore
 21    * Creates an <code>Event</code> instance.Functions can be attached to objects,
 22    * to be executed when an event is emitted. These functions are called listeners
 23    * @constructor
 24    * @param {String} type - type of the event
 25    * @param {Function} callback - function to be executed with this event occurs.
 26    * @param {Boolean} once - true if one time listener is added.
 27    */
 28   function Event(type, callback, once) {
 29     var eventInstance = this;
 30 
 31     eventInstance.type = type;
 32     eventInstance.callback = callback;
 33     eventInstance.once = once || false;
 34 
 35     /**
 36      * Fires an event with the given arguments.
 37      * @param args
 38      */
 39     eventInstance.fire = function() {
 40       eventInstance.callback.apply(eventInstance, arguments);
 41 
 42       if (eventInstance.once === true) {
 43         instance.removeListener(type, eventInstance.callback);
 44       }
 45     };
 46   };
 47   
 48   /** @ignore */
 49   var getIndexOf = function(type, callback) {
 50     var indexOf = -1;
 51     if (listeners.hasOwnProperty(type)) {
 52       listeners[type].forEach(function(currentListener, index) {
 53         if (currentListener.callback === callback && indexOf == -1) {
 54           indexOf = index;
 55         }
 56       });
 57     }
 58     
 59     return indexOf;
 60   };
 61 
 62   /**
 63    * Add a listener which will be called back. An alias for the <code>on()</code> function.
 64    */
 65   this.addListener = function(type, listener, once) {
 66     if (!listeners.hasOwnProperty(type)) {
 67       listeners[type] = [];
 68     }
 69 
 70     var indexOfListener = getIndexOf(type, listener);
 71     if(indexOfListener == -1) {
 72       listeners[type].push(new Event(type, listener, once));
 73     }
 74     else {
 75       listeners[type][indexOfListener].once = once || false;
 76     }
 77   };
 78 
 79   /**
 80    * Adds a one time listener for the <code>Event</code>. This listener is
 81    * invoked only the next time the event is fired, after which it is removed.
 82    * @param {String} type - type of the listener.
 83    * @param {Function} listener - the listener
 84    * @example
 85    * myemitter.once('something', function() {
 86    *   // will be called only for the first event
 87    * });
 88    */
 89   this.once = function(type, listener) {
 90     if (!listeners.hasOwnProperty(type)) {
 91       listeners[type] = [];
 92     }
 93 
 94     instance.addListener(type, listener, true);
 95   };
 96   
 97   /**
 98    * Stops a specified listener from receiving further events.
 99    * @param {String} type type of the listener
100    * @param {Function} listener the listener
101    */
102   this.removeListener = function(type, listener) {
103     if (listeners.hasOwnProperty(type)) {
104       listeners[type].forEach(function(currentListener, index) {
105         if (currentListener.callback === listener) {
106           listeners[type].splice(index, 1);
107         }
108       });
109     }
110 
111     if (listeners[type] && listeners[type].length == 0) {
112       delete listeners[type];
113     }
114 
115     return this;
116   };
117 
118   /**
119    * Removes all the listeners that are listening for a specific type
120    * of event.  Care should be taken not to disrupt other plug-ins
121    * when calling this on shared event emitters that you do not control
122    * yourself, for example the ones provided by Content Studio.
123    * @param {String} type the type of events that all listeners should be removed.
124    */
125   this.removeAllListeners = function(type) {
126     if (listeners.hasOwnProperty(type)) {
127       delete listeners[type];
128     }
129   };
130 
131   /**
132    * Emit an event of a specific type.  This will notify (invoke) each of the
133    * listeners in order with any the given arguments.
134    * @param {String} type The type of event to emit
135    * @param args Any additional arguments that should be passed to the listeners
136    */
137   this.emit = function(type, args) {
138     if (listeners.hasOwnProperty(type)) {
139       listeners[type].forEach(function(currentListener) {
140         return currentListener.fire(args);
141       });
142     }
143 
144     return this;
145   };
146 
147   /**
148    * Add a listener to this event emitter.  The listener will be called back
149    * the next time someone emits an event of the type specified by the
150    * <code>type</code> parameter.
151    * @function
152    * @param {String} type the type of events the listener is interested in
153    * @param {Function} listener the callback function that will be invoked when the event is emitted
154    * @param {Boolean} {once=false} true if the listener should only
155    *    be notified for the first event, false if the listener should
156    *    be notified for all events.
157    */
158   this.on = this.addListener;
159   
160 }
161