Source: events/EventDispatcher.js

// (c) Andrew Wei

'use strict';

import assert from'../helpers/assert';
import assertType from'../helpers/assertType';

/**
 * @class
 *
 * Custom event dispatcher object.
 *
 * @alias module:requiem~events.EventDispatcher
 */
class EventDispatcher {
  /**
   * Creates a new EventDispatcher instance.
   *
   * @return {EventDispatcher} A new EventDispatcher instance.
   */
  constructor() {
    this.__defineProperties__();
  }

  /**
   * Adds an event listener to this EventDispatcher instance.
   *
   * @param {string} type
   * @param {Function} listener
   */
  addEventListener(type, listener) {
    if (!assertType(type, 'string', false, 'Invalid parameter: type')) return;
    if (!assertType(listener, 'function', false, 'Invalid parameter: listener')) return;

    if (!this.__private__.listenerRegistry[type]) {
      this.__private__.listenerRegistry[type] = [];
    }

    this.__private__.listenerRegistry[type].push(listener);
  }

  /** @see module:requiem~events.EventDispatcher#addEventListener */
  on() { this.addEventListener.apply(this, arguments); }

  /**
   * Removes an event listener from this EventDispatcher instance. If no
   * listener method is specified, all the listeners of the specified type
   * will be removed.
   *
   * @param {string} type
   * @param {Function} listener:undefined
   */
  removeEventListener(type, listener) {
    if (!assertType(type, 'string', false, 'Invalid parameter: type')) return;
    if (!assertType(listener, 'function', true, 'Invalid parameter: listener')) return;
    if (!assert(this.__private__.listenerRegistry, 'Listener map is null.')) return;
    if (!assert(this.__private__.listenerRegistry[type], 'There are no listeners registered for event type: ' + type)) return;

    if (listener) {
      let index = this.__private__.listenerRegistry[type].indexOf(listener);

      if (index > -1) {
        this.__private__.listenerRegistry[type].splice(index, 1);
      }
    } else {
      delete this.__private__.listenerRegistry[type];
    }
  }

  /** @see module:requiem~events.EventDispatcher#removeEventListener */
  off() { this.removeEventListener.apply(this, arguments); }

  /**
   * Removes all cached event listeners from this Element instance.
   */
  removeAllEventListeners() {
    if (this.__private__.listenerRegistry) {
      for (let event in this.__private__.listenerRegistry) {
        this.removeEventListener(event);
      }
    }
  }

  /**
   * Determines whether this EventDispatcher instance has a specific event
   * listener registered. If no listener is specified, it will check if any
   * listener of the specified event type is registered.
   *
   * @param {string} type
   * @param {Function} [listener]
   *
   * @return {boolean}
   */
  hasEventListener(type, listener) {
    if (!assertType(type, 'string', false, 'Invalid parameter: type')) return;
    if (!assertType(listener, 'function', true, 'Invalid parameter: listener')) return;
    if (!assert(this.__private__.listenerRegistry, 'Listener map is null.')) return;
    if (!assert(this.__private__.listenerRegistry[type], 'There are no listeners registered for event type: ' + type)) return;

    if (listener) {
      let index = this.__private__.listenerRegistry[type].indexOf(listener);

      return (index > -1);
    } else {
      return true;
    }
  }

  /**
   * Dispatches the specified event.
   *
   * @param {Event} event
   */
  dispatchEvent(event) {
    if (!assertType(event, Event, false, 'Event must be specified.')) return;
    if (!assert(this.__private__.listenerRegistry, 'Listener map is null.')) return;

    if (!this.__private__.listenerRegistry[event.type]) return;

    let arrlen = this.__private__.listenerRegistry[event.type].length;

    for (let i = 0; i < arrlen; i++) {
      let listener = this.__private__.listenerRegistry[event.type][i];
      listener.call(this, event);
    }
  }

  /**
   * Defines all properties.
   *
   * @private
   */
  __defineProperties__() {
    if (!this.__private__) this.__private__ = {};

    Object.defineProperty(this.__private__, 'listenerRegistry', {
      value: {},
      writable: true
    });
  }
}

export default EventDispatcher;