Source: EventEmitter.js

module.exports = EventEmitter;


function EventEmitter() {}


/**
 * Dependencies.
 */
var debug = require('debug')('JsSIP:EventEmitter');


function Event(type, sender, data) {
  this.type = type;
  this.sender= sender;
  this.data = data;
}


var C = {
  MAX_LISTENERS: 50
};


EventEmitter.prototype = {
  /**
   * Initialize events dictionaries.
   * -param {Array} events
   */
  initEvents: function(events) {
    var idx, length;

    this.maxListeners = C.MAX_LISTENERS;

    this.events = {};
    this.oneTimeListeners = {};

    length = events.length;
    for (idx = 0; idx < length; idx++) {
      this.events[events[idx]] = [];
      this.oneTimeListeners[events[idx]] = [];
    }
  },

  /**
   * Check whether an event exists or not.
   */
  checkEvent: function(event) {
    return !!this.events[event];
  },

  /**
   * Add a listener to the end of the listeners array for the specified event.
   */
  addListener: function(event, listener) {
    if (listener === undefined) {
      return;
    } else if (typeof listener !== 'function') {
      throw new Error('listener must be a function');
    } else if (!this.checkEvent(event)) {
      throw new Error('unable to add a listener to a nonexistent event ' + event);
    }

    if (this.events[event].length >= this.maxListeners) {
      debug('max listeners exceeded for event ' + event);
    }

    this.events[event].push(listener);
  },

  on: function(event, listener) {
    this.addListener(event, listener);
  },

  /**
   * Add a one time listener for the specified event.
   * The listener is invoked only the next time the event is fired, then it is removed.
   */
  once: function(event, listener) {
    this.on(event, listener);
    this.oneTimeListeners[event].push(listener);
  },

  /**
   * Remove a listener from the listener array for the specified event.
   * Note that the order of the array elements will change after removing the listener
   */
  removeListener: function(event, listener) {
    var events, length,
      idx = 0;

    if (listener === undefined) {
      return;
    } else if (typeof listener !== 'function') {
      throw new Error('listener must be a function');
    } else if (!this.checkEvent(event)) {
      throw new Error('unable to remove a listener from a nonexistent event'+ event);
    }

    events = this.events[event];
    length = events.length;

    while (idx < length) {
      if (events[idx] === listener) {
        events.splice(idx,1);
      } else {
        idx ++;
      }
    }
  },

  /**
   * Remove all listeners from the listener array for the specified event.
   */
  removeAllListener: function(event) {
    if (!this.checkEvent(event)) {
      throw new Error('unable to remove listeners from a nonexistent event'+ event);
    }

    this.events[event] = [];
    this.oneTimeListeners[event] = [];
  },

  /**
   * By default EventEmitter will print a warning
   * if more than C.MAX_LISTENERS listeners are added for a particular event.
   * This function allows that limit to be modified.
   */
  setMaxListeners: function(listeners) {
    if (typeof listeners !== 'number' || listeners < 0) {
      throw new Error('listeners must be a positive number');
    }

    this.maxListeners = listeners;
  },

  /**
   * Get the listeners for a specific event.
   */
  listeners: function(event) {
    if (!this.checkEvent(event)) {
      throw new Error('no event '+ event);
    }

    return this.events[event];
  },

  /**
   * Execute each of the listeners in order with the supplied arguments.
   */
  emit: function(event, sender, data) {
    var listeners, length, e, idx;

    if (!this.checkEvent(event)) {
      throw new Error('unable to emit a nonexistent event'+ event);
    }

    debug('emitting event '+ event);

    listeners = this.events[event];
    length = listeners.length;

    e = new Event(event, sender, data);

    listeners.map(function(listener) {
      return function() {
        listener.call(null, e);
      };
    }).forEach(function(callback) {
      try {
        callback();
      } catch(err) {
        debug(err.stack ? err.stack : err);
      }
    });

    // Remove one time listeners
    for (idx in this.oneTimeListeners[event]) {
      this.removeListener(event, this.oneTimeListeners[event][idx]);
    }

    this.oneTimeListeners[event] = [];
  }
};