EventDispatcher.js

/** @module EventDispatcher */

/** 
 * Mimic DOM Element event binding.
 * Purpose is to be able to attach listener to js object 
*/
export class EventDispatcher {

    constructor() {
        this._listeners = []
        this.hasEventListener = this.hasEventListener.bind(this)
        this.addEventListener = this.addEventListener.bind(this)
        this.removeEventListener = this.removeEventListener.bind(this)
        this.dropEventListeners = this.dropEventListeners.bind(this)
        this.dispatchEvent = this.dispatchEvent.bind(this)
    }

    /**
     * @method hasEventListener
     * @description Indicated if a listener function is already registered for a specific event.
     * 
     * @param {String} type 
     * @param {Function} listener 
     * @returns {Boolean}
     */
    hasEventListener(type, listener) {
        return this._listeners.some(item => item.type === type && item.listener === listener)
    }

    /**
     * @method addEventListener
     * @description Add a listener for a specific event type.
     * 
     * @param {String} type event type
     * @param {Function} listener handler function
     * @param {Boolean} once if set handler function will be called only once. 
     * 
     * @returns {EventDispatcher} Instance
     */
    addEventListener(type, listener, once) {
        if (typeof listener === 'function' && !this.hasEventListener(type, listener)) {
            this._listeners.push({ type, listener, options: { once: once ? !!once : false } })
        }
        return this
    }

    /**
     * @method removeEventListener
     * @description Remove a listener for a specific event type.
     * 
     * @param {String} type event type
     * @param {Function} listener handler function
     * 
     * @returns {EventDispatcher} Instance
     */
    removeEventListener(type, listener) {
        let index = this._listeners.findIndex(item => item.type === type && item.listener === listener)
        if (index >= 0) this._listeners.splice(index, 1)
        return this
    }

    /**
     * @method removeEventListeners
     * @description Drop all event listeners.
     * 
     * @returns {EventDispatcher} Instance
    */
    dropEventListeners() {
        this._listeners = []
        return this
    }

    /**
     * @method dispatchEvent
     * @description Call all registered listeners that match the event type.
     * 
     * @param {CustomEvent} event event to be dispatched
     * @returns {EventDispatcher} Instance.
    */
    dispatchEvent(event) {
        for(let index = 0, n = this._listeners.length; index < n; index++) {
            const { type, listener, options: { once } } = this._listeners[index]
            if(type === event.type) {
                listener.call(this, event)
                if(once === true) this.removeEventListener(type, listener)
            }
        }
        return this
    }
}