UNPKG

15.2 kBJavaScriptView Raw
1var __assign = (this && this.__assign) || function () {
2 __assign = Object.assign || function(t) {
3 for (var s, i = 1, n = arguments.length; i < n; i++) {
4 s = arguments[i];
5 for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p))
6 t[p] = s[p];
7 }
8 return t;
9 };
10 return __assign.apply(this, arguments);
11};
12import xs from 'xstream';
13import { ScopeChecker } from './ScopeChecker';
14import { getSelectors, isEqualNamespace } from './utils';
15import { ElementFinder } from './ElementFinder';
16import SymbolTree from './SymbolTree';
17import PriorityQueue from './PriorityQueue';
18import { fromEvent, preventDefaultConditional, } from './fromEvent';
19export var eventTypesThatDontBubble = [
20 "blur",
21 "canplay",
22 "canplaythrough",
23 "durationchange",
24 "emptied",
25 "ended",
26 "focus",
27 "load",
28 "loadeddata",
29 "loadedmetadata",
30 "mouseenter",
31 "mouseleave",
32 "pause",
33 "play",
34 "playing",
35 "ratechange",
36 "reset",
37 "scroll",
38 "seeked",
39 "seeking",
40 "stalled",
41 "submit",
42 "suspend",
43 "timeupdate",
44 "unload",
45 "volumechange",
46 "waiting",
47];
48/**
49 * Manages "Event delegation", by connecting an origin with multiple
50 * destinations.
51 *
52 * Attaches a DOM event listener to the DOM element called the "origin",
53 * and delegates events to "destinations", which are subjects as outputs
54 * for the DOMSource. Simulates bubbling or capturing, with regards to
55 * isolation boundaries too.
56 */
57var EventDelegator = /** @class */ (function () {
58 function EventDelegator(rootElement$, isolateModule) {
59 var _this = this;
60 this.rootElement$ = rootElement$;
61 this.isolateModule = isolateModule;
62 this.virtualListeners = new SymbolTree(function (x) { return x.scope; });
63 this.nonBubblingListenersToAdd = new Set();
64 this.virtualNonBubblingListener = [];
65 this.isolateModule.setEventDelegator(this);
66 this.domListeners = new Map();
67 this.domListenersToAdd = new Map();
68 this.nonBubblingListeners = new Map();
69 rootElement$.addListener({
70 next: function (el) {
71 if (_this.origin !== el) {
72 _this.origin = el;
73 _this.resetEventListeners();
74 _this.domListenersToAdd.forEach(function (passive, type) {
75 return _this.setupDOMListener(type, passive);
76 });
77 _this.domListenersToAdd.clear();
78 }
79 _this.nonBubblingListenersToAdd.forEach(function (arr) {
80 _this.setupNonBubblingListener(arr);
81 });
82 },
83 });
84 }
85 EventDelegator.prototype.addEventListener = function (eventType, namespace, options, bubbles) {
86 var subject = xs.never();
87 var dest;
88 var scopeChecker = new ScopeChecker(namespace, this.isolateModule);
89 var shouldBubble = bubbles === undefined
90 ? eventTypesThatDontBubble.indexOf(eventType) === -1
91 : bubbles;
92 if (shouldBubble) {
93 if (!this.domListeners.has(eventType)) {
94 this.setupDOMListener(eventType, !!options.passive);
95 }
96 dest = this.insertListener(subject, scopeChecker, eventType, options);
97 return subject;
98 }
99 else {
100 var setArray_1 = [];
101 this.nonBubblingListenersToAdd.forEach(function (v) { return setArray_1.push(v); });
102 var found = undefined, index = 0;
103 var length_1 = setArray_1.length;
104 var tester = function (x) {
105 var _sub = x[0], et = x[1], ef = x[2], _ = x[3];
106 return eventType === et && isEqualNamespace(ef.namespace, namespace);
107 };
108 while (!found && index < length_1) {
109 var item = setArray_1[index];
110 found = tester(item) ? item : found;
111 index++;
112 }
113 var input_1 = found;
114 var nonBubbleSubject_1;
115 if (!input_1) {
116 var finder = new ElementFinder(namespace, this.isolateModule);
117 dest = this.insertListener(subject, scopeChecker, eventType, options);
118 input_1 = [subject, eventType, finder, dest];
119 nonBubbleSubject_1 = subject;
120 this.nonBubblingListenersToAdd.add(input_1);
121 this.setupNonBubblingListener(input_1);
122 }
123 else {
124 var sub = input_1[0];
125 nonBubbleSubject_1 = sub;
126 }
127 var self_1 = this;
128 var subscription_1 = null;
129 return xs.create({
130 start: function (listener) {
131 subscription_1 = nonBubbleSubject_1.subscribe(listener);
132 },
133 stop: function () {
134 var _s = input_1[0], et = input_1[1], ef = input_1[2], _d = input_1[3];
135 var elements = ef.call();
136 elements.forEach(function (element) {
137 var subs = element.subs;
138 if (subs && subs[et]) {
139 subs[et].unsubscribe();
140 delete subs[et];
141 }
142 });
143 self_1.nonBubblingListenersToAdd.delete(input_1);
144 subscription_1.unsubscribe();
145 }
146 });
147 }
148 };
149 EventDelegator.prototype.removeElement = function (element, namespace) {
150 if (namespace !== undefined) {
151 this.virtualListeners.delete(namespace);
152 }
153 var toRemove = [];
154 this.nonBubblingListeners.forEach(function (map, type) {
155 if (map.has(element)) {
156 toRemove.push([type, element]);
157 var subs_1 = element.subs;
158 if (subs_1) {
159 Object.keys(subs_1).forEach(function (key) {
160 subs_1[key].unsubscribe();
161 });
162 }
163 }
164 });
165 for (var i = 0; i < toRemove.length; i++) {
166 var map = this.nonBubblingListeners.get(toRemove[i][0]);
167 if (!map) {
168 continue;
169 }
170 map.delete(toRemove[i][1]);
171 if (map.size === 0) {
172 this.nonBubblingListeners.delete(toRemove[i][0]);
173 }
174 else {
175 this.nonBubblingListeners.set(toRemove[i][0], map);
176 }
177 }
178 };
179 EventDelegator.prototype.insertListener = function (subject, scopeChecker, eventType, options) {
180 var relevantSets = [];
181 var n = scopeChecker._namespace;
182 var max = n.length;
183 do {
184 relevantSets.push(this.getVirtualListeners(eventType, n, true, max));
185 max--;
186 } while (max >= 0 && n[max].type !== 'total');
187 var destination = __assign({}, options, { scopeChecker: scopeChecker,
188 subject: subject, bubbles: !!options.bubbles, useCapture: !!options.useCapture, passive: !!options.passive });
189 for (var i = 0; i < relevantSets.length; i++) {
190 relevantSets[i].add(destination, n.length);
191 }
192 return destination;
193 };
194 /**
195 * Returns a set of all virtual listeners in the scope of the namespace
196 * Set `exact` to true to treat sibiling isolated scopes as total scopes
197 */
198 EventDelegator.prototype.getVirtualListeners = function (eventType, namespace, exact, max) {
199 if (exact === void 0) { exact = false; }
200 var _max = max !== undefined ? max : namespace.length;
201 if (!exact) {
202 for (var i = _max - 1; i >= 0; i--) {
203 if (namespace[i].type === 'total') {
204 _max = i + 1;
205 break;
206 }
207 _max = i;
208 }
209 }
210 var map = this.virtualListeners.getDefault(namespace, function () { return new Map(); }, _max);
211 if (!map.has(eventType)) {
212 map.set(eventType, new PriorityQueue());
213 }
214 return map.get(eventType);
215 };
216 EventDelegator.prototype.setupDOMListener = function (eventType, passive) {
217 var _this = this;
218 if (this.origin) {
219 var sub = fromEvent(this.origin, eventType, false, false, passive).subscribe({
220 next: function (event) { return _this.onEvent(eventType, event, passive); },
221 error: function () { },
222 complete: function () { },
223 });
224 this.domListeners.set(eventType, { sub: sub, passive: passive });
225 }
226 else {
227 this.domListenersToAdd.set(eventType, passive);
228 }
229 };
230 EventDelegator.prototype.setupNonBubblingListener = function (input) {
231 var _ = input[0], eventType = input[1], elementFinder = input[2], destination = input[3];
232 if (!this.origin) {
233 return;
234 }
235 var elements = elementFinder.call();
236 if (elements.length) {
237 var self_2 = this;
238 elements.forEach(function (element) {
239 var _a;
240 var subs = element.subs;
241 if (!subs || !subs[eventType]) {
242 var sub = fromEvent(element, eventType, false, false, destination.passive).subscribe({
243 next: function (ev) {
244 return self_2.onEvent(eventType, ev, !!destination.passive, false);
245 },
246 error: function () { },
247 complete: function () { },
248 });
249 if (!self_2.nonBubblingListeners.has(eventType)) {
250 self_2.nonBubblingListeners.set(eventType, new Map());
251 }
252 var map = self_2.nonBubblingListeners.get(eventType);
253 if (!map) {
254 return;
255 }
256 map.set(element, { sub: sub, destination: destination });
257 element.subs = __assign({}, subs, (_a = {}, _a[eventType] = sub, _a));
258 }
259 });
260 }
261 };
262 EventDelegator.prototype.resetEventListeners = function () {
263 var iter = this.domListeners.entries();
264 var curr = iter.next();
265 while (!curr.done) {
266 var _a = curr.value, type = _a[0], _b = _a[1], sub = _b.sub, passive = _b.passive;
267 sub.unsubscribe();
268 this.setupDOMListener(type, passive);
269 curr = iter.next();
270 }
271 };
272 EventDelegator.prototype.putNonBubblingListener = function (eventType, elm, useCapture, passive) {
273 var map = this.nonBubblingListeners.get(eventType);
274 if (!map) {
275 return;
276 }
277 var listener = map.get(elm);
278 if (listener &&
279 listener.destination.passive === passive &&
280 listener.destination.useCapture === useCapture) {
281 this.virtualNonBubblingListener[0] = listener.destination;
282 }
283 };
284 EventDelegator.prototype.onEvent = function (eventType, event, passive, bubbles) {
285 if (bubbles === void 0) { bubbles = true; }
286 var cycleEvent = this.patchEvent(event);
287 var rootElement = this.isolateModule.getRootElement(event.target);
288 if (bubbles) {
289 var namespace = this.isolateModule.getNamespace(event.target);
290 if (!namespace) {
291 return;
292 }
293 var listeners = this.getVirtualListeners(eventType, namespace);
294 this.bubble(eventType, event.target, rootElement, cycleEvent, listeners, namespace, namespace.length - 1, true, passive);
295 this.bubble(eventType, event.target, rootElement, cycleEvent, listeners, namespace, namespace.length - 1, false, passive);
296 }
297 else {
298 this.putNonBubblingListener(eventType, event.target, true, passive);
299 this.doBubbleStep(eventType, event.target, rootElement, cycleEvent, this.virtualNonBubblingListener, true, passive);
300 this.putNonBubblingListener(eventType, event.target, false, passive);
301 this.doBubbleStep(eventType, event.target, rootElement, cycleEvent, this.virtualNonBubblingListener, false, passive);
302 event.stopPropagation(); //fix reset event (spec'ed as non-bubbling, but bubbles in reality
303 }
304 };
305 EventDelegator.prototype.bubble = function (eventType, elm, rootElement, event, listeners, namespace, index, useCapture, passive) {
306 if (!useCapture && !event.propagationHasBeenStopped) {
307 this.doBubbleStep(eventType, elm, rootElement, event, listeners, useCapture, passive);
308 }
309 var newRoot = rootElement;
310 var newIndex = index;
311 if (elm === rootElement) {
312 if (index >= 0 && namespace[index].type === 'sibling') {
313 newRoot = this.isolateModule.getElement(namespace, index);
314 newIndex--;
315 }
316 else {
317 return;
318 }
319 }
320 if (elm.parentNode && newRoot) {
321 this.bubble(eventType, elm.parentNode, newRoot, event, listeners, namespace, newIndex, useCapture, passive);
322 }
323 if (useCapture && !event.propagationHasBeenStopped) {
324 this.doBubbleStep(eventType, elm, rootElement, event, listeners, useCapture, passive);
325 }
326 };
327 EventDelegator.prototype.doBubbleStep = function (eventType, elm, rootElement, event, listeners, useCapture, passive) {
328 if (!rootElement) {
329 return;
330 }
331 this.mutateEventCurrentTarget(event, elm);
332 listeners.forEach(function (dest) {
333 if (dest.passive === passive && dest.useCapture === useCapture) {
334 var sel = getSelectors(dest.scopeChecker.namespace);
335 if (!event.propagationHasBeenStopped &&
336 dest.scopeChecker.isDirectlyInScope(elm) &&
337 ((sel !== '' && elm.matches(sel)) ||
338 (sel === '' && elm === rootElement))) {
339 preventDefaultConditional(event, dest.preventDefault);
340 dest.subject.shamefullySendNext(event);
341 }
342 }
343 });
344 };
345 EventDelegator.prototype.patchEvent = function (event) {
346 var pEvent = event;
347 pEvent.propagationHasBeenStopped = false;
348 var oldStopPropagation = pEvent.stopPropagation;
349 pEvent.stopPropagation = function stopPropagation() {
350 oldStopPropagation.call(this);
351 this.propagationHasBeenStopped = true;
352 };
353 return pEvent;
354 };
355 EventDelegator.prototype.mutateEventCurrentTarget = function (event, currentTargetElement) {
356 try {
357 Object.defineProperty(event, "currentTarget", {
358 value: currentTargetElement,
359 configurable: true,
360 });
361 }
362 catch (err) {
363 console.log("please use event.ownerTarget");
364 }
365 event.ownerTarget = currentTargetElement;
366 };
367 return EventDelegator;
368}());
369export { EventDelegator };
370//# sourceMappingURL=EventDelegator.js.map
\No newline at end of file