Source: eventEmitter.js

/**
An emitter implementation based on the Node.js EventEmitter API:
https://nodejs.org/dist/latest-v6.x/docs/api/events.html#events_class_eventemitter
Adapted from https://git.soma.salesforce.com/aura/lightning-global/blob/master/ui-lightning-components/src/main/modules/lightning/utilsPrivate/eventEmitter.js
**/
/** @ignore **/ 
export class EventEmitter {
    constructor(whitelistedEventNames) {
        this.registry = {};
        this._whitelistedEventNames = whitelistedEventNames ? whitelistedEventNames : null;
    }

    validateEventName(name) {
        if (!this._whitelistedEventNames) {
            return;
        }

        if (!this._whitelistedEventNames.has(name)) {
            throw new Error(`Unsupported event name: ${name}`);
        }
    }

    /**
    Registers a listener on the emitter
    @method EventEmitter#on
    @param {String} name - The name of the event
    @param {Function} listener - The callback function
    @return {EventEmitter} - Returns a reference to the `EventEmitter` so that calls can be chained
    **/
    on(name, listener) {
        this.validateEventName(name);
        this.registry[name] = this.registry[name] || [];
        this.registry[name].push(listener);
        return this;
    }

    /**
    Registers a listener on the emitter that only executes once
    @method EventEmitter#once
    @param {String} name - The name of the event
    @param {Function} listener - The callback function
    @return {EventEmitter} - Returns a reference to the `EventEmitter` so that calls can be chained
    **/
    once(name, listener) {
        this.validateEventName(name);
        const doOnce = function() {
            listener.apply(null, arguments);
            this.removeListener(name, doOnce);
        }.bind(this);
        this.on(name, doOnce);
        return this;
    }

    /**
    Synchronously calls each listener registered with the specified event
    @method EventEmitter#emit
    @param {String} name - The name of the event
    @param {Object} args - The args to be passed over to the listener
    @return {Boolean} - Returns `true` if the event had listeners, `false` otherwise
    **/
    emit(name, ...args) {
        this.validateEventName(name);
        const listeners = this.registry[name];
        let foundListener = false;
        if (listeners) {
            listeners.forEach(listener => {
                foundListener = true;
                listener.apply(null, args);
            });
        }
        return foundListener;
    }

    /**
    Removes the specified `listener` from the listener array for the event named `name`
    @method EventEmitter#removeListener
    @param {String} name - The name of the event
    @param {Function} listener - The callback function
    @return {EventEmitter} - Returns a reference to the `EventEmitter` so that calls can be chained
    **/
    removeListener(name, listener) {
        this.validateEventName(name);
        const listeners = this.registry[name];
        if (listeners) {
            for (let i = 0, len = listeners.length; i < len; i += 1) {
                if (listeners[i] === listener) {
                    listeners.splice(i, 1);
                    return this;
                }
            }
        }
        return this;
    }
}