reffy 20.0.2 → 20.0.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +5 -5
- package/src/postprocessing/events.js +245 -259
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "reffy",
|
|
3
|
-
"version": "20.0.
|
|
3
|
+
"version": "20.0.3",
|
|
4
4
|
"description": "W3C/WHATWG spec dependencies exploration companion. Features a short set of tools to study spec references as well as WebIDL term definitions and references found in W3C specifications.",
|
|
5
5
|
"repository": {
|
|
6
6
|
"type": "git",
|
|
@@ -37,15 +37,15 @@
|
|
|
37
37
|
"ajv-formats": "3.0.1",
|
|
38
38
|
"commander": "14.0.2",
|
|
39
39
|
"fetch-filecache-for-crawling": "5.1.1",
|
|
40
|
-
"puppeteer": "24.
|
|
40
|
+
"puppeteer": "24.31.0",
|
|
41
41
|
"semver": "^7.3.5",
|
|
42
|
-
"web-specs": "3.
|
|
42
|
+
"web-specs": "3.73.0",
|
|
43
43
|
"webidl2": "24.5.0"
|
|
44
44
|
},
|
|
45
45
|
"devDependencies": {
|
|
46
|
-
"respec": "35.6.
|
|
46
|
+
"respec": "35.6.1",
|
|
47
47
|
"respec-hljs": "2.1.1",
|
|
48
|
-
"rollup": "4.53.
|
|
48
|
+
"rollup": "4.53.3",
|
|
49
49
|
"undici": "^7.0.0"
|
|
50
50
|
},
|
|
51
51
|
"overrides": {
|
|
@@ -1,259 +1,245 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Post-processing module that consolidates events extracts into one extract
|
|
3
|
-
* per event.
|
|
4
|
-
*/
|
|
5
|
-
|
|
6
|
-
import { isLatestLevelThatPasses, getInterfaceTreeInfo } from '../lib/util.js';
|
|
7
|
-
|
|
8
|
-
export default {
|
|
9
|
-
dependsOn: ['events'],
|
|
10
|
-
input: 'crawl',
|
|
11
|
-
property: 'events',
|
|
12
|
-
|
|
13
|
-
run: async function(crawl, options) {
|
|
14
|
-
// Collect list of mixin interfaces
|
|
15
|
-
const mixins = {};
|
|
16
|
-
const parsedInterfaces = [];
|
|
17
|
-
crawl.results.forEach(s => {
|
|
18
|
-
if (s.idlparsed) {
|
|
19
|
-
if (s.idlparsed.idlNames) {
|
|
20
|
-
Object.values(s.idlparsed.idlNames).forEach(dfn => {
|
|
21
|
-
if (dfn.type === 'interface' && !dfn.partial) {
|
|
22
|
-
parsedInterfaces.push(dfn);
|
|
23
|
-
}
|
|
24
|
-
});
|
|
25
|
-
}
|
|
26
|
-
if (s.idlparsed.idlExtendedNames) {
|
|
27
|
-
Object.keys(s.idlparsed.idlExtendedNames).forEach(n => {
|
|
28
|
-
s.idlparsed.idlExtendedNames[n].forEach(f => {
|
|
29
|
-
if (f.type === 'includes') {
|
|
30
|
-
if (!mixins[f.includes]) mixins[f.includes] = [];
|
|
31
|
-
mixins[f.includes].push(n);
|
|
32
|
-
}
|
|
33
|
-
});
|
|
34
|
-
});
|
|
35
|
-
}
|
|
36
|
-
}
|
|
37
|
-
});
|
|
38
|
-
|
|
39
|
-
// Only consider latest spec in a series to avoid fake duplicates
|
|
40
|
-
const results = crawl.results.filter(spec =>
|
|
41
|
-
(spec.seriesComposition !== 'delta' && isLatestLevelThatPasses(spec, crawl.results, s => s.events)) ||
|
|
42
|
-
(spec.seriesComposition === 'delta' && spec.events));
|
|
43
|
-
|
|
44
|
-
// Update events in place
|
|
45
|
-
const events = results.map(spec => spec.events.map(e => Object.assign({ spec: spec }, e))).flat();
|
|
46
|
-
for (const event of events) {
|
|
47
|
-
expandMixinTargets(event, mixins);
|
|
48
|
-
setBubblingPerTarget(event, parsedInterfaces);
|
|
49
|
-
}
|
|
50
|
-
|
|
51
|
-
// Consolidate events extended in other specs
|
|
52
|
-
const eventsToDrop = events
|
|
53
|
-
.filter(event => event.isExtension)
|
|
54
|
-
.map(event => {
|
|
55
|
-
const err = extendEvent(event, events);
|
|
56
|
-
if (err) {
|
|
57
|
-
// Event could not be extended, let's keep extension event
|
|
58
|
-
console.warn(err);
|
|
59
|
-
return null;
|
|
60
|
-
}
|
|
61
|
-
else {
|
|
62
|
-
// Event successfully extended, extension can be dropped
|
|
63
|
-
return event;
|
|
64
|
-
}
|
|
65
|
-
})
|
|
66
|
-
.filter(event => !!event);
|
|
67
|
-
|
|
68
|
-
// Before we clean and sort the result, we'll consolidate events that
|
|
69
|
-
// don't always bubble. We'll call them... "babbling" events. Such events
|
|
70
|
-
// should remain exceptions to the rule, and will likely be artificially
|
|
71
|
-
// created through some patching mechanism (in Webref typically) because
|
|
72
|
-
// the events extraction logic does not (yet?) support this scenario.
|
|
73
|
-
return events
|
|
74
|
-
.filter(event => !eventsToDrop.includes(event))
|
|
75
|
-
.filter(event => consolidateBabblingEvent(event, events))
|
|
76
|
-
.map(event => {
|
|
77
|
-
cleanTargetInterfaces(event, parsedInterfaces);
|
|
78
|
-
delete event.spec;
|
|
79
|
-
return event;
|
|
80
|
-
})
|
|
81
|
-
.sort((event1, event2) =>
|
|
82
|
-
event1.type.localeCompare(event2.type, 'en-US') ||
|
|
83
|
-
(!event2.interface ? -1 : 0) ||
|
|
84
|
-
(!event1.interface ? 1 : 0) ||
|
|
85
|
-
event1.interface.localeCompare(event2.interface, 'en-US') ||
|
|
86
|
-
(!event2.href ? -1 : 0) ||
|
|
87
|
-
(!event1.href ? 1 : 0) ||
|
|
88
|
-
event1.href.localeCompare(event2.href, 'en-US'));
|
|
89
|
-
}
|
|
90
|
-
};
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
function expandMixinTargets(event, mixins) {
|
|
94
|
-
const expandedTargets = event.targets?.map(i => mixins[i] || i)?.flat();
|
|
95
|
-
// This assumes a mixin matches more than one interface
|
|
96
|
-
if (expandedTargets && expandedTargets.length !== event.targets?.length) {
|
|
97
|
-
event.targets = expandedTargets;
|
|
98
|
-
return true;
|
|
99
|
-
}
|
|
100
|
-
return false;
|
|
101
|
-
}
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
function setBubblingPerTarget(event, parsedInterfaces) {
|
|
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
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
if
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
.filter(e =>
|
|
247
|
-
e !== event && !e.isExtension && !e.mergedIntoAnotherEvent &&
|
|
248
|
-
e.href && e.href === event.href && e.cancelable === event.cancelable)
|
|
249
|
-
.map(e => {
|
|
250
|
-
// Flag the event as merged so that we can filter it out afterwards
|
|
251
|
-
e.mergedIntoAnotherEvent = true;
|
|
252
|
-
return e.targets;
|
|
253
|
-
})
|
|
254
|
-
.flat();
|
|
255
|
-
if (newTargets.length > 0) {
|
|
256
|
-
event.targets = (event.targets || []).concat(newTargets);
|
|
257
|
-
}
|
|
258
|
-
return event;
|
|
259
|
-
}
|
|
1
|
+
/**
|
|
2
|
+
* Post-processing module that consolidates events extracts into one extract
|
|
3
|
+
* per event.
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import { isLatestLevelThatPasses, getInterfaceTreeInfo } from '../lib/util.js';
|
|
7
|
+
|
|
8
|
+
export default {
|
|
9
|
+
dependsOn: ['events'],
|
|
10
|
+
input: 'crawl',
|
|
11
|
+
property: 'events',
|
|
12
|
+
|
|
13
|
+
run: async function(crawl, options) {
|
|
14
|
+
// Collect list of mixin interfaces
|
|
15
|
+
const mixins = {};
|
|
16
|
+
const parsedInterfaces = [];
|
|
17
|
+
crawl.results.forEach(s => {
|
|
18
|
+
if (s.idlparsed) {
|
|
19
|
+
if (s.idlparsed.idlNames) {
|
|
20
|
+
Object.values(s.idlparsed.idlNames).forEach(dfn => {
|
|
21
|
+
if (dfn.type === 'interface' && !dfn.partial) {
|
|
22
|
+
parsedInterfaces.push(dfn);
|
|
23
|
+
}
|
|
24
|
+
});
|
|
25
|
+
}
|
|
26
|
+
if (s.idlparsed.idlExtendedNames) {
|
|
27
|
+
Object.keys(s.idlparsed.idlExtendedNames).forEach(n => {
|
|
28
|
+
s.idlparsed.idlExtendedNames[n].forEach(f => {
|
|
29
|
+
if (f.type === 'includes') {
|
|
30
|
+
if (!mixins[f.includes]) mixins[f.includes] = [];
|
|
31
|
+
mixins[f.includes].push(n);
|
|
32
|
+
}
|
|
33
|
+
});
|
|
34
|
+
});
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
});
|
|
38
|
+
|
|
39
|
+
// Only consider latest spec in a series to avoid fake duplicates
|
|
40
|
+
const results = crawl.results.filter(spec =>
|
|
41
|
+
(spec.seriesComposition !== 'delta' && isLatestLevelThatPasses(spec, crawl.results, s => s.events)) ||
|
|
42
|
+
(spec.seriesComposition === 'delta' && spec.events));
|
|
43
|
+
|
|
44
|
+
// Update events in place
|
|
45
|
+
const events = results.map(spec => spec.events.map(e => Object.assign({ spec: spec }, e))).flat();
|
|
46
|
+
for (const event of events) {
|
|
47
|
+
expandMixinTargets(event, mixins);
|
|
48
|
+
setBubblingPerTarget(event, parsedInterfaces);
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
// Consolidate events extended in other specs
|
|
52
|
+
const eventsToDrop = events
|
|
53
|
+
.filter(event => event.isExtension)
|
|
54
|
+
.map(event => {
|
|
55
|
+
const err = extendEvent(event, events);
|
|
56
|
+
if (err) {
|
|
57
|
+
// Event could not be extended, let's keep extension event
|
|
58
|
+
console.warn(err);
|
|
59
|
+
return null;
|
|
60
|
+
}
|
|
61
|
+
else {
|
|
62
|
+
// Event successfully extended, extension can be dropped
|
|
63
|
+
return event;
|
|
64
|
+
}
|
|
65
|
+
})
|
|
66
|
+
.filter(event => !!event);
|
|
67
|
+
|
|
68
|
+
// Before we clean and sort the result, we'll consolidate events that
|
|
69
|
+
// don't always bubble. We'll call them... "babbling" events. Such events
|
|
70
|
+
// should remain exceptions to the rule, and will likely be artificially
|
|
71
|
+
// created through some patching mechanism (in Webref typically) because
|
|
72
|
+
// the events extraction logic does not (yet?) support this scenario.
|
|
73
|
+
return events
|
|
74
|
+
.filter(event => !eventsToDrop.includes(event))
|
|
75
|
+
.filter(event => consolidateBabblingEvent(event, events))
|
|
76
|
+
.map(event => {
|
|
77
|
+
cleanTargetInterfaces(event, parsedInterfaces);
|
|
78
|
+
delete event.spec;
|
|
79
|
+
return event;
|
|
80
|
+
})
|
|
81
|
+
.sort((event1, event2) =>
|
|
82
|
+
event1.type.localeCompare(event2.type, 'en-US') ||
|
|
83
|
+
(!event2.interface ? -1 : 0) ||
|
|
84
|
+
(!event1.interface ? 1 : 0) ||
|
|
85
|
+
event1.interface.localeCompare(event2.interface, 'en-US') ||
|
|
86
|
+
(!event2.href ? -1 : 0) ||
|
|
87
|
+
(!event1.href ? 1 : 0) ||
|
|
88
|
+
event1.href.localeCompare(event2.href, 'en-US'));
|
|
89
|
+
}
|
|
90
|
+
};
|
|
91
|
+
|
|
92
|
+
|
|
93
|
+
function expandMixinTargets(event, mixins) {
|
|
94
|
+
const expandedTargets = event.targets?.map(i => mixins[i] || i)?.flat();
|
|
95
|
+
// This assumes a mixin matches more than one interface
|
|
96
|
+
if (expandedTargets && expandedTargets.length !== event.targets?.length) {
|
|
97
|
+
event.targets = expandedTargets;
|
|
98
|
+
return true;
|
|
99
|
+
}
|
|
100
|
+
return false;
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
|
|
104
|
+
function setBubblingPerTarget(event, parsedInterfaces) {
|
|
105
|
+
// If an event targets a non root interface in a tree, we can assume that
|
|
106
|
+
// bubbles is false (ideally, we should check the existence of the event
|
|
107
|
+
// handler on the root interface, but there is no easy way to get a
|
|
108
|
+
// consolidated IDL view of the root at the moment)
|
|
109
|
+
if (!event.targets) return;
|
|
110
|
+
const updatedTargets = [];
|
|
111
|
+
const treeInterfaces = [];
|
|
112
|
+
for (let iface of event.targets) {
|
|
113
|
+
const treeInfo = getInterfaceTreeInfo(iface, parsedInterfaces);
|
|
114
|
+
if (!treeInfo) {
|
|
115
|
+
updatedTargets.push({target: iface});
|
|
116
|
+
continue;
|
|
117
|
+
}
|
|
118
|
+
const { tree, depth, bubblingPath } = treeInfo;
|
|
119
|
+
if (depth === 0) {
|
|
120
|
+
// bubbling doesn't matter on the root interface
|
|
121
|
+
updatedTargets.push({target: iface});
|
|
122
|
+
} else {
|
|
123
|
+
treeInterfaces.push({ iface, bubblingPath });
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
if (!event.hasOwnProperty('bubbles') && treeInterfaces.length > 0) {
|
|
127
|
+
event.bubbles = false;
|
|
128
|
+
}
|
|
129
|
+
for (let { iface, bubblingPath } of treeInterfaces) {
|
|
130
|
+
updatedTargets.push(Object.assign(
|
|
131
|
+
{ target: iface, bubbles: event.bubbles },
|
|
132
|
+
event.bubbles ? { bubblingPath } : {}
|
|
133
|
+
));
|
|
134
|
+
}
|
|
135
|
+
event.targets = updatedTargets;
|
|
136
|
+
delete event.bubbles;
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
|
|
140
|
+
/**
|
|
141
|
+
* Filter the list of target interfaces to remove those that don't need to
|
|
142
|
+
* appear explicitly because they are de facto already covered by another entry
|
|
143
|
+
* in the list.
|
|
144
|
+
*
|
|
145
|
+
* Two reasons to drop a target interface t from the list:
|
|
146
|
+
* 1. There exists another target interface o with similar bubbling properties
|
|
147
|
+
* for the event and t inherits from o. If event fires at o, it can de facto
|
|
148
|
+
* fire at t.
|
|
149
|
+
* 2. There exists another target interface o such that t and o belong to the
|
|
150
|
+
* same bubbling tree, o is at a deeper level than t in the bubbling tree, and
|
|
151
|
+
* event bubbles when it fires at o. Event will de facto fire at t through
|
|
152
|
+
* bubbling when that happens.
|
|
153
|
+
*/
|
|
154
|
+
function cleanTargetInterfaces(event, parsedInterfaces) {
|
|
155
|
+
// Helper function that returns true if the iface interface inherits from the
|
|
156
|
+
// base interface
|
|
157
|
+
function inheritsFrom(iface, base) {
|
|
158
|
+
while (iface) {
|
|
159
|
+
if (iface === base) {
|
|
160
|
+
return true;
|
|
161
|
+
}
|
|
162
|
+
iface = parsedInterfaces.find(i => i.name === iface)?.inheritance;
|
|
163
|
+
}
|
|
164
|
+
return false;
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
if (!event.targets) {
|
|
168
|
+
return;
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
event.targets = event.targets
|
|
172
|
+
.filter(({ target, bubbles }) =>
|
|
173
|
+
// Drop if an ancestor in the inheritance chain is already there
|
|
174
|
+
!event.targets.find(({ target: other, bubbles: otherBubbles}) =>
|
|
175
|
+
target !== other &&
|
|
176
|
+
bubbles === otherBubbles &&
|
|
177
|
+
inheritsFrom(target, other)))
|
|
178
|
+
.filter(({ target, bubbles }) => {
|
|
179
|
+
// Drop if a deeper bubbling target interface in the tree is already there
|
|
180
|
+
const targetTreeInfo = getInterfaceTreeInfo(target, parsedInterfaces);
|
|
181
|
+
return !targetTreeInfo ||
|
|
182
|
+
!event.targets.find(({ target: other, bubbles: otherBubbles }) => {
|
|
183
|
+
if (other === target) {
|
|
184
|
+
return false;
|
|
185
|
+
}
|
|
186
|
+
const otherTreeInfo = getInterfaceTreeInfo(other, parsedInterfaces);
|
|
187
|
+
return otherTreeInfo?.tree === targetTreeInfo.tree &&
|
|
188
|
+
otherBubbles && otherTreeInfo.depth > targetTreeInfo.depth;
|
|
189
|
+
});
|
|
190
|
+
});
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
|
|
194
|
+
function extendEvent(event, events) {
|
|
195
|
+
const extendedEvent =
|
|
196
|
+
events.find(e => !e.isExtension && e.href === event.href) ||
|
|
197
|
+
events.find(e => !e.isExtension && event.href.startsWith(e.spec.crawled) && e.type === event.type);
|
|
198
|
+
if (!extendedEvent) {
|
|
199
|
+
// make this a fatal error
|
|
200
|
+
return `Found extended event with link ${event.href} in ${event.spec.shortname}, but did not find a matching original event`;
|
|
201
|
+
}
|
|
202
|
+
if (extendedEvent.interface && event.interface && extendedEvent.interface !== event.interface) {
|
|
203
|
+
return `Found extended event with link ${event.href} in ${event.spec.shortname} set to use interface ${event.interface}, different from original event interface ${extendedEvent.interface} in ${extendedEvent.spec.shortname}`;
|
|
204
|
+
}
|
|
205
|
+
// Document potential additional targets
|
|
206
|
+
const newTargets = event.targets?.filter(t => !extendedEvent.targets?.find(tt => tt.target === t.target));
|
|
207
|
+
if (newTargets) {
|
|
208
|
+
extendedEvent.targets = (extendedEvent.targets || []).concat(newTargets);
|
|
209
|
+
}
|
|
210
|
+
// Document the fact that the event has been extended
|
|
211
|
+
if (!extendedEvent.extendedIn) {
|
|
212
|
+
extendedEvent.extendedIn = [];
|
|
213
|
+
}
|
|
214
|
+
extendedEvent.extendedIn.push(Object.assign(
|
|
215
|
+
{ spec: event.spec.series.shortname },
|
|
216
|
+
event.src?.href ? { href: event.src?.href } : {}));
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
|
|
220
|
+
/**
|
|
221
|
+
* Consolidate events that got duplicated in the extract because they bubble
|
|
222
|
+
* or don't bubble depending on the target interface.
|
|
223
|
+
*
|
|
224
|
+
* We'll say that these events "babble" because they don't seem to know whether
|
|
225
|
+
* they bubble or not.
|
|
226
|
+
*/
|
|
227
|
+
function consolidateBabblingEvent(event, events) {
|
|
228
|
+
if (event.mergedIntoAnotherEvent) {
|
|
229
|
+
return null;
|
|
230
|
+
}
|
|
231
|
+
const newTargets = events
|
|
232
|
+
.filter(e =>
|
|
233
|
+
e !== event && !e.isExtension && !e.mergedIntoAnotherEvent &&
|
|
234
|
+
e.href && e.href === event.href && e.cancelable === event.cancelable)
|
|
235
|
+
.map(e => {
|
|
236
|
+
// Flag the event as merged so that we can filter it out afterwards
|
|
237
|
+
e.mergedIntoAnotherEvent = true;
|
|
238
|
+
return e.targets;
|
|
239
|
+
})
|
|
240
|
+
.flat();
|
|
241
|
+
if (newTargets.length > 0) {
|
|
242
|
+
event.targets = (event.targets || []).concat(newTargets);
|
|
243
|
+
}
|
|
244
|
+
return event;
|
|
245
|
+
}
|