import { EventEmitter as EE } from 'events';
import _ from 'lodash';
/**
* This takes the node.js 'events' package and adds the following functionality:
* - Registration/unregistration of events. Events cannot be emitted until registered.
* - Pausing / Resuming event emission. Paused events will be added to a queue, and
* can be optionally emitted at resumption of events.
* - Relaying of events from one object to another. A relayed event will appear to be emitted
* from the relaying object, not from the origin object.
*
*/
export default class EventEmitter extends EE {
constructor() {
super(...arguments);
this._registeredEvents = [];
this._eventQueue = [];
}
/**
* Decorates the standard emit() with the following functionality:
* - Checks that event has been registered
* - Checks that events are not currently paused. If so, adds the event to a queue
* @param {array} name - Event name
* @param {...params} - Variable number of additional params
*/
emit(name) { // NOTE: Purposefully do not use an arrow-function, so we have access to arguments
if (_.indexOf(this._registeredEvents, name) === -1) {
throw new Error('Event "' + name + '" is not registered.');
}
if (this.eventsPaused) {
// Add to _eventQueue
this._eventQueue.push(arguments);
return;
}
return super.emit(...arguments);
}
/**
* Registers a single event type to be used by this Class.
* Events must be registered before they can be emitted.
* @param {string} name - Event name
* @return {boolean} isChanged - Whether event was successfully added
*/
registerEvent = (name) => {
return this.registerEvents([name]);
}
/**
* Registers multiple event types to be used by this Class.
* Events must be registered before they can be emitted.
* @param {array} names - Event names
* @return {boolean} isChanged - Whether (any) events were successfully added
*/
registerEvents = (names) => {
const count = this._registeredEvents.length;
this._registeredEvents = _.uniq(_.concat(this._registeredEvents, names));
return this._registeredEvents.length !== count;
}
/**
* Unregisters a single event type from use in this Class.
* @param {string} name - Event name
* @return {boolean} isChanged - Whether event was successfully removed
*/
unregisterEvent = (name) => {
return this.unregisterEvents([name]);
}
/**
* Unregisters multiple event types from use in this Class.
* Events must be registered before they can be emitted.
* @param {array} names - Event names
* @return {boolean} isChanged - Whether (any) events were successfully removed
*/
unregisterEvents = (names) => {
const count = this._registeredEvents.length;
_.pullAll(this._registeredEvents, names);
return this._registeredEvents.length !== count;
}
/**
* Gets array of names of all registered event types.
* @return {array} _registeredEvents - Event names
*/
getRegisteredEvents = () => {
return this._registeredEvents;
}
/**
* Pauses all events.
* Chainable.
* Any events emitted while paused will be added to a queue.
* @return this
*/
pauseEvents = () => {
this.eventsPaused = true;
return this;
}
/**
* Resumes all events.
* Chainable.
* @param {boolean} emitQueuedEvents - Emit the events in queue?
* If false, then queued events will be discarded. Defaults to false.
* @return this
*/
resumeEvents = (emitQueuedEvents = false) => {
this.eventsPaused = false;
if (emitQueuedEvents) {
_.forEach(this._eventQueue, (args) => {
this.emit(...args);
})
}
this._eventQueue = [];
return this;
}
/**
* Relays events from one object to another. A relayed event will appear to be emitted
* from the relaying object (this), not from the origin object.
*/
relayEventsFrom = (origin, events, prefix = '') => {
if (_.isString(events)) {
events = [events];
}
_.each(events, (event) => {
const fullEventName = prefix + event,
oThis = this;
// Register the event in this object, so it can be fired
this.registerEvent(fullEventName);
// Add a listener to the origin
origin.on(event, function() { // NOTE: Purposefully do not use an arrow-function, so we have access to arguments
// Emit the event from this object, passing on any arguments
oThis.emit(fullEventName, ...arguments);
});
});
}
/**
* Adds the same listener to multiple events.
*
* Usage:
*
* - const events = ['foo', 'bar'];
* - const handler = () => {};
* - emitter.addListeners(events, handler);
*/
addListeners = (events, listener) => {
_.each(events, (event) => {
this.on(event, listener);
});
}
/**
* Alias for addListeners
*/
ons = (events, listener) => {
return this.addListeners(events, listener);
}
}