@angular/core 18.0.0-next.4 → 18.0.0-next.6

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.
Files changed (94) hide show
  1. package/esm2022/primitives/event-dispatch/contract_binary.mjs +10 -0
  2. package/esm2022/primitives/event-dispatch/index.mjs +13 -0
  3. package/esm2022/primitives/event-dispatch/src/a11y_click.mjs +54 -0
  4. package/esm2022/primitives/event-dispatch/src/accessibility.mjs +35 -0
  5. package/esm2022/primitives/event-dispatch/src/attribute.mjs +72 -0
  6. package/esm2022/primitives/event-dispatch/src/base_dispatcher.mjs +196 -0
  7. package/esm2022/primitives/event-dispatch/src/cache.mjs +95 -0
  8. package/esm2022/primitives/event-dispatch/src/char.mjs +35 -0
  9. package/esm2022/primitives/event-dispatch/src/custom_events.mjs +63 -0
  10. package/esm2022/primitives/event-dispatch/src/dispatcher.mjs +254 -0
  11. package/esm2022/primitives/event-dispatch/src/dom.mjs +48 -0
  12. package/esm2022/primitives/event-dispatch/src/earlyeventcontract.mjs +36 -0
  13. package/esm2022/primitives/event-dispatch/src/event.mjs +638 -0
  14. package/esm2022/primitives/event-dispatch/src/event_contract_container.mjs +63 -0
  15. package/esm2022/primitives/event-dispatch/src/event_contract_defines.mjs +48 -0
  16. package/esm2022/primitives/event-dispatch/src/event_contract_multi_container.mjs +192 -0
  17. package/esm2022/primitives/event-dispatch/src/event_handler.mjs +9 -0
  18. package/esm2022/primitives/event-dispatch/src/event_info.mjs +199 -0
  19. package/esm2022/primitives/event-dispatch/src/event_type.mjs +244 -0
  20. package/esm2022/primitives/event-dispatch/src/eventcontract.mjs +675 -0
  21. package/esm2022/primitives/event-dispatch/src/key_code.mjs +21 -0
  22. package/esm2022/primitives/event-dispatch/src/legacy_dispatcher.mjs +9 -0
  23. package/esm2022/primitives/event-dispatch/src/property.mjs +35 -0
  24. package/esm2022/primitives/event-dispatch/src/register_events.mjs +32 -0
  25. package/esm2022/primitives/event-dispatch/src/replay.mjs +389 -0
  26. package/esm2022/primitives/event-dispatch/src/restriction.mjs +15 -0
  27. package/esm2022/primitives/signals/index.mjs +3 -3
  28. package/esm2022/primitives/signals/src/computed.mjs +5 -3
  29. package/esm2022/primitives/signals/src/graph.mjs +14 -9
  30. package/esm2022/primitives/signals/src/signal.mjs +2 -2
  31. package/esm2022/primitives/signals/src/watch.mjs +2 -2
  32. package/esm2022/src/application/application_ref.mjs +4 -2
  33. package/esm2022/src/change_detection/scheduling/flags.mjs +10 -0
  34. package/esm2022/src/change_detection/scheduling/ng_zone_scheduling.mjs +18 -33
  35. package/esm2022/src/change_detection/scheduling/zoneless_scheduling.mjs +1 -1
  36. package/esm2022/src/change_detection/scheduling/zoneless_scheduling_impl.mjs +111 -9
  37. package/esm2022/src/core.mjs +3 -1
  38. package/esm2022/src/core_private_export.mjs +2 -3
  39. package/esm2022/src/debug/debug_node.mjs +1 -1
  40. package/esm2022/src/defer/instructions.mjs +31 -8
  41. package/esm2022/src/di/host_tag_name_token.mjs +65 -0
  42. package/esm2022/src/di/index.mjs +2 -1
  43. package/esm2022/src/errors.mjs +1 -1
  44. package/esm2022/src/hydration/annotate.mjs +15 -1
  45. package/esm2022/src/hydration/event_replay.mjs +181 -0
  46. package/esm2022/src/hydration/tokens.mjs +6 -1
  47. package/esm2022/src/metadata/directives.mjs +1 -62
  48. package/esm2022/src/pending_tasks.mjs +54 -11
  49. package/esm2022/src/platform/platform_ref.mjs +3 -3
  50. package/esm2022/src/render3/component_ref.mjs +1 -1
  51. package/esm2022/src/render3/context_discovery.mjs +15 -1
  52. package/esm2022/src/render3/instructions/control_flow.mjs +55 -8
  53. package/esm2022/src/render3/list_reconciliation.mjs +41 -1
  54. package/esm2022/src/testability/testability.mjs +2 -30
  55. package/esm2022/src/util/callback_scheduler.mjs +14 -3
  56. package/esm2022/src/version.mjs +1 -1
  57. package/esm2022/src/zone/ng_zone.mjs +22 -6
  58. package/esm2022/testing/src/async.mjs +2 -10
  59. package/esm2022/testing/src/component_fixture.mjs +7 -12
  60. package/esm2022/testing/src/defer.mjs +1 -2
  61. package/esm2022/testing/src/logger.mjs +3 -3
  62. package/esm2022/testing/src/test_bed_common.mjs +1 -6
  63. package/esm2022/testing/src/testing.mjs +1 -2
  64. package/event-dispatch-contract.min.js +1 -0
  65. package/fesm2022/core.mjs +608 -174
  66. package/fesm2022/core.mjs.map +1 -1
  67. package/fesm2022/primitives/event-dispatch.mjs +3044 -0
  68. package/fesm2022/primitives/event-dispatch.mjs.map +1 -0
  69. package/fesm2022/primitives/signals.mjs +17 -10
  70. package/fesm2022/primitives/signals.mjs.map +1 -1
  71. package/fesm2022/rxjs-interop.mjs +1 -1
  72. package/fesm2022/testing.mjs +35 -54
  73. package/fesm2022/testing.mjs.map +1 -1
  74. package/index.d.ts +199 -115
  75. package/package.json +10 -1
  76. package/primitives/event-dispatch/index.d.ts +627 -0
  77. package/primitives/signals/index.d.ts +1 -1
  78. package/rxjs-interop/index.d.ts +1 -1
  79. package/schematics/migrations/{transfer-state → http-providers}/bundle.js +254 -68
  80. package/schematics/migrations/http-providers/bundle.js.map +7 -0
  81. package/schematics/migrations/invalid-two-way-bindings/bundle.js +690 -413
  82. package/schematics/migrations/invalid-two-way-bindings/bundle.js.map +3 -3
  83. package/schematics/migrations.json +6 -16
  84. package/schematics/ng-generate/control-flow-migration/bundle.js +693 -417
  85. package/schematics/ng-generate/control-flow-migration/bundle.js.map +3 -3
  86. package/schematics/ng-generate/standalone-migration/bundle.js +1072 -735
  87. package/schematics/ng-generate/standalone-migration/bundle.js.map +3 -3
  88. package/testing/index.d.ts +2 -15
  89. package/esm2022/testing/src/private_export.mjs +0 -9
  90. package/schematics/migrations/block-template-entities/bundle.js +0 -22853
  91. package/schematics/migrations/block-template-entities/bundle.js.map +0 -7
  92. package/schematics/migrations/compiler-options/bundle.js +0 -582
  93. package/schematics/migrations/compiler-options/bundle.js.map +0 -7
  94. package/schematics/migrations/transfer-state/bundle.js.map +0 -7
@@ -0,0 +1,3044 @@
1
+ /**
2
+ * @license Angular v18.0.0-next.6
3
+ * (c) 2010-2024 Google LLC. https://angular.io/
4
+ * License: MIT
5
+ */
6
+
7
+ /**
8
+ * Defines special EventInfo and Event properties used when
9
+ * A11Y_SUPPORT_IN_DISPATCHER is enabled.
10
+ */
11
+ var Attribute$1;
12
+ (function (Attribute) {
13
+ /**
14
+ * An event-type set when the event contract detects a KEYDOWN event but
15
+ * doesn't know if the key press can be treated like a click. The dispatcher
16
+ * will use this event-type to parse the keypress and handle it accordingly.
17
+ */
18
+ Attribute["MAYBE_CLICK_EVENT_TYPE"] = "maybe_click";
19
+ /**
20
+ * A property added to a dispatched event that had the MAYBE_CLICK_EVENTTYPE
21
+ * event-type but could not be used as a click. The dispatcher sets this
22
+ * property for non-global dispatches before it retriggers the event and it
23
+ * signifies that the event contract should not dispatch this event globally.
24
+ */
25
+ Attribute["SKIP_GLOBAL_DISPATCH"] = "a11ysgd";
26
+ /**
27
+ * A property added to a dispatched event that had the MAYBE_CLICK_EVENTTYPE
28
+ * event-type but could not be used as a click. The dispatcher sets this
29
+ * property before it retriggers the event and it signifies that the event
30
+ * contract should not look at CLICK actions for KEYDOWN events.
31
+ */
32
+ Attribute["SKIP_A11Y_CHECK"] = "a11ysc";
33
+ })(Attribute$1 || (Attribute$1 = {}));
34
+
35
+ /**
36
+ * Determines if one node is contained within another. Adapted from
37
+ * {@see goog.dom.contains}.
38
+ * @param node Node that should contain otherNode.
39
+ * @param otherNode Node being contained.
40
+ * @return True if otherNode is contained within node.
41
+ */
42
+ function contains(node, otherNode) {
43
+ if (otherNode === null) {
44
+ return false;
45
+ }
46
+ // We use browser specific methods for this if available since it is faster
47
+ // that way.
48
+ // IE DOM
49
+ if ('contains' in node && otherNode.nodeType === 1) {
50
+ return node.contains(otherNode);
51
+ }
52
+ // W3C DOM Level 3
53
+ if ('compareDocumentPosition' in node) {
54
+ return node === otherNode || Boolean(node.compareDocumentPosition(otherNode) & 16);
55
+ }
56
+ // W3C DOM Level 1
57
+ while (otherNode && node !== otherNode) {
58
+ otherNode = otherNode.parentNode;
59
+ }
60
+ return otherNode === node;
61
+ }
62
+ /**
63
+ * Helper method for broadcastCustomEvent. Returns true if any member of
64
+ * the set is an ancestor of element.
65
+ */
66
+ function hasAncestorInNodeList(element, nodeList) {
67
+ for (let idx = 0; idx < nodeList.length; ++idx) {
68
+ const member = nodeList[idx];
69
+ if (member !== element && contains(member, element)) {
70
+ return true;
71
+ }
72
+ }
73
+ return false;
74
+ }
75
+
76
+ /*
77
+ * Names of events that are special to jsaction. These are not all
78
+ * event types that are legal to use in either HTML or the addEvent()
79
+ * API, but these are the ones that are treated specially. All other
80
+ * DOM events can be used in either addEvent() or in the value of the
81
+ * jsaction attribute. Beware of browser specific events or events
82
+ * that don't bubble though: If they are not mentioned here, then
83
+ * event contract doesn't work around their peculiarities.
84
+ */
85
+ const EventType = {
86
+ /**
87
+ * Mouse middle click, introduced in Chrome 55 and not yet supported on
88
+ * other browsers.
89
+ */
90
+ AUXCLICK: 'auxclick',
91
+ /**
92
+ * The change event fired by browsers when the `value` attribute of input,
93
+ * select, and textarea elements are changed.
94
+ */
95
+ CHANGE: 'change',
96
+ /**
97
+ * The click event. In addEvent() refers to all click events, in the
98
+ * jsaction attribute it refers to the unmodified click and Enter/Space
99
+ * keypress events. In the latter case, a jsaction click will be triggered,
100
+ * for accessibility reasons. See clickmod and clickonly, below.
101
+ */
102
+ CLICK: 'click',
103
+ /**
104
+ * Specifies the jsaction for a modified click event (i.e. a mouse
105
+ * click with the modifier key Cmd/Ctrl pressed). This event isn't
106
+ * separately enabled in addEvent(), because in the DOM, it's just a
107
+ * click event.
108
+ */
109
+ CLICKMOD: 'clickmod',
110
+ /**
111
+ * Specifies the jsaction for a click-only event. Click-only doesn't take
112
+ * into account the case where an element with focus receives an Enter/Space
113
+ * keypress. This event isn't separately enabled in addEvent().
114
+ */
115
+ CLICKONLY: 'clickonly',
116
+ /**
117
+ * The dblclick event.
118
+ */
119
+ DBLCLICK: 'dblclick',
120
+ /**
121
+ * Focus doesn't bubble, but you can use it in addEvent() and
122
+ * jsaction anyway. EventContract does the right thing under the
123
+ * hood.
124
+ */
125
+ FOCUS: 'focus',
126
+ /**
127
+ * This event only exists in IE. For addEvent() and jsaction, use
128
+ * focus instead; EventContract does the right thing even though
129
+ * focus doesn't bubble.
130
+ */
131
+ FOCUSIN: 'focusin',
132
+ /**
133
+ * Analog to focus.
134
+ */
135
+ BLUR: 'blur',
136
+ /**
137
+ * Analog to focusin.
138
+ */
139
+ FOCUSOUT: 'focusout',
140
+ /**
141
+ * Submit doesn't bubble, so it cannot be used with event
142
+ * contract. However, the browser helpfully fires a click event on
143
+ * the submit button of a form (even if the form is not submitted by
144
+ * a click on the submit button). So you should handle click on the
145
+ * submit button instead.
146
+ */
147
+ SUBMIT: 'submit',
148
+ /**
149
+ * The keydown event. In addEvent() and non-click jsaction it represents the
150
+ * regular DOM keydown event. It represents click actions in non-Gecko
151
+ * browsers.
152
+ */
153
+ KEYDOWN: 'keydown',
154
+ /**
155
+ * The keypress event. In addEvent() and non-click jsaction it represents the
156
+ * regular DOM keypress event. It represents click actions in Gecko browsers.
157
+ */
158
+ KEYPRESS: 'keypress',
159
+ /**
160
+ * The keyup event. In addEvent() and non-click jsaction it represents the
161
+ * regular DOM keyup event. It represents click actions in non-Gecko
162
+ * browsers.
163
+ */
164
+ KEYUP: 'keyup',
165
+ /**
166
+ * The mouseup event. Can either be used directly or used implicitly to
167
+ * capture mouseup events. In addEvent(), it represents a regular DOM
168
+ * mouseup event.
169
+ */
170
+ MOUSEUP: 'mouseup',
171
+ /**
172
+ * The mousedown event. Can either be used directly or used implicitly to
173
+ * capture mouseenter events. In addEvent(), it represents a regular DOM
174
+ * mouseover event.
175
+ */
176
+ MOUSEDOWN: 'mousedown',
177
+ /**
178
+ * The mouseover event. Can either be used directly or used implicitly to
179
+ * capture mouseenter events. In addEvent(), it represents a regular DOM
180
+ * mouseover event.
181
+ */
182
+ MOUSEOVER: 'mouseover',
183
+ /**
184
+ * The mouseout event. Can either be used directly or used implicitly to
185
+ * capture mouseover events. In addEvent(), it represents a regular DOM
186
+ * mouseout event.
187
+ */
188
+ MOUSEOUT: 'mouseout',
189
+ /**
190
+ * The mouseenter event. Does not bubble and fires individually on each
191
+ * element being entered within a DOM tree.
192
+ */
193
+ MOUSEENTER: 'mouseenter',
194
+ /**
195
+ * The mouseleave event. Does not bubble and fires individually on each
196
+ * element being entered within a DOM tree.
197
+ */
198
+ MOUSELEAVE: 'mouseleave',
199
+ /**
200
+ * The mousemove event.
201
+ */
202
+ MOUSEMOVE: 'mousemove',
203
+ /**
204
+ * The pointerup event. Can either be used directly or used implicitly to
205
+ * capture pointerup events. In addEvent(), it represents a regular DOM
206
+ * pointerup event.
207
+ */
208
+ POINTERUP: 'pointerup',
209
+ /**
210
+ * The pointerdown event. Can either be used directly or used implicitly to
211
+ * capture pointerenter events. In addEvent(), it represents a regular DOM
212
+ * mouseover event.
213
+ */
214
+ POINTERDOWN: 'pointerdown',
215
+ /**
216
+ * The pointerover event. Can either be used directly or used implicitly to
217
+ * capture pointerenter events. In addEvent(), it represents a regular DOM
218
+ * pointerover event.
219
+ */
220
+ POINTEROVER: 'pointerover',
221
+ /**
222
+ * The pointerout event. Can either be used directly or used implicitly to
223
+ * capture pointerover events. In addEvent(), it represents a regular DOM
224
+ * pointerout event.
225
+ */
226
+ POINTEROUT: 'pointerout',
227
+ /**
228
+ * The pointerenter event. Does not bubble and fires individually on each
229
+ * element being entered within a DOM tree.
230
+ */
231
+ POINTERENTER: 'pointerenter',
232
+ /**
233
+ * The pointerleave event. Does not bubble and fires individually on each
234
+ * element being entered within a DOM tree.
235
+ */
236
+ POINTERLEAVE: 'pointerleave',
237
+ /**
238
+ * The pointermove event.
239
+ */
240
+ POINTERMOVE: 'pointermove',
241
+ /**
242
+ * The pointercancel event.
243
+ */
244
+ POINTERCANCEL: 'pointercancel',
245
+ /**
246
+ * The gotpointercapture event is fired when
247
+ * Element.setPointerCapture(pointerId) is called on a mouse input, or
248
+ * implicitly when a touch input begins.
249
+ */
250
+ GOTPOINTERCAPTURE: 'gotpointercapture',
251
+ /**
252
+ * The lostpointercapture event is fired when
253
+ * Element.releasePointerCapture(pointerId) is called, or implicitly after a
254
+ * touch input ends.
255
+ */
256
+ LOSTPOINTERCAPTURE: 'lostpointercapture',
257
+ /**
258
+ * The error event. The error event doesn't bubble, but you can use it in
259
+ * addEvent() and jsaction anyway. EventContract does the right thing under
260
+ * the hood (except in IE8 which does not use error events).
261
+ */
262
+ ERROR: 'error',
263
+ /**
264
+ * The load event. The load event doesn't bubble, but you can use it in
265
+ * addEvent() and jsaction anyway. EventContract does the right thing
266
+ * under the hood.
267
+ */
268
+ LOAD: 'load',
269
+ /**
270
+ * The unload event.
271
+ */
272
+ UNLOAD: 'unload',
273
+ /**
274
+ * The touchstart event. Bubbles, will only ever fire in browsers with
275
+ * touch support.
276
+ */
277
+ TOUCHSTART: 'touchstart',
278
+ /**
279
+ * The touchend event. Bubbles, will only ever fire in browsers with
280
+ * touch support.
281
+ */
282
+ TOUCHEND: 'touchend',
283
+ /**
284
+ * The touchmove event. Bubbles, will only ever fire in browsers with
285
+ * touch support.
286
+ */
287
+ TOUCHMOVE: 'touchmove',
288
+ /**
289
+ * The input event.
290
+ */
291
+ INPUT: 'input',
292
+ /**
293
+ * The scroll event.
294
+ */
295
+ SCROLL: 'scroll',
296
+ /**
297
+ * The toggle event. The toggle event doesn't bubble, but you can use it in
298
+ * addEvent() and jsaction anyway. EventContract does the right thing
299
+ * under the hood.
300
+ */
301
+ TOGGLE: 'toggle',
302
+ /**
303
+ * A custom event. The actual custom event type is declared as the 'type'
304
+ * field in the event details. Supported in Firefox 6+, IE 9+, and all Chrome
305
+ * versions.
306
+ *
307
+ * This is an internal name. Users should use jsaction's fireCustomEvent to
308
+ * fire custom events instead of relying on this type to create them.
309
+ */
310
+ CUSTOM: '_custom',
311
+ };
312
+
313
+ /** Special keycodes used by jsaction for the generic click action. */
314
+ var KeyCode;
315
+ (function (KeyCode) {
316
+ /**
317
+ * If on a Macintosh with an extended keyboard, the Enter key located in the
318
+ * numeric pad has a different ASCII code.
319
+ */
320
+ KeyCode[KeyCode["MAC_ENTER"] = 3] = "MAC_ENTER";
321
+ /** The Enter key. */
322
+ KeyCode[KeyCode["ENTER"] = 13] = "ENTER";
323
+ /** The Space key. */
324
+ KeyCode[KeyCode["SPACE"] = 32] = "SPACE";
325
+ })(KeyCode || (KeyCode = {}));
326
+
327
+ /**
328
+ * Gets a browser event type, if it would differ from the JSAction event type.
329
+ */
330
+ function getBrowserEventType(eventType) {
331
+ // Mouseenter and mouseleave events are not handled directly because they
332
+ // are not available everywhere. In browsers where they are available, they
333
+ // don't bubble and aren't visible at the container boundary. Instead, we
334
+ // synthesize the mouseenter and mouseleave events from mouseover and
335
+ // mouseout events, respectively. Cf. eventcontract.js.
336
+ if (eventType === EventType.MOUSEENTER) {
337
+ return EventType.MOUSEOVER;
338
+ }
339
+ else if (eventType === EventType.MOUSELEAVE) {
340
+ return EventType.MOUSEOUT;
341
+ }
342
+ else if (eventType === EventType.POINTERENTER) {
343
+ return EventType.POINTEROVER;
344
+ }
345
+ else if (eventType === EventType.POINTERLEAVE) {
346
+ return EventType.POINTEROUT;
347
+ }
348
+ return eventType;
349
+ }
350
+ /**
351
+ * Registers the event handler function with the given DOM element for
352
+ * the given event type.
353
+ *
354
+ * @param element The element.
355
+ * @param eventType The event type.
356
+ * @param handler The handler function to install.
357
+ * @return Information needed to uninstall the event handler eventually.
358
+ */
359
+ function addEventListener(element, eventType, handler) {
360
+ // All event handlers are registered in the bubbling
361
+ // phase.
362
+ //
363
+ // All browsers support focus and blur, but these events only are propagated
364
+ // in the capture phase. Very legacy browsers do not support focusin or
365
+ // focusout.
366
+ //
367
+ // It would be a bad idea to register all event handlers in the
368
+ // capture phase because then regular onclick handlers would not be
369
+ // executed at all on events that trigger a jsaction. That's not
370
+ // entirely what we want, at least for now.
371
+ //
372
+ // Error and load events (i.e. on images) do not bubble so they are also
373
+ // handled in the capture phase.
374
+ let capture = false;
375
+ if (eventType === EventType.FOCUS ||
376
+ eventType === EventType.BLUR ||
377
+ eventType === EventType.ERROR ||
378
+ eventType === EventType.LOAD ||
379
+ eventType === EventType.TOGGLE) {
380
+ capture = true;
381
+ }
382
+ element.addEventListener(eventType, handler, capture);
383
+ return { eventType, handler, capture };
384
+ }
385
+ /**
386
+ * Removes the event handler for the given event from the element.
387
+ * the given event type.
388
+ *
389
+ * @param element The element.
390
+ * @param info The information needed to deregister the handler, as returned by
391
+ * addEventListener(), above.
392
+ */
393
+ function removeEventListener(element, info) {
394
+ if (element.removeEventListener) {
395
+ element.removeEventListener(info.eventType, info.handler, info.capture);
396
+ // `detachEvent` is an old DOM API.
397
+ // tslint:disable-next-line:no-any
398
+ }
399
+ else if (element.detachEvent) {
400
+ // `detachEvent` is an old DOM API.
401
+ // tslint:disable-next-line:no-any
402
+ element.detachEvent(`on${info.eventType}`, info.handler);
403
+ }
404
+ }
405
+ /**
406
+ * Cancels propagation of an event.
407
+ * @param e The event to cancel propagation for.
408
+ */
409
+ function stopPropagation$1(e) {
410
+ e.stopPropagation ? e.stopPropagation() : (e.cancelBubble = true);
411
+ }
412
+ /**
413
+ * Prevents the default action of an event.
414
+ * @param e The event to prevent the default action for.
415
+ */
416
+ function preventDefault(e) {
417
+ e.preventDefault ? e.preventDefault() : (e.returnValue = false);
418
+ }
419
+ /**
420
+ * Gets the target Element of the event. In Firefox, a text node may appear as
421
+ * the target of the event, in which case we return the parent element of the
422
+ * text node.
423
+ * @param e The event to get the target of.
424
+ * @return The target element.
425
+ */
426
+ function getTarget(e) {
427
+ let el = e.target;
428
+ // In Firefox, the event may have a text node as its target. We always
429
+ // want the parent Element the text node belongs to, however.
430
+ if (!el.getAttribute && el.parentNode) {
431
+ el = el.parentNode;
432
+ }
433
+ return el;
434
+ }
435
+ /**
436
+ * Whether we are on a Mac. Not pulling in useragent just for this.
437
+ */
438
+ let isMac = typeof navigator !== 'undefined' && /Macintosh/.test(navigator.userAgent);
439
+ /**
440
+ * Determines and returns whether the given event (which is assumed to be a
441
+ * click event) is a middle click.
442
+ * NOTE: There is not a consistent way to identify middle click
443
+ * http://www.unixpapa.com/js/mouse.html
444
+ */
445
+ function isMiddleClick(e) {
446
+ return (
447
+ // `which` is an old DOM API.
448
+ // tslint:disable-next-line:no-any
449
+ e.which === 2 ||
450
+ // `which` is an old DOM API.
451
+ // tslint:disable-next-line:no-any
452
+ (e.which == null &&
453
+ // `button` is an old DOM API.
454
+ // tslint:disable-next-line:no-any
455
+ e.button === 4) // middle click for IE
456
+ );
457
+ }
458
+ /**
459
+ * Determines and returns whether the given event (which is assumed
460
+ * to be a click event) is modified. A middle click is considered a modified
461
+ * click to retain the default browser action, which opens a link in a new tab.
462
+ * @param e The event.
463
+ * @return Whether the given event is modified.
464
+ */
465
+ function isModifiedClickEvent(e) {
466
+ return (
467
+ // `metaKey` is an old DOM API.
468
+ // tslint:disable-next-line:no-any
469
+ (isMac && e.metaKey) ||
470
+ // `ctrlKey` is an old DOM API.
471
+ // tslint:disable-next-line:no-any
472
+ (!isMac && e.ctrlKey) ||
473
+ isMiddleClick(e) ||
474
+ // `shiftKey` is an old DOM API.
475
+ // tslint:disable-next-line:no-any
476
+ e.shiftKey);
477
+ }
478
+ /** Whether we are on WebKit (e.g., Chrome). */
479
+ const isWebKit = typeof navigator !== 'undefined' &&
480
+ !/Opera/.test(navigator.userAgent) &&
481
+ /WebKit/.test(navigator.userAgent);
482
+ /** Whether we are on IE. */
483
+ const isIe = typeof navigator !== 'undefined' &&
484
+ (/MSIE/.test(navigator.userAgent) || /Trident/.test(navigator.userAgent));
485
+ /** Whether we are on Gecko (e.g., Firefox). */
486
+ const isGecko = typeof navigator !== 'undefined' &&
487
+ !/Opera|WebKit/.test(navigator.userAgent) &&
488
+ /Gecko/.test(navigator.product);
489
+ /**
490
+ * Determines and returns whether the given element is a valid target for
491
+ * keypress/keydown DOM events that act like regular DOM clicks.
492
+ * @param el The element.
493
+ * @return Whether the given element is a valid action key target.
494
+ */
495
+ function isValidActionKeyTarget(el) {
496
+ if (!('getAttribute' in el)) {
497
+ return false;
498
+ }
499
+ if (isTextControl(el)) {
500
+ return false;
501
+ }
502
+ if (isNativelyActivatable(el)) {
503
+ return false;
504
+ }
505
+ // `isContentEditable` is an old DOM API.
506
+ // tslint:disable-next-line:no-any
507
+ if (el.isContentEditable) {
508
+ return false;
509
+ }
510
+ return true;
511
+ }
512
+ /**
513
+ * Whether an event has a modifier key activated.
514
+ * @param e The event.
515
+ * @return True, if a modifier key is activated.
516
+ */
517
+ function hasModifierKey(e) {
518
+ return (
519
+ // `ctrlKey` is an old DOM API.
520
+ // tslint:disable-next-line:no-any
521
+ e.ctrlKey ||
522
+ // `shiftKey` is an old DOM API.
523
+ // tslint:disable-next-line:no-any
524
+ e.shiftKey ||
525
+ // `altKey` is an old DOM API.
526
+ // tslint:disable-next-line:no-any
527
+ e.altKey ||
528
+ // `metaKey` is an old DOM API.
529
+ // tslint:disable-next-line:no-any
530
+ e.metaKey);
531
+ }
532
+ /**
533
+ * Determines and returns whether the given event has a target that already
534
+ * has event handlers attached because it is a native HTML control. Used to
535
+ * determine if preventDefault should be called when isActionKeyEvent is true.
536
+ * @param e The event.
537
+ * @return If preventDefault should be called.
538
+ */
539
+ function shouldCallPreventDefaultOnNativeHtmlControl(e) {
540
+ const el = getTarget(e);
541
+ const tagName = el.tagName.toUpperCase();
542
+ const role = (el.getAttribute('role') || '').toUpperCase();
543
+ if (tagName === 'BUTTON' || role === 'BUTTON') {
544
+ return true;
545
+ }
546
+ if (!isNativeHTMLControl(el)) {
547
+ return false;
548
+ }
549
+ if (tagName === 'A') {
550
+ return false;
551
+ }
552
+ /**
553
+ * Fix for physical d-pads on feature phone platforms; the native event
554
+ * (ie. isTrusted: true) needs to fire to show the OPTION list. See
555
+ * b/135288469 for more info.
556
+ */
557
+ if (tagName === 'SELECT') {
558
+ return false;
559
+ }
560
+ if (processSpace(el)) {
561
+ return false;
562
+ }
563
+ if (isTextControl(el)) {
564
+ return false;
565
+ }
566
+ return true;
567
+ }
568
+ /**
569
+ * Determines and returns whether the given event acts like a regular DOM click,
570
+ * and should be handled instead of the click. If this returns true, the caller
571
+ * will call preventDefault() to prevent a possible duplicate event.
572
+ * This is represented by a keypress (keydown on Gecko browsers) on Enter or
573
+ * Space key.
574
+ * @param e The event.
575
+ * @return True, if the event emulates a DOM click.
576
+ */
577
+ function isActionKeyEvent(e) {
578
+ let key =
579
+ // `which` is an old DOM API.
580
+ // tslint:disable-next-line:no-any
581
+ e.which ||
582
+ // `keyCode` is an old DOM API.
583
+ // tslint:disable-next-line:no-any
584
+ e.keyCode;
585
+ if (!key && e.key) {
586
+ key = ACTION_KEY_TO_KEYCODE[e.key];
587
+ }
588
+ if (isWebKit && key === KeyCode.MAC_ENTER) {
589
+ key = KeyCode.ENTER;
590
+ }
591
+ if (key !== KeyCode.ENTER && key !== KeyCode.SPACE) {
592
+ return false;
593
+ }
594
+ const el = getTarget(e);
595
+ if (e.type !== EventType.KEYDOWN || !isValidActionKeyTarget(el) || hasModifierKey(e)) {
596
+ return false;
597
+ }
598
+ // For <input type="checkbox">, we must only handle the browser's native click
599
+ // event, so that the browser can toggle the checkbox.
600
+ if (processSpace(el) && key === KeyCode.SPACE) {
601
+ return false;
602
+ }
603
+ // If this element is non-focusable, ignore stray keystrokes (b/18337209)
604
+ // Sscreen readers can move without tab focus, so any tabIndex is focusable.
605
+ // See B/21809604
606
+ if (!isFocusable(el)) {
607
+ return false;
608
+ }
609
+ const type = (el.getAttribute('role') ||
610
+ el.type ||
611
+ el.tagName).toUpperCase();
612
+ const isSpecificTriggerKey = IDENTIFIER_TO_KEY_TRIGGER_MAPPING[type] % key === 0;
613
+ const isDefaultTriggerKey = !(type in IDENTIFIER_TO_KEY_TRIGGER_MAPPING) && key === KeyCode.ENTER;
614
+ const hasType = el.tagName.toUpperCase() !== 'INPUT' || !!el.type;
615
+ return (isSpecificTriggerKey || isDefaultTriggerKey) && hasType;
616
+ }
617
+ /**
618
+ * Checks whether a DOM element can receive keyboard focus.
619
+ * This code is based on goog.dom.isFocusable, but simplified since we shouldn't
620
+ * care about visibility if we're already handling a keyboard event.
621
+ */
622
+ function isFocusable(el) {
623
+ return ((el.tagName in NATIVELY_FOCUSABLE_ELEMENTS || hasSpecifiedTabIndex(el)) &&
624
+ !el.disabled);
625
+ }
626
+ /**
627
+ * @param element Element to check.
628
+ * @return Whether the element has a specified tab index.
629
+ */
630
+ function hasSpecifiedTabIndex(element) {
631
+ // IE returns 0 for an unset tabIndex, so we must use getAttributeNode(),
632
+ // which returns an object with a 'specified' property if tabIndex is
633
+ // specified. This works on other browsers, too.
634
+ const attrNode = element.getAttributeNode('tabindex'); // Must be lowercase!
635
+ return attrNode != null && attrNode.specified;
636
+ }
637
+ /** Element tagnames that are focusable by default. */
638
+ const NATIVELY_FOCUSABLE_ELEMENTS = {
639
+ 'A': 1,
640
+ 'INPUT': 1,
641
+ 'TEXTAREA': 1,
642
+ 'SELECT': 1,
643
+ 'BUTTON': 1,
644
+ };
645
+ /** @return True, if the Space key was pressed. */
646
+ function isSpaceKeyEvent(e) {
647
+ const key =
648
+ // `which` is an old DOM API.
649
+ // tslint:disable-next-line:no-any
650
+ e.which ||
651
+ // `keyCode` is an old DOM API.
652
+ // tslint:disable-next-line:no-any
653
+ e.keyCode;
654
+ const el = getTarget(e);
655
+ const elementName = (el.type || el.tagName).toUpperCase();
656
+ return key === KeyCode.SPACE && elementName !== 'CHECKBOX';
657
+ }
658
+ /**
659
+ * Determines whether the event corresponds to a non-bubbling mouse
660
+ * event type (mouseenter, mouseleave, pointerenter, and pointerleave).
661
+ *
662
+ * During mouseover (mouseenter) and pointerover (pointerenter), the
663
+ * relatedTarget is the element being entered from. During mouseout (mouseleave)
664
+ * and pointerout (pointerleave), the relatedTarget is the element being exited
665
+ * to.
666
+ *
667
+ * In both cases, if relatedTarget is outside target, then the corresponding
668
+ * special event has occurred, otherwise it hasn't.
669
+ *
670
+ * @param e The mouseover/mouseout event.
671
+ * @param type The type of the mouse special event.
672
+ * @param element The element on which the jsaction for the
673
+ * mouseenter/mouseleave event is defined.
674
+ * @return True if the event is a mouseenter/mouseleave event.
675
+ */
676
+ function isMouseSpecialEvent(e, type, element) {
677
+ // `relatedTarget` is an old DOM API.
678
+ // tslint:disable-next-line:no-any
679
+ const related = e.relatedTarget;
680
+ return (((e.type === EventType.MOUSEOVER && type === EventType.MOUSEENTER) ||
681
+ (e.type === EventType.MOUSEOUT && type === EventType.MOUSELEAVE) ||
682
+ (e.type === EventType.POINTEROVER && type === EventType.POINTERENTER) ||
683
+ (e.type === EventType.POINTEROUT && type === EventType.POINTERLEAVE)) &&
684
+ (!related || (related !== element && !contains(element, related))));
685
+ }
686
+ /**
687
+ * Creates a new EventLike object for a mouseenter/mouseleave event that's
688
+ * derived from the original corresponding mouseover/mouseout event.
689
+ * @param e The event.
690
+ * @param target The element on which the jsaction for the mouseenter/mouseleave
691
+ * event is defined.
692
+ * @return A modified event-like object copied from the event object passed into
693
+ * this function.
694
+ */
695
+ function createMouseSpecialEvent(e, target) {
696
+ // We have to create a copy of the event object because we need to mutate
697
+ // its fields. We do this for the special mouse events because the event
698
+ // target needs to be retargeted to the action element rather than the real
699
+ // element (since we are simulating the special mouse events with mouseover/
700
+ // mouseout).
701
+ //
702
+ // Since we're making a copy anyways, we might as well attempt to convert
703
+ // this event into a pseudo-real mouseenter/mouseleave event by adjusting
704
+ // its type.
705
+ //
706
+ // tslint:disable-next-line:no-any
707
+ const copy = {};
708
+ for (const property in e) {
709
+ if (property === 'srcElement' || property === 'target') {
710
+ continue;
711
+ }
712
+ const key = property;
713
+ // Making a copy requires iterating through all properties of `Event`.
714
+ // tslint:disable-next-line:no-dict-access-on-struct-type
715
+ const value = e[key];
716
+ if (typeof value === 'function') {
717
+ continue;
718
+ }
719
+ // Value should be the expected type, but the value of `key` is not known
720
+ // statically.
721
+ // tslint:disable-next-line:no-any
722
+ copy[key] = value;
723
+ }
724
+ if (e.type === EventType.MOUSEOVER) {
725
+ copy['type'] = EventType.MOUSEENTER;
726
+ }
727
+ else if (e.type === EventType.MOUSEOUT) {
728
+ copy['type'] = EventType.MOUSELEAVE;
729
+ }
730
+ else if (e.type === EventType.POINTEROVER) {
731
+ copy['type'] = EventType.POINTERENTER;
732
+ }
733
+ else {
734
+ copy['type'] = EventType.POINTERLEAVE;
735
+ }
736
+ copy['target'] = copy['srcElement'] = target;
737
+ copy['bubbles'] = false;
738
+ return copy;
739
+ }
740
+ /**
741
+ * Returns touch data extracted from the touch event: clientX, clientY, screenX
742
+ * and screenY. If the event has no touch information at all, the returned
743
+ * value is null.
744
+ *
745
+ * The fields of this Object are unquoted.
746
+ *
747
+ * @param event A touch event.
748
+ */
749
+ function getTouchData(event) {
750
+ const touch = (event.changedTouches && event.changedTouches[0]) || (event.touches && event.touches[0]);
751
+ if (!touch) {
752
+ return null;
753
+ }
754
+ return {
755
+ clientX: touch.clientX,
756
+ clientY: touch.clientY,
757
+ screenX: touch.screenX,
758
+ screenY: touch.screenY,
759
+ };
760
+ }
761
+ /**
762
+ * Creates a new EventLike object for a "click" event that's derived from the
763
+ * original corresponding "touchend" event for a fast-click implementation.
764
+ *
765
+ * It takes a touch event, adds common fields found in a click event and
766
+ * changes the type to 'click', so that the resulting event looks more like
767
+ * a real click event.
768
+ *
769
+ * @param event A touch event.
770
+ * @return A modified event-like object copied from the event object passed into
771
+ * this function.
772
+ */
773
+ function recreateTouchEventAsClick(event) {
774
+ const click = {};
775
+ click['originalEventType'] = event.type;
776
+ click['type'] = EventType.CLICK;
777
+ for (const property in event) {
778
+ if (property === 'type' || property === 'srcElement') {
779
+ continue;
780
+ }
781
+ const key = property;
782
+ // Making a copy requires iterating through all properties of `TouchEvent`.
783
+ // tslint:disable-next-line:no-dict-access-on-struct-type
784
+ const value = event[key];
785
+ if (typeof value === 'function') {
786
+ continue;
787
+ }
788
+ // Value should be the expected type, but the value of `key` is not known
789
+ // statically.
790
+ // tslint:disable-next-line:no-any
791
+ click[key] = value;
792
+ }
793
+ // Ensure that the event has the most recent timestamp. This timestamp
794
+ // may be used in the future to validate or cancel subsequent click events.
795
+ click['timeStamp'] = Date.now();
796
+ // Emulate preventDefault and stopPropagation behavior
797
+ click['defaultPrevented'] = false;
798
+ click['preventDefault'] = syntheticPreventDefault;
799
+ click['_propagationStopped'] = false;
800
+ click['stopPropagation'] = syntheticStopPropagation;
801
+ // Emulate click coordinates using touch info
802
+ const touch = getTouchData(event);
803
+ if (touch) {
804
+ click['clientX'] = touch.clientX;
805
+ click['clientY'] = touch.clientY;
806
+ click['screenX'] = touch.screenX;
807
+ click['screenY'] = touch.screenY;
808
+ }
809
+ return click;
810
+ }
811
+ /**
812
+ * An implementation of "preventDefault" for a synthesized event. Simply
813
+ * sets "defaultPrevented" property to true.
814
+ */
815
+ function syntheticPreventDefault() {
816
+ this.defaultPrevented = true;
817
+ }
818
+ /**
819
+ * An implementation of "stopPropagation" for a synthesized event. It simply
820
+ * sets a synthetic non-standard "_propagationStopped" property to true.
821
+ */
822
+ function syntheticStopPropagation() {
823
+ this._propagationStopped = true;
824
+ }
825
+ /**
826
+ * Mapping of KeyboardEvent.key values to
827
+ * KeyCode values.
828
+ */
829
+ const ACTION_KEY_TO_KEYCODE = {
830
+ 'Enter': KeyCode.ENTER,
831
+ ' ': KeyCode.SPACE,
832
+ };
833
+ /**
834
+ * Mapping of HTML element identifiers (ARIA role, type, or tagName) to the
835
+ * keys (enter and/or space) that should activate them. A value of zero means
836
+ * that both should activate them.
837
+ */
838
+ const IDENTIFIER_TO_KEY_TRIGGER_MAPPING = {
839
+ 'A': KeyCode.ENTER,
840
+ 'BUTTON': 0,
841
+ 'CHECKBOX': KeyCode.SPACE,
842
+ 'COMBOBOX': KeyCode.ENTER,
843
+ 'FILE': 0,
844
+ 'GRIDCELL': KeyCode.ENTER,
845
+ 'LINK': KeyCode.ENTER,
846
+ 'LISTBOX': KeyCode.ENTER,
847
+ 'MENU': 0,
848
+ 'MENUBAR': 0,
849
+ 'MENUITEM': 0,
850
+ 'MENUITEMCHECKBOX': 0,
851
+ 'MENUITEMRADIO': 0,
852
+ 'OPTION': 0,
853
+ 'RADIO': KeyCode.SPACE,
854
+ 'RADIOGROUP': KeyCode.SPACE,
855
+ 'RESET': 0,
856
+ 'SUBMIT': 0,
857
+ 'SWITCH': KeyCode.SPACE,
858
+ 'TAB': 0,
859
+ 'TREE': KeyCode.ENTER,
860
+ 'TREEITEM': KeyCode.ENTER,
861
+ };
862
+ /**
863
+ * Returns whether or not to process space based on the type of the element;
864
+ * checks to make sure that type is not null.
865
+ * @param element The element.
866
+ * @return Whether or not to process space based on type.
867
+ */
868
+ function processSpace(element) {
869
+ const type = (element.getAttribute('type') || element.tagName).toUpperCase();
870
+ return type in PROCESS_SPACE;
871
+ }
872
+ /**
873
+ * Returns whether or not the given element is a text control.
874
+ * @param el The element.
875
+ * @return Whether or not the given element is a text control.
876
+ */
877
+ function isTextControl(el) {
878
+ const type = (el.getAttribute('type') || el.tagName).toUpperCase();
879
+ return type in TEXT_CONTROLS;
880
+ }
881
+ /**
882
+ * Returns if the given element is a native HTML control.
883
+ * @param el The element.
884
+ * @return If the given element is a native HTML control.
885
+ */
886
+ function isNativeHTMLControl(el) {
887
+ return el.tagName.toUpperCase() in NATIVE_HTML_CONTROLS;
888
+ }
889
+ /**
890
+ * Returns if the given element is natively activatable. Browsers emit click
891
+ * events for natively activatable elements, even when activated via keyboard.
892
+ * For these elements, we don't need to raise a11y click events.
893
+ * @param el The element.
894
+ * @return If the given element is a native HTML control.
895
+ */
896
+ function isNativelyActivatable(el) {
897
+ return (el.tagName.toUpperCase() === 'BUTTON' ||
898
+ (!!el.type && el.type.toUpperCase() === 'FILE'));
899
+ }
900
+ /**
901
+ * HTML <input> types (not ARIA roles) which will auto-trigger a click event for
902
+ * the Space key, with side-effects. We will not call preventDefault if space is
903
+ * pressed, nor will we raise a11y click events. For all other elements, we can
904
+ * suppress the default event (which has no desired side-effects) and handle the
905
+ * keydown ourselves.
906
+ */
907
+ const PROCESS_SPACE = {
908
+ 'CHECKBOX': true,
909
+ 'FILE': true,
910
+ 'OPTION': true,
911
+ 'RADIO': true,
912
+ };
913
+ /** TagNames and Input types for which to not process enter/space as click. */
914
+ const TEXT_CONTROLS = {
915
+ 'COLOR': true,
916
+ 'DATE': true,
917
+ 'DATETIME': true,
918
+ 'DATETIME-LOCAL': true,
919
+ 'EMAIL': true,
920
+ 'MONTH': true,
921
+ 'NUMBER': true,
922
+ 'PASSWORD': true,
923
+ 'RANGE': true,
924
+ 'SEARCH': true,
925
+ 'TEL': true,
926
+ 'TEXT': true,
927
+ 'TEXTAREA': true,
928
+ 'TIME': true,
929
+ 'URL': true,
930
+ 'WEEK': true,
931
+ };
932
+ /** TagNames that are native HTML controls. */
933
+ const NATIVE_HTML_CONTROLS = {
934
+ 'A': true,
935
+ 'AREA': true,
936
+ 'BUTTON': true,
937
+ 'DIALOG': true,
938
+ 'IMG': true,
939
+ 'INPUT': true,
940
+ 'LINK': true,
941
+ 'MENU': true,
942
+ 'OPTGROUP': true,
943
+ 'OPTION': true,
944
+ 'PROGRESS': true,
945
+ 'SELECT': true,
946
+ 'TEXTAREA': true,
947
+ };
948
+ /** Exported for testing. */
949
+ const testing$1 = {
950
+ setIsMac(value) {
951
+ isMac = value;
952
+ },
953
+ };
954
+
955
+ /** Added for readability when accessing stable property names. */
956
+ function getEventType(eventInfo) {
957
+ return eventInfo.eventType;
958
+ }
959
+ /** Added for readability when accessing stable property names. */
960
+ function setEventType(eventInfo, eventType) {
961
+ eventInfo.eventType = eventType;
962
+ }
963
+ /** Added for readability when accessing stable property names. */
964
+ function getEvent(eventInfo) {
965
+ return eventInfo.event;
966
+ }
967
+ /** Added for readability when accessing stable property names. */
968
+ function setEvent(eventInfo, event) {
969
+ eventInfo.event = event;
970
+ }
971
+ /** Added for readability when accessing stable property names. */
972
+ function getTargetElement(eventInfo) {
973
+ return eventInfo.targetElement;
974
+ }
975
+ /** Added for readability when accessing stable property names. */
976
+ function setTargetElement(eventInfo, targetElement) {
977
+ eventInfo.targetElement = targetElement;
978
+ }
979
+ /** Added for readability when accessing stable property names. */
980
+ function getContainer(eventInfo) {
981
+ return eventInfo.eic;
982
+ }
983
+ /** Added for readability when accessing stable property names. */
984
+ function setContainer(eventInfo, container) {
985
+ eventInfo.eic = container;
986
+ }
987
+ /** Added for readability when accessing stable property names. */
988
+ function getTimestamp(eventInfo) {
989
+ return eventInfo.timeStamp;
990
+ }
991
+ /** Added for readability when accessing stable property names. */
992
+ function setTimestamp(eventInfo, timestamp) {
993
+ eventInfo.timeStamp = timestamp;
994
+ }
995
+ /** Added for readability when accessing stable property names. */
996
+ function getAction(eventInfo) {
997
+ return eventInfo.eia;
998
+ }
999
+ /** Added for readability when accessing stable property names. */
1000
+ function setAction(eventInfo, actionName, actionElement) {
1001
+ eventInfo.eia = [actionName, actionElement];
1002
+ }
1003
+ /** Added for readability when accessing stable property names. */
1004
+ function unsetAction(eventInfo) {
1005
+ eventInfo.eia = undefined;
1006
+ }
1007
+ /** Added for readability when accessing stable property names. */
1008
+ function getActionName(actionInfo) {
1009
+ return actionInfo[0];
1010
+ }
1011
+ /** Added for readability when accessing stable property names. */
1012
+ function getActionElement(actionInfo) {
1013
+ return actionInfo[1];
1014
+ }
1015
+ /** Added for readability when accessing stable property names. */
1016
+ function getIsReplay(eventInfo) {
1017
+ return eventInfo.eirp;
1018
+ }
1019
+ /** Added for readability when accessing stable property names. */
1020
+ function setIsReplay(eventInfo, replay) {
1021
+ eventInfo.eirp = replay;
1022
+ }
1023
+ /** Added for readability when accessing stable property names. */
1024
+ function getA11yClickKey(eventInfo) {
1025
+ return eventInfo.eiack;
1026
+ }
1027
+ /** Added for readability when accessing stable property names. */
1028
+ function setA11yClickKey(eventInfo, a11yClickKey) {
1029
+ eventInfo.eiack = a11yClickKey;
1030
+ }
1031
+ /** Clones an `EventInfo` */
1032
+ function cloneEventInfo(eventInfo) {
1033
+ return {
1034
+ eventType: eventInfo.eventType,
1035
+ event: eventInfo.event,
1036
+ targetElement: eventInfo.targetElement,
1037
+ eic: eventInfo.eic,
1038
+ eia: eventInfo.eia,
1039
+ timeStamp: eventInfo.timeStamp,
1040
+ eirp: eventInfo.eirp,
1041
+ eiack: eventInfo.eiack,
1042
+ };
1043
+ }
1044
+ /**
1045
+ * Utility function for creating an `EventInfo`.
1046
+ *
1047
+ * This can be used from code-size sensitive compilation units, as taking
1048
+ * parameters vs. an `Object` literal reduces code size.
1049
+ */
1050
+ function createEventInfoFromParameters(eventType, event, targetElement, container, timestamp, action, isReplay, a11yClickKey) {
1051
+ return {
1052
+ eventType,
1053
+ event,
1054
+ targetElement,
1055
+ eic: container,
1056
+ timeStamp: timestamp,
1057
+ eia: action,
1058
+ eirp: isReplay,
1059
+ eiack: a11yClickKey,
1060
+ };
1061
+ }
1062
+ /**
1063
+ * Utility function for creating an `EventInfo`.
1064
+ *
1065
+ * This should be used in compilation units that are less sensitive to code
1066
+ * size.
1067
+ */
1068
+ function createEventInfo({ eventType, event, targetElement, container, timestamp, action, isReplay, a11yClickKey, }) {
1069
+ return {
1070
+ eventType,
1071
+ event,
1072
+ targetElement,
1073
+ eic: container,
1074
+ timeStamp: timestamp,
1075
+ eia: action ? [action.name, action.element] : undefined,
1076
+ eirp: isReplay,
1077
+ eiack: a11yClickKey,
1078
+ };
1079
+ }
1080
+ /**
1081
+ * Utility class around an `EventInfo`.
1082
+ *
1083
+ * This should be used in compilation units that are less sensitive to code
1084
+ * size.
1085
+ */
1086
+ class EventInfoWrapper {
1087
+ constructor(eventInfo) {
1088
+ this.eventInfo = eventInfo;
1089
+ }
1090
+ getEventType() {
1091
+ return getEventType(this.eventInfo);
1092
+ }
1093
+ setEventType(eventType) {
1094
+ setEventType(this.eventInfo, eventType);
1095
+ }
1096
+ getEvent() {
1097
+ return getEvent(this.eventInfo);
1098
+ }
1099
+ setEvent(event) {
1100
+ setEvent(this.eventInfo, event);
1101
+ }
1102
+ getTargetElement() {
1103
+ return getTargetElement(this.eventInfo);
1104
+ }
1105
+ setTargetElement(targetElement) {
1106
+ setTargetElement(this.eventInfo, targetElement);
1107
+ }
1108
+ getContainer() {
1109
+ return getContainer(this.eventInfo);
1110
+ }
1111
+ setContainer(container) {
1112
+ setContainer(this.eventInfo, container);
1113
+ }
1114
+ getTimestamp() {
1115
+ return getTimestamp(this.eventInfo);
1116
+ }
1117
+ setTimestamp(timestamp) {
1118
+ setTimestamp(this.eventInfo, timestamp);
1119
+ }
1120
+ getAction() {
1121
+ const action = getAction(this.eventInfo);
1122
+ if (!action)
1123
+ return undefined;
1124
+ return {
1125
+ name: action[0],
1126
+ element: action[1],
1127
+ };
1128
+ }
1129
+ setAction(action) {
1130
+ if (!action) {
1131
+ unsetAction(this.eventInfo);
1132
+ return;
1133
+ }
1134
+ setAction(this.eventInfo, action.name, action.element);
1135
+ }
1136
+ getIsReplay() {
1137
+ return getIsReplay(this.eventInfo);
1138
+ }
1139
+ setIsReplay(replay) {
1140
+ setIsReplay(this.eventInfo, replay);
1141
+ }
1142
+ clone() {
1143
+ return new EventInfoWrapper(cloneEventInfo(this.eventInfo));
1144
+ }
1145
+ }
1146
+
1147
+ /**
1148
+ * Create a custom event with the specified data.
1149
+ * @param type The type of the action, e.g., 'submit'.
1150
+ * @param data An optional data payload.
1151
+ * @param triggeringEvent The event that triggers this custom event. This can be
1152
+ * accessed from the custom event's action flow like so:
1153
+ * actionFlow.event().detail.triggeringEvent.
1154
+ * @return The new custom event.
1155
+ */
1156
+ function createCustomEvent(type, data, triggeringEvent) {
1157
+ let event;
1158
+ const unrenamedDetail = {
1159
+ '_type': type,
1160
+ };
1161
+ const renamedDetail = {
1162
+ type,
1163
+ data,
1164
+ triggeringEvent,
1165
+ };
1166
+ const detail = { ...unrenamedDetail, ...renamedDetail };
1167
+ try {
1168
+ // We don't use the CustomEvent constructor directly since it isn't
1169
+ // supported in IE 9 or 10 and initCustomEvent below works just fine.
1170
+ event = document.createEvent('CustomEvent');
1171
+ event.initCustomEvent(EventType.CUSTOM, true, false, detail);
1172
+ }
1173
+ catch (e) {
1174
+ // If custom events aren't supported, fall back to custom-named HTMLEvent.
1175
+ // Fallback used by Android Gingerbread, FF4-5.
1176
+ // Hack to emulate `CustomEvent`, `HTMLEvents` doesn't satisfy `CustomEvent`
1177
+ // type.
1178
+ // tslint:disable-next-line:no-any
1179
+ event = document.createEvent('HTMLEvents');
1180
+ event.initEvent(EventType.CUSTOM, true, false);
1181
+ // Hack to emulate `CustomEvent`, `detail` is readonly on `CustomEvent`.
1182
+ // tslint:disable-next-line:no-any
1183
+ event['detail'] = detail;
1184
+ }
1185
+ return event;
1186
+ }
1187
+ /**
1188
+ * Fires a custom event with an optional payload. Only intended to be consumed
1189
+ * by jsaction itself. Supported in Firefox 6+, IE 9+, and all Chrome versions.
1190
+ *
1191
+ * @param target The target element.
1192
+ * @param type The type of the action, e.g., 'submit'.
1193
+ * @param data An optional data payload.
1194
+ * @param triggeringEvent An optional data for the Event triggered this custom
1195
+ * event.
1196
+ */
1197
+ function fireCustomEvent(target, type, data, triggeringEvent) {
1198
+ const event = createCustomEvent(type, data, triggeringEvent);
1199
+ target.dispatchEvent(event);
1200
+ }
1201
+
1202
+ /**
1203
+ * @fileoverview Functions for replaying events by the jsaction
1204
+ * Dispatcher.
1205
+ * All ts-ignores in this file are due to APIs that are no longer in the browser.
1206
+ */
1207
+ /**
1208
+ * Replays an event.
1209
+ */
1210
+ function replayEvent(event, targetElement, eventType) {
1211
+ triggerEvent(targetElement, createEvent(event, eventType));
1212
+ }
1213
+ /**
1214
+ * Checks if a given event was triggered by the keyboard.
1215
+ * @param eventType The event type.
1216
+ * @return Whether it's a keyboard event.
1217
+ */
1218
+ function isKeyboardEvent(eventType) {
1219
+ return (eventType === EventType.KEYPRESS ||
1220
+ eventType === EventType.KEYDOWN ||
1221
+ eventType === EventType.KEYUP);
1222
+ }
1223
+ /**
1224
+ * Checks if a given event was triggered by the mouse.
1225
+ * @param eventType The event type.
1226
+ * @return Whether it's a mouse event.
1227
+ */
1228
+ function isMouseEvent(eventType) {
1229
+ // TODO: Verify if Drag events should be bound here.
1230
+ return (eventType === EventType.CLICK ||
1231
+ eventType === EventType.DBLCLICK ||
1232
+ eventType === EventType.MOUSEDOWN ||
1233
+ eventType === EventType.MOUSEOVER ||
1234
+ eventType === EventType.MOUSEOUT ||
1235
+ eventType === EventType.MOUSEMOVE);
1236
+ }
1237
+ /**
1238
+ * Checks if a given event is a general UI event.
1239
+ * @param eventType The event type.
1240
+ * @return Whether it's a focus event.
1241
+ */
1242
+ function isUiEvent(eventType) {
1243
+ // Almost nobody supports the W3C method of creating FocusEvents.
1244
+ // For now, we're going to use the UIEvent as a super-interface.
1245
+ return (eventType === EventType.FOCUS ||
1246
+ eventType === EventType.BLUR ||
1247
+ eventType === EventType.FOCUSIN ||
1248
+ eventType === EventType.FOCUSOUT ||
1249
+ eventType === EventType.SCROLL);
1250
+ }
1251
+ /**
1252
+ * Create a whitespace-delineated list of modifier keys that should be
1253
+ * considered to be active on the event's key. See details at
1254
+ * https://developer.mozilla.org/en/DOM/KeyboardEvent.
1255
+ * @param alt Alt pressed.
1256
+ * @param ctrl Control pressed.
1257
+ * @param meta Command pressed (OSX only).
1258
+ * @param shift Shift pressed.
1259
+ * @return The constructed modifier keys string.
1260
+ */
1261
+ function createKeyboardModifiersList(alt, ctrl, meta, shift) {
1262
+ const keys = [];
1263
+ if (alt) {
1264
+ keys.push('Alt');
1265
+ }
1266
+ if (ctrl) {
1267
+ keys.push('Control');
1268
+ }
1269
+ if (meta) {
1270
+ keys.push('Meta');
1271
+ }
1272
+ if (shift) {
1273
+ keys.push('Shift');
1274
+ }
1275
+ return keys.join(' ');
1276
+ }
1277
+ /**
1278
+ * Creates a UI event object for replaying through the DOM.
1279
+ * @param original The event to create a new event from.
1280
+ * @param opt_eventType The type this event is being handled as by jsaction.
1281
+ * e.g. blur events are handled as focusout
1282
+ * @return The event object.
1283
+ */
1284
+ function createUiEvent(original, opt_eventType) {
1285
+ let event;
1286
+ if (document.createEvent) {
1287
+ const originalUiEvent = original;
1288
+ // Event creation as per W3C event model specification. This codepath
1289
+ // is used by most non-IE browsers and also by IE 9 and later.
1290
+ event = document.createEvent('UIEvent');
1291
+ // On IE and Opera < 12, we must provide non-undefined values to
1292
+ // initEvent, otherwise it will fail.
1293
+ event.initUIEvent(opt_eventType || originalUiEvent.type, originalUiEvent.bubbles !== undefined ? originalUiEvent.bubbles : true, originalUiEvent.cancelable || false, originalUiEvent.view || window, original.detail || 0);
1294
+ // detail
1295
+ }
1296
+ else {
1297
+ // Older versions of IE (up to version 8) do not support the
1298
+ // W3C event model. Use the IE specific function instead.
1299
+ // Suppressing errors for ts-migration.
1300
+ // TS2339: Property 'createEventObject' does not exist on type 'Document'.
1301
+ // @ts-ignore
1302
+ event = document.createEventObject();
1303
+ event.type = opt_eventType || original.type;
1304
+ event.bubbles = original.bubbles !== undefined ? original.bubbles : true;
1305
+ event.cancelable = original.cancelable || false;
1306
+ event.view = original.view || window;
1307
+ event.detail = original.detail || 0;
1308
+ }
1309
+ // Some focus events also have a nullable relatedTarget value which isn't
1310
+ // directly supported in the initUIEvent() method.
1311
+ event.relatedTarget = original.relatedTarget || null;
1312
+ event.originalTimestamp = original.timeStamp;
1313
+ return event;
1314
+ }
1315
+ /**
1316
+ * Creates a keyboard event object for replaying through the DOM.
1317
+ * @param original The event to create a new event from.
1318
+ * @param opt_eventType The type this event is being handled as by jsaction.
1319
+ * E.g. a keypress is handled as click in some cases.
1320
+ * @return The event object.
1321
+ * @suppress {strictMissingProperties} Two definitions of initKeyboardEvent.
1322
+ */
1323
+ function createKeyboardEvent(original, opt_eventType) {
1324
+ let event;
1325
+ const keyboardEvent = original;
1326
+ if (document.createEvent) {
1327
+ // Event creation as per W3C event model specification. This codepath
1328
+ // is used by most non-IE browsers and also by IE 9 and later.
1329
+ event = document.createEvent('KeyboardEvent');
1330
+ if (event.initKeyboardEvent) {
1331
+ if (isIe) {
1332
+ // IE9+
1333
+ // https://docs.microsoft.com/en-us/previous-versions/windows/internet-explorer/ie-developer/platform-apis/ff975945(v=vs.85)
1334
+ const modifiers = createKeyboardModifiersList(keyboardEvent.altKey, keyboardEvent.ctrlKey, keyboardEvent.metaKey, keyboardEvent.shiftKey);
1335
+ event.initKeyboardEvent(opt_eventType || keyboardEvent.type, true, true, window, keyboardEvent.key, keyboardEvent.location,
1336
+ // Suppressing errors for ts-migration.
1337
+ // TS2345: Argument of type 'string' is not assignable to
1338
+ // parameter of type 'boolean | undefined'.
1339
+ // @ts-ignore
1340
+ modifiers, keyboardEvent.repeat,
1341
+ // @ts-ignore This doesn't exist
1342
+ keyboardEvent.locale);
1343
+ }
1344
+ else {
1345
+ // W3C DOM Level 3 Events model.
1346
+ // https://www.w3.org/TR/uievents/#idl-interface-KeyboardEvent-initializers
1347
+ event.initKeyboardEvent(opt_eventType || original.type, true, true, window, keyboardEvent.key, keyboardEvent.location, keyboardEvent.ctrlKey, keyboardEvent.altKey, keyboardEvent.shiftKey, keyboardEvent.metaKey);
1348
+ Object.defineProperty(event, 'repeat', {
1349
+ get: () => original.repeat,
1350
+ enumerable: true,
1351
+ });
1352
+ // Add missing 'locale' which is not part of the spec.
1353
+ // https://bugs.chromium.org/p/chromium/issues/detail?id=168971
1354
+ Object.defineProperty(event, 'locale', {
1355
+ // Suppressing errors for ts-migration.
1356
+ // TS2339: Property 'locale' does not exist on type 'Event'.
1357
+ // @ts-ignore
1358
+ get: () => original.locale,
1359
+ enumerable: true,
1360
+ });
1361
+ // Apple WebKit has a non-standard altGraphKey that is not implemented
1362
+ // here.
1363
+ // https://developer.apple.com/documentation/webkitjs/keyboardevent/1633753-initkeyboardevent
1364
+ }
1365
+ // Blink and Webkit had a bug that causes the `charCode`, `keyCode`, and
1366
+ // `which` properties to always be unset when synthesizing a keyboard
1367
+ // event. Details at: https://bugs.webkit.org/show_bug.cgi?id=16735. With
1368
+ // these properties being deprecated, the bug has evolved into affecting
1369
+ // the `key` property. We work around it by redefining the `key` and
1370
+ // deprecated properties; a simple assignment here would fail because the
1371
+ // native properties are readonly.
1372
+ if (isWebKit) {
1373
+ if (keyboardEvent.key && event.key === '') {
1374
+ Object.defineProperty(event, 'key', {
1375
+ get: () => keyboardEvent.key,
1376
+ enumerable: true,
1377
+ });
1378
+ }
1379
+ }
1380
+ // Re-implement the deprecated `charCode`, `keyCode` and `which` which
1381
+ // are also an issue on IE9+.
1382
+ if (isWebKit || isIe || isGecko) {
1383
+ Object.defineProperty(event, 'charCode', {
1384
+ get: () => original.charCode,
1385
+ enumerable: true,
1386
+ });
1387
+ const keyCodeGetter = () => original.keyCode;
1388
+ Object.defineProperty(event, 'keyCode', {
1389
+ get: keyCodeGetter,
1390
+ enumerable: true,
1391
+ });
1392
+ Object.defineProperty(event, 'which', {
1393
+ get: keyCodeGetter,
1394
+ enumerable: true,
1395
+ });
1396
+ }
1397
+ }
1398
+ else {
1399
+ // Gecko only supports an older/deprecated version from DOM Level 2. See
1400
+ // https://developer.mozilla.org/en-US/docs/Web/API/KeyboardEvent/initKeyEvent
1401
+ // for details.
1402
+ // @ts-ignore ditto
1403
+ event.initKeyEvent(opt_eventType || original.type, true, true, window,
1404
+ // Suppressing errors for ts-migration.
1405
+ // TS2339: Property 'ctrlKey' does not exist on type 'Event'.
1406
+ // @ts-ignore
1407
+ original.ctrlKey,
1408
+ // Suppressing errors for ts-migration.
1409
+ // TS2339: Property 'altKey' does not exist on type 'Event'.
1410
+ // @ts-ignore
1411
+ original.altKey,
1412
+ // Suppressing errors for ts-migration.
1413
+ // TS2339: Property 'shiftKey' does not exist on type 'Event'.
1414
+ // @ts-ignore
1415
+ original.shiftKey,
1416
+ // Suppressing errors for ts-migration.
1417
+ // TS2339: Property 'metaKey' does not exist on type 'Event'.
1418
+ // @ts-ignore
1419
+ original.metaKey,
1420
+ // Suppressing errors for ts-migration.
1421
+ // TS2339: Property 'keyCode' does not exist on type 'Event'.
1422
+ // @ts-ignore
1423
+ original.keyCode,
1424
+ // Suppressing errors for ts-migration.
1425
+ // TS2339: Property 'charCode' does not exist on type 'Event'.
1426
+ // @ts-ignore
1427
+ original.charCode);
1428
+ }
1429
+ }
1430
+ else {
1431
+ // Older versions of IE (up to version 8) do not support the
1432
+ // W3C event model. Use the IE specific function instead.
1433
+ // Suppressing errors for ts-migration.
1434
+ // @ts-ignore
1435
+ event = document.createEventObject();
1436
+ event.type = opt_eventType || original.type;
1437
+ const originalKeyboardEvent = original;
1438
+ event.repeat = originalKeyboardEvent.repeat;
1439
+ event.ctrlKey = originalKeyboardEvent.ctrlKey;
1440
+ event.altKey = originalKeyboardEvent.altKey;
1441
+ event.shiftKey = originalKeyboardEvent.shiftKey;
1442
+ event.metaKey = originalKeyboardEvent.metaKey;
1443
+ event.key = originalKeyboardEvent.key;
1444
+ event.keyCode = originalKeyboardEvent.keyCode;
1445
+ event.charCode = originalKeyboardEvent.charCode;
1446
+ }
1447
+ event.originalTimestamp = original.timeStamp;
1448
+ return event;
1449
+ }
1450
+ /**
1451
+ * Creates a mouse event object for replaying through the DOM.
1452
+ * @param original The event to create a new event from.
1453
+ * @param opt_eventType The type this event is being handled as by jsaction.
1454
+ * E.g. a keypress is handled as click in some cases.
1455
+ * @return The event object.
1456
+ */
1457
+ function createMouseEvent(original, opt_eventType) {
1458
+ let event;
1459
+ const originalMouseEvent = original;
1460
+ if (document.createEvent) {
1461
+ // Event creation as per W3C event model specification. This codepath
1462
+ // is used by most non-IE browsers and also by IE 9 and later.
1463
+ event = document.createEvent('MouseEvent');
1464
+ // On IE and Opera < 12, we must provide non-undefined values to
1465
+ // initMouseEvent, otherwise it will fail.
1466
+ event.initMouseEvent(opt_eventType || original.type, true, // canBubble
1467
+ true, // cancelable
1468
+ window, original.detail || 1, originalMouseEvent.screenX || 0, originalMouseEvent.screenY || 0, originalMouseEvent.clientX || 0, originalMouseEvent.clientY || 0, originalMouseEvent.ctrlKey || false, originalMouseEvent.altKey || false, originalMouseEvent.shiftKey || false, originalMouseEvent.metaKey || false, originalMouseEvent.button || 0, originalMouseEvent.relatedTarget || null);
1469
+ }
1470
+ else {
1471
+ // Older versions of IE (up to version 8) do not support the
1472
+ // W3C event model. Use the IE specific function instead.
1473
+ // @ts-ignore
1474
+ event = document.createEventObject();
1475
+ event.type = opt_eventType || original.type;
1476
+ event.clientX = originalMouseEvent.clientX;
1477
+ event.clientY = originalMouseEvent.clientY;
1478
+ event.button = originalMouseEvent.button;
1479
+ event.detail = original.detail;
1480
+ event.ctrlKey = originalMouseEvent.ctrlKey;
1481
+ event.altKey = originalMouseEvent.altKey;
1482
+ event.shiftKey = originalMouseEvent.shiftKey;
1483
+ event.metaKey = originalMouseEvent.metaKey;
1484
+ }
1485
+ event.originalTimestamp = original.timeStamp;
1486
+ return event;
1487
+ }
1488
+ /**
1489
+ * Creates a generic event object for replaying through the DOM.
1490
+ * @param original The event to create a new event from.
1491
+ * @param opt_eventType The type this event is being handled as by jsaction.
1492
+ * E.g. a keypress is handled as click in some cases.
1493
+ * @return The event object.
1494
+ */
1495
+ function createGenericEvent(original, opt_eventType) {
1496
+ let event;
1497
+ if (document.createEvent) {
1498
+ // Event creation as per W3C event model specification. This codepath
1499
+ // is used by most non-IE browsers and also by IE 9 and later.
1500
+ event = document.createEvent('Event');
1501
+ event.initEvent(opt_eventType || original.type, true, true);
1502
+ }
1503
+ else {
1504
+ // Older versions of IE (up to version 8) do not support the
1505
+ // W3C event model. Use the IE specific function instead.
1506
+ // Suppressing errors for ts-migration.
1507
+ // TS2339: Property 'createEventObject' does not exist on type 'Document'.
1508
+ // @ts-ignore
1509
+ event = document.createEventObject();
1510
+ event.type = opt_eventType || original.type;
1511
+ }
1512
+ event.originalTimestamp = original.timeStamp;
1513
+ return event;
1514
+ }
1515
+ /**
1516
+ * Creates an event object for replaying through the DOM.
1517
+ * NOTE: This function is visible just for testing. Please don't use
1518
+ * it outside JsAction internal testing.
1519
+ * TODO: Add support for FocusEvent and WheelEvent.
1520
+ * @param base The event to create a new event from.
1521
+ * @param opt_eventType The type this event is being handled as by jsaction.
1522
+ * E.g. a keypress is handled as click in some cases.
1523
+ * @return The event object.
1524
+ */
1525
+ function createEvent(base, opt_eventType) {
1526
+ const original = base;
1527
+ let event;
1528
+ let eventType;
1529
+ if (original.type === EventType.CUSTOM) {
1530
+ eventType = EventType.CUSTOM;
1531
+ }
1532
+ else {
1533
+ eventType = opt_eventType || original.type;
1534
+ }
1535
+ if (isKeyboardEvent(eventType)) {
1536
+ event = createKeyboardEvent(original, opt_eventType);
1537
+ }
1538
+ else if (isMouseEvent(eventType)) {
1539
+ event = createMouseEvent(original, opt_eventType);
1540
+ }
1541
+ else if (isUiEvent(eventType)) {
1542
+ event = createUiEvent(original, opt_eventType);
1543
+ }
1544
+ else if (eventType === EventType.CUSTOM) {
1545
+ event = createCustomEvent(opt_eventType, original['detail']['data'], original['detail']['triggeringEvent']);
1546
+ event.originalTimestamp = original.timeStamp;
1547
+ }
1548
+ else {
1549
+ // This ensures we don't send an undefined event object to the replayer.
1550
+ event = createGenericEvent(original, opt_eventType);
1551
+ }
1552
+ return event;
1553
+ }
1554
+ /**
1555
+ * Sends an event for replay to the DOM.
1556
+ * @param target The target for the event.
1557
+ * @param event The event object.
1558
+ * @return The return value of the event replay, i.e., whether preventDefault()
1559
+ * was called on it.
1560
+ */
1561
+ function triggerEvent(target, event) {
1562
+ if (target.dispatchEvent) {
1563
+ return target.dispatchEvent(event);
1564
+ }
1565
+ else {
1566
+ // Suppressing errors for ts-migration.
1567
+ // TS2339: Property 'fireEvent' does not exist on type 'Element'.
1568
+ // @ts-ignore
1569
+ return target.fireEvent('on' + event.type, event);
1570
+ }
1571
+ }
1572
+ /** Do not use outiside of testing. */
1573
+ const testing = {
1574
+ createKeyboardModifiersList,
1575
+ createGenericEvent,
1576
+ isKeyboardEvent,
1577
+ isMouseEvent,
1578
+ isUiEvent,
1579
+ };
1580
+
1581
+ /**
1582
+ * @fileoverview An enum to control who can call certain jsaction APIs.
1583
+ */
1584
+ var Restriction;
1585
+ (function (Restriction) {
1586
+ Restriction[Restriction["I_AM_THE_JSACTION_FRAMEWORK"] = 1] = "I_AM_THE_JSACTION_FRAMEWORK";
1587
+ })(Restriction || (Restriction = {}));
1588
+
1589
+ /**
1590
+ * Receives a DOM event, determines the jsaction associated with the source
1591
+ * element of the DOM event, and invokes the handler associated with the
1592
+ * jsaction.
1593
+ */
1594
+ class BaseDispatcher {
1595
+ /**
1596
+ * Options are:
1597
+ * 1. `eventReplayer`: When the event contract dispatches replay events
1598
+ * to the Dispatcher, the Dispatcher collects them and in the next tick
1599
+ * dispatches them to the `eventReplayer`.
1600
+ * @param dispatchDelegate A function that should handle dispatching an `EventInfoWrapper` to handlers.
1601
+ */
1602
+ constructor(dispatchDelegate, { eventReplayer = undefined } = {}) {
1603
+ this.dispatchDelegate = dispatchDelegate;
1604
+ /** The queue of events. */
1605
+ this.queuedEventInfoWrappers = [];
1606
+ /** Whether the event replay is scheduled. */
1607
+ this.eventReplayScheduled = false;
1608
+ this.eventReplayer = eventReplayer;
1609
+ }
1610
+ /**
1611
+ * Receives an event or the event queue from the EventContract. The event
1612
+ * queue is copied and it attempts to replay.
1613
+ * If event info is passed in it looks for an action handler that can handle
1614
+ * the given event. If there is no handler registered queues the event and
1615
+ * checks if a loader is registered for the given namespace. If so, calls it.
1616
+ *
1617
+ * Alternatively, if in global dispatch mode, calls all registered global
1618
+ * handlers for the appropriate event type.
1619
+ *
1620
+ * The three functionalities of this call are deliberately not split into
1621
+ * three methods (and then declared as an abstract interface), because the
1622
+ * interface is used by EventContract, which lives in a different jsbinary.
1623
+ * Therefore the interface between the three is defined entirely in terms that
1624
+ * are invariant under jscompiler processing (Function and Array, as opposed
1625
+ * to a custom type with method names).
1626
+ *
1627
+ * @param eventInfo The info for the event that triggered this call or the
1628
+ * queue of events from EventContract.
1629
+ * @param isGlobalDispatch If true, dispatches a global event instead of a
1630
+ * regular jsaction handler.
1631
+ * @return An `EventInfo` for the `EventContract` to handle again if the
1632
+ * `Dispatcher` tried to resolve an a11y event as a click but failed.
1633
+ */
1634
+ dispatch(eventInfo, isGlobalDispatch) {
1635
+ const eventInfoWrapper = new EventInfoWrapper(eventInfo);
1636
+ if (eventInfoWrapper.getIsReplay()) {
1637
+ if (isGlobalDispatch || !this.eventReplayer) {
1638
+ return;
1639
+ }
1640
+ const resolved = resolveA11yEvent(eventInfoWrapper, isGlobalDispatch);
1641
+ if (!resolved) {
1642
+ // Send the event back through the `EventContract` by dispatching to
1643
+ // the browser.
1644
+ replayEvent(eventInfoWrapper.getEvent(), eventInfoWrapper.getTargetElement(), eventInfoWrapper.getEventType());
1645
+ return;
1646
+ }
1647
+ this.queueEventInfoWrapper(eventInfoWrapper);
1648
+ this.scheduleEventReplay();
1649
+ return;
1650
+ }
1651
+ const resolved = resolveA11yEvent(eventInfoWrapper, isGlobalDispatch);
1652
+ if (!resolved) {
1653
+ // Reset action information.
1654
+ eventInfoWrapper.setAction(undefined);
1655
+ return eventInfoWrapper.eventInfo;
1656
+ }
1657
+ this.dispatchDelegate(eventInfoWrapper, isGlobalDispatch);
1658
+ }
1659
+ /** Queue an `EventInfoWrapper` for replay. */
1660
+ queueEventInfoWrapper(eventInfoWrapper) {
1661
+ this.queuedEventInfoWrappers.push(eventInfoWrapper);
1662
+ }
1663
+ /**
1664
+ * Replays queued events, if any. The replaying will happen in its own
1665
+ * stack once the current flow cedes control. This is done to mimic
1666
+ * browser event handling.
1667
+ */
1668
+ scheduleEventReplay() {
1669
+ if (this.eventReplayScheduled ||
1670
+ !this.eventReplayer ||
1671
+ this.queuedEventInfoWrappers.length === 0) {
1672
+ return;
1673
+ }
1674
+ this.eventReplayScheduled = true;
1675
+ Promise.resolve().then(() => {
1676
+ this.eventReplayScheduled = false;
1677
+ this.eventReplayer(this.queuedEventInfoWrappers);
1678
+ });
1679
+ }
1680
+ }
1681
+ /**
1682
+ * If a 'MAYBE_CLICK_EVENT_TYPE' event was dispatched, updates the eventType
1683
+ * to either click or keydown based on whether the keydown action can be
1684
+ * treated as a click. For MAYBE_CLICK_EVENT_TYPE events that are just
1685
+ * keydowns, we set flags on the event object so that the event contract
1686
+ * does't try to dispatch it as a MAYBE_CLICK_EVENT_TYPE again.
1687
+ *
1688
+ * @param isGlobalDispatch Whether the eventInfo is meant to be dispatched to
1689
+ * the global handlers.
1690
+ * @return Returns false if the a11y event could not be resolved and should
1691
+ * be re-dispatched.
1692
+ */
1693
+ function resolveA11yEvent(eventInfoWrapper, isGlobalDispatch = false) {
1694
+ if (eventInfoWrapper.getEventType() !== Attribute$1.MAYBE_CLICK_EVENT_TYPE) {
1695
+ return true;
1696
+ }
1697
+ if (isA11yClickEvent(eventInfoWrapper, isGlobalDispatch)) {
1698
+ if (shouldPreventDefault(eventInfoWrapper)) {
1699
+ preventDefault(eventInfoWrapper.getEvent());
1700
+ }
1701
+ // If the keydown event can be treated as a click, we change the eventType
1702
+ // to 'click' so that the dispatcher can retrieve the right handler for
1703
+ // it. Even though EventInfo['action'] corresponds to the click action,
1704
+ // the global handler and any custom 'getHandler' implementations may rely
1705
+ // on the eventType instead.
1706
+ eventInfoWrapper.setEventType(EventType.CLICK);
1707
+ }
1708
+ else {
1709
+ // Otherwise, if the keydown can't be treated as a click, we need to
1710
+ // retrigger it because now we need to look for 'keydown' actions instead.
1711
+ eventInfoWrapper.setEventType(EventType.KEYDOWN);
1712
+ if (!isGlobalDispatch) {
1713
+ // This prevents the event contract from setting the
1714
+ // AccessibilityAttribute.MAYBE_CLICK_EVENT_TYPE type for Keydown
1715
+ // events.
1716
+ eventInfoWrapper.getEvent()[Attribute$1.SKIP_A11Y_CHECK] = true;
1717
+ // Since globally dispatched events will get handled by the dispatcher,
1718
+ // don't have the event contract dispatch it again.
1719
+ eventInfoWrapper.getEvent()[Attribute$1.SKIP_GLOBAL_DISPATCH] = true;
1720
+ return false;
1721
+ }
1722
+ }
1723
+ return true;
1724
+ }
1725
+ /**
1726
+ * Returns true if the default action for this event should be prevented
1727
+ * before the event handler is envoked.
1728
+ */
1729
+ function shouldPreventDefault(eventInfoWrapper) {
1730
+ const actionElement = eventInfoWrapper.getAction()?.element;
1731
+ // For parity with no-a11y-support behavior.
1732
+ if (!actionElement) {
1733
+ return false;
1734
+ }
1735
+ // Prevent scrolling if the Space key was pressed
1736
+ if (isSpaceKeyEvent(eventInfoWrapper.getEvent())) {
1737
+ return true;
1738
+ }
1739
+ // or prevent the browser's default action for native HTML controls.
1740
+ if (shouldCallPreventDefaultOnNativeHtmlControl(eventInfoWrapper.getEvent())) {
1741
+ return true;
1742
+ }
1743
+ // Prevent browser from following <a> node links if a jsaction is present
1744
+ // and we are dispatching the action now. Note that the targetElement may be
1745
+ // a child of an anchor that has a jsaction attached. For that reason, we
1746
+ // need to check the actionElement rather than the targetElement.
1747
+ if (actionElement.tagName === 'A') {
1748
+ return true;
1749
+ }
1750
+ return false;
1751
+ }
1752
+ /**
1753
+ * Returns true if the given key event can be treated as a 'click'.
1754
+ *
1755
+ * @param isGlobalDispatch Whether the eventInfo is meant to be dispatched to
1756
+ * the global handlers.
1757
+ */
1758
+ function isA11yClickEvent(eventInfoWrapper, isGlobalDispatch) {
1759
+ return ((isGlobalDispatch || eventInfoWrapper.getAction() !== undefined) &&
1760
+ isActionKeyEvent(eventInfoWrapper.getEvent()));
1761
+ }
1762
+ /**
1763
+ * Registers deferred functionality for an EventContract and a Jsaction
1764
+ * Dispatcher.
1765
+ */
1766
+ function registerDispatcher$1(eventContract, dispatcher) {
1767
+ eventContract.ecrd((eventInfo, globalDispatch) => {
1768
+ return dispatcher.dispatch(eventInfo, globalDispatch);
1769
+ }, Restriction.I_AM_THE_JSACTION_FRAMEWORK);
1770
+ }
1771
+
1772
+ const Char = {
1773
+ /**
1774
+ * The separator between the namespace and the action name in the
1775
+ * jsaction attribute value.
1776
+ */
1777
+ NAMESPACE_ACTION_SEPARATOR: '.',
1778
+ /**
1779
+ * The separator between the event name and action in the jsaction
1780
+ * attribute value.
1781
+ */
1782
+ EVENT_ACTION_SEPARATOR: ':',
1783
+ /**
1784
+ * The separator between the logged oi attribute values in the &oi=
1785
+ * URL parameter value.
1786
+ */
1787
+ OI_SEPARATOR: '.',
1788
+ /**
1789
+ * The separator between the key and the value pairs in the &cad=
1790
+ * URL parameter value.
1791
+ */
1792
+ CAD_KEY_VALUE_SEPARATOR: ':',
1793
+ /**
1794
+ * The separator between the key-value pairs in the &cad= URL
1795
+ * parameter value.
1796
+ */
1797
+ CAD_SEPARATOR: ',',
1798
+ };
1799
+
1800
+ /**
1801
+ * Receives a DOM event, determines the jsaction associated with the source
1802
+ * element of the DOM event, and invokes the handler associated with the
1803
+ * jsaction.
1804
+ */
1805
+ class Dispatcher {
1806
+ /**
1807
+ * Receives a DOM event, determines the jsaction associated with the source
1808
+ * element of the DOM event, and invokes the handler associated with the
1809
+ * jsaction.
1810
+ *
1811
+ * @param getHandler A function that knows how to get the handler for a
1812
+ * given event info.
1813
+ */
1814
+ constructor(getHandler, { stopPropagation = false, eventReplayer = undefined, } = {}) {
1815
+ this.getHandler = getHandler;
1816
+ /**
1817
+ * The actions that are registered for this Dispatcher instance.
1818
+ * This should be the primary one used once migration off of registerHandlers
1819
+ * is done.
1820
+ */
1821
+ this.actions = {};
1822
+ /** A map of global event handlers, where each key is an event type. */
1823
+ this.globalHandlers = new Map();
1824
+ this.baseDispatcher = new BaseDispatcher((eventInfoWrapper, isGlobalDispatch) => {
1825
+ this.dispatchToHandler(eventInfoWrapper, isGlobalDispatch);
1826
+ }, {
1827
+ eventReplayer: (eventInfoWrappers) => {
1828
+ this.eventReplayer?.(eventInfoWrappers, this);
1829
+ },
1830
+ });
1831
+ this.stopPropagation = stopPropagation;
1832
+ }
1833
+ /**
1834
+ * Receives an event or the event queue from the EventContract. The event
1835
+ * queue is copied and it attempts to replay.
1836
+ * If event info is passed in it looks for an action handler that can handle
1837
+ * the given event. If there is no handler registered queues the event and
1838
+ * checks if a loader is registered for the given namespace. If so, calls it.
1839
+ *
1840
+ * Alternatively, if in global dispatch mode, calls all registered global
1841
+ * handlers for the appropriate event type.
1842
+ *
1843
+ * The three functionalities of this call are deliberately not split into
1844
+ * three methods (and then declared as an abstract interface), because the
1845
+ * interface is used by EventContract, which lives in a different jsbinary.
1846
+ * Therefore the interface between the three is defined entirely in terms that
1847
+ * are invariant under jscompiler processing (Function and Array, as opposed
1848
+ * to a custom type with method names).
1849
+ *
1850
+ * @param eventInfo The info for the event that triggered this call or the
1851
+ * queue of events from EventContract.
1852
+ * @param isGlobalDispatch If true, dispatches a global event instead of a
1853
+ * regular jsaction handler.
1854
+ * @return An `EventInfo` for the `EventContract` to handle again if the
1855
+ * `Dispatcher` tried to resolve an a11y event as a click but failed.
1856
+ */
1857
+ dispatch(eventInfo, isGlobalDispatch) {
1858
+ return this.baseDispatcher.dispatch(eventInfo, isGlobalDispatch);
1859
+ }
1860
+ /**
1861
+ * Dispatches an `EventInfoWrapper`.
1862
+ */
1863
+ dispatchToHandler(eventInfoWrapper, isGlobalDispatch) {
1864
+ if (isGlobalDispatch) {
1865
+ // Skip everything related to jsaction handlers, and execute the global
1866
+ // handlers.
1867
+ const ev = eventInfoWrapper.getEvent();
1868
+ const eventTypeHandlers = this.globalHandlers.get(eventInfoWrapper.getEventType());
1869
+ let shouldPreventDefault = false;
1870
+ if (eventTypeHandlers) {
1871
+ for (const handler of eventTypeHandlers) {
1872
+ if (handler(ev) === false) {
1873
+ shouldPreventDefault = true;
1874
+ }
1875
+ }
1876
+ }
1877
+ if (shouldPreventDefault) {
1878
+ preventDefault(ev);
1879
+ }
1880
+ return;
1881
+ }
1882
+ if (this.stopPropagation) {
1883
+ stopPropagation(eventInfoWrapper);
1884
+ }
1885
+ const action = eventInfoWrapper.getAction();
1886
+ let handler = undefined;
1887
+ if (this.getHandler) {
1888
+ handler = this.getHandler(eventInfoWrapper);
1889
+ }
1890
+ if (!handler) {
1891
+ handler = this.actions[action.name];
1892
+ }
1893
+ if (handler) {
1894
+ handler(eventInfoWrapper);
1895
+ return;
1896
+ }
1897
+ // No handler was found.
1898
+ this.baseDispatcher.queueEventInfoWrapper(eventInfoWrapper);
1899
+ }
1900
+ /**
1901
+ * Registers multiple methods all bound to the same object
1902
+ * instance. This is a common case: an application module binds
1903
+ * multiple of its methods under public names to the event contract of
1904
+ * the application. So we provide a shortcut for it.
1905
+ * Attempts to replay the queued events after registering the handlers.
1906
+ *
1907
+ * @param namespace The namespace of the jsaction name.
1908
+ *
1909
+ * @param instance The object to bind the methods to. If this is null, then
1910
+ * the functions are not bound, but directly added under the public names.
1911
+ *
1912
+ * @param methods A map from public name to functions that will be bound to
1913
+ * instance and registered as action under the public name. I.e. the
1914
+ * property names are the public names. The property values are the
1915
+ * methods of instance.
1916
+ */
1917
+ registerEventInfoHandlers(namespace, instance, methods) {
1918
+ for (const [name, method] of Object.entries(methods)) {
1919
+ const handler = instance ? method.bind(instance) : method;
1920
+ if (namespace) {
1921
+ // Include a '.' separator between namespace name and action name.
1922
+ // In the case that no namespace name is provided, the jsaction name
1923
+ // consists of the action name only (no period).
1924
+ const fullName = namespace + Char.NAMESPACE_ACTION_SEPARATOR + name;
1925
+ this.actions[fullName] = handler;
1926
+ }
1927
+ else {
1928
+ this.actions[name] = handler;
1929
+ }
1930
+ }
1931
+ this.baseDispatcher.scheduleEventReplay();
1932
+ }
1933
+ /**
1934
+ * Unregisters an action. Provided as an easy way to reverse the effects of
1935
+ * registerHandlers.
1936
+ * @param namespace The namespace of the jsaction name.
1937
+ * @param name The action name to unbind.
1938
+ */
1939
+ unregisterHandler(namespace, name) {
1940
+ const fullName = namespace ? namespace + Char.NAMESPACE_ACTION_SEPARATOR + name : name;
1941
+ delete this.actions[fullName];
1942
+ }
1943
+ /** Registers a global event handler. */
1944
+ registerGlobalHandler(eventType, handler) {
1945
+ if (!this.globalHandlers.has(eventType)) {
1946
+ this.globalHandlers.set(eventType, new Set([handler]));
1947
+ }
1948
+ else {
1949
+ this.globalHandlers.get(eventType).add(handler);
1950
+ }
1951
+ }
1952
+ /** Unregisters a global event handler. */
1953
+ unregisterGlobalHandler(eventType, handler) {
1954
+ if (this.globalHandlers.has(eventType)) {
1955
+ this.globalHandlers.get(eventType).delete(handler);
1956
+ }
1957
+ }
1958
+ /**
1959
+ * Checks whether there is an action registered under the given
1960
+ * name. This returns true if there is a namespace handler, even
1961
+ * if it can not yet handle the event.
1962
+ *
1963
+ * @param name Action name.
1964
+ * @return Whether the name is registered.
1965
+ * @see #canDispatch
1966
+ */
1967
+ hasAction(name) {
1968
+ return this.actions.hasOwnProperty(name);
1969
+ }
1970
+ /**
1971
+ * Whether this dispatcher can dispatch the event. This can be used by
1972
+ * event replayer to check whether the dispatcher can replay an event.
1973
+ */
1974
+ canDispatch(eventInfoWrapper) {
1975
+ const action = eventInfoWrapper.getAction();
1976
+ if (!action) {
1977
+ return false;
1978
+ }
1979
+ return this.hasAction(action.name);
1980
+ }
1981
+ /**
1982
+ * Sets the event replayer, enabling queued events to be replayed when actions
1983
+ * are bound. To replay events, you must register the dispatcher to the
1984
+ * contract after setting the `EventReplayer`. The event replayer takes as
1985
+ * parameters the queue of events and the dispatcher (used to check whether
1986
+ * actions have handlers registered and can be replayed). The event replayer
1987
+ * is also responsible for dequeuing events.
1988
+ *
1989
+ * Example: An event replayer that replays only the last event.
1990
+ *
1991
+ * const dispatcher = new Dispatcher();
1992
+ * // ...
1993
+ * dispatcher.setEventReplayer((queue, dispatcher) => {
1994
+ * const lastEventInfoWrapper = queue[queue.length -1];
1995
+ * if (dispatcher.canDispatch(lastEventInfoWrapper.getAction())) {
1996
+ * jsaction.replay.replayEvent(
1997
+ * lastEventInfoWrapper.getEvent(),
1998
+ * lastEventInfoWrapper.getTargetElement(),
1999
+ * lastEventInfoWrapper.getEventType(),
2000
+ * );
2001
+ * queue.length = 0;
2002
+ * }
2003
+ * });
2004
+ *
2005
+ * @param eventReplayer It allows elements to be replayed and dequeuing.
2006
+ */
2007
+ setEventReplayer(eventReplayer) {
2008
+ this.eventReplayer = eventReplayer;
2009
+ }
2010
+ }
2011
+ /** Stop propagation for an `EventInfo`. */
2012
+ function stopPropagation(eventInfoWrapper) {
2013
+ if (isGecko &&
2014
+ (eventInfoWrapper.getTargetElement().tagName === 'INPUT' ||
2015
+ eventInfoWrapper.getTargetElement().tagName === 'TEXTAREA') &&
2016
+ eventInfoWrapper.getEventType() === EventType.FOCUS) {
2017
+ /**
2018
+ * Do nothing since stopping propagation on a focus event on an input
2019
+ * element in Firefox makes the text cursor disappear:
2020
+ * https://bugzilla.mozilla.org/show_bug.cgi?id=509684
2021
+ */
2022
+ return;
2023
+ }
2024
+ const event = eventInfoWrapper.getEvent();
2025
+ // There are some cases where users of the `Dispatcher` will call dispatch
2026
+ // with a fake event that does not support `stopPropagation`.
2027
+ if (!event.stopPropagation) {
2028
+ return;
2029
+ }
2030
+ event.stopPropagation();
2031
+ }
2032
+ /**
2033
+ * Registers deferred functionality for an EventContract and a Jsaction
2034
+ * Dispatcher.
2035
+ */
2036
+ function registerDispatcher(eventContract, dispatcher) {
2037
+ eventContract.ecrd((eventInfo, globalDispatch) => {
2038
+ return dispatcher.dispatch(eventInfo, globalDispatch);
2039
+ }, Restriction.I_AM_THE_JSACTION_FRAMEWORK);
2040
+ }
2041
+
2042
+ /**
2043
+ * Whether the user agent is running on iOS.
2044
+ */
2045
+ const isIos = typeof navigator !== 'undefined' && /iPhone|iPad|iPod/.test(navigator.userAgent);
2046
+ /**
2047
+ * A class representing a container node and all the event handlers
2048
+ * installed on it. Used so that handlers can be cleaned up if the
2049
+ * container is removed from the contract.
2050
+ */
2051
+ class EventContractContainer {
2052
+ /**
2053
+ * @param element The container Element.
2054
+ */
2055
+ constructor(element) {
2056
+ this.element = element;
2057
+ /**
2058
+ * Array of event handlers and their corresponding event types that are
2059
+ * installed on this container.
2060
+ *
2061
+ */
2062
+ this.handlerInfos = [];
2063
+ }
2064
+ /**
2065
+ * Installs the provided installer on the element owned by this container,
2066
+ * and maintains a reference to resulting handler in order to remove it
2067
+ * later if desired.
2068
+ */
2069
+ addEventListener(eventType, getHandler) {
2070
+ // In iOS, event bubbling doesn't happen automatically in any DOM element,
2071
+ // unless it has an onclick attribute or DOM event handler attached to it.
2072
+ // This breaks JsAction in some cases. See "Making Elements Clickable"
2073
+ // section at http://goo.gl/2VoGnB.
2074
+ //
2075
+ // A workaround for this issue is to change the CSS cursor style to 'pointer'
2076
+ // for the container element, which magically turns on event bubbling. This
2077
+ // solution is described in the comments section at http://goo.gl/6pEO1z.
2078
+ //
2079
+ // We use a navigator.userAgent check here as this problem is present both
2080
+ // on Mobile Safari and thin WebKit wrappers, such as Chrome for iOS.
2081
+ if (isIos) {
2082
+ this.element.style.cursor = 'pointer';
2083
+ }
2084
+ this.handlerInfos.push(addEventListener(this.element, eventType, getHandler(this.element)));
2085
+ }
2086
+ /**
2087
+ * Removes all the handlers installed on this container.
2088
+ */
2089
+ cleanUp() {
2090
+ for (let i = 0; i < this.handlerInfos.length; i++) {
2091
+ removeEventListener(this.element, this.handlerInfos[i]);
2092
+ }
2093
+ this.handlerInfos = [];
2094
+ }
2095
+ }
2096
+
2097
+ /**
2098
+ * Update `EventInfo` to be `eventType = 'click'` and sets `a11yClickKey` if it
2099
+ * is a a11y click.
2100
+ */
2101
+ function updateEventInfoForA11yClick(eventInfo) {
2102
+ if (!isActionKeyEvent(getEvent(eventInfo))) {
2103
+ return;
2104
+ }
2105
+ setA11yClickKey(eventInfo, true);
2106
+ // A 'click' triggered by a DOM keypress should be mapped to the 'click'
2107
+ // jsaction.
2108
+ setEventType(eventInfo, EventType.CLICK);
2109
+ }
2110
+ /**
2111
+ * Call `preventDefault` on an a11y click if it is space key or to prevent the
2112
+ * browser's default action for native HTML controls.
2113
+ */
2114
+ function preventDefaultForA11yClick(eventInfo) {
2115
+ if (!getA11yClickKey(eventInfo) ||
2116
+ (!isSpaceKeyEvent(getEvent(eventInfo)) &&
2117
+ !shouldCallPreventDefaultOnNativeHtmlControl(getEvent(eventInfo)))) {
2118
+ return;
2119
+ }
2120
+ preventDefault(getEvent(eventInfo));
2121
+ }
2122
+ /**
2123
+ * Sets the `action` to `clickonly` for a click event that is not an a11y click
2124
+ * and if there is not already a click action.
2125
+ */
2126
+ function populateClickOnlyAction(actionElement, eventInfo, actionMap) {
2127
+ if (
2128
+ // If there's already an action, don't attempt to set a CLICKONLY
2129
+ getAction(eventInfo) ||
2130
+ // Only attempt CLICKONLY if the type is CLICK
2131
+ getEventType(eventInfo) !== EventType.CLICK ||
2132
+ // a11y clicks are never CLICKONLY
2133
+ getA11yClickKey(eventInfo) ||
2134
+ actionMap[EventType.CLICKONLY] === undefined) {
2135
+ return;
2136
+ }
2137
+ setEventType(eventInfo, EventType.CLICKONLY);
2138
+ setAction(eventInfo, actionMap[EventType.CLICKONLY], actionElement);
2139
+ }
2140
+
2141
+ var Attribute;
2142
+ (function (Attribute) {
2143
+ /**
2144
+ * The jsaction attribute defines a mapping of a DOM event to a
2145
+ * generic event (aka jsaction), to which the actual event handlers
2146
+ * that implement the behavior of the application are bound. The
2147
+ * value is a semicolon separated list of colon separated pairs of
2148
+ * an optional DOM event name and a jsaction name. If the optional
2149
+ * DOM event name is omitted, 'click' is assumed. The jsaction names
2150
+ * are dot separated pairs of a namespace and a simple jsaction
2151
+ * name. If the namespace is absent, it is taken from the closest
2152
+ * ancestor element with a jsnamespace attribute, if there is
2153
+ * any. If there is no ancestor with a jsnamespace attribute, the
2154
+ * simple name is assumed to be the jsaction name.
2155
+ *
2156
+ * Used by EventContract.
2157
+ */
2158
+ Attribute["JSACTION"] = "jsaction";
2159
+ /**
2160
+ * The jsnamespace attribute provides the namespace part of the
2161
+ * jaction names occurring in the jsaction attribute where it's
2162
+ * missing.
2163
+ *
2164
+ * Used by EventContract.
2165
+ */
2166
+ Attribute["JSNAMESPACE"] = "jsnamespace";
2167
+ /**
2168
+ * The oi attribute is a log impression tag for impression logging
2169
+ * and action tracking. For an element that carries a jsaction
2170
+ * attribute, the element is identified for the purpose of
2171
+ * impression logging and click tracking by the dot separated path
2172
+ * of all oi attributes in the chain of ancestors of the element.
2173
+ *
2174
+ * Used by ActionFlow.
2175
+ */
2176
+ Attribute["OI"] = "oi";
2177
+ /**
2178
+ * The ved attribute is an encoded ClickTrackingCGI proto to track
2179
+ * visual elements.
2180
+ *
2181
+ * Used by ActionFlow.
2182
+ */
2183
+ Attribute["VED"] = "ved";
2184
+ /**
2185
+ * The vet attribute is the visual element type used to identify tracked
2186
+ * visual elements.
2187
+ */
2188
+ Attribute["VET"] = "vet";
2189
+ /**
2190
+ * Support for iteration on reprocessing.
2191
+ *
2192
+ * Used by ActionFlow.
2193
+ */
2194
+ Attribute["JSINSTANCE"] = "jsinstance";
2195
+ /**
2196
+ * All click jsactions that happen on the element that carries this
2197
+ * attribute or its descendants are automatically logged.
2198
+ * Impressions of jsactions on these elements are tracked too, if
2199
+ * requested by the impression() method of ActionFlow.
2200
+ *
2201
+ * Used by ActionFlow.
2202
+ */
2203
+ Attribute["JSTRACK"] = "jstrack";
2204
+ })(Attribute || (Attribute = {}));
2205
+
2206
+ /** All properties that are used by jsaction. */
2207
+ var Property;
2208
+ (function (Property) {
2209
+ /**
2210
+ * The parsed value of the jsaction attribute is stored in this
2211
+ * property on the DOM node. The parsed value is an Object. The
2212
+ * property names of the object are the events; the values are the
2213
+ * names of the actions. This property is attached even on nodes
2214
+ * that don't have a jsaction attribute as an optimization, because
2215
+ * property lookup is faster than attribute access.
2216
+ */
2217
+ Property["JSACTION"] = "__jsaction";
2218
+ /**
2219
+ * The parsed value of the jsnamespace attribute is stored in this
2220
+ * property on the DOM node.
2221
+ */
2222
+ Property["JSNAMESPACE"] = "__jsnamespace";
2223
+ /** The value of the oi attribute as a property, for faster access. */
2224
+ Property["OI"] = "__oi";
2225
+ /**
2226
+ * The owner property references an a logical owner for a DOM node. JSAction
2227
+ * will follow this reference instead of parentNode when traversing the DOM
2228
+ * to find jsaction attributes. This allows overlaying a logical structure
2229
+ * over a document where the DOM structure can't reflect that structure.
2230
+ */
2231
+ Property["OWNER"] = "__owner";
2232
+ })(Property || (Property = {}));
2233
+
2234
+ /**
2235
+ * Map from jsaction annotation to a parsed map from event name to action name.
2236
+ */
2237
+ const parseCache = {};
2238
+ /**
2239
+ * Reads the jsaction parser cache from the given DOM Element.
2240
+ *
2241
+ * @param element .
2242
+ * @return Map from event to qualified name of the jsaction bound to it.
2243
+ */
2244
+ function get(element) {
2245
+ // @ts-ignore
2246
+ return element[Property.JSACTION];
2247
+ }
2248
+ /**
2249
+ * Writes the jsaction parser cache to the given DOM Element.
2250
+ *
2251
+ * @param element .
2252
+ * @param actionMap Map from event to qualified name of the jsaction bound to
2253
+ * it.
2254
+ */
2255
+ function set(element, actionMap) {
2256
+ // @ts-ignore
2257
+ element[Property.JSACTION] = actionMap;
2258
+ }
2259
+ /**
2260
+ * Looks up the parsed action map from the source jsaction attribute value.
2261
+ *
2262
+ * @param text Unparsed jsaction attribute value.
2263
+ * @return Parsed jsaction attribute value, if already present in the cache.
2264
+ */
2265
+ function getParsed(text) {
2266
+ return parseCache[text];
2267
+ }
2268
+ /**
2269
+ * Inserts the parse result for the given source jsaction value into the cache.
2270
+ *
2271
+ * @param text Unparsed jsaction attribute value.
2272
+ * @param parsed Attribute value parsed into the action map.
2273
+ */
2274
+ function setParsed(text, parsed) {
2275
+ parseCache[text] = parsed;
2276
+ }
2277
+ /**
2278
+ * Clears the jsaction parser cache from the given DOM Element.
2279
+ *
2280
+ * @param element .
2281
+ */
2282
+ function clear(element) {
2283
+ if (Property.JSACTION in element) {
2284
+ delete element[Property.JSACTION];
2285
+ }
2286
+ }
2287
+ /**
2288
+ * Reads the cached jsaction namespace from the given DOM
2289
+ * Element. Undefined means there is no cached value; null is a cached
2290
+ * jsnamespace attribute that's absent.
2291
+ *
2292
+ * @param element .
2293
+ * @return .
2294
+ */
2295
+ function getNamespace(element) {
2296
+ // @ts-ignore
2297
+ return element[Property.JSNAMESPACE];
2298
+ }
2299
+ /**
2300
+ * Writes the cached jsaction namespace to the given DOM Element. Null
2301
+ * represents a jsnamespace attribute that's absent.
2302
+ *
2303
+ * @param element .
2304
+ * @param jsnamespace .
2305
+ */
2306
+ function setNamespace(element, jsnamespace) {
2307
+ // @ts-ignore
2308
+ element[Property.JSNAMESPACE] = jsnamespace;
2309
+ }
2310
+ /**
2311
+ * Clears the cached jsaction namespace from the given DOM Element.
2312
+ *
2313
+ * @param element .
2314
+ */
2315
+ function clearNamespace(element) {
2316
+ if (Property.JSNAMESPACE in element) {
2317
+ delete element[Property.JSNAMESPACE];
2318
+ }
2319
+ }
2320
+
2321
+ /**
2322
+ * @define Support for jsnamespace attribute. This flag can be overridden in a
2323
+ * build rule to trim down the EventContract's binary size.
2324
+ */
2325
+ const JSNAMESPACE_SUPPORT = true;
2326
+ /**
2327
+ * @define Handles a11y click casting in the dispatcher rather than
2328
+ * the event contract. When enabled, it will enable
2329
+ * EventContract.A11Y_CLICK_SUPPORT as well as both are required for this
2330
+ * functionality.
2331
+ */
2332
+ const A11Y_SUPPORT_IN_DISPATCHER = false;
2333
+ /**
2334
+ * @define Support for accessible click actions. This flag can be overridden in
2335
+ * a build rule.
2336
+ */
2337
+ const A11Y_CLICK_SUPPORT_FLAG_ENABLED = false;
2338
+ /**
2339
+ * Enables a11y click casting when either A11Y_CLICK_SUPPORT_FLAG_ENABLED or
2340
+ * A11Y_SUPPORT_IN_DISPATCHER.
2341
+ */
2342
+ const A11Y_CLICK_SUPPORT = A11Y_CLICK_SUPPORT_FLAG_ENABLED || A11Y_SUPPORT_IN_DISPATCHER;
2343
+ /**
2344
+ * @define Support for the non-bubbling mouseenter and mouseleave events. This
2345
+ * flag can be overridden in a build rule.
2346
+ */
2347
+ const MOUSE_SPECIAL_SUPPORT = false;
2348
+ /**
2349
+ * @define Call stopPropagation on handled events. When integrating with
2350
+ * non-jsaction event handler based code, you will likely want to turn this flag
2351
+ * off. While most event handlers will continue to work, jsaction binds focus
2352
+ * and blur events in the capture phase and thus with stopPropagation, none of
2353
+ * your non-jsaction-handlers will ever see it.
2354
+ */
2355
+ const STOP_PROPAGATION = true;
2356
+ /**
2357
+ * @define Support for custom events, which are type EventType.CUSTOM. These are
2358
+ * native DOM events with an additional type field and an optional payload.
2359
+ */
2360
+ const CUSTOM_EVENT_SUPPORT = false;
2361
+
2362
+ /**
2363
+ * @fileoverview Implements the local event handling contract. This
2364
+ * allows DOM objects in a container that enters into this contract to
2365
+ * define event handlers which are executed in a local context.
2366
+ *
2367
+ * One EventContract instance can manage the contract for multiple
2368
+ * containers, which are added using the addContainer() method.
2369
+ *
2370
+ * Events can be registered using the addEvent() method.
2371
+ *
2372
+ * A Dispatcher is added using the registerDispatcher() method. Until there is
2373
+ * a dispatcher, events are queued. The idea is that the EventContract
2374
+ * class is inlined in the HTML of the top level page and instantiated
2375
+ * right after the start of <body>. The Dispatcher class is contained
2376
+ * in the external deferred js, and instantiated and registered with
2377
+ * EventContract when the external javascript in the page loads. The
2378
+ * external javascript will also register the jsaction handlers, which
2379
+ * then pick up the queued events at the time of registration.
2380
+ *
2381
+ * Since this class is meant to be inlined in the main page HTML, the
2382
+ * size of the binary compiled from this file MUST be kept as small as
2383
+ * possible and thus its dependencies to a minimum.
2384
+ */
2385
+ const DEFAULT_EVENT_TYPE = EventType.CLICK;
2386
+ /**
2387
+ * Since maps from event to action are immutable we can use a single map
2388
+ * to represent the empty map.
2389
+ */
2390
+ const EMPTY_ACTION_MAP = {};
2391
+ /**
2392
+ * This regular expression matches a semicolon.
2393
+ */
2394
+ const REGEXP_SEMICOLON = /\s*;\s*/;
2395
+ /**
2396
+ * EventContract intercepts events in the bubbling phase at the
2397
+ * boundary of a container element, and maps them to generic actions
2398
+ * which are specified using the custom jsaction attribute in
2399
+ * HTML. Behavior of the application is then specified in terms of
2400
+ * handler for such actions, cf. jsaction.Dispatcher in dispatcher.js.
2401
+ *
2402
+ * This has several benefits: (1) No DOM event handlers need to be
2403
+ * registered on the specific elements in the UI. (2) The set of
2404
+ * events that the application has to handle can be specified in terms
2405
+ * of the semantics of the application, rather than in terms of DOM
2406
+ * events. (3) Invocation of handlers can be delayed and handlers can
2407
+ * be delay loaded in a generic way.
2408
+ */
2409
+ class EventContract {
2410
+ static { this.CUSTOM_EVENT_SUPPORT = CUSTOM_EVENT_SUPPORT; }
2411
+ static { this.STOP_PROPAGATION = STOP_PROPAGATION; }
2412
+ static { this.A11Y_SUPPORT_IN_DISPATCHER = A11Y_SUPPORT_IN_DISPATCHER; }
2413
+ static { this.A11Y_CLICK_SUPPORT = A11Y_CLICK_SUPPORT; }
2414
+ static { this.MOUSE_SPECIAL_SUPPORT = MOUSE_SPECIAL_SUPPORT; }
2415
+ static { this.JSNAMESPACE_SUPPORT = JSNAMESPACE_SUPPORT; }
2416
+ constructor(containerManager) {
2417
+ /**
2418
+ * The DOM events which this contract covers. Used to prevent double
2419
+ * registration of event types. The value of the map is the
2420
+ * internally created DOM event handler function that handles the
2421
+ * DOM events. See addEvent().
2422
+ *
2423
+ */
2424
+ this.eventHandlers = {};
2425
+ this.browserEventTypeToExtraEventTypes = {};
2426
+ /**
2427
+ * The dispatcher function. Events are passed to this function for
2428
+ * handling once it was set using the registerDispatcher() method. This is
2429
+ * done because the function is passed from another jsbinary, so passing the
2430
+ * instance and invoking the method here would require to leave the method
2431
+ * unobfuscated.
2432
+ */
2433
+ this.dispatcher = null;
2434
+ /**
2435
+ * The list of suspended `EventInfo` that will be dispatched
2436
+ * as soon as the `Dispatcher` is registered.
2437
+ */
2438
+ this.queuedEventInfos = [];
2439
+ /** Whether a11y click support has been loaded or not. */
2440
+ this.hasA11yClickSupport = false;
2441
+ /** Whether to add an a11y click listener. */
2442
+ this.addA11yClickListener = EventContract.A11Y_SUPPORT_IN_DISPATCHER;
2443
+ this.updateEventInfoForA11yClick = undefined;
2444
+ this.preventDefaultForA11yClick = undefined;
2445
+ this.populateClickOnlyAction = undefined;
2446
+ this.containerManager = containerManager;
2447
+ if (EventContract.CUSTOM_EVENT_SUPPORT) {
2448
+ this.addEvent(EventType.CUSTOM);
2449
+ }
2450
+ if (EventContract.A11Y_CLICK_SUPPORT) {
2451
+ // Add a11y click support to the `EventContract`.
2452
+ this.addA11yClickSupport();
2453
+ }
2454
+ }
2455
+ handleEvent(eventType, event, container) {
2456
+ const eventInfo = createEventInfoFromParameters(
2457
+ /* eventType= */ eventType,
2458
+ /* event= */ event,
2459
+ /* targetElement= */ event.target,
2460
+ /* container= */ container,
2461
+ /* timestamp= */ Date.now());
2462
+ this.handleEventInfo(eventInfo);
2463
+ }
2464
+ /**
2465
+ * Handle an `EventInfo`.
2466
+ * @param allowRehandling Used in the case of a11y click casting to prevent
2467
+ * us from trying to rehandle in an infinite loop.
2468
+ */
2469
+ handleEventInfo(eventInfo, allowRehandling = true) {
2470
+ if (!this.dispatcher) {
2471
+ // All events are queued when the dispatcher isn't yet loaded.
2472
+ setIsReplay(eventInfo, true);
2473
+ this.queuedEventInfos?.push(eventInfo);
2474
+ }
2475
+ if (EventContract.CUSTOM_EVENT_SUPPORT &&
2476
+ getEventType(eventInfo) === EventType.CUSTOM) {
2477
+ const detail = getEvent(eventInfo).detail;
2478
+ // For custom events, use a secondary dispatch based on the internal
2479
+ // custom type of the event.
2480
+ if (!detail || !detail['_type']) {
2481
+ // This should never happen.
2482
+ return;
2483
+ }
2484
+ setEventType(eventInfo, detail['_type']);
2485
+ }
2486
+ this.populateAction(eventInfo);
2487
+ if (this.dispatcher &&
2488
+ !getEvent(eventInfo)[Attribute$1.SKIP_GLOBAL_DISPATCH]) {
2489
+ const globalEventInfo = cloneEventInfo(eventInfo);
2490
+ // In some cases, `populateAction` will rewrite `click` events to
2491
+ // `clickonly`. Revert back to a regular click, otherwise we won't be able
2492
+ // to execute global event handlers registered on click events.
2493
+ if (getEventType(globalEventInfo) === EventType.CLICKONLY) {
2494
+ setEventType(globalEventInfo, EventType.CLICK);
2495
+ }
2496
+ this.dispatcher(globalEventInfo, /* dispatch global event */ true);
2497
+ }
2498
+ const action = getAction(eventInfo);
2499
+ if (!action && !checkDispatcherForA11yClick(eventInfo)) {
2500
+ return;
2501
+ }
2502
+ if (this.dispatcher) {
2503
+ if (action &&
2504
+ shouldPreventDefaultBeforeDispatching(getActionElement(action), eventInfo)) {
2505
+ preventDefault(getEvent(eventInfo));
2506
+ }
2507
+ const unresolvedEventInfo = this.dispatcher(eventInfo);
2508
+ if (unresolvedEventInfo && allowRehandling) {
2509
+ // The dispatcher only returns an event for MAYBE_CLICK_EVENT_TYPE
2510
+ // events that can't be casted to a click. We run it through the
2511
+ // handler again to find keydown actions for it.
2512
+ this.handleEventInfo(unresolvedEventInfo, /* allowRehandling= */ false);
2513
+ return;
2514
+ }
2515
+ }
2516
+ }
2517
+ /**
2518
+ * Searches for a jsaction that the DOM event maps to and creates an
2519
+ * object containing event information used for dispatching by
2520
+ * jsaction.Dispatcher. This method populates the `action` and `actionElement`
2521
+ * fields of the EventInfo object passed in by finding the first
2522
+ * jsaction attribute above the target Node of the event, and below
2523
+ * the container Node, that specifies a jsaction for the event
2524
+ * type. If no such jsaction is found, then action is undefined.
2525
+ *
2526
+ * @param eventInfo `EventInfo` to set `action` and `actionElement` if an
2527
+ * action is found on any `Element` in the path of the `Event`.
2528
+ */
2529
+ populateAction(eventInfo) {
2530
+ // We distinguish modified and plain clicks in order to support the
2531
+ // default browser behavior of modified clicks on links; usually to
2532
+ // open the URL of the link in new tab or new window on ctrl/cmd
2533
+ // click. A DOM 'click' event is mapped to the jsaction 'click'
2534
+ // event iff there is no modifier present on the event. If there is
2535
+ // a modifier, it's mapped to 'clickmod' instead.
2536
+ //
2537
+ // It's allowed to omit the event in the jsaction attribute. In that
2538
+ // case, 'click' is assumed. Thus the following two are equivalent:
2539
+ //
2540
+ // <a href="someurl" jsaction="gna.fu">
2541
+ // <a href="someurl" jsaction="click:gna.fu">
2542
+ //
2543
+ // For unmodified clicks, EventContract invokes the jsaction
2544
+ // 'gna.fu'. For modified clicks, EventContract won't find a
2545
+ // suitable action and leave the event to be handled by the
2546
+ // browser.
2547
+ //
2548
+ // In order to also invoke a jsaction handler for a modifier click,
2549
+ // 'clickmod' needs to be used:
2550
+ //
2551
+ // <a href="someurl" jsaction="clickmod:gna.fu">
2552
+ //
2553
+ // EventContract invokes the jsaction 'gna.fu' for modified
2554
+ // clicks. Unmodified clicks are left to the browser.
2555
+ //
2556
+ // In order to set up the event contract to handle both clickonly and
2557
+ // clickmod, only addEvent(EventType.CLICK) is necessary.
2558
+ //
2559
+ // In order to set up the event contract to handle click,
2560
+ // addEvent() is necessary for CLICK, KEYDOWN, and KEYPRESS event types. If
2561
+ // a11y click support is enabled, addEvent() will set up the appropriate key
2562
+ // event handler automatically.
2563
+ if (getEventType(eventInfo) === EventType.CLICK &&
2564
+ isModifiedClickEvent(getEvent(eventInfo))) {
2565
+ setEventType(eventInfo, EventType.CLICKMOD);
2566
+ }
2567
+ else if (this.hasA11yClickSupport) {
2568
+ this.updateEventInfoForA11yClick(eventInfo);
2569
+ }
2570
+ else if (EventContract.A11Y_SUPPORT_IN_DISPATCHER &&
2571
+ getEventType(eventInfo) === EventType.KEYDOWN &&
2572
+ !getEvent(eventInfo)[Attribute$1.SKIP_A11Y_CHECK]) {
2573
+ // We use a string literal as this value needs to be referenced in the
2574
+ // dispatcher's binary.
2575
+ setEventType(eventInfo, Attribute$1.MAYBE_CLICK_EVENT_TYPE);
2576
+ }
2577
+ // Walk to the parent node, unless the node has a different owner in
2578
+ // which case we walk to the owner. Attempt to walk to host of a
2579
+ // shadow root if needed.
2580
+ let actionElement = getTargetElement(eventInfo);
2581
+ while (actionElement && actionElement !== getContainer(eventInfo)) {
2582
+ this.populateActionOnElement(actionElement, eventInfo);
2583
+ if (getAction(eventInfo)) {
2584
+ // An event is handled by at most one jsaction. Thus we stop at the
2585
+ // first matching jsaction specified in a jsaction attribute up the
2586
+ // ancestor chain of the event target node.
2587
+ break;
2588
+ }
2589
+ if (actionElement[Property.OWNER]) {
2590
+ actionElement = actionElement[Property.OWNER];
2591
+ continue;
2592
+ }
2593
+ if (actionElement.parentNode?.nodeName !== '#document-fragment') {
2594
+ actionElement = actionElement.parentNode;
2595
+ }
2596
+ else {
2597
+ actionElement = actionElement.parentNode?.host ?? null;
2598
+ }
2599
+ }
2600
+ const action = getAction(eventInfo);
2601
+ if (!action) {
2602
+ // No action found.
2603
+ return;
2604
+ }
2605
+ if (this.hasA11yClickSupport) {
2606
+ this.preventDefaultForA11yClick(eventInfo);
2607
+ }
2608
+ // We attempt to handle the mouseenter/mouseleave events here by
2609
+ // detecting whether the mouseover/mouseout events correspond to
2610
+ // entering/leaving an element.
2611
+ if (EventContract.MOUSE_SPECIAL_SUPPORT &&
2612
+ (getEventType(eventInfo) === EventType.MOUSEENTER ||
2613
+ getEventType(eventInfo) === EventType.MOUSELEAVE ||
2614
+ getEventType(eventInfo) === EventType.POINTERENTER ||
2615
+ getEventType(eventInfo) === EventType.POINTERLEAVE)) {
2616
+ // We attempt to handle the mouseenter/mouseleave events here by
2617
+ // detecting whether the mouseover/mouseout events correspond to
2618
+ // entering/leaving an element.
2619
+ if (isMouseSpecialEvent(getEvent(eventInfo), getEventType(eventInfo), getActionElement(action))) {
2620
+ // If both mouseover/mouseout and mouseenter/mouseleave events are
2621
+ // enabled, two separate handlers for mouseover/mouseout are
2622
+ // registered. Both handlers will see the same event instance
2623
+ // so we create a copy to avoid interfering with the dispatching of
2624
+ // the mouseover/mouseout event.
2625
+ const copiedEvent = createMouseSpecialEvent(getEvent(eventInfo), getActionElement(action));
2626
+ setEvent(eventInfo, copiedEvent);
2627
+ // Since the mouseenter/mouseleave events do not bubble, the target
2628
+ // of the event is technically the `actionElement` (the node with the
2629
+ // `jsaction` attribute)
2630
+ setTargetElement(eventInfo, getActionElement(action));
2631
+ }
2632
+ else {
2633
+ unsetAction(eventInfo);
2634
+ }
2635
+ }
2636
+ }
2637
+ /**
2638
+ * Accesses the jsaction map on a node and retrieves the name of the
2639
+ * action the given event is mapped to, if any. It parses the
2640
+ * attribute value and stores it in a property on the node for
2641
+ * subsequent retrieval without re-parsing and re-accessing the
2642
+ * attribute. In order to fully qualify jsaction names using a
2643
+ * namespace, the DOM is searched starting at the current node and
2644
+ * going through ancestor nodes until a jsnamespace attribute is
2645
+ * found.
2646
+ *
2647
+ * @param actionElement The DOM node to retrieve the jsaction map from.
2648
+ * @param eventInfo `EventInfo` to set `action` and `actionElement` if an
2649
+ * action is found on the `actionElement`.
2650
+ */
2651
+ populateActionOnElement(actionElement, eventInfo) {
2652
+ const actionMap = parseActions(actionElement, getContainer(eventInfo));
2653
+ const actionName = actionMap[getEventType(eventInfo)];
2654
+ if (actionName !== undefined) {
2655
+ setAction(eventInfo, actionName, actionElement);
2656
+ }
2657
+ if (this.hasA11yClickSupport) {
2658
+ this.populateClickOnlyAction(actionElement, eventInfo, actionMap);
2659
+ }
2660
+ if (EventContract.A11Y_SUPPORT_IN_DISPATCHER) {
2661
+ if (getEventType(eventInfo) === Attribute$1.MAYBE_CLICK_EVENT_TYPE &&
2662
+ actionMap[EventType.CLICK] !== undefined) {
2663
+ // We'll take the first CLICK action we find and have the dispatcher
2664
+ // check if the keydown event can be used as a CLICK. If not, the
2665
+ // dispatcher will retrigger the event so that we can find a keydown
2666
+ // event instead.
2667
+ // When we get MAYBE_CLICK_EVENT_TYPE as an eventType, we want to
2668
+ // retrieve the action corresponding to CLICK, but still keep the
2669
+ // eventInfoLib.getEventType(eventInfo, ) as MAYBE_CLICK_EVENT_TYPE. The
2670
+ // dispatcher uses this event type to determine if it should get the
2671
+ // handler for the action.
2672
+ setAction(eventInfo, actionMap[EventType.CLICK], actionElement);
2673
+ }
2674
+ else {
2675
+ populateClickOnlyAction(actionElement, eventInfo, actionMap);
2676
+ }
2677
+ }
2678
+ }
2679
+ /**
2680
+ * Enables jsaction handlers to be called for the event type given by
2681
+ * name.
2682
+ *
2683
+ * If the event is already registered, this does nothing.
2684
+ *
2685
+ * @param prefixedEventType If supplied, this event is used in
2686
+ * the actual browser event registration instead of the name that is
2687
+ * exposed to jsaction. Use this if you e.g. want users to be able
2688
+ * to subscribe to jsaction="transitionEnd:foo" while the underlying
2689
+ * event is webkitTransitionEnd in one browser and mozTransitionEnd
2690
+ * in another.
2691
+ */
2692
+ addEvent(eventType, prefixedEventType) {
2693
+ if (eventType in this.eventHandlers || !this.containerManager) {
2694
+ return;
2695
+ }
2696
+ if (!EventContract.MOUSE_SPECIAL_SUPPORT &&
2697
+ (eventType === EventType.MOUSEENTER ||
2698
+ eventType === EventType.MOUSELEAVE ||
2699
+ eventType === EventType.POINTERENTER ||
2700
+ eventType === EventType.POINTERLEAVE)) {
2701
+ return;
2702
+ }
2703
+ const eventHandler = (eventType, event, container) => {
2704
+ this.handleEvent(eventType, event, container);
2705
+ };
2706
+ // Store the callback to allow us to replay events.
2707
+ this.eventHandlers[eventType] = eventHandler;
2708
+ const browserEventType = getBrowserEventType(prefixedEventType || eventType);
2709
+ if (browserEventType !== eventType) {
2710
+ const eventTypes = this.browserEventTypeToExtraEventTypes[browserEventType] || [];
2711
+ eventTypes.push(eventType);
2712
+ this.browserEventTypeToExtraEventTypes[browserEventType] = eventTypes;
2713
+ }
2714
+ this.containerManager.addEventListener(browserEventType, (element) => {
2715
+ return (event) => {
2716
+ eventHandler(eventType, event, element);
2717
+ };
2718
+ });
2719
+ // Automatically install a keypress/keydown event handler if support for
2720
+ // accessible clicks is turned on.
2721
+ if (this.addA11yClickListener && eventType === EventType.CLICK) {
2722
+ this.addEvent(EventType.KEYDOWN);
2723
+ }
2724
+ }
2725
+ /**
2726
+ * Gets the queued early events and replay them using the appropriate handler
2727
+ * in the provided event contract. Once all the events are replayed, it cleans
2728
+ * up the early contract.
2729
+ */
2730
+ replayEarlyEvents() {
2731
+ // Check if the early contract is present and prevent calling this function
2732
+ // more than once.
2733
+ const earlyJsactionData = window._ejsa;
2734
+ if (!earlyJsactionData) {
2735
+ return;
2736
+ }
2737
+ // Replay the early contract events.
2738
+ const earlyEventInfos = earlyJsactionData.q;
2739
+ for (let idx = 0; idx < earlyEventInfos.length; idx++) {
2740
+ const earlyEventInfo = earlyEventInfos[idx];
2741
+ const eventTypes = this.getEventTypesForBrowserEventType(earlyEventInfo.eventType);
2742
+ for (let i = 0; i < eventTypes.length; i++) {
2743
+ const eventInfo = cloneEventInfo(earlyEventInfo);
2744
+ // EventInfo eventType maps to JSAction's internal event type,
2745
+ // rather than the browser event type.
2746
+ setEventType(eventInfo, eventTypes[i]);
2747
+ this.handleEventInfo(eventInfo);
2748
+ }
2749
+ }
2750
+ // Clean up the early contract.
2751
+ const earlyEventTypes = earlyJsactionData.et;
2752
+ const earlyEventHandler = earlyJsactionData.h;
2753
+ for (let idx = 0; idx < earlyEventTypes.length; idx++) {
2754
+ const eventType = earlyEventTypes[idx];
2755
+ window.document.documentElement.removeEventListener(eventType, earlyEventHandler);
2756
+ }
2757
+ delete window._ejsa;
2758
+ }
2759
+ /**
2760
+ * Returns all JSAction event types that have been registered for a given
2761
+ * browser event type.
2762
+ */
2763
+ getEventTypesForBrowserEventType(browserEventType) {
2764
+ const eventTypes = [];
2765
+ if (this.eventHandlers[browserEventType]) {
2766
+ eventTypes.push(browserEventType);
2767
+ }
2768
+ if (this.browserEventTypeToExtraEventTypes[browserEventType]) {
2769
+ eventTypes.push(...this.browserEventTypeToExtraEventTypes[browserEventType]);
2770
+ }
2771
+ return eventTypes;
2772
+ }
2773
+ /**
2774
+ * Returns the event handler function for a given event type.
2775
+ */
2776
+ handler(eventType) {
2777
+ return this.eventHandlers[eventType];
2778
+ }
2779
+ /**
2780
+ * Cleans up the event contract. This resets all of the `EventContract`'s
2781
+ * internal state. Users are responsible for not using this `EventContract`
2782
+ * after it has been cleaned up.
2783
+ */
2784
+ cleanUp() {
2785
+ this.containerManager.cleanUp();
2786
+ this.containerManager = null;
2787
+ this.eventHandlers = {};
2788
+ this.browserEventTypeToExtraEventTypes = {};
2789
+ this.dispatcher = null;
2790
+ this.queuedEventInfos = [];
2791
+ }
2792
+ /**
2793
+ * Register a dispatcher function. Event info of each event mapped to
2794
+ * a jsaction is passed for handling to this callback. The queued
2795
+ * events are passed as well to the dispatcher for later replaying
2796
+ * once the dispatcher is registered. Clears the event queue to null.
2797
+ *
2798
+ * @param dispatcher The dispatcher function.
2799
+ * @param restriction
2800
+ */
2801
+ registerDispatcher(dispatcher, restriction) {
2802
+ this.ecrd(dispatcher, restriction);
2803
+ }
2804
+ /**
2805
+ * Unrenamed alias for registerDispatcher. Necessary for any codebases that
2806
+ * split the `EventContract` and `Dispatcher` code into different compilation
2807
+ * units.
2808
+ */
2809
+ ecrd(dispatcher, restriction) {
2810
+ this.dispatcher = dispatcher;
2811
+ if (this.queuedEventInfos?.length) {
2812
+ for (let i = 0; i < this.queuedEventInfos.length; i++) {
2813
+ this.handleEventInfo(this.queuedEventInfos[i]);
2814
+ }
2815
+ this.queuedEventInfos = null;
2816
+ }
2817
+ }
2818
+ /**
2819
+ * Adds a11y click support to the given `EventContract`. Meant to be called in
2820
+ * the same compilation unit as the `EventContract`.
2821
+ */
2822
+ addA11yClickSupport() {
2823
+ this.addA11yClickSupportImpl(updateEventInfoForA11yClick, preventDefaultForA11yClick, populateClickOnlyAction);
2824
+ }
2825
+ /**
2826
+ * Enables a11y click support to be deferred. Meant to be called in the same
2827
+ * compilation unit as the `EventContract`.
2828
+ */
2829
+ exportAddA11yClickSupport() {
2830
+ this.addA11yClickListener = true;
2831
+ this.ecaacs = this.addA11yClickSupportImpl.bind(this);
2832
+ }
2833
+ /**
2834
+ * Unrenamed function that loads a11yClickSupport.
2835
+ */
2836
+ addA11yClickSupportImpl(updateEventInfoForA11yClick, preventDefaultForA11yClick, populateClickOnlyAction) {
2837
+ this.addA11yClickListener = true;
2838
+ this.hasA11yClickSupport = true;
2839
+ this.updateEventInfoForA11yClick = updateEventInfoForA11yClick;
2840
+ this.preventDefaultForA11yClick = preventDefaultForA11yClick;
2841
+ this.populateClickOnlyAction = populateClickOnlyAction;
2842
+ }
2843
+ }
2844
+ /**
2845
+ * Adds a11y click support to the given `EventContract`. Meant to be called
2846
+ * in a different compilation unit from the `EventContract`. The `EventContract`
2847
+ * must have called `exportAddA11yClickSupport` in its compilation unit for this
2848
+ * to have any effect.
2849
+ */
2850
+ function addDeferredA11yClickSupport(eventContract) {
2851
+ eventContract.ecaacs?.(updateEventInfoForA11yClick, preventDefaultForA11yClick, populateClickOnlyAction);
2852
+ }
2853
+ /**
2854
+ * Determines whether or not the `EventContract` needs to check with the
2855
+ * dispatcher even if there's no action.
2856
+ */
2857
+ function checkDispatcherForA11yClick(eventInfo) {
2858
+ return (EventContract.A11Y_SUPPORT_IN_DISPATCHER &&
2859
+ getEventType(eventInfo) === Attribute$1.MAYBE_CLICK_EVENT_TYPE);
2860
+ }
2861
+ /**
2862
+ * Returns true if the default action of this event should be prevented before
2863
+ * this event is dispatched.
2864
+ */
2865
+ function shouldPreventDefaultBeforeDispatching(actionElement, eventInfo) {
2866
+ // Prevent browser from following <a> node links if a jsaction is present
2867
+ // and we are dispatching the action now. Note that the targetElement may be
2868
+ // a child of an anchor that has a jsaction attached. For that reason, we
2869
+ // need to check the actionElement rather than the targetElement.
2870
+ return (actionElement.tagName === 'A' &&
2871
+ (getEventType(eventInfo) === EventType.CLICK ||
2872
+ getEventType(eventInfo) === EventType.CLICKMOD));
2873
+ }
2874
+ /**
2875
+ * Parses and caches an element's jsaction element into a map.
2876
+ *
2877
+ * This is primarily for internal use.
2878
+ *
2879
+ * @param actionElement The DOM node to retrieve the jsaction map from.
2880
+ * @param container The node which limits the namespace lookup for a jsaction
2881
+ * name. The container node itself will not be searched.
2882
+ * @return Map from event to qualified name of the jsaction bound to it.
2883
+ */
2884
+ function parseActions(actionElement, container) {
2885
+ let actionMap = get(actionElement);
2886
+ if (!actionMap) {
2887
+ const jsactionAttribute = getAttr(actionElement, Attribute.JSACTION);
2888
+ if (!jsactionAttribute) {
2889
+ actionMap = EMPTY_ACTION_MAP;
2890
+ set(actionElement, actionMap);
2891
+ }
2892
+ else {
2893
+ actionMap = getParsed(jsactionAttribute);
2894
+ if (!actionMap) {
2895
+ actionMap = {};
2896
+ const values = jsactionAttribute.split(REGEXP_SEMICOLON);
2897
+ for (let idx = 0; idx < values.length; idx++) {
2898
+ const value = values[idx];
2899
+ if (!value) {
2900
+ continue;
2901
+ }
2902
+ const colon = value.indexOf(Char.EVENT_ACTION_SEPARATOR);
2903
+ const hasColon = colon !== -1;
2904
+ const type = hasColon ? stringTrim(value.substr(0, colon)) : DEFAULT_EVENT_TYPE;
2905
+ const action = hasColon ? stringTrim(value.substr(colon + 1)) : value;
2906
+ actionMap[type] = action;
2907
+ }
2908
+ setParsed(jsactionAttribute, actionMap);
2909
+ }
2910
+ // If namespace support is active we need to augment the (potentially
2911
+ // cached) jsaction mapping with the namespace.
2912
+ if (EventContract.JSNAMESPACE_SUPPORT) {
2913
+ const noNs = actionMap;
2914
+ actionMap = {};
2915
+ for (const type in noNs) {
2916
+ actionMap[type] = getFullyQualifiedAction(noNs[type], actionElement, container);
2917
+ }
2918
+ }
2919
+ set(actionElement, actionMap);
2920
+ }
2921
+ }
2922
+ return actionMap;
2923
+ }
2924
+ /**
2925
+ * Returns the fully qualified jsaction action. If the given jsaction
2926
+ * name doesn't already contain the namespace, the function iterates
2927
+ * over ancestor nodes until a jsnamespace attribute is found, and
2928
+ * uses the value of that attribute as the namespace.
2929
+ *
2930
+ * @param action The jsaction action to resolve.
2931
+ * @param start The node from which to start searching for a jsnamespace
2932
+ * attribute.
2933
+ * @param container The node which limits the search for a jsnamespace
2934
+ * attribute. This node will be searched.
2935
+ * @return The fully qualified name of the jsaction. If no namespace is found,
2936
+ * returns the unqualified name in case it exists in the global namespace.
2937
+ */
2938
+ function getFullyQualifiedAction(action, start, container) {
2939
+ if (EventContract.JSNAMESPACE_SUPPORT) {
2940
+ if (isNamespacedAction(action)) {
2941
+ return action;
2942
+ }
2943
+ let node = start;
2944
+ while (node) {
2945
+ const namespace = getNamespaceFromElement(node);
2946
+ if (namespace) {
2947
+ return namespace + Char.NAMESPACE_ACTION_SEPARATOR + action;
2948
+ }
2949
+ // If this node is the container, stop.
2950
+ if (node === container) {
2951
+ break;
2952
+ }
2953
+ node = node.parentNode;
2954
+ }
2955
+ }
2956
+ return action;
2957
+ }
2958
+ /**
2959
+ * Checks if a jsaction action contains a namespace part.
2960
+ */
2961
+ function isNamespacedAction(action) {
2962
+ return action.indexOf(Char.NAMESPACE_ACTION_SEPARATOR) >= 0;
2963
+ }
2964
+ /**
2965
+ * Returns the value of the jsnamespace attribute of the given node.
2966
+ * Also caches the value for subsequent lookups.
2967
+ * @param element The node whose jsnamespace attribute is being asked for.
2968
+ * @return The value of the jsnamespace attribute, or null if not found.
2969
+ */
2970
+ function getNamespaceFromElement(element) {
2971
+ let namespace = getNamespace(element);
2972
+ // Only query for the attribute if it has not been queried for
2973
+ // before. getAttr() returns null if an attribute is not present. Thus,
2974
+ // namespace is string|null if the query took place in the past, or
2975
+ // undefined if the query did not take place.
2976
+ if (namespace === undefined) {
2977
+ namespace = getAttr(element, Attribute.JSNAMESPACE);
2978
+ setNamespace(element, namespace);
2979
+ }
2980
+ return namespace;
2981
+ }
2982
+ /**
2983
+ * Accesses the event handler attribute value of a DOM node. It guards
2984
+ * against weird situations (described in the body) that occur in
2985
+ * connection with nodes that are removed from their document.
2986
+ * @param element The DOM element.
2987
+ * @param attribute The name of the attribute to access.
2988
+ * @return The attribute value if it was found, null otherwise.
2989
+ */
2990
+ function getAttr(element, attribute) {
2991
+ let value = null;
2992
+ // NOTE: Nodes in IE do not always have a getAttribute
2993
+ // method defined. This is the case where sourceElement has in
2994
+ // fact been removed from the DOM before eventContract begins
2995
+ // handling - where a parentNode does not have getAttribute
2996
+ // defined.
2997
+ // NOTE: We must use the 'in' operator instead of the regular dot
2998
+ // notation, since the latter fails in IE8 if the getAttribute method is not
2999
+ // defined. See b/7139109.
3000
+ if ('getAttribute' in element) {
3001
+ value = element.getAttribute(attribute);
3002
+ }
3003
+ return value;
3004
+ }
3005
+ /**
3006
+ * Helper function to trim whitespace from the beginning and the end
3007
+ * of the string. This deliberately doesn't use the closure equivalent
3008
+ * to keep dependencies small.
3009
+ * @param str Input string.
3010
+ * @return Trimmed string.
3011
+ */
3012
+ function stringTrim(str) {
3013
+ if (typeof String.prototype.trim === 'function') {
3014
+ return str.trim();
3015
+ }
3016
+ const trimmedLeft = str.replace(/^\s+/, '');
3017
+ return trimmedLeft.replace(/\s+$/, '');
3018
+ }
3019
+
3020
+ /**
3021
+ * Provides a factory function for bootstrapping an event contract on a
3022
+ * window object.
3023
+ * @param field The property on the window that the event contract will be placed on.
3024
+ * @param container The container that listens to events
3025
+ * @param appId A given identifier for an application. If there are multiple apps on the page
3026
+ * then this is how contracts can be initialized for each one.
3027
+ * @param events An array of event names that should be listened to.
3028
+ * @param anyWindow The global window object that should receive the event contract.
3029
+ * @returns The `event` contract. This is both assigned to `anyWindow` and returned for testing.
3030
+ */
3031
+ function bootstrapEventContract(field, container, appId, events, anyWindow = window) {
3032
+ if (!anyWindow[field]) {
3033
+ anyWindow[field] = {};
3034
+ }
3035
+ const eventContract = new EventContract(new EventContractContainer(container));
3036
+ anyWindow[field][appId] = eventContract;
3037
+ for (const ev of events) {
3038
+ eventContract.addEvent(ev);
3039
+ }
3040
+ return eventContract;
3041
+ }
3042
+
3043
+ export { Dispatcher, EventContract, EventContractContainer, EventInfoWrapper, bootstrapEventContract, registerDispatcher };
3044
+ //# sourceMappingURL=event-dispatch.mjs.map