-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathCustomEventTarget.js
128 lines (94 loc) · 4.26 KB
/
CustomEventTarget.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
import EventListener from './EventListener.js';
import EventOptions from './EventOptions.js';
import ConstructableEventTarget from './ConstructableEventTarget.js'
const listenerSymbol = Symbol('Event Listeners');
const listenerMap = new WeakMap();
const targetSymbol = Symbol('Event Listener Key');
class CustomEventTarget extends ConstructableEventTarget {
constructor() {
super();
const weakMapKey = {};
this[targetSymbol] = weakMapKey;
const listenerMap = CustomEventTarget[listenerSymbol];
listenerMap.set(weakMapKey, {references: new WeakMap(), listeners: []});
}
addEventListener(eventType, pListener, pOptions) {
if (!(this instanceof CustomEventTarget)) {
throw new TypeError('Illegal invocation');
}
const listener = typeof pListener === 'function'
? new EventListener({handleEvent: pListener}, false)
: new EventListener(pListener, true);
const options = typeof pOptions === 'boolean'
? new EventOptions({capture: pOptions})
: new EventOptions(pOptions);
const {references, listeners} = this[listenerSymbol];
const eventHandlerList = _getListenerReferences(references, eventType, pListener, options);
const alreadyAttached = eventHandlerList.some(item => options.equal(item));
if (alreadyAttached) return;
eventHandlerList.push(options);
listeners.push({eventType, listener, options});
function _getListenerReferences(references, eventType, pListener, options) {
const existingHandlerMap = references.get(pListener);
const handlerMap = existingHandlerMap || new Map();
if (!existingHandlerMap) {
references.set(pListener, handlerMap);
}
const existingEventHandlerList = handlerMap.get(eventType);
const eventHandlerList = existingEventHandlerList || [];
if (!existingEventHandlerList) {
handlerMap.set(eventType, eventHandlerList);
}
return eventHandlerList;
}
}
removeEventListener(eventType, pListener, pOptions) {
if (!(this instanceof CustomEventTarget)) {
throw new TypeError('Illegal invocation');
}
const options = typeof pOptions === 'boolean'
? new EventOptions({capture: pOptions})
: new EventOptions(pOptions);
const {references, listeners} = this[listenerSymbol];
const handlerMap = references.get(pListener);
if (!handlerMap) return;
const eventHandlerList = handlerMap.get(eventType);
if (!eventHandlerList) return;
const optionIndex = eventHandlerList.findIndex(item => options.equal(item));
if (optionIndex === -1) return;
const foundOptions = eventHandlerList[optionIndex];
eventHandlerList.splice(optionIndex, 1);
const listenerIndex = listeners.findIndex(item => item.options === foundOptions);
listeners.splice(listenerIndex, 1);
}
dispatchEvent(event) {
if (!(this instanceof CustomEventTarget)) {
throw new TypeError('Illegal invocation');
}
const {references, listeners} = this[listenerSymbol];
const {capture, noCapture} = listeners.reduce((o, item) => {
if (item.eventType === event.type) {
if (item.options.capture) o.capture.push(item);
else o.noCapture.push(item);
}
return o;
}, {
capture: [],
noCapture: []
});
capture
.filter(item => item.options.shouldHandle(event, true, this, item.listener))
.forEach(item => {item.listener.handle(event, this)});
noCapture
.filter(item => item.options.shouldHandle(event, false, this, item.listener))
.forEach(item => {item.listener.handle(event, this)});
return true;
}
get [listenerSymbol]() {
return CustomEventTarget[listenerSymbol].get(this[targetSymbol]);
}
static get [listenerSymbol]() {
return listenerMap;
}
}
export default CustomEventTarget;