@b9g/crank 0.4.3 → 0.5.0-beta.0
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/README.md +15 -50
- package/crank.cjs +192 -171
- package/crank.cjs.map +1 -1
- package/crank.d.ts +3 -3
- package/crank.js +192 -172
- 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/index.cjs +3 -1
- package/index.cjs.map +1 -1
- package/index.d.ts +1 -1
- package/index.js +2 -1
- package/index.js.map +1 -1
- package/package.json +32 -24
- package/umd.js +192 -171
- package/umd.js.map +1 -1
- package/xm.cjs +431 -0
- package/xm.cjs.map +1 -0
- package/xm.d.ts +2 -0
- package/xm.js +428 -0
- package/xm.js.map +1 -0
package/README.md
CHANGED
|
@@ -1,55 +1,25 @@
|
|
|
1
1
|
# Crank.js
|
|
2
|
-
|
|
2
|
+
### The most “Just JavaScript” web framework.
|
|
3
3
|
|
|
4
|
-
|
|
4
|
+
Crank.js is a framework where components are defined with *Plain Old JavaScript Functions*. But not just regular functions! Components can also be defined with async functions for working with promises, and generator functions for working with local state.
|
|
5
5
|
|
|
6
|
-
|
|
7
|
-
### Declarative
|
|
8
|
-
Crank uses the same JSX syntax and diffing algorithm popularized by React, allowing you to write HTML-like code directly in JavaScript.
|
|
6
|
+
Its API aims to be minimal and transparent. By relying on standard JavaScript control flow operators and data structures, and by making the process of rendering explicit, Crank.js helps developers write durable, bug-free applications with the latest and greatest libraries and APIs.
|
|
9
7
|
|
|
10
|
-
|
|
11
|
-
All components in Crank are just functions or generator functions. No classes, hooks, proxies or template languages are needed.
|
|
8
|
+
Rather than forcing developers to work with increasingly convoluted reactive solutions and bespoke templating languages, Crank.js uses the platform. It is a dedication to the web, to the promise of an accessible and inclusive medium for self-expression and commerce, built on the thesis that simpler code is how we’ll push the frontier.
|
|
12
9
|
|
|
13
|
-
|
|
14
|
-
Crank provides first-class support for promises. You can define components as async functions and race renderings to display fallback UIs.
|
|
10
|
+
## Get Started
|
|
15
11
|
|
|
16
|
-
|
|
17
|
-
Crank has no dependencies, and its core is a single file. It currently measures at [5KB minified and gzipped](https://bundlephobia.com/result?p=@b9g/crank).
|
|
18
|
-
|
|
19
|
-
### Performant
|
|
20
|
-
[According to benchmarks](https://github.com/krausest/js-framework-benchmark), Crank beats React in terms of speed and memory usage, and is currently comparable to Preact or Vue.
|
|
21
|
-
|
|
22
|
-
### Extensible
|
|
23
|
-
The core renderer can be extended to target alternative environments such as WebGL libraries, terminals, smartphones or smart TVs.
|
|
24
|
-
|
|
25
|
-
## Installation
|
|
26
|
-
Crank is available on [NPM](https://npmjs.org/@b9g/crank) in the ESModule and CommonJS formats.
|
|
12
|
+
The documentation for Crank.js is available at [crank.js.org](https://crank.js.org). Crank.js is published on NPM under the `@b9g` organization (short for “b*ikeshavin*g”).
|
|
27
13
|
|
|
28
14
|
```shell
|
|
29
15
|
$ npm install @b9g/crank
|
|
30
16
|
```
|
|
31
17
|
|
|
32
|
-
|
|
33
|
-
/** @jsx createElement */
|
|
34
|
-
import {createElement} from "@b9g/crank";
|
|
35
|
-
import {renderer} from "@b9g/crank/dom";
|
|
36
|
-
|
|
37
|
-
renderer.render(<div id="hello">Hello world</div>, document.body);
|
|
38
|
-
```
|
|
18
|
+
### Key Examples
|
|
39
19
|
|
|
40
|
-
|
|
20
|
+
#### A Simple Component
|
|
41
21
|
|
|
42
|
-
```jsx
|
|
43
|
-
/** @jsx createElement */
|
|
44
|
-
import {createElement} from "@b9g/crank/cjs";
|
|
45
|
-
import {renderer} from "@b9g/crank/cjs/dom";
|
|
46
|
-
|
|
47
|
-
renderer.render(<div id="hello">Hello world</div>, document.body);
|
|
48
|
-
```
|
|
49
|
-
|
|
50
|
-
## Key Examples
|
|
51
|
-
### A Simple Component
|
|
52
|
-
```jsx
|
|
22
|
+
```jsx live
|
|
53
23
|
/** @jsx createElement */
|
|
54
24
|
import {createElement} from "@b9g/crank";
|
|
55
25
|
import {renderer} from "@b9g/crank/dom";
|
|
@@ -63,10 +33,9 @@ function Greeting({name = "World"}) {
|
|
|
63
33
|
renderer.render(<Greeting />, document.body);
|
|
64
34
|
```
|
|
65
35
|
|
|
66
|
-
|
|
36
|
+
#### A Stateful Component
|
|
67
37
|
|
|
68
|
-
|
|
69
|
-
```jsx
|
|
38
|
+
```jsx live
|
|
70
39
|
/** @jsx createElement */
|
|
71
40
|
import {createElement} from "@b9g/crank";
|
|
72
41
|
import {renderer} from "@b9g/crank/dom";
|
|
@@ -89,10 +58,9 @@ function *Timer() {
|
|
|
89
58
|
renderer.render(<Timer />, document.body);
|
|
90
59
|
```
|
|
91
60
|
|
|
92
|
-
|
|
61
|
+
#### An Async Component
|
|
93
62
|
|
|
94
|
-
|
|
95
|
-
```jsx
|
|
63
|
+
```jsx live
|
|
96
64
|
/** @jsx createElement */
|
|
97
65
|
import {createElement} from "@b9g/crank";
|
|
98
66
|
import {renderer} from "@b9g/crank/dom";
|
|
@@ -110,10 +78,9 @@ async function QuoteOfTheDay() {
|
|
|
110
78
|
renderer.render(<QuoteOfTheDay />, document.body);
|
|
111
79
|
```
|
|
112
80
|
|
|
113
|
-
[Try on CodeSandbox](https://codesandbox.io/s/an-async-crank-component-ru02q)
|
|
114
|
-
|
|
115
81
|
### A Loading Component
|
|
116
|
-
|
|
82
|
+
|
|
83
|
+
```jsx live
|
|
117
84
|
/** @jsx createElement */
|
|
118
85
|
import {createElement, Fragment} from "@b9g/crank";
|
|
119
86
|
import {renderer} from "@b9g/crank/dom";
|
|
@@ -167,5 +134,3 @@ function *RandomDogApp() {
|
|
|
167
134
|
|
|
168
135
|
renderer.render(<RandomDogApp />, document.body);
|
|
169
136
|
```
|
|
170
|
-
|
|
171
|
-
[Try on CodeSandbox](https://codesandbox.io/s/a-loading-crank-component-pci9d)
|
package/crank.cjs
CHANGED
|
@@ -108,6 +108,7 @@ class Element {
|
|
|
108
108
|
this.static_ = static_;
|
|
109
109
|
}
|
|
110
110
|
}
|
|
111
|
+
// See Element interface
|
|
111
112
|
Element.prototype.$$typeof = ElementSymbol;
|
|
112
113
|
function isElement(value) {
|
|
113
114
|
return value != null && value.$$typeof === ElementSymbol;
|
|
@@ -130,6 +131,7 @@ function createElement(tag, props, ...children) {
|
|
|
130
131
|
switch (name) {
|
|
131
132
|
case "crank-key":
|
|
132
133
|
case "c-key":
|
|
134
|
+
case "$key":
|
|
133
135
|
// We have to make sure we don’t assign null to the key because we
|
|
134
136
|
// don’t check for null keys in the diffing functions.
|
|
135
137
|
if (props[name] != null) {
|
|
@@ -138,12 +140,14 @@ function createElement(tag, props, ...children) {
|
|
|
138
140
|
break;
|
|
139
141
|
case "crank-ref":
|
|
140
142
|
case "c-ref":
|
|
143
|
+
case "$ref":
|
|
141
144
|
if (typeof props[name] === "function") {
|
|
142
145
|
ref = props[name];
|
|
143
146
|
}
|
|
144
147
|
break;
|
|
145
148
|
case "crank-static":
|
|
146
149
|
case "c-static":
|
|
150
|
+
case "$static":
|
|
147
151
|
static_ = !!props[name];
|
|
148
152
|
break;
|
|
149
153
|
default:
|
|
@@ -157,11 +161,27 @@ function createElement(tag, props, ...children) {
|
|
|
157
161
|
else if (children.length === 1) {
|
|
158
162
|
props1.children = children[0];
|
|
159
163
|
}
|
|
164
|
+
// string aliases for the special tags
|
|
165
|
+
// TODO: Does this logic belong here, or in the Element constructor
|
|
166
|
+
switch (tag) {
|
|
167
|
+
case "$FRAGMENT":
|
|
168
|
+
tag = Fragment;
|
|
169
|
+
break;
|
|
170
|
+
case "$PORTAL":
|
|
171
|
+
tag = Portal;
|
|
172
|
+
break;
|
|
173
|
+
case "$COPY":
|
|
174
|
+
tag = Copy;
|
|
175
|
+
break;
|
|
176
|
+
case "$RAW":
|
|
177
|
+
tag = Raw;
|
|
178
|
+
break;
|
|
179
|
+
}
|
|
160
180
|
return new Element(tag, props1, key, ref, static_);
|
|
161
181
|
}
|
|
162
|
-
/**
|
|
163
|
-
|
|
164
|
-
*/
|
|
182
|
+
/** A single-letter alias for createElement */
|
|
183
|
+
const c = createElement;
|
|
184
|
+
/** Clones a given element, shallowly copying the props object. */
|
|
165
185
|
function cloneElement(el) {
|
|
166
186
|
if (!isElement(el)) {
|
|
167
187
|
throw new TypeError("Cannot clone non-element");
|
|
@@ -735,9 +755,9 @@ const IsDone = 1 << 4;
|
|
|
735
755
|
*
|
|
736
756
|
* NOTE: This is mainly used to prevent some false positives in component
|
|
737
757
|
* yields or returns undefined warnings. The reason we’re using this versus
|
|
738
|
-
* IsUnmounted is a very troubling
|
|
739
|
-
*
|
|
740
|
-
*
|
|
758
|
+
* IsUnmounted is a very troubling test (cascades sync generator parent and
|
|
759
|
+
* sync generator child) where synchronous code causes a stack overflow error
|
|
760
|
+
* in a non-deterministic way. Deeply disturbing stuff.
|
|
741
761
|
*/
|
|
742
762
|
const IsErrored = 1 << 5;
|
|
743
763
|
/**
|
|
@@ -958,13 +978,170 @@ class Context {
|
|
|
958
978
|
provisions.set(key, value);
|
|
959
979
|
}
|
|
960
980
|
addEventListener(type, listener, options) {
|
|
961
|
-
|
|
981
|
+
const impl = this[$ContextImpl];
|
|
982
|
+
let listeners;
|
|
983
|
+
if (!isListenerOrListenerObject(listener)) {
|
|
984
|
+
return;
|
|
985
|
+
}
|
|
986
|
+
else {
|
|
987
|
+
const listeners1 = listenersMap.get(impl);
|
|
988
|
+
if (listeners1) {
|
|
989
|
+
listeners = listeners1;
|
|
990
|
+
}
|
|
991
|
+
else {
|
|
992
|
+
listeners = [];
|
|
993
|
+
listenersMap.set(impl, listeners);
|
|
994
|
+
}
|
|
995
|
+
}
|
|
996
|
+
options = normalizeListenerOptions(options);
|
|
997
|
+
let callback;
|
|
998
|
+
if (typeof listener === "object") {
|
|
999
|
+
callback = () => listener.handleEvent.apply(listener, arguments);
|
|
1000
|
+
}
|
|
1001
|
+
else {
|
|
1002
|
+
callback = listener;
|
|
1003
|
+
}
|
|
1004
|
+
const record = { type, callback, listener, options };
|
|
1005
|
+
if (options.once) {
|
|
1006
|
+
record.callback = function () {
|
|
1007
|
+
const i = listeners.indexOf(record);
|
|
1008
|
+
if (i !== -1) {
|
|
1009
|
+
listeners.splice(i, 1);
|
|
1010
|
+
}
|
|
1011
|
+
return callback.apply(this, arguments);
|
|
1012
|
+
};
|
|
1013
|
+
}
|
|
1014
|
+
if (listeners.some((record1) => record.type === record1.type &&
|
|
1015
|
+
record.listener === record1.listener &&
|
|
1016
|
+
!record.options.capture === !record1.options.capture)) {
|
|
1017
|
+
return;
|
|
1018
|
+
}
|
|
1019
|
+
listeners.push(record);
|
|
1020
|
+
// TODO: is it possible to separate out the EventTarget delegation logic
|
|
1021
|
+
for (const value of getChildValues(impl.ret)) {
|
|
1022
|
+
if (isEventTarget(value)) {
|
|
1023
|
+
value.addEventListener(record.type, record.callback, record.options);
|
|
1024
|
+
}
|
|
1025
|
+
}
|
|
962
1026
|
}
|
|
963
1027
|
removeEventListener(type, listener, options) {
|
|
964
|
-
|
|
1028
|
+
const impl = this[$ContextImpl];
|
|
1029
|
+
const listeners = listenersMap.get(impl);
|
|
1030
|
+
if (listeners == null || !isListenerOrListenerObject(listener)) {
|
|
1031
|
+
return;
|
|
1032
|
+
}
|
|
1033
|
+
const options1 = normalizeListenerOptions(options);
|
|
1034
|
+
const i = listeners.findIndex((record) => record.type === type &&
|
|
1035
|
+
record.listener === listener &&
|
|
1036
|
+
!record.options.capture === !options1.capture);
|
|
1037
|
+
if (i === -1) {
|
|
1038
|
+
return;
|
|
1039
|
+
}
|
|
1040
|
+
const record = listeners[i];
|
|
1041
|
+
listeners.splice(i, 1);
|
|
1042
|
+
// TODO: is it possible to separate out the EventTarget delegation logic
|
|
1043
|
+
for (const value of getChildValues(impl.ret)) {
|
|
1044
|
+
if (isEventTarget(value)) {
|
|
1045
|
+
value.removeEventListener(record.type, record.callback, record.options);
|
|
1046
|
+
}
|
|
1047
|
+
}
|
|
965
1048
|
}
|
|
966
1049
|
dispatchEvent(ev) {
|
|
967
|
-
|
|
1050
|
+
const impl = this[$ContextImpl];
|
|
1051
|
+
const path = [];
|
|
1052
|
+
for (let parent = impl.parent; parent !== undefined; parent = parent.parent) {
|
|
1053
|
+
path.push(parent);
|
|
1054
|
+
}
|
|
1055
|
+
// We patch the stopImmediatePropagation method because ev.cancelBubble
|
|
1056
|
+
// only informs us if stopPropagation was called and there are no
|
|
1057
|
+
// properties which inform us if stopImmediatePropagation was called.
|
|
1058
|
+
let immediateCancelBubble = false;
|
|
1059
|
+
const stopImmediatePropagation = ev.stopImmediatePropagation;
|
|
1060
|
+
setEventProperty(ev, "stopImmediatePropagation", () => {
|
|
1061
|
+
immediateCancelBubble = true;
|
|
1062
|
+
return stopImmediatePropagation.call(ev);
|
|
1063
|
+
});
|
|
1064
|
+
setEventProperty(ev, "target", impl.ctx);
|
|
1065
|
+
// The only possible errors in this block are errors thrown by callbacks,
|
|
1066
|
+
// and dispatchEvent will only log these errors rather than throwing
|
|
1067
|
+
// them. Therefore, we place all code in a try block, log errors in the
|
|
1068
|
+
// catch block, and use an unsafe return statement in the finally block.
|
|
1069
|
+
//
|
|
1070
|
+
// Each early return within the try block returns true because while the
|
|
1071
|
+
// return value is overridden in the finally block, TypeScript
|
|
1072
|
+
// (justifiably) does not recognize the unsafe return statement.
|
|
1073
|
+
//
|
|
1074
|
+
// TODO: Run all callbacks even if one of them errors
|
|
1075
|
+
try {
|
|
1076
|
+
setEventProperty(ev, "eventPhase", CAPTURING_PHASE);
|
|
1077
|
+
for (let i = path.length - 1; i >= 0; i--) {
|
|
1078
|
+
const target = path[i];
|
|
1079
|
+
const listeners = listenersMap.get(target);
|
|
1080
|
+
if (listeners) {
|
|
1081
|
+
setEventProperty(ev, "currentTarget", target.ctx);
|
|
1082
|
+
for (const record of listeners) {
|
|
1083
|
+
if (record.type === ev.type && record.options.capture) {
|
|
1084
|
+
record.callback.call(target.ctx, ev);
|
|
1085
|
+
if (immediateCancelBubble) {
|
|
1086
|
+
return true;
|
|
1087
|
+
}
|
|
1088
|
+
}
|
|
1089
|
+
}
|
|
1090
|
+
}
|
|
1091
|
+
if (ev.cancelBubble) {
|
|
1092
|
+
return true;
|
|
1093
|
+
}
|
|
1094
|
+
}
|
|
1095
|
+
{
|
|
1096
|
+
const listeners = listenersMap.get(impl);
|
|
1097
|
+
if (listeners) {
|
|
1098
|
+
setEventProperty(ev, "eventPhase", AT_TARGET);
|
|
1099
|
+
setEventProperty(ev, "currentTarget", impl.ctx);
|
|
1100
|
+
for (const record of listeners) {
|
|
1101
|
+
if (record.type === ev.type) {
|
|
1102
|
+
record.callback.call(impl.ctx, ev);
|
|
1103
|
+
if (immediateCancelBubble) {
|
|
1104
|
+
return true;
|
|
1105
|
+
}
|
|
1106
|
+
}
|
|
1107
|
+
}
|
|
1108
|
+
if (ev.cancelBubble) {
|
|
1109
|
+
return true;
|
|
1110
|
+
}
|
|
1111
|
+
}
|
|
1112
|
+
}
|
|
1113
|
+
if (ev.bubbles) {
|
|
1114
|
+
setEventProperty(ev, "eventPhase", BUBBLING_PHASE);
|
|
1115
|
+
for (let i = 0; i < path.length; i++) {
|
|
1116
|
+
const target = path[i];
|
|
1117
|
+
const listeners = listenersMap.get(target);
|
|
1118
|
+
if (listeners) {
|
|
1119
|
+
setEventProperty(ev, "currentTarget", target.ctx);
|
|
1120
|
+
for (const record of listeners) {
|
|
1121
|
+
if (record.type === ev.type && !record.options.capture) {
|
|
1122
|
+
record.callback.call(target.ctx, ev);
|
|
1123
|
+
if (immediateCancelBubble) {
|
|
1124
|
+
return true;
|
|
1125
|
+
}
|
|
1126
|
+
}
|
|
1127
|
+
}
|
|
1128
|
+
}
|
|
1129
|
+
if (ev.cancelBubble) {
|
|
1130
|
+
return true;
|
|
1131
|
+
}
|
|
1132
|
+
}
|
|
1133
|
+
}
|
|
1134
|
+
}
|
|
1135
|
+
catch (err) {
|
|
1136
|
+
// TODO: Use setTimeout to rethrow the error.
|
|
1137
|
+
console.error(err);
|
|
1138
|
+
}
|
|
1139
|
+
finally {
|
|
1140
|
+
setEventProperty(ev, "eventPhase", NONE);
|
|
1141
|
+
setEventProperty(ev, "currentTarget", null);
|
|
1142
|
+
// eslint-disable-next-line no-unsafe-finally
|
|
1143
|
+
return !ev.defaultPrevented;
|
|
1144
|
+
}
|
|
968
1145
|
}
|
|
969
1146
|
}
|
|
970
1147
|
/*** PRIVATE CONTEXT FUNCTIONS ***/
|
|
@@ -1365,168 +1542,11 @@ const CAPTURING_PHASE = 1;
|
|
|
1365
1542
|
const AT_TARGET = 2;
|
|
1366
1543
|
const BUBBLING_PHASE = 3;
|
|
1367
1544
|
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
|
-
}
|
|
1545
|
+
function isListenerOrListenerObject(value) {
|
|
1546
|
+
return (typeof value === "function" ||
|
|
1547
|
+
(value !== null &&
|
|
1548
|
+
typeof value === "object" &&
|
|
1549
|
+
typeof value.handleEvent === "function"));
|
|
1530
1550
|
}
|
|
1531
1551
|
function normalizeListenerOptions(options) {
|
|
1532
1552
|
if (typeof options === "boolean") {
|
|
@@ -1645,6 +1665,7 @@ exports.Fragment = Fragment;
|
|
|
1645
1665
|
exports.Portal = Portal;
|
|
1646
1666
|
exports.Raw = Raw;
|
|
1647
1667
|
exports.Renderer = Renderer;
|
|
1668
|
+
exports.c = c;
|
|
1648
1669
|
exports.cloneElement = cloneElement;
|
|
1649
1670
|
exports.createElement = createElement;
|
|
1650
1671
|
exports["default"] = crank;
|