@b9g/crank 0.4.3 → 0.4.4

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/crank.js CHANGED
@@ -955,13 +955,170 @@ class Context {
955
955
  provisions.set(key, value);
956
956
  }
957
957
  addEventListener(type, listener, options) {
958
- return addEventListener(this[$ContextImpl], type, listener, options);
958
+ const impl = this[$ContextImpl];
959
+ let listeners;
960
+ if (!isListenerOrListenerObject(listener)) {
961
+ return;
962
+ }
963
+ else {
964
+ const listeners1 = listenersMap.get(impl);
965
+ if (listeners1) {
966
+ listeners = listeners1;
967
+ }
968
+ else {
969
+ listeners = [];
970
+ listenersMap.set(impl, listeners);
971
+ }
972
+ }
973
+ options = normalizeListenerOptions(options);
974
+ let callback;
975
+ if (typeof listener === "object") {
976
+ callback = () => listener.handleEvent.apply(listener, arguments);
977
+ }
978
+ else {
979
+ callback = listener;
980
+ }
981
+ const record = { type, callback, listener, options };
982
+ if (options.once) {
983
+ record.callback = function () {
984
+ const i = listeners.indexOf(record);
985
+ if (i !== -1) {
986
+ listeners.splice(i, 1);
987
+ }
988
+ return callback.apply(this, arguments);
989
+ };
990
+ }
991
+ if (listeners.some((record1) => record.type === record1.type &&
992
+ record.listener === record1.listener &&
993
+ !record.options.capture === !record1.options.capture)) {
994
+ return;
995
+ }
996
+ listeners.push(record);
997
+ // TODO: is it possible to separate out the EventTarget delegation logic
998
+ for (const value of getChildValues(impl.ret)) {
999
+ if (isEventTarget(value)) {
1000
+ value.addEventListener(record.type, record.callback, record.options);
1001
+ }
1002
+ }
959
1003
  }
960
1004
  removeEventListener(type, listener, options) {
961
- return removeEventListener(this[$ContextImpl], type, listener, options);
1005
+ const impl = this[$ContextImpl];
1006
+ const listeners = listenersMap.get(impl);
1007
+ if (listeners == null || !isListenerOrListenerObject(listener)) {
1008
+ return;
1009
+ }
1010
+ const options1 = normalizeListenerOptions(options);
1011
+ const i = listeners.findIndex((record) => record.type === type &&
1012
+ record.listener === listener &&
1013
+ !record.options.capture === !options1.capture);
1014
+ if (i === -1) {
1015
+ return;
1016
+ }
1017
+ const record = listeners[i];
1018
+ listeners.splice(i, 1);
1019
+ // TODO: is it possible to separate out the EventTarget delegation logic
1020
+ for (const value of getChildValues(impl.ret)) {
1021
+ if (isEventTarget(value)) {
1022
+ value.removeEventListener(record.type, record.callback, record.options);
1023
+ }
1024
+ }
962
1025
  }
963
1026
  dispatchEvent(ev) {
964
- return dispatchEvent(this[$ContextImpl], ev);
1027
+ const impl = this[$ContextImpl];
1028
+ const path = [];
1029
+ for (let parent = impl.parent; parent !== undefined; parent = parent.parent) {
1030
+ path.push(parent);
1031
+ }
1032
+ // We patch the stopImmediatePropagation method because ev.cancelBubble
1033
+ // only informs us if stopPropagation was called and there are no
1034
+ // properties which inform us if stopImmediatePropagation was called.
1035
+ let immediateCancelBubble = false;
1036
+ const stopImmediatePropagation = ev.stopImmediatePropagation;
1037
+ setEventProperty(ev, "stopImmediatePropagation", () => {
1038
+ immediateCancelBubble = true;
1039
+ return stopImmediatePropagation.call(ev);
1040
+ });
1041
+ setEventProperty(ev, "target", impl.ctx);
1042
+ // The only possible errors in this block are errors thrown by callbacks,
1043
+ // and dispatchEvent will only log these errors rather than throwing
1044
+ // them. Therefore, we place all code in a try block, log errors in the
1045
+ // catch block, and use an unsafe return statement in the finally block.
1046
+ //
1047
+ // Each early return within the try block returns true because while the
1048
+ // return value is overridden in the finally block, TypeScript
1049
+ // (justifiably) does not recognize the unsafe return statement.
1050
+ //
1051
+ // TODO: Run all callbacks even if one of them errors
1052
+ try {
1053
+ setEventProperty(ev, "eventPhase", CAPTURING_PHASE);
1054
+ for (let i = path.length - 1; i >= 0; i--) {
1055
+ const target = path[i];
1056
+ const listeners = listenersMap.get(target);
1057
+ if (listeners) {
1058
+ setEventProperty(ev, "currentTarget", target.ctx);
1059
+ for (const record of listeners) {
1060
+ if (record.type === ev.type && record.options.capture) {
1061
+ record.callback.call(target.ctx, ev);
1062
+ if (immediateCancelBubble) {
1063
+ return true;
1064
+ }
1065
+ }
1066
+ }
1067
+ }
1068
+ if (ev.cancelBubble) {
1069
+ return true;
1070
+ }
1071
+ }
1072
+ {
1073
+ const listeners = listenersMap.get(impl);
1074
+ if (listeners) {
1075
+ setEventProperty(ev, "eventPhase", AT_TARGET);
1076
+ setEventProperty(ev, "currentTarget", impl.ctx);
1077
+ for (const record of listeners) {
1078
+ if (record.type === ev.type) {
1079
+ record.callback.call(impl.ctx, ev);
1080
+ if (immediateCancelBubble) {
1081
+ return true;
1082
+ }
1083
+ }
1084
+ }
1085
+ if (ev.cancelBubble) {
1086
+ return true;
1087
+ }
1088
+ }
1089
+ }
1090
+ if (ev.bubbles) {
1091
+ setEventProperty(ev, "eventPhase", BUBBLING_PHASE);
1092
+ for (let i = 0; i < path.length; i++) {
1093
+ const target = path[i];
1094
+ const listeners = listenersMap.get(target);
1095
+ if (listeners) {
1096
+ setEventProperty(ev, "currentTarget", target.ctx);
1097
+ for (const record of listeners) {
1098
+ if (record.type === ev.type && !record.options.capture) {
1099
+ record.callback.call(target.ctx, ev);
1100
+ if (immediateCancelBubble) {
1101
+ return true;
1102
+ }
1103
+ }
1104
+ }
1105
+ }
1106
+ if (ev.cancelBubble) {
1107
+ return true;
1108
+ }
1109
+ }
1110
+ }
1111
+ }
1112
+ catch (err) {
1113
+ // TODO: Use setTimeout to rethrow the error.
1114
+ console.error(err);
1115
+ }
1116
+ finally {
1117
+ setEventProperty(ev, "eventPhase", NONE);
1118
+ setEventProperty(ev, "currentTarget", null);
1119
+ // eslint-disable-next-line no-unsafe-finally
1120
+ return !ev.defaultPrevented;
1121
+ }
965
1122
  }
966
1123
  }
967
1124
  /*** PRIVATE CONTEXT FUNCTIONS ***/
@@ -1362,168 +1519,11 @@ const CAPTURING_PHASE = 1;
1362
1519
  const AT_TARGET = 2;
1363
1520
  const BUBBLING_PHASE = 3;
1364
1521
  const listenersMap = new WeakMap();
1365
- function addEventListener(ctx, type, listener, options) {
1366
- let listeners;
1367
- if (listener == null) {
1368
- return;
1369
- }
1370
- else {
1371
- const listeners1 = listenersMap.get(ctx);
1372
- if (listeners1) {
1373
- listeners = listeners1;
1374
- }
1375
- else {
1376
- listeners = [];
1377
- listenersMap.set(ctx, listeners);
1378
- }
1379
- }
1380
- options = normalizeListenerOptions(options);
1381
- let callback;
1382
- if (typeof listener === "object") {
1383
- callback = () => listener.handleEvent.apply(listener, arguments);
1384
- }
1385
- else {
1386
- callback = listener;
1387
- }
1388
- const record = { type, callback, listener, options };
1389
- if (options.once) {
1390
- record.callback = function () {
1391
- const i = listeners.indexOf(record);
1392
- if (i !== -1) {
1393
- listeners.splice(i, 1);
1394
- }
1395
- return callback.apply(this, arguments);
1396
- };
1397
- }
1398
- if (listeners.some((record1) => record.type === record1.type &&
1399
- record.listener === record1.listener &&
1400
- !record.options.capture === !record1.options.capture)) {
1401
- return;
1402
- }
1403
- listeners.push(record);
1404
- // TODO: is it possible to separate out the EventTarget delegation logic
1405
- for (const value of getChildValues(ctx.ret)) {
1406
- if (isEventTarget(value)) {
1407
- value.addEventListener(record.type, record.callback, record.options);
1408
- }
1409
- }
1410
- }
1411
- function removeEventListener(ctx, type, listener, options) {
1412
- const listeners = listenersMap.get(ctx);
1413
- if (listener == null || listeners == null) {
1414
- return;
1415
- }
1416
- const options1 = normalizeListenerOptions(options);
1417
- const i = listeners.findIndex((record) => record.type === type &&
1418
- record.listener === listener &&
1419
- !record.options.capture === !options1.capture);
1420
- if (i === -1) {
1421
- return;
1422
- }
1423
- const record = listeners[i];
1424
- listeners.splice(i, 1);
1425
- // TODO: is it possible to separate out the EventTarget delegation logic
1426
- for (const value of getChildValues(ctx.ret)) {
1427
- if (isEventTarget(value)) {
1428
- value.removeEventListener(record.type, record.callback, record.options);
1429
- }
1430
- }
1431
- }
1432
- function dispatchEvent(ctx, ev) {
1433
- const path = [];
1434
- for (let parent = ctx.parent; parent !== undefined; parent = parent.parent) {
1435
- path.push(parent);
1436
- }
1437
- // We patch the stopImmediatePropagation method because ev.cancelBubble
1438
- // only informs us if stopPropagation was called and there are no
1439
- // properties which inform us if stopImmediatePropagation was called.
1440
- let immediateCancelBubble = false;
1441
- const stopImmediatePropagation = ev.stopImmediatePropagation;
1442
- setEventProperty(ev, "stopImmediatePropagation", () => {
1443
- immediateCancelBubble = true;
1444
- return stopImmediatePropagation.call(ev);
1445
- });
1446
- setEventProperty(ev, "target", ctx.ctx);
1447
- // The only possible errors in this block are errors thrown by callbacks,
1448
- // and dispatchEvent will only log these errors rather than throwing
1449
- // them. Therefore, we place all code in a try block, log errors in the
1450
- // catch block, and use an unsafe return statement in the finally block.
1451
- //
1452
- // Each early return within the try block returns true because while the
1453
- // return value is overridden in the finally block, TypeScript
1454
- // (justifiably) does not recognize the unsafe return statement.
1455
- //
1456
- // TODO: Run all callbacks even if one of them errors
1457
- try {
1458
- setEventProperty(ev, "eventPhase", CAPTURING_PHASE);
1459
- for (let i = path.length - 1; i >= 0; i--) {
1460
- const target = path[i];
1461
- const listeners = listenersMap.get(target);
1462
- if (listeners) {
1463
- setEventProperty(ev, "currentTarget", target.ctx);
1464
- for (const record of listeners) {
1465
- if (record.type === ev.type && record.options.capture) {
1466
- record.callback.call(target.ctx, ev);
1467
- if (immediateCancelBubble) {
1468
- return true;
1469
- }
1470
- }
1471
- }
1472
- }
1473
- if (ev.cancelBubble) {
1474
- return true;
1475
- }
1476
- }
1477
- {
1478
- const listeners = listenersMap.get(ctx);
1479
- if (listeners) {
1480
- setEventProperty(ev, "eventPhase", AT_TARGET);
1481
- setEventProperty(ev, "currentTarget", ctx.ctx);
1482
- for (const record of listeners) {
1483
- if (record.type === ev.type) {
1484
- record.callback.call(ctx.ctx, ev);
1485
- if (immediateCancelBubble) {
1486
- return true;
1487
- }
1488
- }
1489
- }
1490
- if (ev.cancelBubble) {
1491
- return true;
1492
- }
1493
- }
1494
- }
1495
- if (ev.bubbles) {
1496
- setEventProperty(ev, "eventPhase", BUBBLING_PHASE);
1497
- for (let i = 0; i < path.length; i++) {
1498
- const target = path[i];
1499
- const listeners = listenersMap.get(target);
1500
- if (listeners) {
1501
- setEventProperty(ev, "currentTarget", target.ctx);
1502
- for (const record of listeners) {
1503
- if (record.type === ev.type && !record.options.capture) {
1504
- record.callback.call(target.ctx, ev);
1505
- if (immediateCancelBubble) {
1506
- return true;
1507
- }
1508
- }
1509
- }
1510
- }
1511
- if (ev.cancelBubble) {
1512
- return true;
1513
- }
1514
- }
1515
- }
1516
- }
1517
- catch (err) {
1518
- // TODO: Use setTimeout to rethrow the error.
1519
- console.error(err);
1520
- }
1521
- finally {
1522
- setEventProperty(ev, "eventPhase", NONE);
1523
- setEventProperty(ev, "currentTarget", null);
1524
- // eslint-disable-next-line no-unsafe-finally
1525
- return !ev.defaultPrevented;
1526
- }
1522
+ function isListenerOrListenerObject(value) {
1523
+ return (typeof value === "function" ||
1524
+ (value !== null &&
1525
+ typeof value === "object" &&
1526
+ typeof value.handleEvent === "function"));
1527
1527
  }
1528
1528
  function normalizeListenerOptions(options) {
1529
1529
  if (typeof options === "boolean") {