UNPKG

minsky-kit

Version:
230 lines (186 loc) 6.63 kB
// 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); } }