minsky-kit
Version:
Kit of components for MINSKY web agency
230 lines (186 loc) • 6.63 kB
JavaScript
// imports
import Base from '../core/Base';
import Event from './Event';
// private static
// class
export default class EventDispatcher extends Base {
// constructor
constructor (args = {}, objectName = 'Event Dispatcher') {
// call super constructor
super(args, objectName);
// set properties
this.listeners = {};
this.isSilent = args.silent || false;
this._q = [];
// check queue to see if anything needs to happen after initialization
setTimeout(() => {
// loop over queue & clear the array
if (this._q.length) {
this._q.forEach((item) => this.dispatch(item.e, item.data, item.process));
this._q.length = 0;
}
// prevent usage of queue any further
this._q = null;
}, 1);
}
// methods
initialize (args = {}) {
super.initialize(args);
return this;
}
on (event, data, callback = null) {
addEvent(this.listeners, event, data, callback, false);
return this;
}
once (event, data, callback = null) {
addEvent(this.listeners, event, data, callback, true);
return this;
}
off (event, callback = null) {
// process parameters
let list;
const events = processMultiTypeParam(event);
const callbacks = processMultiTypeParam(callback);
// start removal process
if (events[0]) {
// if event is given => remove callbacks from given events
for (let i = 0, len = events.length; i < len; i++) {
list = this.listeners[events[i]];
if (list.length) removeCallback(list, callbacks);
}
}
else {
// if event wasn't given => remove given callbacks from all events
for (const evt of Object.keys(this.listeners)) {
list = this.listeners[evt];
if (list.length) removeCallback(list, callbacks);
}
}
return this;
}
dispatch (event, extraData = {}, process = null) {
// check if listeners exists
// object can sometimes call dispatch multiple times and destroy itself in between those moments
// check for listeners => if listeners === undefined || null => object is destroyed or corrupt
// bad coding shoudl avoided but sometimes this can not be avoided
if (!this.listeners) return this;
// block if in silent mode
if (this.silent) return this;
// loop over events and run their callbacks
let listeners; let listener; let evt; let
eventObj;
const events = processMultiTypeParam(event);
// push '' as extra event to dispatch all any event
events.push('');
for (let i = 0; i < events.length; i++) {
evt = events[i];
listeners = this.listeners[evt];
if (listeners) {
// loop over callbacks and run them all one by one
for (let j = 0; j < listeners.length; j++) {
listener = listeners[j];
// create event object
// merge data objects into a new object to avoid top level cross references
eventObj = new Event({
type: evt,
currentTarget: this,
data: { ...listener.data, ...extraData },
});
// process newly created event object before it's dispatched
if (process) process(eventObj);
// dispatch event
listener.callback.call(this, eventObj);
// remove listener if once = true
if (listener.once) {
this.off(evt, listener.callback);
j--;
}
// check if continuation is ensured
if (eventObj.allDefaultsPrevented) return this;
if (eventObj.defaultPrevented) break;
}
}
else {
if (this._q) this._q.push({ e: evt, data: extraData, process });
}
}
return this;
}
hasListener (events, listener) {
// force array
if (!Array.isArray(events)) events = [events];
// loop over evernts & check with instance
return events.find((e) => {
if (this.listeners[e]) {
return this.listeners[e].find((l) => l === listener);
}
return false;
});
}
destroy () {
this.listeners = null;
super.destroy();
return this;
}
// getters & setters
get silent () { return this.isSilent; }
set silent (value) { this.isSilent = !!value; }
// statics
}
// private methods
function processMultiTypeParam (param) {
// param is either a string or a function
// !! param is allowed as it will never be a boolean
if (Array.isArray(param)) {
return param;
}
return [param];
}
function addEvent (listeners, event, data, callback, once) {
let eArr; let d; let
cArr;
// process event parameter
if (!Array.isArray(event) && typeof event !== 'string') {
// event is data
d = event;
}
else {
eArr = processMultiTypeParam(event);
}
if (Array.isArray(data) || typeof data === 'function') {
// data is callback
d = {};
cArr = processMultiTypeParam(data);
}
else {
d = data;
cArr = processMultiTypeParam(callback);
}
// loop over events and callbacks to create listener objects as dispatching info
// clone data object to avoid interference
let eKey;
for (let i = 0; i < eArr.length; i++) {
eKey = eArr[i];
// ensure presens of event object in listeners
if (!listeners[eKey]) listeners[eKey] = [];
for (let j = 0; j < cArr.length; j++) {
listeners[eKey].push({
data: { ...d },
callback: cArr[j],
once,
});
}
}
}
function removeCallback (cbList, callbacks) {
for (let i = 0; i < callbacks.length; i++) {
let index = -1;
for (let j = 0; j < cbList.length; j++) {
if (cbList[j].callback === callbacks[i]) {
index = j;
break;
}
}
if (index > -1) cbList.splice(index, 1);
}
}