UNPKG

@actualwave/event-dispatcher

Version:

EventDispatcher is a JavaScript class that adds events support to custom objects.

186 lines (178 loc) 5.91 kB
'use strict'; Object.defineProperty(exports, '__esModule', { value: true }); const isObject = (value) => typeof value === 'object' && value !== null; class Event { constructor(type, data = null) { this.defaultPrevented = false; this.type = type; this.data = data; } toJSON() { return { type: this.type, data: this.data }; } isDefaultPrevented() { return this.defaultPrevented; } preventDefault() { this.defaultPrevented = true; } } const getEvent = (eventOrType, optionalData) => { if (!isObject(eventOrType)) { return new Event(String(eventOrType), optionalData); } return eventOrType; }; class ListenersRunner { constructor(listeners, onStopped, onComplete) { this.index = -1; this.immediatelyStopped = false; this.stopImmediatePropagation = () => { this.immediatelyStopped = true; }; this.listeners = listeners; this.onStopped = onStopped; this.onComplete = onComplete; } run(event) { const dispatched = event; dispatched.stopPropagation = this.onStopped; dispatched.stopImmediatePropagation = this.stopImmediatePropagation; for (this.index = 0; this.index < this.listeners.length; this.index++) { if (this.immediatelyStopped) break; this.listeners[this.index](dispatched); } delete event.stopPropagation; delete event.stopImmediatePropagation; this.onComplete(this); } listenerRemoved(listeners, index) { if (listeners === this.listeners && index <= this.index) { this.index--; } } } class EventListeners { constructor() { this._listeners = {}; this._runners = []; this.removeRunner = (runner) => { this._runners.splice(this._runners.indexOf(runner), 1); }; } createList(eventType, priority) { const target = this.getPrioritiesByKey(eventType); const key = String(priority); if (Object.hasOwn(target, key)) { return target[key]; } const value = []; target[key] = value; return value; } getPrioritiesByKey(key) { if (Object.hasOwn(this._listeners, key)) { return this._listeners[key]; } const value = {}; this._listeners[key] = value; return value; } add(eventType, handler, priority) { const handlers = this.createList(eventType, priority); if (handlers.indexOf(handler) < 0) { handlers.push(handler); } } has(eventType) { if (!Object.hasOwn(this._listeners, eventType)) { return false; } return Object.keys(this._listeners[eventType]).length > 0; } remove(eventType, handler) { if (!Object.hasOwn(this._listeners, eventType)) { return; } const priorities = this._listeners[eventType]; const list = Object.getOwnPropertyNames(priorities); for (const priority of list) { const handlers = priorities[priority]; const handlerIndex = handlers.indexOf(handler); if (handlerIndex >= 0) { handlers.splice(handlerIndex, 1); if (!handlers.length) { delete priorities[priority]; } this._runners.forEach((runner) => { runner.listenerRemoved(handlers, handlerIndex); }); } } } removeAll(eventType) { delete this._listeners[eventType]; } createRunner(handlers, onStopped) { const runner = new ListenersRunner(handlers, onStopped, this.removeRunner); this._runners.push(runner); return runner; } call(event) { if (!Object.hasOwn(this._listeners, event.type)) { return; } const priorities = this._listeners[event.type]; let stopped = false; const stopPropagation = () => { stopped = true; }; const list = Object.getOwnPropertyNames(priorities).sort((a, b) => Number(a) - Number(b)); for (const key of list) { if (stopped) break; const handlers = priorities[key]; if (handlers) { const runner = this.createRunner(handlers, stopPropagation); runner.run(event); if (runner.immediatelyStopped) break; } } } } class EventDispatcher { constructor(eventPreprocessor = null) { this._eventPreprocessor = eventPreprocessor; this._listeners = new EventListeners(); } addEventListener(eventType, listener, priority = 0) { // Negate priority so higher values sort first (ascending sort order in EventListeners) this._listeners.add(eventType, listener, -priority || 0); } hasEventListener(eventType) { return this._listeners.has(eventType); } removeEventListener(eventType, listener) { this._listeners.remove(eventType, listener); } removeAllEventListeners(eventType) { this._listeners.removeAll(eventType); } dispatchEvent(event, data) { let eventObject = getEvent(event, data); if (this._eventPreprocessor) { eventObject = this._eventPreprocessor.call(this, eventObject); } this._listeners.call(eventObject); } } const createEventDispatcher = (eventPreprocessor) => new EventDispatcher(eventPreprocessor ?? null); exports.Event = Event; exports.EventDispatcher = EventDispatcher; exports.createEventDispatcher = createEventDispatcher; exports.default = EventDispatcher; exports.getEvent = getEvent; exports.isObject = isObject; //# sourceMappingURL=index.js.map