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