Source: wk/pattern/observer/EventDispatcher.js

/**
 * @author    Wolfgang Kowarschick <kowa@hs-augsburg.de>
 * @copyright 2017 - 2018
 * @license   CC-BY-NC-SA-4.0
 */

import Event from './Event.js';

/**
 * Partly implements the interface EventTarget.
 * @class
 * @see https://developer.mozilla.org/en-US/docs/Web/API/EventTarget
 * @cmp https://developer.mozilla.org/en-US/docs/Mozilla/Tech/XPCOM/Language_Bindings/Components.utils.cloneInto
 */
class EventDispatcher
{ constructor()
  { this.v_listeners = {}; }

  /**
   * Adds a callback function for a specific type. One and the same
   * function can only be added once for each type. If it is tried to
   * add a function again for a specific type, the method returns
   * without any modification or reaction.
   *
   * @param {string}   p_type
   * @param {Function} p_callback
   *
   * @fires this.v_initial_events[p_type]
   */
  addEventListener(p_type, p_callback)
  { let
      l_listeners = this.v_listeners[p_type];
    
    if (l_listeners === undefined)
    { l_listeners = this.v_listeners[p_type] = []; }
    
    if (l_listeners.findIndex(v => v === p_callback) > 0)
    { return; }
    
    l_listeners.push(p_callback);
  }

  /**
   * Removes a callback function for a specific type, if it exists.
   *
   * @param {string}   p_type
   * @param {Function} p_callback
   */
  removeEventListener(p_type, p_callback)
  { if (!(p_type in this.v_listeners))
    { return; }

    const
      l_callbacks = this.v_listeners[p_type];
  
    l_callbacks.splice(l_callbacks.findIndex(v => v === p_callback), 1);
  }

  /**
   * Handles events of type String or {@link Event} by invoking
   * all callback functions that have been registered for that
   * type.
   *
   * @param  {string|Event} p_type
   * @param  {?Object}      p_detail
   *         Detail information passed to the listener
   */
  dispatchEvent(p_type, p_detail=null)
  { let l_event;

    if (p_type instanceof Event)
    { if (!(p_type.type in this.v_listeners))
      { return; }
      if (p_detail === null)
      { l_event = p_type; }
      else
      { l_event = new Event({type: p_type.type, detail: p_detail}); }
    }
    else
    { if (!(p_type in this.v_listeners))
      { return; }
      else
      { l_event = new Event({type: p_type, detail: p_detail}); }
    }

    const
      l_callbacks = this.v_listeners[l_event.type];

    for (let l_callback of l_callbacks)
    { l_callback.call(this, l_event); }
  }
}

export {EventDispatcher};
export default EventDispatcher;