-
Notifications
You must be signed in to change notification settings - Fork 1
/
Copy pathindex.js
166 lines (132 loc) · 4.43 KB
/
index.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
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
const useCapture = [
'onload', 'onunload', 'onscroll', 'onfocus', 'onblur', 'onloadstart',
'onprogress', 'onerror', 'onabort', 'onload', 'onloadend', 'onpointerenter',
'onpointerleave',
];
const eventNames = [];
const handlers = new Map();
const bounded = new Set();
// Ensure we don't get user added event/properties.
const cloneDoc = document.cloneNode();
// Fill up event names.
for (const name in cloneDoc) {
if (name.indexOf('on') === 0) {
eventNames.push(name);
}
}
class SyntheticEvent {}
const cloneEvent = (ev, ov = {}) => {
const newEvent = new SyntheticEvent();
// Copy over original event getters/setters first, will need some extra
// intelligence to ensure getters/setters work, thx @kofifus.
for (let key in ev) {
const desc = Object.getOwnPropertyDescriptor(ev, key);
if (desc && (desc.get || desc.set)) {
Object.defineProperty(newEvent, key, desc);
}
else {
newEvent[key] = ev[key];
}
}
// Copy over overrides.
for (let key in ov) {
newEvent[key] = ov[key];
}
Object.setPrototypeOf(newEvent, ev);
return newEvent;
}
const getShadowRoot = node => {
while (node = node.parentNode) {
if (node.toString() === "[object ShadowRoot]") {
return node;
}
}
return false;
};
// Set up global event delegation, once clicked call the saved handlers.
const bindEventsTo = domNode => {
const rootNode = getShadowRoot(domNode) || domNode.ownerDocument;
const { addEventListener } = rootNode;
if (bounded.has(rootNode)) {
return false;
}
bounded.add(rootNode);
eventNames.forEach(eventName => addEventListener(eventName.slice(2), ev => {
let target = ev.target;
let eventHandler = null;
const path = ev.path ? ev.path : ev.composedPath ? ev.composedPath() : [];
// If we were unable to get the path via some kind of standard approach,
// build it up manually.
if (!path.length) {
for (let node = target; node; node = node.parentNode) {
path.push(node);
}
}
for (let i = 0; i < path.length; i++) {
const node = path[i];
if (handlers.has(node)) {
const hasEventHandler = handlers.get(node)[eventName];
if (hasEventHandler) {
eventHandler = hasEventHandler;
}
break;
}
}
const syntheticEvent = cloneEvent(ev, {
stopPropagation() { ev.stopPropagation(); },
preventDefault() { ev.preventDefault(); },
nativeEvent: ev
});
eventHandler && eventHandler(syntheticEvent);
}, useCapture.includes(eventName) ? true : false));
}
const syntheticEvents = (options = {}) => () => ({ state, patches }) => {
const { internals: { NodeCache } } = state;
const { SET_ATTRIBUTE, REMOVE_ATTRIBUTE } = patches;
if (SET_ATTRIBUTE.length) {
for (let i = 0; i < SET_ATTRIBUTE.length; i += 3) {
const vTree = SET_ATTRIBUTE[i];
const name = SET_ATTRIBUTE[i + 1];
const value = SET_ATTRIBUTE[i + 2];
const domNode = NodeCache.get(vTree);
const eventName = name.toLowerCase();
// Remove inline event binding from element and add to handlers.
if (eventNames.includes(eventName)) {
const handler = domNode[name];
domNode[eventName] = undefined;
const newHandlers = handlers.get(domNode) || {};
// If the value passed is a function, that's what we're looking for.
if (typeof handler === 'function') {
newHandlers[eventName] = handler;
}
// If the value passed is a string name for a global function, use
// that.
else if (typeof window[handler] === 'function') {
newHandlers[eventName] = window[handler];
}
// Remove the event association if the value passed was not a
// function.
else {
delete newHandlers[eventName];
}
handlers.set(domNode, newHandlers);
bindEventsTo(domNode);
}
}
}
if (REMOVE_ATTRIBUTE.length) {
for (let i = 0; i < REMOVE_ATTRIBUTE.length; i += 2) {
const vTree = REMOVE_ATTRIBUTE[i];
const name = REMOVE_ATTRIBUTE[i + 1];
const domNode = NodeCache.get(vTree);
const eventName = name.toLowerCase();
// Remove event binding from element and instead add to handlers.
if (eventNames.includes(eventName)) {
const newHandlers = handlers.get(domNode) || {};
delete newHandlers[eventName];
handlers.set(domNode, newHandlers);
}
}
}
};
export default syntheticEvents;