Source: events/EventTimer.js

// (c) Andrew Wei

'use strict';

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

/**
 * @class
 *
 * Class for handling timed events.
 *
 * @alias module:requiem~events.EventTimer
 */
class EventTimer extends EventDispatcher {
  /**
   * Gets the singleton instance of EventTimer.
   *
   * @return {EventTimer} The singleton EventTimer instance.
   */
  static get sharedInstance() {
    if (!EventTimer.__private__) EventTimer.__private__ = {};
    if (!EventTimer.__private__.sharedInstance) EventTimer.__private__.sharedInstance = new EventTimer();
    return EventTimer.__private__.sharedInstance;
  }

  /**
   * @see module:requiem~events.EventTimer.eventPool
   */
  static get eventPool() {
    return EventTimer.sharedInstance.eventPool;
  }

  /**
   * @see module:requiem~events.EventTimer.addEvent
   */
  static addEvent() {
    EventTimer.sharedInstance.addEvent.apply(EventTimer.sharedInstance, arguments);
  }

  /**
   * @see module:requiem~events.EventTimer.removeEvent
   */
  static removeEvent() {
    EventTimer.sharedInstance.removeEvent.apply(EventTimer.sharedInstance, arguments);
  }

  /**
   * @see module:requiem~events.EventTimer.removeAllEvents
   */
  static removeAllEvents() {
    EventTimer.sharedInstance.removeAllEvents.apply(EventTimer.sharedInstance, arguments);
  }

  /**
   * @see module:requiem~events.EventTimer.addEventListener
   */
  static addEventListener() {
    EventTimer.sharedInstance.addEventListener.apply(EventTimer.sharedInstance, arguments);
  }

  /**
   * @see module:requiem~events.EventTimer.removeEventListener
   */
  static removeEventListener() {
    EventTimer.sharedInstance.removeEventListener.apply(EventTimer.sharedInstance, arguments);
  }

  /**
   * @see module:requiem~events.EventTimer.hasEventLisetener
   */
  static hasEventLisetener() {
    EventTimer.sharedInstance.hasEventLisetener.apply(EventTimer.sharedInstance, arguments);
  }

  /**
   * The current event pool.
   *
   * @return {Object} The current event pool.
   */
  get eventPool() {
    if (!this.__private__.eventPool) this.__private__.eventPool = {};
    return this.__private__.eventPool;
  }

  /**
   * Adds an event to the EventTimer.
   *
   * @param {string} id - Unique ID of the timed event.
   * @param {Function} func - Callback to be invoked when event triggers.
   * @param {number} [delay=0] - Delay (interval) of event (in ms).
   * @param {number} [count=1] - Number of times the event should invoke. If set
   *                             to 0, it will be infinite.
   * @param {boolean} [overwrite=true] - Specifies whether this timed event
   *                                     should overwrite an existing one with
   *                                     the same name.
   * @param {string [eventName] - The event name of the custom event to be
   *                              dispatched.
   * @param {*} [...params] - Additional parameters that are passed through to
   *                          the function specified by 'func'.
   */
  addEvent() {
    let id = arguments[0];
    let func = arguments[1];
    let delay = (arguments[2] === undefined) ? 0 : arguments[2];
    let count = (arguments[3] === undefined) ? 1 : arguments[3];
    let overwrite = (arguments[4] === undefined) ? true : arguments[4];
    let eventName = arguments[5];
    let params = arguments[6];

    assert(!this.eventPool.hasOwnProperty(id) || overwrite, `Duplicate timed event with id ${id}`);
    assertType(func, 'function', false, `Invalid function provided`);
    assertType(delay, 'number', false, `Invalid delay specified: ${delay}`);
    assertType(count, 'number', false, `Invalid count specified: ${count}`);
    assertType(overwrite, 'boolean', false, `Invalid overwrite flag specified: ${overwrite}`);
    assertType(eventName, 'string', true, `Invalid event name specified: ${eventName}`);

    if (overwrite) this.removeEvent(id);

    // Process params for func.
    if (params !== undefined) {
      params = [];
      for (let i = 6; i < arguments.length; i++) {
        params.push(arguments[i]);
      }
    }

    this.eventPool[id] = {
      event: setInterval(() => {
        this.eventPool[id].iteration++;
        func.apply(this.params);

        if (eventName !== undefined) {
          let e = new CustomEvent(eventName, {
            detail: {
              id: id,
              iteration: this.eventPool[id].iteration,
              count: this.eventPool[id].count
            }
          });

          this.dispatchEvent(e);
        }

        if ((this.eventPool[id].count > 0) && (this.eventPool[id].iteration >= this.eventPool[id].count)) {
          this.removeEvent(id);
        }
      }, delay),
      iteration: 0,
      count: count
    };
  }

  /**
   * Removes a timed event by its ID.
   *
   * @param {string} id - ID of the timed event.
   */
  removeEvent(id) {
    if (this.eventPool.hasOwnProperty(id)) {
      clearInterval(this.eventPool[id].event);
      delete this.eventPool[id];
    }
  }

  /**
   * Removes all timed events from the EventTimer.
   */
  removeAllEvents() {
    for (let k in this.eventPool) {
      let v = this.eventPool[k];
      clearInterval(v.event);
      delete this.eventPool[k];
    }
  }
}

export default EventTimer;