// https://dom.spec.whatwg.org/#interface-eventtarget const wm = new WeakMap(); function dispatch(event, listener) { if (typeof listener === 'function') listener.call(event.target, event); else listener.handleEvent(event); return event._stopImmediatePropagationFlag; } function invokeListeners({currentTarget, target}) { const map = wm.get(currentTarget); if (map && map.has(this.type)) { const listeners = map.get(this.type); if (currentTarget === target) { this.eventPhase = this.AT_TARGET; } else { this.eventPhase = this.BUBBLING_PHASE; } this.currentTarget = currentTarget; this.target = target; for (const [listener, options] of listeners) { if (options && options.once) listeners.delete(listener); if (dispatch(this, listener)) break; } delete this.currentTarget; delete this.target; return this.cancelBubble; } } /** * @implements globalThis.EventTarget */ class DOMEventTarget { constructor() { wm.set(this, new Map); } /** * @protected */ _getParent() { return null; } addEventListener(type, listener, options) { const map = wm.get(this); if (!map.has(type)) map.set(type, new Map); map.get(type).set(listener, options); } removeEventListener(type, listener) { const map = wm.get(this); if (map.has(type)) { const listeners = map.get(type); if (listeners.delete(listener) && !listeners.size) map.delete(type); } } dispatchEvent(event) { let node = this; event.eventPhase = event.CAPTURING_PHASE; // intentionally simplified, specs imply way more code: https://dom.spec.whatwg.org/#event-path while (node) { if (node.dispatchEvent) event._path.push({currentTarget: node, target: this}); node = event.bubbles && node._getParent && node._getParent(); } event._path.some(invokeListeners, event); event._path = []; event.eventPhase = event.NONE; return !event.defaultPrevented; } } export {DOMEventTarget as EventTarget};