Source: events/EventObject.js

import { isFunction } from '../utils/utils.js';

/**
 * A prototyp of a basic event interface. Classes might inherit
 * from this class or provide the same shape by convention.
 * 
 * The field-map "events" defines possible *event names* pointing to an
 * array of *listeners* for a special class. For example:
 * 
 * events = {
 *   'changed': []
 * }
 * 
 * Listeners a functions which are called with an optional payload
 * object.
 * 
 * @author rkoppe <roland.koppe@awi.de>
 */
export class EventObject {

    /**
      * {@code events} are optional and can also be defined later.
      * 
      * @param {Object} events (optional)
      */
    constructor(events) {
        this.events = events || {};
    }

    /**
     * Registers the given listener function for the event name.
     * 
     * @param {string} name 
     * @param {function} listener 
     */
    on(name, listener) {
        if (!(name in this.events)) throw new Error('Event ' + name + ' is not supported.');

        if (!isFunction(listener)) throw new Error('Listener must be a function.');
        if (!listener || (Object.prototype.toString.call(listener) !== '[object Function]')) throw new Error('Listener must be a function.');
        this.events[name].push(listener);
    }

    /**
     * Unregisters all listeners from an event
     * 
     * @param {string} name 
     */
    off(name) {
        if (name in this.events) {
            this.events[name] = [];
        } else {
            throw new Error('Event ' + name + ' is not supported.');
        }
    }

    /**
     * Fires an event with given name and payload.
     * 
     * @param {string} name 
     * @param {object} payload 
     */
    fire(name, payload) {
        if (!(name in this.events)) throw new Error('Event ' + name + ' is not supported.');
        for (let i = 0; i < this.events[name].length; i++) {
            this.events[name][i].call(this, payload);
        }
    }
}