@ima/core
Version:
IMA.js framework for isomorphic javascript application
175 lines (174 loc) • 6.04 kB
JavaScript
import { Dispatcher } from './Dispatcher';
import { GenericError } from '../error/GenericError';
/**
* An empty immutable map of event listener to scopes, used for a mismatch in
* the {@link _eventListeners} map.
*/ const EMPTY_MAP = Object.freeze(new Map());
/**
* An empty immutable set of event listener scopes, used for a mismatch in the
* {@link _eventListeners} map.
*/ const EMPTY_SET = Object.freeze(new Set());
/**
* Default implementation of the {@link Dispatcher} interface.
*/ export class DispatcherImpl extends Dispatcher {
_eventListeners;
_eventListenersAll;
static $dependencies = [];
/**
* Initializes the dispatcher.
*/ constructor(){
super();
/**
* Map of event names to a map of event listeners to a set of scopes to
* which the event listener should be bound when being executed due to
* the event.
*/ this._eventListeners = new Map();
this._eventListenersAll = new Map();
}
/**
* @inheritDoc
*/ clear() {
this._eventListeners.clear();
return this;
}
/**
* @inheritDoc
*/ listen(event, listener, scope) {
if ($Debug) {
if (typeof listener !== 'function') {
throw new GenericError(`The listener must be a function, ${listener} provided.`);
}
}
if (!this._eventListeners.has(event)) {
this._createNewEvent(event);
}
const listeners = this._getListenersOf(event);
if (!listeners.has(listener)) {
this._createNewListener(event, listener);
}
this._getScopesOf(event, listener).add(scope);
return this;
}
/**
* @inheritDoc
*/ listenAll(listener, scope) {
if ($Debug) {
if (typeof listener !== 'function') {
throw new GenericError(`The listener must be a function, ${listener} provided.`);
}
}
if (!this._eventListenersAll.has(listener)) {
const scopes = new Set();
this._eventListenersAll.set(listener, scopes);
}
this._eventListenersAll.get(listener).add(scope);
return this;
}
/**
* @inheritDoc
*/ unlisten(event, listener, scope) {
const scopes = this._getScopesOf(event, listener);
if ($Debug) {
if (!scopes.has(scope)) {
console.warn('ima.core.event.DispatcherImpl.unlisten(): the provided ' + `listener '${listener}' is not registered for the ` + `specified event '${event}' and scope '${scope}'. Check ` + `your workflow.`, {
event: event,
listener: listener,
scope: scope
});
}
}
scopes.delete(scope);
if (!scopes.size) {
const listeners = this._getListenersOf(event);
listeners.delete(listener);
if (!listeners.size) {
this._eventListeners.delete(event);
}
}
return this;
}
unlistenAll(listener, scope) {
const scopes = this._eventListenersAll.get(listener) ?? EMPTY_SET;
if ($Debug) {
if (!scopes.has(scope)) {
console.warn('ima.core.event.DispatcherImpl.unlistenAll(): the provided ' + `listener '${listener}' is not registered for` + `scope '${scope}'. Check ` + `your workflow.`, {
listener: listener,
scope: scope
});
}
}
scopes.delete(scope);
if (!scopes.size) {
this._eventListenersAll.delete(listener);
}
return this;
}
/**
* @inheritDoc
*/ fire(event, data) {
const listeners = this._getListenersOf(event);
for (const [listener, scopes] of this._eventListenersAll.entries()){
for (const scope of scopes){
listener.bind(scope)(event, data);
}
}
for (const [listener, scopes] of listeners){
for (const scope of scopes){
listener.bind(scope)(data);
}
}
return this;
}
/**
* Create new Map storage of listeners for the specified event.
*
* @param event The name of the event.
*/ _createNewEvent(event) {
const listeners = new Map();
this._eventListeners.set(event, listeners);
}
/**
* Create new Set storage of scopes for the specified event and listener.
*
* @param event The name of the event.
* @param listener The event listener.
*/ _createNewListener(event, listener) {
const scopes = new Set();
const listeners = this._eventListeners.get(event);
if (listeners) {
listeners.set(listener, scopes);
}
}
/**
* Retrieves the scopes in which the specified event listener should be
* executed for the specified event.
*
* @param event The name of the event.
* @param listener The event listener.
* @return The scopes in which the specified listeners
* should be executed in case of the specified event. The returned
* set is an unmodifiable empty set if no listeners are registered
* for the event.
*/ _getScopesOf(event, listener) {
const listenersToScopes = this._getListenersOf(event);
if (listenersToScopes.has(listener)) {
return listenersToScopes.get(listener);
}
return EMPTY_SET;
}
/**
* Retrieves the map of event listeners to scopes they are bound to.
*
* @param event The name of the event.
* @return A map of event listeners to the
* scopes in which they should be executed. The returned map is an
* unmodifiable empty map if no listeners are registered for the
* event.
*/ _getListenersOf(event) {
if (this._eventListeners.has(event)) {
return this._eventListeners.get(event);
}
return EMPTY_MAP;
}
}
//# sourceMappingURL=DispatcherImpl.js.map