@angular/core 18.0.0-rc.0 → 18.0.0-rc.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/esm2022/primitives/event-dispatch/contract_binary.mjs +3 -3
- package/esm2022/primitives/event-dispatch/index.mjs +2 -2
- package/esm2022/primitives/event-dispatch/src/action_resolver.mjs +221 -0
- package/esm2022/primitives/event-dispatch/src/attribute.mjs +63 -65
- package/esm2022/primitives/event-dispatch/src/cache.mjs +10 -10
- package/esm2022/primitives/event-dispatch/src/dispatcher.mjs +52 -176
- package/esm2022/primitives/event-dispatch/src/earlyeventcontract.mjs +15 -9
- package/esm2022/primitives/event-dispatch/src/event_contract_defines.mjs +1 -19
- package/esm2022/primitives/event-dispatch/src/eventcontract.mjs +24 -362
- package/esm2022/primitives/event-dispatch/src/key_code.mjs +11 -13
- package/esm2022/primitives/event-dispatch/src/legacy_dispatcher.mjs +252 -2
- package/esm2022/primitives/event-dispatch/src/property.mjs +30 -27
- package/esm2022/primitives/event-dispatch/src/register_events.mjs +16 -17
- package/esm2022/primitives/event-dispatch/src/restriction.mjs +2 -2
- package/esm2022/src/application/application_ref.mjs +6 -3
- package/esm2022/src/application/create_application.mjs +12 -5
- package/esm2022/src/change_detection/scheduling/exhaustive_check_no_changes.mjs +150 -0
- package/esm2022/src/change_detection/scheduling/ng_zone_scheduling.mjs +6 -4
- package/esm2022/src/change_detection/scheduling/zoneless_scheduling.mjs +3 -1
- package/esm2022/src/change_detection/scheduling/zoneless_scheduling_impl.mjs +9 -6
- package/esm2022/src/core.mjs +2 -1
- package/esm2022/src/core_private_export.mjs +2 -1
- package/esm2022/src/core_reactivity_export_internal.mjs +1 -3
- package/esm2022/src/core_render3_private_export.mjs +1 -3
- package/esm2022/src/defer/instructions.mjs +20 -12
- package/esm2022/src/defer/interfaces.mjs +1 -3
- package/esm2022/src/errors.mjs +1 -1
- package/esm2022/src/hydration/event_replay.mjs +67 -79
- package/esm2022/src/hydration/utils.mjs +1 -2
- package/esm2022/src/metadata/directives.mjs +1 -1
- package/esm2022/src/platform/platform_ref.mjs +10 -4
- package/esm2022/src/render3/after_render_hooks.mjs +4 -2
- package/esm2022/src/render3/component_ref.mjs +1 -1
- package/esm2022/src/render3/index.mjs +1 -3
- package/esm2022/src/render3/instructions/change_detection.mjs +13 -10
- package/esm2022/src/render3/instructions/listener.mjs +12 -1
- package/esm2022/src/render3/interfaces/public_definitions.mjs +1 -1
- package/esm2022/src/render3/state.mjs +14 -4
- package/esm2022/src/render3/view_ref.mjs +3 -2
- package/esm2022/src/util/callback_scheduler.mjs +12 -26
- package/esm2022/src/version.mjs +1 -1
- package/esm2022/src/zone/ng_zone.mjs +9 -23
- package/esm2022/testing/src/component_fixture.mjs +2 -4
- package/esm2022/testing/src/defer.mjs +1 -2
- package/esm2022/testing/src/logger.mjs +3 -3
- package/esm2022/testing/src/test_bed.mjs +1 -3
- package/esm2022/testing/src/test_bed_compiler.mjs +3 -6
- package/event-dispatch-contract.min.js +1 -1
- package/fesm2022/core.mjs +800 -660
- package/fesm2022/core.mjs.map +1 -1
- package/fesm2022/primitives/event-dispatch.mjs +484 -807
- package/fesm2022/primitives/event-dispatch.mjs.map +1 -1
- package/fesm2022/primitives/signals.mjs +1 -1
- package/fesm2022/rxjs-interop.mjs +1 -1
- package/fesm2022/testing.mjs +4 -10
- package/fesm2022/testing.mjs.map +1 -1
- package/index.d.ts +36 -4
- package/package.json +1 -1
- package/primitives/event-dispatch/index.d.ts +111 -162
- package/primitives/signals/index.d.ts +1 -1
- package/rxjs-interop/index.d.ts +1 -1
- package/schematics/migrations/http-providers/bundle.js +110 -71
- package/schematics/migrations/http-providers/bundle.js.map +3 -3
- package/schematics/migrations/invalid-two-way-bindings/bundle.js +197 -167
- package/schematics/migrations/invalid-two-way-bindings/bundle.js.map +2 -2
- package/schematics/ng-generate/control-flow-migration/bundle.js +205 -175
- package/schematics/ng-generate/control-flow-migration/bundle.js.map +2 -2
- package/schematics/ng-generate/standalone-migration/bundle.js +481 -451
- package/schematics/ng-generate/standalone-migration/bundle.js.map +2 -2
- package/testing/index.d.ts +1 -4
- package/esm2022/primitives/event-dispatch/src/base_dispatcher.mjs +0 -96
- package/esm2022/primitives/event-dispatch/src/custom_events.mjs +0 -63
- package/esm2022/primitives/event-dispatch/src/replay.mjs +0 -389
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* @license Angular v18.0.0-rc.
|
|
2
|
+
* @license Angular v18.0.0-rc.2
|
|
3
3
|
* (c) 2010-2024 Google LLC. https://angular.io/
|
|
4
4
|
* License: MIT
|
|
5
5
|
*/
|
|
@@ -196,170 +196,6 @@ class EventInfoWrapper {
|
|
|
196
196
|
}
|
|
197
197
|
}
|
|
198
198
|
|
|
199
|
-
/**
|
|
200
|
-
* @fileoverview An enum to control who can call certain jsaction APIs.
|
|
201
|
-
*/
|
|
202
|
-
var Restriction;
|
|
203
|
-
(function (Restriction) {
|
|
204
|
-
Restriction[Restriction["I_AM_THE_JSACTION_FRAMEWORK"] = 1] = "I_AM_THE_JSACTION_FRAMEWORK";
|
|
205
|
-
})(Restriction || (Restriction = {}));
|
|
206
|
-
|
|
207
|
-
/**
|
|
208
|
-
* Receives a DOM event, determines the jsaction associated with the source
|
|
209
|
-
* element of the DOM event, and invokes the handler associated with the
|
|
210
|
-
* jsaction.
|
|
211
|
-
*/
|
|
212
|
-
class BaseDispatcher {
|
|
213
|
-
/**
|
|
214
|
-
* Options are:
|
|
215
|
-
* 1. `eventReplayer`: When the event contract dispatches replay events
|
|
216
|
-
* to the Dispatcher, the Dispatcher collects them and in the next tick
|
|
217
|
-
* dispatches them to the `eventReplayer`.
|
|
218
|
-
* @param dispatchDelegate A function that should handle dispatching an `EventInfoWrapper` to handlers.
|
|
219
|
-
*/
|
|
220
|
-
constructor(dispatchDelegate, { eventReplayer = undefined } = {}) {
|
|
221
|
-
this.dispatchDelegate = dispatchDelegate;
|
|
222
|
-
/** The queue of events. */
|
|
223
|
-
this.queuedEventInfoWrappers = [];
|
|
224
|
-
/** Whether the event replay is scheduled. */
|
|
225
|
-
this.eventReplayScheduled = false;
|
|
226
|
-
this.eventReplayer = eventReplayer;
|
|
227
|
-
}
|
|
228
|
-
/**
|
|
229
|
-
* Receives an event or the event queue from the EventContract. The event
|
|
230
|
-
* queue is copied and it attempts to replay.
|
|
231
|
-
* If event info is passed in it looks for an action handler that can handle
|
|
232
|
-
* the given event. If there is no handler registered queues the event and
|
|
233
|
-
* checks if a loader is registered for the given namespace. If so, calls it.
|
|
234
|
-
*
|
|
235
|
-
* Alternatively, if in global dispatch mode, calls all registered global
|
|
236
|
-
* handlers for the appropriate event type.
|
|
237
|
-
*
|
|
238
|
-
* The three functionalities of this call are deliberately not split into
|
|
239
|
-
* three methods (and then declared as an abstract interface), because the
|
|
240
|
-
* interface is used by EventContract, which lives in a different jsbinary.
|
|
241
|
-
* Therefore the interface between the three is defined entirely in terms that
|
|
242
|
-
* are invariant under jscompiler processing (Function and Array, as opposed
|
|
243
|
-
* to a custom type with method names).
|
|
244
|
-
*
|
|
245
|
-
* @param eventInfo The info for the event that triggered this call or the
|
|
246
|
-
* queue of events from EventContract.
|
|
247
|
-
* @param isGlobalDispatch If true, dispatches a global event instead of a
|
|
248
|
-
* regular jsaction handler.
|
|
249
|
-
*/
|
|
250
|
-
dispatch(eventInfo, isGlobalDispatch) {
|
|
251
|
-
const eventInfoWrapper = new EventInfoWrapper(eventInfo);
|
|
252
|
-
if (eventInfoWrapper.getIsReplay()) {
|
|
253
|
-
if (isGlobalDispatch || !this.eventReplayer) {
|
|
254
|
-
return;
|
|
255
|
-
}
|
|
256
|
-
this.queueEventInfoWrapper(eventInfoWrapper);
|
|
257
|
-
this.scheduleEventReplay();
|
|
258
|
-
return;
|
|
259
|
-
}
|
|
260
|
-
this.dispatchDelegate(eventInfoWrapper, isGlobalDispatch);
|
|
261
|
-
}
|
|
262
|
-
/** Queue an `EventInfoWrapper` for replay. */
|
|
263
|
-
queueEventInfoWrapper(eventInfoWrapper) {
|
|
264
|
-
this.queuedEventInfoWrappers.push(eventInfoWrapper);
|
|
265
|
-
}
|
|
266
|
-
/**
|
|
267
|
-
* Replays queued events, if any. The replaying will happen in its own
|
|
268
|
-
* stack once the current flow cedes control. This is done to mimic
|
|
269
|
-
* browser event handling.
|
|
270
|
-
*/
|
|
271
|
-
scheduleEventReplay() {
|
|
272
|
-
if (this.eventReplayScheduled ||
|
|
273
|
-
!this.eventReplayer ||
|
|
274
|
-
this.queuedEventInfoWrappers.length === 0) {
|
|
275
|
-
return;
|
|
276
|
-
}
|
|
277
|
-
this.eventReplayScheduled = true;
|
|
278
|
-
Promise.resolve().then(() => {
|
|
279
|
-
this.eventReplayScheduled = false;
|
|
280
|
-
this.eventReplayer(this.queuedEventInfoWrappers);
|
|
281
|
-
});
|
|
282
|
-
}
|
|
283
|
-
}
|
|
284
|
-
/**
|
|
285
|
-
* Registers deferred functionality for an EventContract and a Jsaction
|
|
286
|
-
* Dispatcher.
|
|
287
|
-
*/
|
|
288
|
-
function registerDispatcher$1(eventContract, dispatcher) {
|
|
289
|
-
eventContract.ecrd((eventInfo, globalDispatch) => {
|
|
290
|
-
dispatcher.dispatch(eventInfo, globalDispatch);
|
|
291
|
-
}, Restriction.I_AM_THE_JSACTION_FRAMEWORK);
|
|
292
|
-
}
|
|
293
|
-
|
|
294
|
-
const Char = {
|
|
295
|
-
/**
|
|
296
|
-
* The separator between the namespace and the action name in the
|
|
297
|
-
* jsaction attribute value.
|
|
298
|
-
*/
|
|
299
|
-
NAMESPACE_ACTION_SEPARATOR: '.',
|
|
300
|
-
/**
|
|
301
|
-
* The separator between the event name and action in the jsaction
|
|
302
|
-
* attribute value.
|
|
303
|
-
*/
|
|
304
|
-
EVENT_ACTION_SEPARATOR: ':',
|
|
305
|
-
/**
|
|
306
|
-
* The separator between the logged oi attribute values in the &oi=
|
|
307
|
-
* URL parameter value.
|
|
308
|
-
*/
|
|
309
|
-
OI_SEPARATOR: '.',
|
|
310
|
-
/**
|
|
311
|
-
* The separator between the key and the value pairs in the &cad=
|
|
312
|
-
* URL parameter value.
|
|
313
|
-
*/
|
|
314
|
-
CAD_KEY_VALUE_SEPARATOR: ':',
|
|
315
|
-
/**
|
|
316
|
-
* The separator between the key-value pairs in the &cad= URL
|
|
317
|
-
* parameter value.
|
|
318
|
-
*/
|
|
319
|
-
CAD_SEPARATOR: ',',
|
|
320
|
-
};
|
|
321
|
-
|
|
322
|
-
/**
|
|
323
|
-
* Determines if one node is contained within another. Adapted from
|
|
324
|
-
* {@see goog.dom.contains}.
|
|
325
|
-
* @param node Node that should contain otherNode.
|
|
326
|
-
* @param otherNode Node being contained.
|
|
327
|
-
* @return True if otherNode is contained within node.
|
|
328
|
-
*/
|
|
329
|
-
function contains(node, otherNode) {
|
|
330
|
-
if (otherNode === null) {
|
|
331
|
-
return false;
|
|
332
|
-
}
|
|
333
|
-
// We use browser specific methods for this if available since it is faster
|
|
334
|
-
// that way.
|
|
335
|
-
// IE DOM
|
|
336
|
-
if ('contains' in node && otherNode.nodeType === 1) {
|
|
337
|
-
return node.contains(otherNode);
|
|
338
|
-
}
|
|
339
|
-
// W3C DOM Level 3
|
|
340
|
-
if ('compareDocumentPosition' in node) {
|
|
341
|
-
return node === otherNode || Boolean(node.compareDocumentPosition(otherNode) & 16);
|
|
342
|
-
}
|
|
343
|
-
// W3C DOM Level 1
|
|
344
|
-
while (otherNode && node !== otherNode) {
|
|
345
|
-
otherNode = otherNode.parentNode;
|
|
346
|
-
}
|
|
347
|
-
return otherNode === node;
|
|
348
|
-
}
|
|
349
|
-
/**
|
|
350
|
-
* Helper method for broadcastCustomEvent. Returns true if any member of
|
|
351
|
-
* the set is an ancestor of element.
|
|
352
|
-
*/
|
|
353
|
-
function hasAncestorInNodeList(element, nodeList) {
|
|
354
|
-
for (let idx = 0; idx < nodeList.length; ++idx) {
|
|
355
|
-
const member = nodeList[idx];
|
|
356
|
-
if (member !== element && contains(member, element)) {
|
|
357
|
-
return true;
|
|
358
|
-
}
|
|
359
|
-
}
|
|
360
|
-
return false;
|
|
361
|
-
}
|
|
362
|
-
|
|
363
199
|
/*
|
|
364
200
|
* Names of events that are special to jsaction. These are not all
|
|
365
201
|
* event types that are legal to use in either HTML or the addEvent()
|
|
@@ -597,19 +433,66 @@ const EventType = {
|
|
|
597
433
|
CUSTOM: '_custom',
|
|
598
434
|
};
|
|
599
435
|
|
|
436
|
+
/**
|
|
437
|
+
* @fileoverview An enum to control who can call certain jsaction APIs.
|
|
438
|
+
*/
|
|
439
|
+
var Restriction;
|
|
440
|
+
(function (Restriction) {
|
|
441
|
+
Restriction[Restriction["I_AM_THE_JSACTION_FRAMEWORK"] = 0] = "I_AM_THE_JSACTION_FRAMEWORK";
|
|
442
|
+
})(Restriction || (Restriction = {}));
|
|
443
|
+
|
|
444
|
+
/**
|
|
445
|
+
* Determines if one node is contained within another. Adapted from
|
|
446
|
+
* {@see goog.dom.contains}.
|
|
447
|
+
* @param node Node that should contain otherNode.
|
|
448
|
+
* @param otherNode Node being contained.
|
|
449
|
+
* @return True if otherNode is contained within node.
|
|
450
|
+
*/
|
|
451
|
+
function contains(node, otherNode) {
|
|
452
|
+
if (otherNode === null) {
|
|
453
|
+
return false;
|
|
454
|
+
}
|
|
455
|
+
// We use browser specific methods for this if available since it is faster
|
|
456
|
+
// that way.
|
|
457
|
+
// IE DOM
|
|
458
|
+
if ('contains' in node && otherNode.nodeType === 1) {
|
|
459
|
+
return node.contains(otherNode);
|
|
460
|
+
}
|
|
461
|
+
// W3C DOM Level 3
|
|
462
|
+
if ('compareDocumentPosition' in node) {
|
|
463
|
+
return node === otherNode || Boolean(node.compareDocumentPosition(otherNode) & 16);
|
|
464
|
+
}
|
|
465
|
+
// W3C DOM Level 1
|
|
466
|
+
while (otherNode && node !== otherNode) {
|
|
467
|
+
otherNode = otherNode.parentNode;
|
|
468
|
+
}
|
|
469
|
+
return otherNode === node;
|
|
470
|
+
}
|
|
471
|
+
/**
|
|
472
|
+
* Helper method for broadcastCustomEvent. Returns true if any member of
|
|
473
|
+
* the set is an ancestor of element.
|
|
474
|
+
*/
|
|
475
|
+
function hasAncestorInNodeList(element, nodeList) {
|
|
476
|
+
for (let idx = 0; idx < nodeList.length; ++idx) {
|
|
477
|
+
const member = nodeList[idx];
|
|
478
|
+
if (member !== element && contains(member, element)) {
|
|
479
|
+
return true;
|
|
480
|
+
}
|
|
481
|
+
}
|
|
482
|
+
return false;
|
|
483
|
+
}
|
|
484
|
+
|
|
485
|
+
/**
|
|
486
|
+
* If on a Macintosh with an extended keyboard, the Enter key located in the
|
|
487
|
+
* numeric pad has a different ASCII code.
|
|
488
|
+
*/
|
|
489
|
+
const MAC_ENTER = 3;
|
|
490
|
+
/** The Enter key. */
|
|
491
|
+
const ENTER = 13;
|
|
492
|
+
/** The Space key. */
|
|
493
|
+
const SPACE = 32;
|
|
600
494
|
/** Special keycodes used by jsaction for the generic click action. */
|
|
601
|
-
|
|
602
|
-
(function (KeyCode) {
|
|
603
|
-
/**
|
|
604
|
-
* If on a Macintosh with an extended keyboard, the Enter key located in the
|
|
605
|
-
* numeric pad has a different ASCII code.
|
|
606
|
-
*/
|
|
607
|
-
KeyCode[KeyCode["MAC_ENTER"] = 3] = "MAC_ENTER";
|
|
608
|
-
/** The Enter key. */
|
|
609
|
-
KeyCode[KeyCode["ENTER"] = 13] = "ENTER";
|
|
610
|
-
/** The Space key. */
|
|
611
|
-
KeyCode[KeyCode["SPACE"] = 32] = "SPACE";
|
|
612
|
-
})(KeyCode || (KeyCode = {}));
|
|
495
|
+
const KeyCode = { MAC_ENTER, ENTER, SPACE };
|
|
613
496
|
|
|
614
497
|
/**
|
|
615
498
|
* Gets a browser event type, if it would differ from the JSAction event type.
|
|
@@ -1246,32 +1129,20 @@ const testing = {
|
|
|
1246
1129
|
*/
|
|
1247
1130
|
class Dispatcher {
|
|
1248
1131
|
/**
|
|
1249
|
-
*
|
|
1250
|
-
*
|
|
1251
|
-
*
|
|
1252
|
-
*
|
|
1253
|
-
* @param
|
|
1254
|
-
* given event info.
|
|
1132
|
+
* Options are:
|
|
1133
|
+
* 1. `eventReplayer`: When the event contract dispatches replay events
|
|
1134
|
+
* to the Dispatcher, the Dispatcher collects them and in the next tick
|
|
1135
|
+
* dispatches them to the `eventReplayer`.
|
|
1136
|
+
* @param dispatchDelegate A function that should handle dispatching an `EventInfoWrapper` to handlers.
|
|
1255
1137
|
*/
|
|
1256
|
-
constructor(
|
|
1257
|
-
this.
|
|
1258
|
-
/**
|
|
1259
|
-
|
|
1260
|
-
|
|
1261
|
-
|
|
1262
|
-
|
|
1263
|
-
this.actions = {};
|
|
1264
|
-
/** A map of global event handlers, where each key is an event type. */
|
|
1265
|
-
this.globalHandlers = new Map();
|
|
1138
|
+
constructor(dispatchDelegate, { actionResolver = undefined, eventReplayer = undefined, } = {}) {
|
|
1139
|
+
this.dispatchDelegate = dispatchDelegate;
|
|
1140
|
+
/** Whether the event replay is scheduled. */
|
|
1141
|
+
this.eventReplayScheduled = false;
|
|
1142
|
+
/** The queue of events. */
|
|
1143
|
+
this.replayEventInfoWrappers = [];
|
|
1144
|
+
this.actionResolver = actionResolver;
|
|
1266
1145
|
this.eventReplayer = eventReplayer;
|
|
1267
|
-
this.baseDispatcher = new BaseDispatcher((eventInfoWrapper, isGlobalDispatch) => {
|
|
1268
|
-
this.dispatchToHandler(eventInfoWrapper, isGlobalDispatch);
|
|
1269
|
-
}, {
|
|
1270
|
-
eventReplayer: (eventInfoWrappers) => {
|
|
1271
|
-
this.eventReplayer?.(eventInfoWrappers, this);
|
|
1272
|
-
},
|
|
1273
|
-
});
|
|
1274
|
-
this.stopPropagation = stopPropagation;
|
|
1275
1146
|
}
|
|
1276
1147
|
/**
|
|
1277
1148
|
* Receives an event or the event queue from the EventContract. The event
|
|
@@ -1292,161 +1163,38 @@ class Dispatcher {
|
|
|
1292
1163
|
*
|
|
1293
1164
|
* @param eventInfo The info for the event that triggered this call or the
|
|
1294
1165
|
* queue of events from EventContract.
|
|
1295
|
-
|
|
1296
|
-
|
|
1297
|
-
|
|
1298
|
-
|
|
1299
|
-
this.baseDispatcher.dispatch(eventInfo, isGlobalDispatch);
|
|
1300
|
-
}
|
|
1301
|
-
/**
|
|
1302
|
-
* Dispatches an `EventInfoWrapper`.
|
|
1303
|
-
*/
|
|
1304
|
-
dispatchToHandler(eventInfoWrapper, isGlobalDispatch) {
|
|
1305
|
-
if (isGlobalDispatch) {
|
|
1306
|
-
// Skip everything related to jsaction handlers, and execute the global
|
|
1307
|
-
// handlers.
|
|
1308
|
-
const ev = eventInfoWrapper.getEvent();
|
|
1309
|
-
const eventTypeHandlers = this.globalHandlers.get(eventInfoWrapper.getEventType());
|
|
1310
|
-
let shouldPreventDefault = false;
|
|
1311
|
-
if (eventTypeHandlers) {
|
|
1312
|
-
for (const handler of eventTypeHandlers) {
|
|
1313
|
-
if (handler(ev) === false) {
|
|
1314
|
-
shouldPreventDefault = true;
|
|
1315
|
-
}
|
|
1316
|
-
}
|
|
1317
|
-
}
|
|
1318
|
-
if (shouldPreventDefault) {
|
|
1319
|
-
preventDefault(ev);
|
|
1320
|
-
}
|
|
1321
|
-
return;
|
|
1322
|
-
}
|
|
1323
|
-
if (this.stopPropagation) {
|
|
1324
|
-
stopPropagation(eventInfoWrapper);
|
|
1325
|
-
}
|
|
1166
|
+
*/
|
|
1167
|
+
dispatch(eventInfo) {
|
|
1168
|
+
const eventInfoWrapper = new EventInfoWrapper(eventInfo);
|
|
1169
|
+
this.actionResolver?.resolve(eventInfo);
|
|
1326
1170
|
const action = eventInfoWrapper.getAction();
|
|
1327
|
-
|
|
1328
|
-
|
|
1329
|
-
handler = this.getHandler(eventInfoWrapper);
|
|
1330
|
-
}
|
|
1331
|
-
if (!handler) {
|
|
1332
|
-
handler = this.actions[action.name];
|
|
1333
|
-
}
|
|
1334
|
-
if (handler) {
|
|
1335
|
-
handler(eventInfoWrapper);
|
|
1336
|
-
return;
|
|
1171
|
+
if (action && shouldPreventDefaultBeforeDispatching(action.element, eventInfoWrapper)) {
|
|
1172
|
+
preventDefault(eventInfoWrapper.getEvent());
|
|
1337
1173
|
}
|
|
1338
|
-
|
|
1339
|
-
|
|
1340
|
-
|
|
1341
|
-
/**
|
|
1342
|
-
* Registers multiple methods all bound to the same object
|
|
1343
|
-
* instance. This is a common case: an application module binds
|
|
1344
|
-
* multiple of its methods under public names to the event contract of
|
|
1345
|
-
* the application. So we provide a shortcut for it.
|
|
1346
|
-
* Attempts to replay the queued events after registering the handlers.
|
|
1347
|
-
*
|
|
1348
|
-
* @param namespace The namespace of the jsaction name.
|
|
1349
|
-
*
|
|
1350
|
-
* @param instance The object to bind the methods to. If this is null, then
|
|
1351
|
-
* the functions are not bound, but directly added under the public names.
|
|
1352
|
-
*
|
|
1353
|
-
* @param methods A map from public name to functions that will be bound to
|
|
1354
|
-
* instance and registered as action under the public name. I.e. the
|
|
1355
|
-
* property names are the public names. The property values are the
|
|
1356
|
-
* methods of instance.
|
|
1357
|
-
*/
|
|
1358
|
-
registerEventInfoHandlers(namespace, instance, methods) {
|
|
1359
|
-
for (const [name, method] of Object.entries(methods)) {
|
|
1360
|
-
const handler = instance ? method.bind(instance) : method;
|
|
1361
|
-
if (namespace) {
|
|
1362
|
-
// Include a '.' separator between namespace name and action name.
|
|
1363
|
-
// In the case that no namespace name is provided, the jsaction name
|
|
1364
|
-
// consists of the action name only (no period).
|
|
1365
|
-
const fullName = namespace + Char.NAMESPACE_ACTION_SEPARATOR + name;
|
|
1366
|
-
this.actions[fullName] = handler;
|
|
1367
|
-
}
|
|
1368
|
-
else {
|
|
1369
|
-
this.actions[name] = handler;
|
|
1174
|
+
if (eventInfoWrapper.getIsReplay()) {
|
|
1175
|
+
if (!this.eventReplayer) {
|
|
1176
|
+
return;
|
|
1370
1177
|
}
|
|
1178
|
+
this.scheduleEventInfoWrapperReplay(eventInfoWrapper);
|
|
1179
|
+
return;
|
|
1371
1180
|
}
|
|
1372
|
-
this.
|
|
1373
|
-
}
|
|
1374
|
-
/**
|
|
1375
|
-
* Unregisters an action. Provided as an easy way to reverse the effects of
|
|
1376
|
-
* registerHandlers.
|
|
1377
|
-
* @param namespace The namespace of the jsaction name.
|
|
1378
|
-
* @param name The action name to unbind.
|
|
1379
|
-
*/
|
|
1380
|
-
unregisterHandler(namespace, name) {
|
|
1381
|
-
const fullName = namespace ? namespace + Char.NAMESPACE_ACTION_SEPARATOR + name : name;
|
|
1382
|
-
delete this.actions[fullName];
|
|
1383
|
-
}
|
|
1384
|
-
/** Registers a global event handler. */
|
|
1385
|
-
registerGlobalHandler(eventType, handler) {
|
|
1386
|
-
if (!this.globalHandlers.has(eventType)) {
|
|
1387
|
-
this.globalHandlers.set(eventType, new Set([handler]));
|
|
1388
|
-
}
|
|
1389
|
-
else {
|
|
1390
|
-
this.globalHandlers.get(eventType).add(handler);
|
|
1391
|
-
}
|
|
1392
|
-
}
|
|
1393
|
-
/** Unregisters a global event handler. */
|
|
1394
|
-
unregisterGlobalHandler(eventType, handler) {
|
|
1395
|
-
if (this.globalHandlers.has(eventType)) {
|
|
1396
|
-
this.globalHandlers.get(eventType).delete(handler);
|
|
1397
|
-
}
|
|
1398
|
-
}
|
|
1399
|
-
/**
|
|
1400
|
-
* Checks whether there is an action registered under the given
|
|
1401
|
-
* name. This returns true if there is a namespace handler, even
|
|
1402
|
-
* if it can not yet handle the event.
|
|
1403
|
-
*
|
|
1404
|
-
* @param name Action name.
|
|
1405
|
-
* @return Whether the name is registered.
|
|
1406
|
-
* @see #canDispatch
|
|
1407
|
-
*/
|
|
1408
|
-
hasAction(name) {
|
|
1409
|
-
return this.actions.hasOwnProperty(name);
|
|
1181
|
+
this.dispatchDelegate(eventInfoWrapper);
|
|
1410
1182
|
}
|
|
1411
1183
|
/**
|
|
1412
|
-
*
|
|
1413
|
-
*
|
|
1184
|
+
* Schedules an `EventInfoWrapper` for replay. The replaying will happen in its own
|
|
1185
|
+
* stack once the current flow cedes control. This is done to mimic
|
|
1186
|
+
* browser event handling.
|
|
1414
1187
|
*/
|
|
1415
|
-
|
|
1416
|
-
|
|
1417
|
-
if (!
|
|
1418
|
-
return
|
|
1188
|
+
scheduleEventInfoWrapperReplay(eventInfoWrapper) {
|
|
1189
|
+
this.replayEventInfoWrappers.push(eventInfoWrapper);
|
|
1190
|
+
if (this.eventReplayScheduled || !this.eventReplayer) {
|
|
1191
|
+
return;
|
|
1419
1192
|
}
|
|
1420
|
-
|
|
1421
|
-
|
|
1422
|
-
|
|
1423
|
-
|
|
1424
|
-
|
|
1425
|
-
* contract after setting the `EventReplayer`. The event replayer takes as
|
|
1426
|
-
* parameters the queue of events and the dispatcher (used to check whether
|
|
1427
|
-
* actions have handlers registered and can be replayed). The event replayer
|
|
1428
|
-
* is also responsible for dequeuing events.
|
|
1429
|
-
*
|
|
1430
|
-
* Example: An event replayer that replays only the last event.
|
|
1431
|
-
*
|
|
1432
|
-
* const dispatcher = new Dispatcher();
|
|
1433
|
-
* // ...
|
|
1434
|
-
* dispatcher.setEventReplayer((queue, dispatcher) => {
|
|
1435
|
-
* const lastEventInfoWrapper = queue[queue.length -1];
|
|
1436
|
-
* if (dispatcher.canDispatch(lastEventInfoWrapper.getAction())) {
|
|
1437
|
-
* jsaction.replay.replayEvent(
|
|
1438
|
-
* lastEventInfoWrapper.getEvent(),
|
|
1439
|
-
* lastEventInfoWrapper.getTargetElement(),
|
|
1440
|
-
* lastEventInfoWrapper.getEventType(),
|
|
1441
|
-
* );
|
|
1442
|
-
* queue.length = 0;
|
|
1443
|
-
* }
|
|
1444
|
-
* });
|
|
1445
|
-
*
|
|
1446
|
-
* @param eventReplayer It allows elements to be replayed and dequeuing.
|
|
1447
|
-
*/
|
|
1448
|
-
setEventReplayer(eventReplayer) {
|
|
1449
|
-
this.eventReplayer = eventReplayer;
|
|
1193
|
+
this.eventReplayScheduled = true;
|
|
1194
|
+
Promise.resolve().then(() => {
|
|
1195
|
+
this.eventReplayScheduled = false;
|
|
1196
|
+
this.eventReplayer(this.replayEventInfoWrappers);
|
|
1197
|
+
});
|
|
1450
1198
|
}
|
|
1451
1199
|
}
|
|
1452
1200
|
/** Stop propagation for an `EventInfo`. */
|
|
@@ -1471,12 +1219,24 @@ function stopPropagation(eventInfoWrapper) {
|
|
|
1471
1219
|
event.stopPropagation();
|
|
1472
1220
|
}
|
|
1473
1221
|
/**
|
|
1474
|
-
*
|
|
1222
|
+
* Returns true if the default action of this event should be prevented before
|
|
1223
|
+
* this event is dispatched.
|
|
1224
|
+
*/
|
|
1225
|
+
function shouldPreventDefaultBeforeDispatching(actionElement, eventInfoWrapper) {
|
|
1226
|
+
// Prevent browser from following <a> node links if a jsaction is present
|
|
1227
|
+
// and we are dispatching the action now. Note that the targetElement may be
|
|
1228
|
+
// a child of an anchor that has a jsaction attached. For that reason, we
|
|
1229
|
+
// need to check the actionElement rather than the targetElement.
|
|
1230
|
+
return ((actionElement.tagName === 'A' && eventInfoWrapper.getEventType() === EventType.CLICK) ||
|
|
1231
|
+
eventInfoWrapper.getEventType() === EventType.CLICKMOD);
|
|
1232
|
+
}
|
|
1233
|
+
/**
|
|
1234
|
+
* Registers deferred functionality for an EventContract and a Jsaction
|
|
1475
1235
|
* Dispatcher.
|
|
1476
1236
|
*/
|
|
1477
1237
|
function registerDispatcher(eventContract, dispatcher) {
|
|
1478
|
-
eventContract.ecrd((eventInfo
|
|
1479
|
-
dispatcher.dispatch(eventInfo
|
|
1238
|
+
eventContract.ecrd((eventInfo) => {
|
|
1239
|
+
dispatcher.dispatch(eventInfo);
|
|
1480
1240
|
}, Restriction.I_AM_THE_JSACTION_FRAMEWORK);
|
|
1481
1241
|
}
|
|
1482
1242
|
|
|
@@ -1579,98 +1339,127 @@ function populateClickOnlyAction(actionElement, eventInfo, actionMap) {
|
|
|
1579
1339
|
setAction(eventInfo, actionMap[EventType.CLICKONLY], actionElement);
|
|
1580
1340
|
}
|
|
1581
1341
|
|
|
1582
|
-
|
|
1583
|
-
|
|
1584
|
-
|
|
1585
|
-
|
|
1586
|
-
|
|
1587
|
-
|
|
1588
|
-
|
|
1589
|
-
|
|
1590
|
-
|
|
1591
|
-
|
|
1592
|
-
|
|
1593
|
-
|
|
1594
|
-
|
|
1595
|
-
|
|
1596
|
-
|
|
1597
|
-
|
|
1598
|
-
|
|
1599
|
-
|
|
1600
|
-
|
|
1601
|
-
|
|
1602
|
-
|
|
1603
|
-
|
|
1604
|
-
|
|
1605
|
-
|
|
1606
|
-
|
|
1607
|
-
|
|
1342
|
+
/**
|
|
1343
|
+
* The jsaction attribute defines a mapping of a DOM event to a
|
|
1344
|
+
* generic event (aka jsaction), to which the actual event handlers
|
|
1345
|
+
* that implement the behavior of the application are bound. The
|
|
1346
|
+
* value is a semicolon separated list of colon separated pairs of
|
|
1347
|
+
* an optional DOM event name and a jsaction name. If the optional
|
|
1348
|
+
* DOM event name is omitted, 'click' is assumed. The jsaction names
|
|
1349
|
+
* are dot separated pairs of a namespace and a simple jsaction
|
|
1350
|
+
* name. If the namespace is absent, it is taken from the closest
|
|
1351
|
+
* ancestor element with a jsnamespace attribute, if there is
|
|
1352
|
+
* any. If there is no ancestor with a jsnamespace attribute, the
|
|
1353
|
+
* simple name is assumed to be the jsaction name.
|
|
1354
|
+
*
|
|
1355
|
+
* Used by EventContract.
|
|
1356
|
+
*/
|
|
1357
|
+
const JSACTION$1 = 'jsaction';
|
|
1358
|
+
/**
|
|
1359
|
+
* The jsnamespace attribute provides the namespace part of the
|
|
1360
|
+
* jaction names occurring in the jsaction attribute where it's
|
|
1361
|
+
* missing.
|
|
1362
|
+
*
|
|
1363
|
+
* Used by EventContract.
|
|
1364
|
+
*/
|
|
1365
|
+
const JSNAMESPACE$1 = 'jsnamespace';
|
|
1366
|
+
/**
|
|
1367
|
+
* The oi attribute is a log impression tag for impression logging
|
|
1368
|
+
* and action tracking. For an element that carries a jsaction
|
|
1369
|
+
* attribute, the element is identified for the purpose of
|
|
1370
|
+
* impression logging and click tracking by the dot separated path
|
|
1371
|
+
* of all oi attributes in the chain of ancestors of the element.
|
|
1372
|
+
*
|
|
1373
|
+
* Used by ActionFlow.
|
|
1374
|
+
*/
|
|
1375
|
+
const OI$1 = 'oi';
|
|
1376
|
+
/**
|
|
1377
|
+
* The ved attribute is an encoded ClickTrackingCGI proto to track
|
|
1378
|
+
* visual elements.
|
|
1379
|
+
*
|
|
1380
|
+
* Used by ActionFlow.
|
|
1381
|
+
*/
|
|
1382
|
+
const VED = 'ved';
|
|
1383
|
+
/**
|
|
1384
|
+
* The vet attribute is the visual element type used to identify tracked
|
|
1385
|
+
* visual elements.
|
|
1386
|
+
*/
|
|
1387
|
+
const VET = 'vet';
|
|
1388
|
+
/**
|
|
1389
|
+
* Support for iteration on reprocessing.
|
|
1390
|
+
*
|
|
1391
|
+
* Used by ActionFlow.
|
|
1392
|
+
*/
|
|
1393
|
+
const JSINSTANCE = 'jsinstance';
|
|
1394
|
+
/**
|
|
1395
|
+
* All click jsactions that happen on the element that carries this
|
|
1396
|
+
* attribute or its descendants are automatically logged.
|
|
1397
|
+
* Impressions of jsactions on these elements are tracked too, if
|
|
1398
|
+
* requested by the impression() method of ActionFlow.
|
|
1399
|
+
*
|
|
1400
|
+
* Used by ActionFlow.
|
|
1401
|
+
*/
|
|
1402
|
+
const JSTRACK = 'jstrack';
|
|
1403
|
+
const Attribute = { JSACTION: JSACTION$1, JSNAMESPACE: JSNAMESPACE$1, OI: OI$1, VED, VET, JSINSTANCE, JSTRACK };
|
|
1404
|
+
|
|
1405
|
+
const Char = {
|
|
1608
1406
|
/**
|
|
1609
|
-
* The
|
|
1610
|
-
*
|
|
1611
|
-
* attribute, the element is identified for the purpose of
|
|
1612
|
-
* impression logging and click tracking by the dot separated path
|
|
1613
|
-
* of all oi attributes in the chain of ancestors of the element.
|
|
1614
|
-
*
|
|
1615
|
-
* Used by ActionFlow.
|
|
1407
|
+
* The separator between the namespace and the action name in the
|
|
1408
|
+
* jsaction attribute value.
|
|
1616
1409
|
*/
|
|
1617
|
-
|
|
1410
|
+
NAMESPACE_ACTION_SEPARATOR: '.',
|
|
1618
1411
|
/**
|
|
1619
|
-
* The
|
|
1620
|
-
*
|
|
1621
|
-
*
|
|
1622
|
-
* Used by ActionFlow.
|
|
1412
|
+
* The separator between the event name and action in the jsaction
|
|
1413
|
+
* attribute value.
|
|
1623
1414
|
*/
|
|
1624
|
-
|
|
1415
|
+
EVENT_ACTION_SEPARATOR: ':',
|
|
1625
1416
|
/**
|
|
1626
|
-
* The
|
|
1627
|
-
*
|
|
1417
|
+
* The separator between the logged oi attribute values in the &oi=
|
|
1418
|
+
* URL parameter value.
|
|
1628
1419
|
*/
|
|
1629
|
-
|
|
1420
|
+
OI_SEPARATOR: '.',
|
|
1630
1421
|
/**
|
|
1631
|
-
*
|
|
1632
|
-
*
|
|
1633
|
-
* Used by ActionFlow.
|
|
1422
|
+
* The separator between the key and the value pairs in the &cad=
|
|
1423
|
+
* URL parameter value.
|
|
1634
1424
|
*/
|
|
1635
|
-
|
|
1425
|
+
CAD_KEY_VALUE_SEPARATOR: ':',
|
|
1636
1426
|
/**
|
|
1637
|
-
*
|
|
1638
|
-
*
|
|
1639
|
-
* Impressions of jsactions on these elements are tracked too, if
|
|
1640
|
-
* requested by the impression() method of ActionFlow.
|
|
1641
|
-
*
|
|
1642
|
-
* Used by ActionFlow.
|
|
1427
|
+
* The separator between the key-value pairs in the &cad= URL
|
|
1428
|
+
* parameter value.
|
|
1643
1429
|
*/
|
|
1644
|
-
|
|
1645
|
-
}
|
|
1430
|
+
CAD_SEPARATOR: ',',
|
|
1431
|
+
};
|
|
1646
1432
|
|
|
1433
|
+
/**
|
|
1434
|
+
* The parsed value of the jsaction attribute is stored in this
|
|
1435
|
+
* property on the DOM node. The parsed value is an Object. The
|
|
1436
|
+
* property names of the object are the events; the values are the
|
|
1437
|
+
* names of the actions. This property is attached even on nodes
|
|
1438
|
+
* that don't have a jsaction attribute as an optimization, because
|
|
1439
|
+
* property lookup is faster than attribute access.
|
|
1440
|
+
*/
|
|
1441
|
+
const JSACTION = '__jsaction';
|
|
1442
|
+
/**
|
|
1443
|
+
* The parsed value of the jsnamespace attribute is stored in this
|
|
1444
|
+
* property on the DOM node.
|
|
1445
|
+
*/
|
|
1446
|
+
const JSNAMESPACE = '__jsnamespace';
|
|
1447
|
+
/** The value of the oi attribute as a property, for faster access. */
|
|
1448
|
+
const OI = '__oi';
|
|
1449
|
+
/**
|
|
1450
|
+
* The owner property references an a logical owner for a DOM node. JSAction
|
|
1451
|
+
* will follow this reference instead of parentNode when traversing the DOM
|
|
1452
|
+
* to find jsaction attributes. This allows overlaying a logical structure
|
|
1453
|
+
* over a document where the DOM structure can't reflect that structure.
|
|
1454
|
+
*/
|
|
1455
|
+
const OWNER = '__owner';
|
|
1647
1456
|
/** All properties that are used by jsaction. */
|
|
1648
|
-
|
|
1649
|
-
|
|
1650
|
-
|
|
1651
|
-
|
|
1652
|
-
|
|
1653
|
-
|
|
1654
|
-
* names of the actions. This property is attached even on nodes
|
|
1655
|
-
* that don't have a jsaction attribute as an optimization, because
|
|
1656
|
-
* property lookup is faster than attribute access.
|
|
1657
|
-
*/
|
|
1658
|
-
Property["JSACTION"] = "__jsaction";
|
|
1659
|
-
/**
|
|
1660
|
-
* The parsed value of the jsnamespace attribute is stored in this
|
|
1661
|
-
* property on the DOM node.
|
|
1662
|
-
*/
|
|
1663
|
-
Property["JSNAMESPACE"] = "__jsnamespace";
|
|
1664
|
-
/** The value of the oi attribute as a property, for faster access. */
|
|
1665
|
-
Property["OI"] = "__oi";
|
|
1666
|
-
/**
|
|
1667
|
-
* The owner property references an a logical owner for a DOM node. JSAction
|
|
1668
|
-
* will follow this reference instead of parentNode when traversing the DOM
|
|
1669
|
-
* to find jsaction attributes. This allows overlaying a logical structure
|
|
1670
|
-
* over a document where the DOM structure can't reflect that structure.
|
|
1671
|
-
*/
|
|
1672
|
-
Property["OWNER"] = "__owner";
|
|
1673
|
-
})(Property || (Property = {}));
|
|
1457
|
+
const Property = {
|
|
1458
|
+
JSACTION,
|
|
1459
|
+
JSNAMESPACE,
|
|
1460
|
+
OI,
|
|
1461
|
+
OWNER,
|
|
1462
|
+
};
|
|
1674
1463
|
|
|
1675
1464
|
/**
|
|
1676
1465
|
* Map from jsaction annotation to a parsed map from event name to action name.
|
|
@@ -1684,7 +1473,7 @@ const parseCache = {};
|
|
|
1684
1473
|
*/
|
|
1685
1474
|
function get(element) {
|
|
1686
1475
|
// @ts-ignore
|
|
1687
|
-
return element[
|
|
1476
|
+
return element[JSACTION];
|
|
1688
1477
|
}
|
|
1689
1478
|
/**
|
|
1690
1479
|
* Writes the jsaction parser cache to the given DOM Element.
|
|
@@ -1695,7 +1484,7 @@ function get(element) {
|
|
|
1695
1484
|
*/
|
|
1696
1485
|
function set(element, actionMap) {
|
|
1697
1486
|
// @ts-ignore
|
|
1698
|
-
element[
|
|
1487
|
+
element[JSACTION] = actionMap;
|
|
1699
1488
|
}
|
|
1700
1489
|
/**
|
|
1701
1490
|
* Looks up the parsed action map from the source jsaction attribute value.
|
|
@@ -1721,8 +1510,8 @@ function setParsed(text, parsed) {
|
|
|
1721
1510
|
* @param element .
|
|
1722
1511
|
*/
|
|
1723
1512
|
function clear(element) {
|
|
1724
|
-
if (
|
|
1725
|
-
delete element[
|
|
1513
|
+
if (JSACTION in element) {
|
|
1514
|
+
delete element[JSACTION];
|
|
1726
1515
|
}
|
|
1727
1516
|
}
|
|
1728
1517
|
/**
|
|
@@ -1735,7 +1524,7 @@ function clear(element) {
|
|
|
1735
1524
|
*/
|
|
1736
1525
|
function getNamespace(element) {
|
|
1737
1526
|
// @ts-ignore
|
|
1738
|
-
return element[
|
|
1527
|
+
return element[JSNAMESPACE];
|
|
1739
1528
|
}
|
|
1740
1529
|
/**
|
|
1741
1530
|
* Writes the cached jsaction namespace to the given DOM Element. Null
|
|
@@ -1746,7 +1535,7 @@ function getNamespace(element) {
|
|
|
1746
1535
|
*/
|
|
1747
1536
|
function setNamespace(element, jsnamespace) {
|
|
1748
1537
|
// @ts-ignore
|
|
1749
|
-
element[
|
|
1538
|
+
element[JSNAMESPACE] = jsnamespace;
|
|
1750
1539
|
}
|
|
1751
1540
|
/**
|
|
1752
1541
|
* Clears the cached jsaction namespace from the given DOM Element.
|
|
@@ -1754,64 +1543,11 @@ function setNamespace(element, jsnamespace) {
|
|
|
1754
1543
|
* @param element .
|
|
1755
1544
|
*/
|
|
1756
1545
|
function clearNamespace(element) {
|
|
1757
|
-
if (
|
|
1758
|
-
delete element[
|
|
1546
|
+
if (JSNAMESPACE in element) {
|
|
1547
|
+
delete element[JSNAMESPACE];
|
|
1759
1548
|
}
|
|
1760
1549
|
}
|
|
1761
1550
|
|
|
1762
|
-
/**
|
|
1763
|
-
* @define Support for jsnamespace attribute. This flag can be overridden in a
|
|
1764
|
-
* build rule to trim down the EventContract's binary size.
|
|
1765
|
-
*/
|
|
1766
|
-
const JSNAMESPACE_SUPPORT = true;
|
|
1767
|
-
/**
|
|
1768
|
-
* @define Support for accessible click actions. This flag can be overridden in
|
|
1769
|
-
* a build rule.
|
|
1770
|
-
*/
|
|
1771
|
-
const A11Y_CLICK_SUPPORT = false;
|
|
1772
|
-
/**
|
|
1773
|
-
* @define Support for the non-bubbling mouseenter and mouseleave events. This
|
|
1774
|
-
* flag can be overridden in a build rule.
|
|
1775
|
-
*/
|
|
1776
|
-
const MOUSE_SPECIAL_SUPPORT = false;
|
|
1777
|
-
/**
|
|
1778
|
-
* @define Call stopPropagation on handled events. When integrating with
|
|
1779
|
-
* non-jsaction event handler based code, you will likely want to turn this flag
|
|
1780
|
-
* off. While most event handlers will continue to work, jsaction binds focus
|
|
1781
|
-
* and blur events in the capture phase and thus with stopPropagation, none of
|
|
1782
|
-
* your non-jsaction-handlers will ever see it.
|
|
1783
|
-
*/
|
|
1784
|
-
const STOP_PROPAGATION = true;
|
|
1785
|
-
/**
|
|
1786
|
-
* @define Support for custom events, which are type EventType.CUSTOM. These are
|
|
1787
|
-
* native DOM events with an additional type field and an optional payload.
|
|
1788
|
-
*/
|
|
1789
|
-
const CUSTOM_EVENT_SUPPORT = false;
|
|
1790
|
-
|
|
1791
|
-
/**
|
|
1792
|
-
* @fileoverview Implements the local event handling contract. This
|
|
1793
|
-
* allows DOM objects in a container that enters into this contract to
|
|
1794
|
-
* define event handlers which are executed in a local context.
|
|
1795
|
-
*
|
|
1796
|
-
* One EventContract instance can manage the contract for multiple
|
|
1797
|
-
* containers, which are added using the addContainer() method.
|
|
1798
|
-
*
|
|
1799
|
-
* Events can be registered using the addEvent() method.
|
|
1800
|
-
*
|
|
1801
|
-
* A Dispatcher is added using the registerDispatcher() method. Until there is
|
|
1802
|
-
* a dispatcher, events are queued. The idea is that the EventContract
|
|
1803
|
-
* class is inlined in the HTML of the top level page and instantiated
|
|
1804
|
-
* right after the start of <body>. The Dispatcher class is contained
|
|
1805
|
-
* in the external deferred js, and instantiated and registered with
|
|
1806
|
-
* EventContract when the external javascript in the page loads. The
|
|
1807
|
-
* external javascript will also register the jsaction handlers, which
|
|
1808
|
-
* then pick up the queued events at the time of registration.
|
|
1809
|
-
*
|
|
1810
|
-
* Since this class is meant to be inlined in the main page HTML, the
|
|
1811
|
-
* size of the binary compiled from this file MUST be kept as small as
|
|
1812
|
-
* possible and thus its dependencies to a minimum.
|
|
1813
|
-
*/
|
|
1814
|
-
const DEFAULT_EVENT_TYPE = EventType.CLICK;
|
|
1815
1551
|
/**
|
|
1816
1552
|
* Since maps from event to action are immutable we can use a single map
|
|
1817
1553
|
* to represent the empty map.
|
|
@@ -1821,114 +1557,19 @@ const EMPTY_ACTION_MAP = {};
|
|
|
1821
1557
|
* This regular expression matches a semicolon.
|
|
1822
1558
|
*/
|
|
1823
1559
|
const REGEXP_SEMICOLON = /\s*;\s*/;
|
|
1824
|
-
/**
|
|
1825
|
-
|
|
1826
|
-
|
|
1827
|
-
|
|
1828
|
-
|
|
1829
|
-
|
|
1830
|
-
*
|
|
1831
|
-
* This has several benefits: (1) No DOM event handlers need to be
|
|
1832
|
-
* registered on the specific elements in the UI. (2) The set of
|
|
1833
|
-
* events that the application has to handle can be specified in terms
|
|
1834
|
-
* of the semantics of the application, rather than in terms of DOM
|
|
1835
|
-
* events. (3) Invocation of handlers can be delayed and handlers can
|
|
1836
|
-
* be delay loaded in a generic way.
|
|
1837
|
-
*/
|
|
1838
|
-
class EventContract {
|
|
1839
|
-
static { this.CUSTOM_EVENT_SUPPORT = CUSTOM_EVENT_SUPPORT; }
|
|
1840
|
-
static { this.STOP_PROPAGATION = STOP_PROPAGATION; }
|
|
1841
|
-
static { this.A11Y_CLICK_SUPPORT = A11Y_CLICK_SUPPORT; }
|
|
1842
|
-
static { this.MOUSE_SPECIAL_SUPPORT = MOUSE_SPECIAL_SUPPORT; }
|
|
1843
|
-
static { this.JSNAMESPACE_SUPPORT = JSNAMESPACE_SUPPORT; }
|
|
1844
|
-
constructor(containerManager) {
|
|
1845
|
-
/**
|
|
1846
|
-
* The DOM events which this contract covers. Used to prevent double
|
|
1847
|
-
* registration of event types. The value of the map is the
|
|
1848
|
-
* internally created DOM event handler function that handles the
|
|
1849
|
-
* DOM events. See addEvent().
|
|
1850
|
-
*
|
|
1851
|
-
*/
|
|
1852
|
-
this.eventHandlers = {};
|
|
1853
|
-
this.browserEventTypeToExtraEventTypes = {};
|
|
1854
|
-
/**
|
|
1855
|
-
* The dispatcher function. Events are passed to this function for
|
|
1856
|
-
* handling once it was set using the registerDispatcher() method. This is
|
|
1857
|
-
* done because the function is passed from another jsbinary, so passing the
|
|
1858
|
-
* instance and invoking the method here would require to leave the method
|
|
1859
|
-
* unobfuscated.
|
|
1860
|
-
*/
|
|
1861
|
-
this.dispatcher = null;
|
|
1862
|
-
/**
|
|
1863
|
-
* The list of suspended `EventInfo` that will be dispatched
|
|
1864
|
-
* as soon as the `Dispatcher` is registered.
|
|
1865
|
-
*/
|
|
1866
|
-
this.queuedEventInfos = [];
|
|
1867
|
-
/** Whether a11y click support has been loaded or not. */
|
|
1868
|
-
this.hasA11yClickSupport = false;
|
|
1869
|
-
/** Whether to add an a11y click listener. */
|
|
1870
|
-
this.addA11yClickListener = false;
|
|
1560
|
+
/** If no event type is defined, defaults to `click`. */
|
|
1561
|
+
const DEFAULT_EVENT_TYPE = EventType.CLICK;
|
|
1562
|
+
/** Resolves actions for Events. */
|
|
1563
|
+
class ActionResolver {
|
|
1564
|
+
constructor({ syntheticMouseEventSupport = false, } = {}) {
|
|
1565
|
+
this.a11yClickSupport = false;
|
|
1871
1566
|
this.updateEventInfoForA11yClick = undefined;
|
|
1872
1567
|
this.preventDefaultForA11yClick = undefined;
|
|
1873
1568
|
this.populateClickOnlyAction = undefined;
|
|
1874
|
-
this.
|
|
1875
|
-
if (EventContract.CUSTOM_EVENT_SUPPORT) {
|
|
1876
|
-
this.addEvent(EventType.CUSTOM);
|
|
1877
|
-
}
|
|
1878
|
-
if (EventContract.A11Y_CLICK_SUPPORT) {
|
|
1879
|
-
// Add a11y click support to the `EventContract`.
|
|
1880
|
-
this.addA11yClickSupport();
|
|
1881
|
-
}
|
|
1882
|
-
}
|
|
1883
|
-
handleEvent(eventType, event, container) {
|
|
1884
|
-
const eventInfo = createEventInfoFromParameters(
|
|
1885
|
-
/* eventType= */ eventType,
|
|
1886
|
-
/* event= */ event,
|
|
1887
|
-
/* targetElement= */ event.target,
|
|
1888
|
-
/* container= */ container,
|
|
1889
|
-
/* timestamp= */ Date.now());
|
|
1890
|
-
this.handleEventInfo(eventInfo);
|
|
1569
|
+
this.syntheticMouseEventSupport = syntheticMouseEventSupport;
|
|
1891
1570
|
}
|
|
1892
|
-
|
|
1893
|
-
* Handle an `EventInfo`.
|
|
1894
|
-
*/
|
|
1895
|
-
handleEventInfo(eventInfo) {
|
|
1896
|
-
if (!this.dispatcher) {
|
|
1897
|
-
// All events are queued when the dispatcher isn't yet loaded.
|
|
1898
|
-
setIsReplay(eventInfo, true);
|
|
1899
|
-
this.queuedEventInfos?.push(eventInfo);
|
|
1900
|
-
}
|
|
1901
|
-
if (EventContract.CUSTOM_EVENT_SUPPORT &&
|
|
1902
|
-
getEventType(eventInfo) === EventType.CUSTOM) {
|
|
1903
|
-
const detail = getEvent(eventInfo).detail;
|
|
1904
|
-
// For custom events, use a secondary dispatch based on the internal
|
|
1905
|
-
// custom type of the event.
|
|
1906
|
-
if (!detail || !detail['_type']) {
|
|
1907
|
-
// This should never happen.
|
|
1908
|
-
return;
|
|
1909
|
-
}
|
|
1910
|
-
setEventType(eventInfo, detail['_type']);
|
|
1911
|
-
}
|
|
1571
|
+
resolve(eventInfo) {
|
|
1912
1572
|
this.populateAction(eventInfo);
|
|
1913
|
-
if (!this.dispatcher) {
|
|
1914
|
-
return;
|
|
1915
|
-
}
|
|
1916
|
-
const globalEventInfo = cloneEventInfo(eventInfo);
|
|
1917
|
-
// In some cases, `populateAction` will rewrite `click` events to
|
|
1918
|
-
// `clickonly`. Revert back to a regular click, otherwise we won't be able
|
|
1919
|
-
// to execute global event handlers registered on click events.
|
|
1920
|
-
if (getEventType(globalEventInfo) === EventType.CLICKONLY) {
|
|
1921
|
-
setEventType(globalEventInfo, EventType.CLICK);
|
|
1922
|
-
}
|
|
1923
|
-
this.dispatcher(globalEventInfo, /* dispatch global event */ true);
|
|
1924
|
-
const action = getAction(eventInfo);
|
|
1925
|
-
if (!action) {
|
|
1926
|
-
return;
|
|
1927
|
-
}
|
|
1928
|
-
if (shouldPreventDefaultBeforeDispatching(getActionElement(action), eventInfo)) {
|
|
1929
|
-
preventDefault(getEvent(eventInfo));
|
|
1930
|
-
}
|
|
1931
|
-
this.dispatcher(eventInfo);
|
|
1932
1573
|
}
|
|
1933
1574
|
/**
|
|
1934
1575
|
* Searches for a jsaction that the DOM event maps to and creates an
|
|
@@ -1980,7 +1621,7 @@ class EventContract {
|
|
|
1980
1621
|
isModifiedClickEvent(getEvent(eventInfo))) {
|
|
1981
1622
|
setEventType(eventInfo, EventType.CLICKMOD);
|
|
1982
1623
|
}
|
|
1983
|
-
else if (this.
|
|
1624
|
+
else if (this.a11yClickSupport) {
|
|
1984
1625
|
this.updateEventInfoForA11yClick(eventInfo);
|
|
1985
1626
|
}
|
|
1986
1627
|
// Walk to the parent node, unless the node has a different owner in
|
|
@@ -1988,15 +1629,17 @@ class EventContract {
|
|
|
1988
1629
|
// shadow root if needed.
|
|
1989
1630
|
let actionElement = getTargetElement(eventInfo);
|
|
1990
1631
|
while (actionElement && actionElement !== getContainer(eventInfo)) {
|
|
1991
|
-
|
|
1632
|
+
if (actionElement.nodeType === Node.ELEMENT_NODE) {
|
|
1633
|
+
this.populateActionOnElement(actionElement, eventInfo);
|
|
1634
|
+
}
|
|
1992
1635
|
if (getAction(eventInfo)) {
|
|
1993
1636
|
// An event is handled by at most one jsaction. Thus we stop at the
|
|
1994
1637
|
// first matching jsaction specified in a jsaction attribute up the
|
|
1995
1638
|
// ancestor chain of the event target node.
|
|
1996
1639
|
break;
|
|
1997
1640
|
}
|
|
1998
|
-
if (actionElement[
|
|
1999
|
-
actionElement = actionElement[
|
|
1641
|
+
if (actionElement[OWNER]) {
|
|
1642
|
+
actionElement = actionElement[OWNER];
|
|
2000
1643
|
continue;
|
|
2001
1644
|
}
|
|
2002
1645
|
if (actionElement.parentNode?.nodeName !== '#document-fragment') {
|
|
@@ -2011,35 +1654,36 @@ class EventContract {
|
|
|
2011
1654
|
// No action found.
|
|
2012
1655
|
return;
|
|
2013
1656
|
}
|
|
2014
|
-
if (this.
|
|
1657
|
+
if (this.a11yClickSupport) {
|
|
2015
1658
|
this.preventDefaultForA11yClick(eventInfo);
|
|
2016
1659
|
}
|
|
2017
1660
|
// We attempt to handle the mouseenter/mouseleave events here by
|
|
2018
1661
|
// detecting whether the mouseover/mouseout events correspond to
|
|
2019
1662
|
// entering/leaving an element.
|
|
2020
|
-
if (
|
|
2021
|
-
(getEventType(eventInfo) === EventType.MOUSEENTER ||
|
|
1663
|
+
if (this.syntheticMouseEventSupport) {
|
|
1664
|
+
if (getEventType(eventInfo) === EventType.MOUSEENTER ||
|
|
2022
1665
|
getEventType(eventInfo) === EventType.MOUSELEAVE ||
|
|
2023
1666
|
getEventType(eventInfo) === EventType.POINTERENTER ||
|
|
2024
|
-
getEventType(eventInfo) === EventType.POINTERLEAVE)
|
|
2025
|
-
|
|
2026
|
-
|
|
2027
|
-
|
|
2028
|
-
|
|
2029
|
-
|
|
2030
|
-
|
|
2031
|
-
|
|
2032
|
-
|
|
2033
|
-
|
|
2034
|
-
|
|
2035
|
-
|
|
2036
|
-
|
|
2037
|
-
|
|
2038
|
-
|
|
2039
|
-
|
|
2040
|
-
|
|
2041
|
-
|
|
2042
|
-
|
|
1667
|
+
getEventType(eventInfo) === EventType.POINTERLEAVE) {
|
|
1668
|
+
// We attempt to handle the mouseenter/mouseleave events here by
|
|
1669
|
+
// detecting whether the mouseover/mouseout events correspond to
|
|
1670
|
+
// entering/leaving an element.
|
|
1671
|
+
if (isMouseSpecialEvent(getEvent(eventInfo), getEventType(eventInfo), getActionElement(action))) {
|
|
1672
|
+
// If both mouseover/mouseout and mouseenter/mouseleave events are
|
|
1673
|
+
// enabled, two separate handlers for mouseover/mouseout are
|
|
1674
|
+
// registered. Both handlers will see the same event instance
|
|
1675
|
+
// so we create a copy to avoid interfering with the dispatching of
|
|
1676
|
+
// the mouseover/mouseout event.
|
|
1677
|
+
const copiedEvent = createMouseSpecialEvent(getEvent(eventInfo), getActionElement(action));
|
|
1678
|
+
setEvent(eventInfo, copiedEvent);
|
|
1679
|
+
// Since the mouseenter/mouseleave events do not bubble, the target
|
|
1680
|
+
// of the event is technically the `actionElement` (the node with the
|
|
1681
|
+
// `jsaction` attribute)
|
|
1682
|
+
setTargetElement(eventInfo, getActionElement(action));
|
|
1683
|
+
}
|
|
1684
|
+
else {
|
|
1685
|
+
unsetAction(eventInfo);
|
|
1686
|
+
}
|
|
2043
1687
|
}
|
|
2044
1688
|
}
|
|
2045
1689
|
}
|
|
@@ -2048,25 +1692,181 @@ class EventContract {
|
|
|
2048
1692
|
* action the given event is mapped to, if any. It parses the
|
|
2049
1693
|
* attribute value and stores it in a property on the node for
|
|
2050
1694
|
* subsequent retrieval without re-parsing and re-accessing the
|
|
2051
|
-
* attribute.
|
|
2052
|
-
* namespace, the DOM is searched starting at the current node and
|
|
2053
|
-
* going through ancestor nodes until a jsnamespace attribute is
|
|
2054
|
-
* found.
|
|
1695
|
+
* attribute.
|
|
2055
1696
|
*
|
|
2056
1697
|
* @param actionElement The DOM node to retrieve the jsaction map from.
|
|
2057
1698
|
* @param eventInfo `EventInfo` to set `action` and `actionElement` if an
|
|
2058
1699
|
* action is found on the `actionElement`.
|
|
2059
1700
|
*/
|
|
2060
1701
|
populateActionOnElement(actionElement, eventInfo) {
|
|
2061
|
-
const actionMap = parseActions(actionElement
|
|
1702
|
+
const actionMap = this.parseActions(actionElement);
|
|
2062
1703
|
const actionName = actionMap[getEventType(eventInfo)];
|
|
2063
1704
|
if (actionName !== undefined) {
|
|
2064
1705
|
setAction(eventInfo, actionName, actionElement);
|
|
2065
1706
|
}
|
|
2066
|
-
if (this.
|
|
1707
|
+
if (this.a11yClickSupport) {
|
|
2067
1708
|
this.populateClickOnlyAction(actionElement, eventInfo, actionMap);
|
|
2068
1709
|
}
|
|
2069
1710
|
}
|
|
1711
|
+
/**
|
|
1712
|
+
* Parses and caches an element's jsaction element into a map.
|
|
1713
|
+
*
|
|
1714
|
+
* This is primarily for internal use.
|
|
1715
|
+
*
|
|
1716
|
+
* @param actionElement The DOM node to retrieve the jsaction map from.
|
|
1717
|
+
* @return Map from event to qualified name of the jsaction bound to it.
|
|
1718
|
+
*/
|
|
1719
|
+
parseActions(actionElement) {
|
|
1720
|
+
let actionMap = get(actionElement);
|
|
1721
|
+
if (!actionMap) {
|
|
1722
|
+
const jsactionAttribute = actionElement.getAttribute(Attribute.JSACTION);
|
|
1723
|
+
if (!jsactionAttribute) {
|
|
1724
|
+
actionMap = EMPTY_ACTION_MAP;
|
|
1725
|
+
set(actionElement, actionMap);
|
|
1726
|
+
}
|
|
1727
|
+
else {
|
|
1728
|
+
actionMap = getParsed(jsactionAttribute);
|
|
1729
|
+
if (!actionMap) {
|
|
1730
|
+
actionMap = {};
|
|
1731
|
+
const values = jsactionAttribute.split(REGEXP_SEMICOLON);
|
|
1732
|
+
for (let idx = 0; idx < values.length; idx++) {
|
|
1733
|
+
const value = values[idx];
|
|
1734
|
+
if (!value) {
|
|
1735
|
+
continue;
|
|
1736
|
+
}
|
|
1737
|
+
const colon = value.indexOf(Char.EVENT_ACTION_SEPARATOR);
|
|
1738
|
+
const hasColon = colon !== -1;
|
|
1739
|
+
const type = hasColon ? value.substr(0, colon).trim() : DEFAULT_EVENT_TYPE;
|
|
1740
|
+
const action = hasColon ? value.substr(colon + 1).trim() : value;
|
|
1741
|
+
actionMap[type] = action;
|
|
1742
|
+
}
|
|
1743
|
+
setParsed(jsactionAttribute, actionMap);
|
|
1744
|
+
}
|
|
1745
|
+
set(actionElement, actionMap);
|
|
1746
|
+
}
|
|
1747
|
+
}
|
|
1748
|
+
return actionMap;
|
|
1749
|
+
}
|
|
1750
|
+
addA11yClickSupport(updateEventInfoForA11yClick, preventDefaultForA11yClick, populateClickOnlyAction) {
|
|
1751
|
+
this.a11yClickSupport = true;
|
|
1752
|
+
this.updateEventInfoForA11yClick = updateEventInfoForA11yClick;
|
|
1753
|
+
this.preventDefaultForA11yClick = preventDefaultForA11yClick;
|
|
1754
|
+
this.populateClickOnlyAction = populateClickOnlyAction;
|
|
1755
|
+
}
|
|
1756
|
+
}
|
|
1757
|
+
|
|
1758
|
+
/**
|
|
1759
|
+
* @define Support for accessible click actions. This flag can be overridden in
|
|
1760
|
+
* a build rule.
|
|
1761
|
+
*/
|
|
1762
|
+
const A11Y_CLICK_SUPPORT = false;
|
|
1763
|
+
/**
|
|
1764
|
+
* @define Support for the non-bubbling mouseenter and mouseleave events. This
|
|
1765
|
+
* flag can be overridden in a build rule.
|
|
1766
|
+
*/
|
|
1767
|
+
const MOUSE_SPECIAL_SUPPORT = false;
|
|
1768
|
+
|
|
1769
|
+
/**
|
|
1770
|
+
* @fileoverview Implements the local event handling contract. This
|
|
1771
|
+
* allows DOM objects in a container that enters into this contract to
|
|
1772
|
+
* define event handlers which are executed in a local context.
|
|
1773
|
+
*
|
|
1774
|
+
* One EventContract instance can manage the contract for multiple
|
|
1775
|
+
* containers, which are added using the addContainer() method.
|
|
1776
|
+
*
|
|
1777
|
+
* Events can be registered using the addEvent() method.
|
|
1778
|
+
*
|
|
1779
|
+
* A Dispatcher is added using the registerDispatcher() method. Until there is
|
|
1780
|
+
* a dispatcher, events are queued. The idea is that the EventContract
|
|
1781
|
+
* class is inlined in the HTML of the top level page and instantiated
|
|
1782
|
+
* right after the start of <body>. The Dispatcher class is contained
|
|
1783
|
+
* in the external deferred js, and instantiated and registered with
|
|
1784
|
+
* EventContract when the external javascript in the page loads. The
|
|
1785
|
+
* external javascript will also register the jsaction handlers, which
|
|
1786
|
+
* then pick up the queued events at the time of registration.
|
|
1787
|
+
*
|
|
1788
|
+
* Since this class is meant to be inlined in the main page HTML, the
|
|
1789
|
+
* size of the binary compiled from this file MUST be kept as small as
|
|
1790
|
+
* possible and thus its dependencies to a minimum.
|
|
1791
|
+
*/
|
|
1792
|
+
/**
|
|
1793
|
+
* EventContract intercepts events in the bubbling phase at the
|
|
1794
|
+
* boundary of a container element, and maps them to generic actions
|
|
1795
|
+
* which are specified using the custom jsaction attribute in
|
|
1796
|
+
* HTML. Behavior of the application is then specified in terms of
|
|
1797
|
+
* handler for such actions, cf. jsaction.Dispatcher in dispatcher.js.
|
|
1798
|
+
*
|
|
1799
|
+
* This has several benefits: (1) No DOM event handlers need to be
|
|
1800
|
+
* registered on the specific elements in the UI. (2) The set of
|
|
1801
|
+
* events that the application has to handle can be specified in terms
|
|
1802
|
+
* of the semantics of the application, rather than in terms of DOM
|
|
1803
|
+
* events. (3) Invocation of handlers can be delayed and handlers can
|
|
1804
|
+
* be delay loaded in a generic way.
|
|
1805
|
+
*/
|
|
1806
|
+
class EventContract {
|
|
1807
|
+
static { this.A11Y_CLICK_SUPPORT = A11Y_CLICK_SUPPORT; }
|
|
1808
|
+
static { this.MOUSE_SPECIAL_SUPPORT = MOUSE_SPECIAL_SUPPORT; }
|
|
1809
|
+
constructor(containerManager, useActionResolver = true) {
|
|
1810
|
+
this.useActionResolver = useActionResolver;
|
|
1811
|
+
/**
|
|
1812
|
+
* The DOM events which this contract covers. Used to prevent double
|
|
1813
|
+
* registration of event types. The value of the map is the
|
|
1814
|
+
* internally created DOM event handler function that handles the
|
|
1815
|
+
* DOM events. See addEvent().
|
|
1816
|
+
*
|
|
1817
|
+
*/
|
|
1818
|
+
this.eventHandlers = {};
|
|
1819
|
+
this.browserEventTypeToExtraEventTypes = {};
|
|
1820
|
+
/**
|
|
1821
|
+
* The dispatcher function. Events are passed to this function for
|
|
1822
|
+
* handling once it was set using the registerDispatcher() method. This is
|
|
1823
|
+
* done because the function is passed from another jsbinary, so passing the
|
|
1824
|
+
* instance and invoking the method here would require to leave the method
|
|
1825
|
+
* unobfuscated.
|
|
1826
|
+
*/
|
|
1827
|
+
this.dispatcher = null;
|
|
1828
|
+
/**
|
|
1829
|
+
* The list of suspended `EventInfo` that will be dispatched
|
|
1830
|
+
* as soon as the `Dispatcher` is registered.
|
|
1831
|
+
*/
|
|
1832
|
+
this.queuedEventInfos = [];
|
|
1833
|
+
/** Whether to add an a11y click listener. */
|
|
1834
|
+
this.addA11yClickListener = false;
|
|
1835
|
+
this.containerManager = containerManager;
|
|
1836
|
+
if (this.useActionResolver) {
|
|
1837
|
+
this.actionResolver = new ActionResolver({
|
|
1838
|
+
syntheticMouseEventSupport: EventContract.MOUSE_SPECIAL_SUPPORT,
|
|
1839
|
+
});
|
|
1840
|
+
}
|
|
1841
|
+
if (EventContract.A11Y_CLICK_SUPPORT) {
|
|
1842
|
+
// Add a11y click support to the `EventContract`.
|
|
1843
|
+
this.addA11yClickSupport();
|
|
1844
|
+
}
|
|
1845
|
+
}
|
|
1846
|
+
handleEvent(eventType, event, container) {
|
|
1847
|
+
const eventInfo = createEventInfoFromParameters(
|
|
1848
|
+
/* eventType= */ eventType,
|
|
1849
|
+
/* event= */ event,
|
|
1850
|
+
/* targetElement= */ event.target,
|
|
1851
|
+
/* container= */ container,
|
|
1852
|
+
/* timestamp= */ Date.now());
|
|
1853
|
+
this.handleEventInfo(eventInfo);
|
|
1854
|
+
}
|
|
1855
|
+
/**
|
|
1856
|
+
* Handle an `EventInfo`.
|
|
1857
|
+
*/
|
|
1858
|
+
handleEventInfo(eventInfo) {
|
|
1859
|
+
if (!this.dispatcher) {
|
|
1860
|
+
// All events are queued when the dispatcher isn't yet loaded.
|
|
1861
|
+
setIsReplay(eventInfo, true);
|
|
1862
|
+
this.queuedEventInfos?.push(eventInfo);
|
|
1863
|
+
return;
|
|
1864
|
+
}
|
|
1865
|
+
if (this.useActionResolver) {
|
|
1866
|
+
this.actionResolver.resolve(eventInfo);
|
|
1867
|
+
}
|
|
1868
|
+
this.dispatcher(eventInfo);
|
|
1869
|
+
}
|
|
2070
1870
|
/**
|
|
2071
1871
|
* Enables jsaction handlers to be called for the event type given by
|
|
2072
1872
|
* name.
|
|
@@ -2118,10 +1918,10 @@ class EventContract {
|
|
|
2118
1918
|
* in the provided event contract. Once all the events are replayed, it cleans
|
|
2119
1919
|
* up the early contract.
|
|
2120
1920
|
*/
|
|
2121
|
-
replayEarlyEvents() {
|
|
1921
|
+
replayEarlyEvents(earlyJsactionContainer = window) {
|
|
2122
1922
|
// Check if the early contract is present and prevent calling this function
|
|
2123
1923
|
// more than once.
|
|
2124
|
-
const earlyJsactionData =
|
|
1924
|
+
const earlyJsactionData = earlyJsactionContainer._ejsa;
|
|
2125
1925
|
if (!earlyJsactionData) {
|
|
2126
1926
|
return;
|
|
2127
1927
|
}
|
|
@@ -2139,13 +1939,10 @@ class EventContract {
|
|
|
2139
1939
|
}
|
|
2140
1940
|
}
|
|
2141
1941
|
// Clean up the early contract.
|
|
2142
|
-
const earlyEventTypes = earlyJsactionData.et;
|
|
2143
1942
|
const earlyEventHandler = earlyJsactionData.h;
|
|
2144
|
-
|
|
2145
|
-
|
|
2146
|
-
|
|
2147
|
-
}
|
|
2148
|
-
delete window._ejsa;
|
|
1943
|
+
removeEventListeners(earlyJsactionData.c, earlyJsactionData.et, earlyEventHandler);
|
|
1944
|
+
removeEventListeners(earlyJsactionData.c, earlyJsactionData.etc, earlyEventHandler, true);
|
|
1945
|
+
delete earlyJsactionContainer._ejsa;
|
|
2149
1946
|
}
|
|
2150
1947
|
/**
|
|
2151
1948
|
* Returns all JSAction event types that have been registered for a given
|
|
@@ -2226,10 +2023,14 @@ class EventContract {
|
|
|
2226
2023
|
*/
|
|
2227
2024
|
addA11yClickSupportImpl(updateEventInfoForA11yClick, preventDefaultForA11yClick, populateClickOnlyAction) {
|
|
2228
2025
|
this.addA11yClickListener = true;
|
|
2229
|
-
this.
|
|
2230
|
-
|
|
2231
|
-
|
|
2232
|
-
|
|
2026
|
+
if (this.useActionResolver) {
|
|
2027
|
+
this.actionResolver.addA11yClickSupport(updateEventInfoForA11yClick, preventDefaultForA11yClick, populateClickOnlyAction);
|
|
2028
|
+
}
|
|
2029
|
+
}
|
|
2030
|
+
}
|
|
2031
|
+
function removeEventListeners(container, eventTypes, earlyEventHandler, capture) {
|
|
2032
|
+
for (let idx = 0; idx < eventTypes.length; idx++) {
|
|
2033
|
+
container.removeEventListener(eventTypes[idx], earlyEventHandler, /* useCapture */ capture);
|
|
2233
2034
|
}
|
|
2234
2035
|
}
|
|
2235
2036
|
/**
|
|
@@ -2241,187 +2042,63 @@ class EventContract {
|
|
|
2241
2042
|
function addDeferredA11yClickSupport(eventContract) {
|
|
2242
2043
|
eventContract.ecaacs?.(updateEventInfoForA11yClick, preventDefaultForA11yClick, populateClickOnlyAction);
|
|
2243
2044
|
}
|
|
2045
|
+
|
|
2244
2046
|
/**
|
|
2245
|
-
*
|
|
2246
|
-
*
|
|
2247
|
-
|
|
2248
|
-
|
|
2249
|
-
|
|
2250
|
-
|
|
2251
|
-
|
|
2252
|
-
|
|
2253
|
-
|
|
2254
|
-
|
|
2255
|
-
|
|
2256
|
-
|
|
2257
|
-
|
|
2258
|
-
|
|
2259
|
-
|
|
2260
|
-
|
|
2261
|
-
|
|
2262
|
-
|
|
2263
|
-
* @param container The node which limits the namespace lookup for a jsaction
|
|
2264
|
-
* name. The container node itself will not be searched.
|
|
2265
|
-
* @return Map from event to qualified name of the jsaction bound to it.
|
|
2266
|
-
*/
|
|
2267
|
-
function parseActions(actionElement, container) {
|
|
2268
|
-
let actionMap = get(actionElement);
|
|
2269
|
-
if (!actionMap) {
|
|
2270
|
-
const jsactionAttribute = getAttr(actionElement, Attribute.JSACTION);
|
|
2271
|
-
if (!jsactionAttribute) {
|
|
2272
|
-
actionMap = EMPTY_ACTION_MAP;
|
|
2273
|
-
set(actionElement, actionMap);
|
|
2274
|
-
}
|
|
2275
|
-
else {
|
|
2276
|
-
actionMap = getParsed(jsactionAttribute);
|
|
2277
|
-
if (!actionMap) {
|
|
2278
|
-
actionMap = {};
|
|
2279
|
-
const values = jsactionAttribute.split(REGEXP_SEMICOLON);
|
|
2280
|
-
for (let idx = 0; idx < values.length; idx++) {
|
|
2281
|
-
const value = values[idx];
|
|
2282
|
-
if (!value) {
|
|
2283
|
-
continue;
|
|
2284
|
-
}
|
|
2285
|
-
const colon = value.indexOf(Char.EVENT_ACTION_SEPARATOR);
|
|
2286
|
-
const hasColon = colon !== -1;
|
|
2287
|
-
const type = hasColon ? stringTrim(value.substr(0, colon)) : DEFAULT_EVENT_TYPE;
|
|
2288
|
-
const action = hasColon ? stringTrim(value.substr(colon + 1)) : value;
|
|
2289
|
-
actionMap[type] = action;
|
|
2290
|
-
}
|
|
2291
|
-
setParsed(jsactionAttribute, actionMap);
|
|
2292
|
-
}
|
|
2293
|
-
// If namespace support is active we need to augment the (potentially
|
|
2294
|
-
// cached) jsaction mapping with the namespace.
|
|
2295
|
-
if (EventContract.JSNAMESPACE_SUPPORT) {
|
|
2296
|
-
const noNs = actionMap;
|
|
2297
|
-
actionMap = {};
|
|
2298
|
-
for (const type in noNs) {
|
|
2299
|
-
actionMap[type] = getFullyQualifiedAction(noNs[type], actionElement, container);
|
|
2300
|
-
}
|
|
2301
|
-
}
|
|
2302
|
-
set(actionElement, actionMap);
|
|
2303
|
-
}
|
|
2047
|
+
* EarlyEventContract intercepts events in the bubbling phase at the
|
|
2048
|
+
* boundary of the document body. This mapping will be passed to the
|
|
2049
|
+
* late-loaded EventContract.
|
|
2050
|
+
*/
|
|
2051
|
+
class EarlyEventContract {
|
|
2052
|
+
constructor(replaySink = window, container = window.document.documentElement) {
|
|
2053
|
+
this.replaySink = replaySink;
|
|
2054
|
+
this.container = container;
|
|
2055
|
+
replaySink._ejsa = {
|
|
2056
|
+
c: container,
|
|
2057
|
+
q: [],
|
|
2058
|
+
et: [],
|
|
2059
|
+
etc: [],
|
|
2060
|
+
h: (event) => {
|
|
2061
|
+
const eventInfo = createEventInfoFromParameters(event.type, event, event.target, container, Date.now());
|
|
2062
|
+
replaySink._ejsa.q.push(eventInfo);
|
|
2063
|
+
},
|
|
2064
|
+
};
|
|
2304
2065
|
}
|
|
2305
|
-
|
|
2306
|
-
|
|
2307
|
-
|
|
2308
|
-
|
|
2309
|
-
|
|
2310
|
-
|
|
2311
|
-
|
|
2312
|
-
|
|
2313
|
-
|
|
2314
|
-
|
|
2315
|
-
* attribute.
|
|
2316
|
-
* @param container The node which limits the search for a jsnamespace
|
|
2317
|
-
* attribute. This node will be searched.
|
|
2318
|
-
* @return The fully qualified name of the jsaction. If no namespace is found,
|
|
2319
|
-
* returns the unqualified name in case it exists in the global namespace.
|
|
2320
|
-
*/
|
|
2321
|
-
function getFullyQualifiedAction(action, start, container) {
|
|
2322
|
-
if (EventContract.JSNAMESPACE_SUPPORT) {
|
|
2323
|
-
if (isNamespacedAction(action)) {
|
|
2324
|
-
return action;
|
|
2325
|
-
}
|
|
2326
|
-
let node = start;
|
|
2327
|
-
while (node) {
|
|
2328
|
-
const namespace = getNamespaceFromElement(node);
|
|
2329
|
-
if (namespace) {
|
|
2330
|
-
return namespace + Char.NAMESPACE_ACTION_SEPARATOR + action;
|
|
2331
|
-
}
|
|
2332
|
-
// If this node is the container, stop.
|
|
2333
|
-
if (node === container) {
|
|
2334
|
-
break;
|
|
2335
|
-
}
|
|
2336
|
-
node = node.parentNode;
|
|
2066
|
+
/**
|
|
2067
|
+
* Installs a list of event types for container .
|
|
2068
|
+
*/
|
|
2069
|
+
addEvents(types, capture) {
|
|
2070
|
+
const replaySink = this.replaySink._ejsa;
|
|
2071
|
+
for (let idx = 0; idx < types.length; idx++) {
|
|
2072
|
+
const eventType = types[idx];
|
|
2073
|
+
const eventTypes = capture ? replaySink.etc : replaySink.et;
|
|
2074
|
+
eventTypes.push(eventType);
|
|
2075
|
+
this.container.addEventListener(eventType, replaySink.h, capture);
|
|
2337
2076
|
}
|
|
2338
2077
|
}
|
|
2339
|
-
return action;
|
|
2340
|
-
}
|
|
2341
|
-
/**
|
|
2342
|
-
* Checks if a jsaction action contains a namespace part.
|
|
2343
|
-
*/
|
|
2344
|
-
function isNamespacedAction(action) {
|
|
2345
|
-
return action.indexOf(Char.NAMESPACE_ACTION_SEPARATOR) >= 0;
|
|
2346
|
-
}
|
|
2347
|
-
/**
|
|
2348
|
-
* Returns the value of the jsnamespace attribute of the given node.
|
|
2349
|
-
* Also caches the value for subsequent lookups.
|
|
2350
|
-
* @param element The node whose jsnamespace attribute is being asked for.
|
|
2351
|
-
* @return The value of the jsnamespace attribute, or null if not found.
|
|
2352
|
-
*/
|
|
2353
|
-
function getNamespaceFromElement(element) {
|
|
2354
|
-
let namespace = getNamespace(element);
|
|
2355
|
-
// Only query for the attribute if it has not been queried for
|
|
2356
|
-
// before. getAttr() returns null if an attribute is not present. Thus,
|
|
2357
|
-
// namespace is string|null if the query took place in the past, or
|
|
2358
|
-
// undefined if the query did not take place.
|
|
2359
|
-
if (namespace === undefined) {
|
|
2360
|
-
namespace = getAttr(element, Attribute.JSNAMESPACE);
|
|
2361
|
-
setNamespace(element, namespace);
|
|
2362
|
-
}
|
|
2363
|
-
return namespace;
|
|
2364
|
-
}
|
|
2365
|
-
/**
|
|
2366
|
-
* Accesses the event handler attribute value of a DOM node. It guards
|
|
2367
|
-
* against weird situations (described in the body) that occur in
|
|
2368
|
-
* connection with nodes that are removed from their document.
|
|
2369
|
-
* @param element The DOM element.
|
|
2370
|
-
* @param attribute The name of the attribute to access.
|
|
2371
|
-
* @return The attribute value if it was found, null otherwise.
|
|
2372
|
-
*/
|
|
2373
|
-
function getAttr(element, attribute) {
|
|
2374
|
-
let value = null;
|
|
2375
|
-
// NOTE: Nodes in IE do not always have a getAttribute
|
|
2376
|
-
// method defined. This is the case where sourceElement has in
|
|
2377
|
-
// fact been removed from the DOM before eventContract begins
|
|
2378
|
-
// handling - where a parentNode does not have getAttribute
|
|
2379
|
-
// defined.
|
|
2380
|
-
// NOTE: We must use the 'in' operator instead of the regular dot
|
|
2381
|
-
// notation, since the latter fails in IE8 if the getAttribute method is not
|
|
2382
|
-
// defined. See b/7139109.
|
|
2383
|
-
if ('getAttribute' in element) {
|
|
2384
|
-
value = element.getAttribute(attribute);
|
|
2385
|
-
}
|
|
2386
|
-
return value;
|
|
2387
|
-
}
|
|
2388
|
-
/**
|
|
2389
|
-
* Helper function to trim whitespace from the beginning and the end
|
|
2390
|
-
* of the string. This deliberately doesn't use the closure equivalent
|
|
2391
|
-
* to keep dependencies small.
|
|
2392
|
-
* @param str Input string.
|
|
2393
|
-
* @return Trimmed string.
|
|
2394
|
-
*/
|
|
2395
|
-
function stringTrim(str) {
|
|
2396
|
-
if (typeof String.prototype.trim === 'function') {
|
|
2397
|
-
return str.trim();
|
|
2398
|
-
}
|
|
2399
|
-
const trimmedLeft = str.replace(/^\s+/, '');
|
|
2400
|
-
return trimmedLeft.replace(/\s+$/, '');
|
|
2401
2078
|
}
|
|
2402
2079
|
|
|
2403
2080
|
/**
|
|
2404
2081
|
* Provides a factory function for bootstrapping an event contract on a
|
|
2405
|
-
*
|
|
2406
|
-
* @param field The property on the
|
|
2082
|
+
* specified object (by default, exposed on the `window`).
|
|
2083
|
+
* @param field The property on the object that the event contract will be placed on.
|
|
2407
2084
|
* @param container The container that listens to events
|
|
2408
2085
|
* @param appId A given identifier for an application. If there are multiple apps on the page
|
|
2409
2086
|
* then this is how contracts can be initialized for each one.
|
|
2410
|
-
* @param
|
|
2411
|
-
* @param
|
|
2412
|
-
* @
|
|
2087
|
+
* @param eventTypes An array of event names that should be listened to.
|
|
2088
|
+
* @param captureEventTypes An array of event names that should be listened to with capture.
|
|
2089
|
+
* @param earlyJsactionTracker The object that should receive the event contract.
|
|
2413
2090
|
*/
|
|
2414
|
-
function
|
|
2415
|
-
if (!
|
|
2416
|
-
|
|
2417
|
-
}
|
|
2418
|
-
const eventContract = new EventContract(new EventContractContainer(container));
|
|
2419
|
-
anyWindow[field][appId] = eventContract;
|
|
2420
|
-
for (const ev of events) {
|
|
2421
|
-
eventContract.addEvent(ev);
|
|
2091
|
+
function bootstrapEarlyEventContract(field, container, appId, eventTypes, captureEventTypes, earlyJsactionTracker = window) {
|
|
2092
|
+
if (!earlyJsactionTracker[field]) {
|
|
2093
|
+
earlyJsactionTracker[field] = {};
|
|
2422
2094
|
}
|
|
2423
|
-
|
|
2095
|
+
earlyJsactionTracker[field][appId] = {};
|
|
2096
|
+
const eventContract = new EarlyEventContract(earlyJsactionTracker[field][appId], container);
|
|
2097
|
+
if (eventTypes)
|
|
2098
|
+
eventContract.addEvents(eventTypes);
|
|
2099
|
+
if (captureEventTypes)
|
|
2100
|
+
eventContract.addEvents(captureEventTypes, true);
|
|
2424
2101
|
}
|
|
2425
2102
|
|
|
2426
|
-
export { Dispatcher, EventContract, EventContractContainer, EventInfoWrapper,
|
|
2103
|
+
export { Dispatcher, EventContract, EventContractContainer, EventInfoWrapper, bootstrapEarlyEventContract, registerDispatcher };
|
|
2427
2104
|
//# sourceMappingURL=event-dispatch.mjs.map
|