UNPKG

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