Source: libs/EventDispatcher.js

/**
* @file Extensible class intended to (nearly) mimic standard DOM event dispatchers.
*
* @version 0.4.1
*/
/**
* @class Extend this class to enable standard event dispatcher functionality
* in non-native (DOM) objects.
*
* <p><b><i>This class requires ECMAScript 2017 support.</i></b></p>
*
* @example
*
* class myEventDispatcher extends EventDispatcher {
*  constructor() {
*   this.dispatchEvent(new Event("created"));
*  }
* }
*
* @version 0.2.3
* @mixin
* @mixes Object
* @see https://www.w3.org/TR/uievents/
*/
class EventDispatcher {

   constructor () {
       this._registrations = new Object();
   }

   /**
   * Returns all registered listeners for a specific event type.
   *
   * @param {String} type The event type to return registered listeners for.
   *
   * @return {Array} A list of registered event listener objects for the specific event.
   * Each object contains a <code>listener</code> function reference and an
   * execution <code>context</code> reference.
   */
   getListeners (type) {
       if ((this._registrations[type] == undefined) || (this._registrations[type] == null)) {
          this._registrations[type] = new Array();
       }
       return (this._registrations[type]);
   }

   /**
   * Registers a new event listener with the extending class instance.
   *
   * @param {String} type The event type to register.
   * @param {Function} listener The listening function to invoke on the event.
   * @param {Object} [context=null] Unlike the traditional <code>addEventListener</code> parameter, this is
   * the context or scope in which to invoke the listening function (since we can't use capture phases).
   * If null, the listener is invoked in the context of the EventDispatcher or extending instance.
   */
   addEventListener (type, listener, eventContext = null) {
       var listeners = this.getListeners(type);
       var listenerObj = new Object();
       listenerObj.listener = listener;
       listenerObj.context = eventContext;
       listeners.push(listenerObj);
   }

   /**
   * Removes an event listener from the extending instance.
   *
   * @param {String} type The event type to remove the function from.
   * @param {Function} listener The listening function to remove.
   * @param {Object} [context=null] The context or scope in which the listener exists.
   *
   * @augments EventDispatcher
   */
   removeEventListener (type, listener, context=null) {
       var listeners = this.getListeners(type);
       for (var count=0; count < listeners.length; count++) {
          if (context == null) {
             if (listeners[count].listener === listener) {
                listeners.splice(count, 1);
                return;
             }
          } else {
             if ((listeners[count].listener === listener) && (listeners[count].context === context)) {
                listeners.splice(count, 1);
                return;
             }
          }
       }
   }

   dispatchEvent (evt) {
       Object.defineProperty(evt, "target", {writable: true});
       evt.target = this; //typically this is null in custom event instances
       var listeners = Array.from(this.getListeners(evt.type));
       for (var i= 0; i < listeners.length; i++) {
         if (listeners[i].context != null) {
            listeners[i].listener.call(listeners[i].context, evt);
         } else {
            listeners[i].listener.call(this, evt);
         }
       }
       return !evt.defaultPrevented;
   }

   toString() {
      return ("[object EventDispatcher]");
   }
}