@haxiomic/event-signal
Version:
A single event emitter with priority support for TypeScript
92 lines (91 loc) • 3.09 kB
JavaScript
/**
* Event emitter with a notion of explicit ordering via priority
*/
export class EventSignal {
listeners = new Array();
addListener(listener, priority = 0) {
let eventObj = {
priority,
listener,
remove: () => this.removeListener(listener),
};
if (listener !== null) {
this.listeners.push(eventObj);
}
return eventObj;
}
/** Alias for addListener */
on(listener, priority) {
return this.addListener(listener, priority);
}
removeListener(listener) {
// remove listener from array
let i = 0;
for (; i < this.listeners.length; i++) {
if (this.listeners[i].listener === listener) {
break;
}
}
this.listeners.splice(i, 1);
}
once(listener, priority = 0) {
const tempListener = (event) => {
listener(event);
this.removeListener(tempListener);
};
return this.addListener(tempListener, priority);
}
/**
* Dispatch an event by providing a payload
* The underlying event object will be created and populated with the payload
*
* @param maxPriority If provided, only listeners with a priority equal or lower than this will be called
*/
dispatch(payload, maxPriority) {
if (this.listeners.length === 0)
return;
return this.dispatchWithExistingEvent(payload, maxPriority);
}
/**
* Dispatch an event with an existing event object
*
* This is useful if you want to forward an event from another source like a DOM event
*/
dispatchWithExistingEvent(payload, maxPriority = Infinity) {
if (this.listeners.length === 0)
return;
let event = this.patchPayload(payload);
// sort listeners by priority before dispatch (priority can change at runtime)
this.sortPriorityDescending();
// enumerate listeners, highest priority first
for (let i = 0; i < this.listeners.length; i++) {
let item = this.listeners[i];
if (item.priority > maxPriority)
continue; // skip
this.listeners[i].listener(event);
// stop propagation if event was prevented
if (typeof event === 'object' && event.propagationStopped) {
return;
}
}
}
hasListeners() {
return this.listeners.length > 0;
}
sortPriorityDescending() {
this.listeners.sort((a, b) => b.priority - a.priority);
}
patchPayload(payload) {
// patch event object to support `propagationStopped`
if (payload instanceof Event && payload.propagationStopped === undefined) {
payload.propagationStopped = false;
payload._stopPropagation = payload.stopPropagation;
payload.stopPropagation = stopPropagationOverride;
}
return payload;
}
}
function stopPropagationOverride() {
this.propagationStopped = true;
this._stopPropagation();
}