You cannot select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
89 lines
2.0 KiB
JavaScript
89 lines
2.0 KiB
JavaScript
// 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};
|