tutuca 0.9.65 → 0.9.67
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/dist/tutuca-cli.js +144 -89
- package/dist/tutuca-dev.ext.js +11601 -0
- package/dist/tutuca-dev.js +136 -86
- package/dist/tutuca-dev.min.js +2 -2
- package/dist/tutuca-extra.ext.js +4190 -0
- package/dist/tutuca-extra.js +113 -86
- package/dist/tutuca-extra.min.js +2 -2
- package/dist/tutuca.ext.js +3828 -0
- package/dist/tutuca.js +113 -86
- package/dist/tutuca.min.js +2 -2
- package/package.json +17 -2
- package/skill/tutuca/core.md +10 -2
- package/skill/tutuca/testing.md +3 -2
- package/skill/tutuca-source/SKILL.md +33 -0
- package/skill/tutuca-source/tutuca.ext.js +3828 -0
- package/skill/SKILL.md +0 -46
- package/skill/advanced.md +0 -146
- package/skill/cli.md +0 -117
- package/skill/core.md +0 -793
|
@@ -0,0 +1,3828 @@
|
|
|
1
|
+
// src/value.js
|
|
2
|
+
import { is } from "immutable";
|
|
3
|
+
|
|
4
|
+
// src/path.js
|
|
5
|
+
var NONE = Symbol("NONE");
|
|
6
|
+
|
|
7
|
+
class Step {
|
|
8
|
+
lookup(_v, dval = null) {
|
|
9
|
+
return dval;
|
|
10
|
+
}
|
|
11
|
+
setValue(root, _v) {
|
|
12
|
+
return root;
|
|
13
|
+
}
|
|
14
|
+
enterFrame(stack, _prev, next) {
|
|
15
|
+
return stack.enter(next, {}, true);
|
|
16
|
+
}
|
|
17
|
+
toAbstractPathStep() {
|
|
18
|
+
return this;
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
class BindStep extends Step {
|
|
23
|
+
constructor(binds) {
|
|
24
|
+
super();
|
|
25
|
+
this.binds = binds;
|
|
26
|
+
}
|
|
27
|
+
lookup(v, _dval) {
|
|
28
|
+
return v;
|
|
29
|
+
}
|
|
30
|
+
setValue(_root, v) {
|
|
31
|
+
return v;
|
|
32
|
+
}
|
|
33
|
+
enterFrame(stack, _prev, next) {
|
|
34
|
+
return stack.enter(next, { ...this.binds }, false);
|
|
35
|
+
}
|
|
36
|
+
withIndex(i) {
|
|
37
|
+
return new BindStep({ ...this.binds, key: i });
|
|
38
|
+
}
|
|
39
|
+
withKey(key) {
|
|
40
|
+
return new BindStep({ ...this.binds, key });
|
|
41
|
+
}
|
|
42
|
+
toAbstractPathStep() {
|
|
43
|
+
return null;
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
class FieldStep extends Step {
|
|
48
|
+
constructor(field) {
|
|
49
|
+
super();
|
|
50
|
+
this.field = field;
|
|
51
|
+
}
|
|
52
|
+
lookup(v, dval = null) {
|
|
53
|
+
return v?.get ? v.get(this.field, dval) : dval;
|
|
54
|
+
}
|
|
55
|
+
setValue(root, v) {
|
|
56
|
+
return root.set(this.field, v);
|
|
57
|
+
}
|
|
58
|
+
withIndex(i) {
|
|
59
|
+
return new SeqStep(this.field, i);
|
|
60
|
+
}
|
|
61
|
+
withKey(k) {
|
|
62
|
+
return new SeqStep(this.field, k);
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
class SeqStep extends Step {
|
|
67
|
+
constructor(field, key) {
|
|
68
|
+
super();
|
|
69
|
+
this.field = field;
|
|
70
|
+
this.key = key;
|
|
71
|
+
}
|
|
72
|
+
lookup(v, dval = null) {
|
|
73
|
+
const o = v?.get(this.field, null);
|
|
74
|
+
return o?.get ? o.get(this.key, dval) : dval;
|
|
75
|
+
}
|
|
76
|
+
setValue(root, v) {
|
|
77
|
+
const seq = root?.get(this.field, null);
|
|
78
|
+
return seq ? root.set(this.field, seq.set(this.key, v)) : root;
|
|
79
|
+
}
|
|
80
|
+
enterFrame(stack, _prev, next) {
|
|
81
|
+
return stack.enter(next, { key: this.key }, true);
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
class SeqAccessStep extends Step {
|
|
86
|
+
constructor(seqField, keyField) {
|
|
87
|
+
super();
|
|
88
|
+
this.seqField = seqField;
|
|
89
|
+
this.keyField = keyField;
|
|
90
|
+
}
|
|
91
|
+
lookup(v, dval = null) {
|
|
92
|
+
const seq = v?.get(this.seqField, NONE);
|
|
93
|
+
const key = v?.get(this.keyField, NONE);
|
|
94
|
+
return key !== NONE && seq?.get ? seq.get(key, dval) : dval;
|
|
95
|
+
}
|
|
96
|
+
setValue(root, v) {
|
|
97
|
+
const seq = root?.get(this.seqField, NONE);
|
|
98
|
+
const key = root?.get(this.keyField, NONE);
|
|
99
|
+
return seq === NONE || key === NONE ? root : root.set(this.seqField, seq.set(key, v));
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
class EachBindStep extends Step {
|
|
104
|
+
constructor(seqVal, key) {
|
|
105
|
+
super();
|
|
106
|
+
this.seqVal = seqVal;
|
|
107
|
+
this.key = key;
|
|
108
|
+
}
|
|
109
|
+
lookup(v, _dval) {
|
|
110
|
+
return v;
|
|
111
|
+
}
|
|
112
|
+
setValue(_root, v) {
|
|
113
|
+
return v;
|
|
114
|
+
}
|
|
115
|
+
enterFrame(stack, _prev, next) {
|
|
116
|
+
const item = this.seqVal.eval(stack)?.get(this.key, null);
|
|
117
|
+
return stack.enter(next, { key: this.key, value: item }, false);
|
|
118
|
+
}
|
|
119
|
+
toAbstractPathStep() {
|
|
120
|
+
return null;
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
class EachRenderItStep extends SeqStep {
|
|
125
|
+
enterFrame(stack, _prev, next) {
|
|
126
|
+
return stack.enter(next, { key: this.key, value: next }, false).enter(next, {}, true);
|
|
127
|
+
}
|
|
128
|
+
toAbstractPathStep() {
|
|
129
|
+
return new SeqStep(this.field, this.key);
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
function warnRawDynStep(op, step) {
|
|
133
|
+
console.warn(`Path.${op} reached a DynStep: call toTransactionPath() first`, step);
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
class DynStep extends Step {
|
|
137
|
+
constructor(producerCompId, producerSteps) {
|
|
138
|
+
super();
|
|
139
|
+
this.producerCompId = producerCompId;
|
|
140
|
+
this.producerSteps = producerSteps;
|
|
141
|
+
this.interiorCids = new Set;
|
|
142
|
+
}
|
|
143
|
+
teleportSteps() {
|
|
144
|
+
return this.producerSteps;
|
|
145
|
+
}
|
|
146
|
+
lookup(_v, dval = null) {
|
|
147
|
+
warnRawDynStep("lookup", this);
|
|
148
|
+
return dval;
|
|
149
|
+
}
|
|
150
|
+
setValue(root, _v) {
|
|
151
|
+
warnRawDynStep("setValue", this);
|
|
152
|
+
return root;
|
|
153
|
+
}
|
|
154
|
+
enterFrame(stack, _prev, _next) {
|
|
155
|
+
warnRawDynStep("enterFrame", this);
|
|
156
|
+
return stack;
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
class DynEachStep extends DynStep {
|
|
161
|
+
constructor(producerCompId, producerSteps, key) {
|
|
162
|
+
super(producerCompId, producerSteps);
|
|
163
|
+
this.key = key;
|
|
164
|
+
}
|
|
165
|
+
teleportSteps() {
|
|
166
|
+
const { producerSteps, key } = this;
|
|
167
|
+
if (producerSteps.length === 0)
|
|
168
|
+
return producerSteps;
|
|
169
|
+
const last = producerSteps[producerSteps.length - 1];
|
|
170
|
+
if (!(last instanceof FieldStep)) {
|
|
171
|
+
console.warn("DynEachStep: seq-access dynamic cannot be iterated", this);
|
|
172
|
+
return producerSteps;
|
|
173
|
+
}
|
|
174
|
+
return producerSteps.slice(0, -1).concat(new SeqStep(last.field, key));
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
class Path {
|
|
179
|
+
constructor(steps = []) {
|
|
180
|
+
this.steps = steps;
|
|
181
|
+
}
|
|
182
|
+
concat(steps) {
|
|
183
|
+
return new Path(this.steps.concat(steps));
|
|
184
|
+
}
|
|
185
|
+
popStep() {
|
|
186
|
+
return new Path(this.steps.slice(0, -1));
|
|
187
|
+
}
|
|
188
|
+
compact() {
|
|
189
|
+
const out = [];
|
|
190
|
+
for (const step of this.steps) {
|
|
191
|
+
const s = step.toAbstractPathStep();
|
|
192
|
+
if (s !== null) {
|
|
193
|
+
if (s !== step)
|
|
194
|
+
s._originCid = step._originCid;
|
|
195
|
+
out.push(s);
|
|
196
|
+
}
|
|
197
|
+
}
|
|
198
|
+
return new Path(out);
|
|
199
|
+
}
|
|
200
|
+
toTransactionPath() {
|
|
201
|
+
let hasDyn = false;
|
|
202
|
+
for (const step of this.steps)
|
|
203
|
+
if (step instanceof DynStep) {
|
|
204
|
+
hasDyn = true;
|
|
205
|
+
break;
|
|
206
|
+
}
|
|
207
|
+
if (!hasDyn)
|
|
208
|
+
return this;
|
|
209
|
+
const out = [];
|
|
210
|
+
for (const step of this.steps) {
|
|
211
|
+
if (step instanceof DynStep) {
|
|
212
|
+
while (out.length > 0 && step.interiorCids.has(out[out.length - 1]._originCid))
|
|
213
|
+
out.pop();
|
|
214
|
+
for (const ts of step.teleportSteps()) {
|
|
215
|
+
ts._originCid = step.producerCompId;
|
|
216
|
+
out.push(ts);
|
|
217
|
+
}
|
|
218
|
+
} else
|
|
219
|
+
out.push(step);
|
|
220
|
+
}
|
|
221
|
+
return new Path(out);
|
|
222
|
+
}
|
|
223
|
+
lookup(v, dval = null) {
|
|
224
|
+
let curVal = v;
|
|
225
|
+
for (const step of this.steps) {
|
|
226
|
+
curVal = step.lookup(curVal, NONE);
|
|
227
|
+
if (curVal === NONE)
|
|
228
|
+
return dval;
|
|
229
|
+
}
|
|
230
|
+
return curVal;
|
|
231
|
+
}
|
|
232
|
+
setValue(root, v) {
|
|
233
|
+
const intermediates = new Array(this.steps.length);
|
|
234
|
+
let curVal = root;
|
|
235
|
+
for (let i = 0;i < this.steps.length; i++) {
|
|
236
|
+
intermediates[i] = curVal;
|
|
237
|
+
curVal = this.steps[i].lookup(curVal, NONE);
|
|
238
|
+
if (curVal === NONE)
|
|
239
|
+
return root;
|
|
240
|
+
}
|
|
241
|
+
let newVal = v;
|
|
242
|
+
for (let i = this.steps.length - 1;i >= 0; i--) {
|
|
243
|
+
newVal = this.steps[i].setValue(intermediates[i], newVal);
|
|
244
|
+
intermediates[i] = newVal;
|
|
245
|
+
}
|
|
246
|
+
return newVal;
|
|
247
|
+
}
|
|
248
|
+
buildStack(stack) {
|
|
249
|
+
let prev = stack.it;
|
|
250
|
+
for (const step of this.steps) {
|
|
251
|
+
const next = step.lookup(prev, NONE);
|
|
252
|
+
if (next === NONE) {
|
|
253
|
+
console.warn("bad PathItem", { root: stack.it, step, path: this });
|
|
254
|
+
return null;
|
|
255
|
+
}
|
|
256
|
+
stack = step.enterFrame(stack, prev, next);
|
|
257
|
+
prev = next;
|
|
258
|
+
}
|
|
259
|
+
return stack;
|
|
260
|
+
}
|
|
261
|
+
static fromNodeAndEventName(node, eventName, rootNode, maxDepth, comps, stopOnNoEvent = true) {
|
|
262
|
+
const pathSteps = [];
|
|
263
|
+
const pendingDyns = [];
|
|
264
|
+
const bubbles = BUBBLING_EVENTS.has(eventName);
|
|
265
|
+
let depth = 0;
|
|
266
|
+
let eventIds = [];
|
|
267
|
+
let handlers = null;
|
|
268
|
+
let nodeIds = [];
|
|
269
|
+
let isLeafComponent = true;
|
|
270
|
+
const crossComponent = (cidNum, vid) => {
|
|
271
|
+
const comp = comps.getComponentForId(cidNum);
|
|
272
|
+
let pushStep = true;
|
|
273
|
+
if (handlers === null && (isLeafComponent || bubbles)) {
|
|
274
|
+
handlers = findHandlers(comp, eventIds, vid, eventName);
|
|
275
|
+
if (handlers === null) {
|
|
276
|
+
if (isLeafComponent && stopOnNoEvent && !bubbles)
|
|
277
|
+
return false;
|
|
278
|
+
} else if (!isLeafComponent) {
|
|
279
|
+
pathSteps.length = 0;
|
|
280
|
+
pendingDyns.length = 0;
|
|
281
|
+
pushStep = false;
|
|
282
|
+
}
|
|
283
|
+
}
|
|
284
|
+
isLeafComponent = false;
|
|
285
|
+
for (const dyn of pendingDyns)
|
|
286
|
+
dyn.interiorCids.add(cidNum);
|
|
287
|
+
if (pushStep) {
|
|
288
|
+
const step = resolvePathStep(comp, nodeIds, vid);
|
|
289
|
+
if (step) {
|
|
290
|
+
step._originCid = cidNum;
|
|
291
|
+
pathSteps.push(step);
|
|
292
|
+
if (step instanceof DynStep) {
|
|
293
|
+
step.interiorCids.add(cidNum);
|
|
294
|
+
pendingDyns.push(step);
|
|
295
|
+
}
|
|
296
|
+
}
|
|
297
|
+
}
|
|
298
|
+
for (let i = pendingDyns.length - 1;i >= 0; i--)
|
|
299
|
+
if (pendingDyns[i].producerCompId === cidNum)
|
|
300
|
+
pendingDyns.splice(i, 1);
|
|
301
|
+
eventIds = [];
|
|
302
|
+
nodeIds = [];
|
|
303
|
+
return true;
|
|
304
|
+
};
|
|
305
|
+
while (node && node !== rootNode && depth < maxDepth) {
|
|
306
|
+
if (node?.dataset) {
|
|
307
|
+
const { eid, cid, vid } = node.dataset;
|
|
308
|
+
if (eid !== undefined)
|
|
309
|
+
eventIds.push(eid);
|
|
310
|
+
const metas = metaChain(node.previousSibling);
|
|
311
|
+
let sawComp = false;
|
|
312
|
+
for (let i = 0;i < metas.length; i++) {
|
|
313
|
+
const m = metas[i];
|
|
314
|
+
if (m.$ === "Comp") {
|
|
315
|
+
sawComp = true;
|
|
316
|
+
if (!crossComponent(m.cid, m.vid))
|
|
317
|
+
return NO_EVENT_INFO;
|
|
318
|
+
const outer = metas[i + 1];
|
|
319
|
+
if (outer?.$ === "Each" && outer.nid === m.nid) {
|
|
320
|
+
nodeIds.push({ nid: outer.nid, si: outer.si, sk: outer.sk });
|
|
321
|
+
i += 1;
|
|
322
|
+
} else {
|
|
323
|
+
nodeIds.push({ nid: m.nid });
|
|
324
|
+
}
|
|
325
|
+
} else {
|
|
326
|
+
nodeIds.push({ nid: m.nid, si: m.si, sk: m.sk });
|
|
327
|
+
}
|
|
328
|
+
}
|
|
329
|
+
if (!sawComp && cid !== undefined && !crossComponent(+cid, vid))
|
|
330
|
+
return NO_EVENT_INFO;
|
|
331
|
+
}
|
|
332
|
+
depth += 1;
|
|
333
|
+
node = node.parentNode;
|
|
334
|
+
}
|
|
335
|
+
if (pendingDyns.length > 0)
|
|
336
|
+
console.warn("event reconstruction: dynamic-var producer not found", pendingDyns);
|
|
337
|
+
return [new Path(pathSteps.reverse()), handlers];
|
|
338
|
+
}
|
|
339
|
+
static fromEvent(e, rNode, maxDepth, comps, stopOnNoEvent = true) {
|
|
340
|
+
const { type, target } = e;
|
|
341
|
+
return Path.fromNodeAndEventName(target, type, rNode, maxDepth, comps, stopOnNoEvent);
|
|
342
|
+
}
|
|
343
|
+
}
|
|
344
|
+
function metaChain(n) {
|
|
345
|
+
const out = [];
|
|
346
|
+
while (n?.nodeType === 8 && n.textContent[0] === "§") {
|
|
347
|
+
try {
|
|
348
|
+
out.push(JSON.parse(n.textContent.slice(1, -1)));
|
|
349
|
+
} catch (err) {
|
|
350
|
+
console.warn(err, n);
|
|
351
|
+
}
|
|
352
|
+
n = n.previousSibling;
|
|
353
|
+
}
|
|
354
|
+
return out;
|
|
355
|
+
}
|
|
356
|
+
function findHandlers(comp, eventIds, vid, eventName) {
|
|
357
|
+
for (const eid of eventIds) {
|
|
358
|
+
const handlers = comp.getEventForId(+eid, vid).getHandlersFor(eventName);
|
|
359
|
+
if (handlers !== null)
|
|
360
|
+
return handlers;
|
|
361
|
+
}
|
|
362
|
+
return null;
|
|
363
|
+
}
|
|
364
|
+
|
|
365
|
+
class StepCtx {
|
|
366
|
+
constructor(comp, nodeIds, idx, vid) {
|
|
367
|
+
this.comp = comp;
|
|
368
|
+
this.nodeIds = nodeIds;
|
|
369
|
+
this.idx = idx;
|
|
370
|
+
this.vid = vid;
|
|
371
|
+
}
|
|
372
|
+
get meta() {
|
|
373
|
+
return this.nodeIds[this.idx];
|
|
374
|
+
}
|
|
375
|
+
get key() {
|
|
376
|
+
const m = this.meta;
|
|
377
|
+
return m.si !== undefined ? +m.si : m.sk;
|
|
378
|
+
}
|
|
379
|
+
get hasKey() {
|
|
380
|
+
const m = this.meta;
|
|
381
|
+
return m.si !== undefined || m.sk !== undefined;
|
|
382
|
+
}
|
|
383
|
+
next() {
|
|
384
|
+
const { idx, nodeIds } = this;
|
|
385
|
+
return idx + 1 < nodeIds.length ? new StepCtx(this.comp, nodeIds, idx + 1, this.vid) : null;
|
|
386
|
+
}
|
|
387
|
+
resolveNode() {
|
|
388
|
+
return this.comp.getNodeForId(+this.meta.nid, this.vid);
|
|
389
|
+
}
|
|
390
|
+
applyKey(pi) {
|
|
391
|
+
if (pi === null)
|
|
392
|
+
return null;
|
|
393
|
+
const m = this.meta;
|
|
394
|
+
if (m.si !== undefined)
|
|
395
|
+
return pi.withIndex(+m.si);
|
|
396
|
+
if (m.sk !== undefined)
|
|
397
|
+
return pi.withKey(m.sk);
|
|
398
|
+
return pi;
|
|
399
|
+
}
|
|
400
|
+
}
|
|
401
|
+
function resolvePathStep(comp, nodeIds, vid) {
|
|
402
|
+
for (let i = 0;i < nodeIds.length; i++) {
|
|
403
|
+
const ctx = new StepCtx(comp, nodeIds, i, vid);
|
|
404
|
+
const step = ctx.resolveNode().toPathStep(ctx);
|
|
405
|
+
if (step !== null)
|
|
406
|
+
return step;
|
|
407
|
+
}
|
|
408
|
+
return null;
|
|
409
|
+
}
|
|
410
|
+
var NO_EVENT_INFO = [null, null];
|
|
411
|
+
var BUBBLING_EVENTS = new Set(["drop"]);
|
|
412
|
+
|
|
413
|
+
class PathBuilder {
|
|
414
|
+
constructor() {
|
|
415
|
+
this.pathChanges = [];
|
|
416
|
+
}
|
|
417
|
+
add(pathChange) {
|
|
418
|
+
this.pathChanges.push(pathChange);
|
|
419
|
+
return this;
|
|
420
|
+
}
|
|
421
|
+
field(name) {
|
|
422
|
+
return this.add(new FieldStep(name));
|
|
423
|
+
}
|
|
424
|
+
index(name, index) {
|
|
425
|
+
return this.add(new SeqStep(name, index));
|
|
426
|
+
}
|
|
427
|
+
key(name, key) {
|
|
428
|
+
return this.add(new SeqStep(name, key));
|
|
429
|
+
}
|
|
430
|
+
}
|
|
431
|
+
|
|
432
|
+
// src/value.js
|
|
433
|
+
var VALID_VAL_ID_RE = /^[a-zA-Z][a-zA-Z0-9_]*\??$/;
|
|
434
|
+
var isValidValId = (name) => VALID_VAL_ID_RE.test(name);
|
|
435
|
+
var VALID_FLOAT_RE = /^-?[0-9]+(\.[0-9]+)?$/;
|
|
436
|
+
var STR_TPL_SPLIT_RE = /(\{[^}]+\})/g;
|
|
437
|
+
var mkVal = (name, Cls) => isValidValId(name) ? new Cls(name) : null;
|
|
438
|
+
var VAL_TOKEN_RE = /\$'(?:[^'\\]|\\.)*'|'(?:[^'\\]|\\.)*'|\S+/g;
|
|
439
|
+
var tokenizeValue = (s) => s.match(VAL_TOKEN_RE) ?? [];
|
|
440
|
+
var unescapeStr = (s) => s.replace(/\\(['\\])/g, "$1");
|
|
441
|
+
var K_CONST = 1;
|
|
442
|
+
var K_STRTPL = 2;
|
|
443
|
+
var K_FIELD = 4;
|
|
444
|
+
var K_BIND = 8;
|
|
445
|
+
var K_DYN = 16;
|
|
446
|
+
var K_NAME = 32;
|
|
447
|
+
var K_TYPE = 64;
|
|
448
|
+
var K_REQUEST = 128;
|
|
449
|
+
var K_SEQ = 256;
|
|
450
|
+
var K_STR = 512;
|
|
451
|
+
var K_METHOD = 1024;
|
|
452
|
+
var G_BOOL = K_FIELD | K_METHOD | K_BIND | K_DYN | K_CONST;
|
|
453
|
+
var G_TEXT = G_BOOL | K_STRTPL;
|
|
454
|
+
var G_COMPONENT = K_FIELD | K_SEQ | K_DYN;
|
|
455
|
+
var G_SEQUENCE = K_FIELD | K_DYN;
|
|
456
|
+
var G_FIELD = K_FIELD | K_METHOD | K_CONST | K_STR | K_SEQ;
|
|
457
|
+
var G_VALUE = K_FIELD | K_METHOD | K_BIND | K_DYN | K_NAME | K_TYPE | K_REQUEST | K_CONST;
|
|
458
|
+
var G_PRED_ARG = G_BOOL | K_STR;
|
|
459
|
+
var G_HANDLER_ARG = G_VALUE | K_STR;
|
|
460
|
+
var G_ALL = G_VALUE | K_STRTPL | K_SEQ;
|
|
461
|
+
function sizeOf(v) {
|
|
462
|
+
if (v == null)
|
|
463
|
+
return null;
|
|
464
|
+
const s = v.size;
|
|
465
|
+
if (typeof s === "number")
|
|
466
|
+
return s;
|
|
467
|
+
const l = v.length;
|
|
468
|
+
return typeof l === "number" ? l : null;
|
|
469
|
+
}
|
|
470
|
+
var predTruthy = (v) => {
|
|
471
|
+
const n = sizeOf(v);
|
|
472
|
+
return n === null ? !!v : n > 0;
|
|
473
|
+
};
|
|
474
|
+
var PREDICATES = {
|
|
475
|
+
"empty?": { name: "empty?", arity: 1, fn: (v) => v == null || sizeOf(v) === 0 },
|
|
476
|
+
"truthy?": { name: "truthy?", arity: 1, fn: predTruthy },
|
|
477
|
+
"falsy?": { name: "falsy?", arity: 1, fn: (v) => !predTruthy(v) },
|
|
478
|
+
"null?": { name: "null?", arity: 1, fn: (v) => v == null },
|
|
479
|
+
"equals?": { name: "equals?", arity: 2, fn: (a, b) => is(a, b) }
|
|
480
|
+
};
|
|
481
|
+
|
|
482
|
+
class ValParser {
|
|
483
|
+
constructor() {
|
|
484
|
+
this.bindValIt = new BindVal("it");
|
|
485
|
+
this.nullConstVal = new ConstVal(null);
|
|
486
|
+
}
|
|
487
|
+
const(v) {
|
|
488
|
+
return new ConstVal(v);
|
|
489
|
+
}
|
|
490
|
+
parseToken(s, px) {
|
|
491
|
+
const c0 = s.charCodeAt(0);
|
|
492
|
+
if (c0 === 39)
|
|
493
|
+
return s.length >= 2 && s.charCodeAt(s.length - 1) === 39 ? new ConstVal(unescapeStr(s.slice(1, -1)), K_STR | K_STRTPL) : null;
|
|
494
|
+
if (c0 === 36 && s.charCodeAt(1) === 39)
|
|
495
|
+
return s.length >= 3 && s.charCodeAt(s.length - 1) === 39 ? StrTplVal.parse(s.slice(2, -1), px) : null;
|
|
496
|
+
if (s.indexOf("[") !== -1 || s.indexOf("]") !== -1)
|
|
497
|
+
return this._parseSeqAccess(s, px);
|
|
498
|
+
if (s.indexOf("{") !== -1 || s.indexOf("}") !== -1)
|
|
499
|
+
return null;
|
|
500
|
+
switch (c0) {
|
|
501
|
+
case 94: {
|
|
502
|
+
const name = s.slice(1);
|
|
503
|
+
const newS = px.frame.macroVars?.[name];
|
|
504
|
+
if (newS !== undefined) {
|
|
505
|
+
const tokens = tokenizeValue(newS.trim());
|
|
506
|
+
if (tokens.length !== 1)
|
|
507
|
+
return null;
|
|
508
|
+
const val = this.parseToken(tokens[0], px);
|
|
509
|
+
if (val instanceof ConstVal)
|
|
510
|
+
val.fromMacroVar = true;
|
|
511
|
+
return val;
|
|
512
|
+
}
|
|
513
|
+
px.onParseIssue("bad-value", { role: "macro-var", name, value: s });
|
|
514
|
+
return null;
|
|
515
|
+
}
|
|
516
|
+
case 36:
|
|
517
|
+
return mkVal(s.slice(1), MethodVal);
|
|
518
|
+
case 64:
|
|
519
|
+
return mkVal(s.slice(1), BindVal);
|
|
520
|
+
case 42:
|
|
521
|
+
return mkVal(s.slice(1), DynVal);
|
|
522
|
+
case 46:
|
|
523
|
+
return mkVal(s.slice(1), FieldVal);
|
|
524
|
+
case 33:
|
|
525
|
+
return mkVal(s.slice(1), RequestVal);
|
|
526
|
+
}
|
|
527
|
+
const num = VALID_FLOAT_RE.test(s) ? parseFloat(s) : null;
|
|
528
|
+
if (Number.isFinite(num))
|
|
529
|
+
return new ConstVal(num);
|
|
530
|
+
if (s === "true" || s === "false")
|
|
531
|
+
return new ConstVal(s === "true");
|
|
532
|
+
if (c0 >= 97 && c0 <= 122)
|
|
533
|
+
return mkVal(s, NameVal);
|
|
534
|
+
if (c0 >= 65 && c0 <= 90)
|
|
535
|
+
return mkVal(s, TypeVal);
|
|
536
|
+
return null;
|
|
537
|
+
}
|
|
538
|
+
_parseSeqAccess(s, px) {
|
|
539
|
+
const open = s.indexOf("[");
|
|
540
|
+
const close = s.indexOf("]");
|
|
541
|
+
if (open < 1 || close !== s.length - 1 || close < open || s.indexOf("[", open + 1) !== -1)
|
|
542
|
+
return null;
|
|
543
|
+
const left = this.parseToken(s.slice(0, open), px);
|
|
544
|
+
const right = this.parseToken(s.slice(open + 1, close), px);
|
|
545
|
+
return left instanceof FieldVal && right instanceof FieldVal ? new SeqAccessVal(left, right) : null;
|
|
546
|
+
}
|
|
547
|
+
_parseSingle(s, px, group) {
|
|
548
|
+
const tokens = tokenizeValue(s.trim());
|
|
549
|
+
if (tokens.length !== 1)
|
|
550
|
+
return null;
|
|
551
|
+
const val = this.parseToken(tokens[0], px);
|
|
552
|
+
return val !== null && kindOf(val) & group ? val : null;
|
|
553
|
+
}
|
|
554
|
+
parseBool(s, px) {
|
|
555
|
+
const t = s.trim();
|
|
556
|
+
const tokens = tokenizeValue(t);
|
|
557
|
+
if (tokens.length !== 1)
|
|
558
|
+
return tokens.length === 0 ? null : this._parsePredicate(t, tokens, px);
|
|
559
|
+
const val = this.parseToken(tokens[0], px);
|
|
560
|
+
return val !== null && kindOf(val) & G_BOOL ? val : null;
|
|
561
|
+
}
|
|
562
|
+
parseText(s, px) {
|
|
563
|
+
return this._parseSingle(s, px, G_TEXT);
|
|
564
|
+
}
|
|
565
|
+
parseComponent(s, px) {
|
|
566
|
+
return this._parseSingle(s, px, G_COMPONENT);
|
|
567
|
+
}
|
|
568
|
+
parseSequence(s, px) {
|
|
569
|
+
return this._parseSingle(s, px, G_SEQUENCE);
|
|
570
|
+
}
|
|
571
|
+
parseField(s, px) {
|
|
572
|
+
return this._parseSingle(s, px, G_FIELD);
|
|
573
|
+
}
|
|
574
|
+
parseHandlerArg(s, px) {
|
|
575
|
+
return this._parseSingle(s, px, G_HANDLER_ARG);
|
|
576
|
+
}
|
|
577
|
+
parseMacroAttr(s, px) {
|
|
578
|
+
return this._parseSingle(s, px, G_ALL);
|
|
579
|
+
}
|
|
580
|
+
parseInputHandler(s, px) {
|
|
581
|
+
return this._parseHandler(s, px, "input", true, true);
|
|
582
|
+
}
|
|
583
|
+
parseAlterHandler(s, px) {
|
|
584
|
+
const r = this._parseHandler(s, px, "alter", false, false);
|
|
585
|
+
return r === null ? null : r.handlerVal;
|
|
586
|
+
}
|
|
587
|
+
_parseHandler(s, px, namespace, allowArgs, report) {
|
|
588
|
+
const tokens = tokenizeValue(s.trim());
|
|
589
|
+
const headTok = tokens[0] ?? "";
|
|
590
|
+
const head = headTok === "" ? null : this.parseToken(headTok, px);
|
|
591
|
+
const hk = kindOf(head);
|
|
592
|
+
let handlerVal;
|
|
593
|
+
if (hk & K_METHOD)
|
|
594
|
+
handlerVal = head;
|
|
595
|
+
else if (hk & K_NAME)
|
|
596
|
+
handlerVal = new HandlerNameVal(head.name, namespace);
|
|
597
|
+
else {
|
|
598
|
+
if (report)
|
|
599
|
+
px.onParseIssue("bad-value", { role: "handler-name", value: headTok });
|
|
600
|
+
return null;
|
|
601
|
+
}
|
|
602
|
+
if (!allowArgs)
|
|
603
|
+
return tokens.length === 1 ? { handlerVal, args: [] } : null;
|
|
604
|
+
const args = new Array(tokens.length - 1);
|
|
605
|
+
for (let i = 1;i < tokens.length; i++) {
|
|
606
|
+
const val = this.parseToken(tokens[i], px);
|
|
607
|
+
if (val !== null && kindOf(val) & G_HANDLER_ARG)
|
|
608
|
+
args[i - 1] = val;
|
|
609
|
+
else {
|
|
610
|
+
if (report)
|
|
611
|
+
px.onParseIssue("bad-value", { role: "handler-arg", value: tokens[i] });
|
|
612
|
+
args[i - 1] = this.nullConstVal;
|
|
613
|
+
}
|
|
614
|
+
}
|
|
615
|
+
return { handlerVal, args };
|
|
616
|
+
}
|
|
617
|
+
_parsePredicate(s, tokens, px) {
|
|
618
|
+
const predName = tokens[0];
|
|
619
|
+
const pred = PREDICATES[predName];
|
|
620
|
+
if (pred === undefined) {
|
|
621
|
+
px.onParseIssue("bad-value", { role: "predicate", value: predName });
|
|
622
|
+
return null;
|
|
623
|
+
}
|
|
624
|
+
const arity = tokens.length - 1;
|
|
625
|
+
if (arity !== pred.arity) {
|
|
626
|
+
px.onParseIssue("bad-value", { role: "predicate-arity", value: s, predicate: predName });
|
|
627
|
+
return null;
|
|
628
|
+
}
|
|
629
|
+
const args = new Array(arity);
|
|
630
|
+
for (let i = 0;i < arity; i++) {
|
|
631
|
+
const tok = tokens[i + 1];
|
|
632
|
+
const val = this.parseToken(tok, px);
|
|
633
|
+
if (val === null || !(kindOf(val) & G_PRED_ARG)) {
|
|
634
|
+
px.onParseIssue("bad-value", { role: "predicate-arg", value: tok });
|
|
635
|
+
return null;
|
|
636
|
+
}
|
|
637
|
+
args[i] = val;
|
|
638
|
+
}
|
|
639
|
+
return new PredicateVal(pred, args);
|
|
640
|
+
}
|
|
641
|
+
}
|
|
642
|
+
function kindOf(val) {
|
|
643
|
+
if (val === null)
|
|
644
|
+
return 0;
|
|
645
|
+
if (val instanceof ConstVal)
|
|
646
|
+
return val.kind;
|
|
647
|
+
if (val instanceof StrTplVal)
|
|
648
|
+
return K_STRTPL;
|
|
649
|
+
if (val instanceof SeqAccessVal)
|
|
650
|
+
return K_SEQ;
|
|
651
|
+
if (val instanceof FieldVal)
|
|
652
|
+
return K_FIELD;
|
|
653
|
+
if (val instanceof MethodVal)
|
|
654
|
+
return K_METHOD;
|
|
655
|
+
if (val instanceof BindVal)
|
|
656
|
+
return K_BIND;
|
|
657
|
+
if (val instanceof DynVal)
|
|
658
|
+
return K_DYN;
|
|
659
|
+
if (val instanceof RequestVal)
|
|
660
|
+
return K_REQUEST;
|
|
661
|
+
if (val instanceof TypeVal)
|
|
662
|
+
return K_TYPE;
|
|
663
|
+
if (val instanceof NameVal)
|
|
664
|
+
return K_NAME;
|
|
665
|
+
return 0;
|
|
666
|
+
}
|
|
667
|
+
|
|
668
|
+
class BaseVal {
|
|
669
|
+
render(_stack, _rx) {}
|
|
670
|
+
eval(_stack) {}
|
|
671
|
+
toPathItem() {
|
|
672
|
+
return null;
|
|
673
|
+
}
|
|
674
|
+
evalAsHandler(stack) {
|
|
675
|
+
return this.eval(stack);
|
|
676
|
+
}
|
|
677
|
+
}
|
|
678
|
+
|
|
679
|
+
class ConstVal extends BaseVal {
|
|
680
|
+
constructor(val, kind = K_CONST) {
|
|
681
|
+
super();
|
|
682
|
+
this.val = val;
|
|
683
|
+
this.kind = kind;
|
|
684
|
+
}
|
|
685
|
+
render(_stack, _rx) {
|
|
686
|
+
return this.val;
|
|
687
|
+
}
|
|
688
|
+
eval(_stack) {
|
|
689
|
+
return this.val;
|
|
690
|
+
}
|
|
691
|
+
toString() {
|
|
692
|
+
const v = this.val;
|
|
693
|
+
return typeof v === "string" ? `'${v.replace(/(['\\])/g, "\\$1")}'` : `${v}`;
|
|
694
|
+
}
|
|
695
|
+
}
|
|
696
|
+
|
|
697
|
+
class PredicateVal extends BaseVal {
|
|
698
|
+
constructor(pred, args) {
|
|
699
|
+
super();
|
|
700
|
+
this.pred = pred;
|
|
701
|
+
this.args = args;
|
|
702
|
+
}
|
|
703
|
+
eval(stack) {
|
|
704
|
+
const n = this.args.length;
|
|
705
|
+
const vals = new Array(n);
|
|
706
|
+
for (let i = 0;i < n; i++)
|
|
707
|
+
vals[i] = this.args[i].eval(stack);
|
|
708
|
+
return this.pred.fn(...vals);
|
|
709
|
+
}
|
|
710
|
+
toString() {
|
|
711
|
+
return `${this.pred.name} ${this.args.map(String).join(" ")}`;
|
|
712
|
+
}
|
|
713
|
+
}
|
|
714
|
+
|
|
715
|
+
class VarVal extends BaseVal {
|
|
716
|
+
}
|
|
717
|
+
|
|
718
|
+
class StrTplVal extends VarVal {
|
|
719
|
+
constructor(vals) {
|
|
720
|
+
super();
|
|
721
|
+
this.vals = vals;
|
|
722
|
+
}
|
|
723
|
+
render(stack, _rx) {
|
|
724
|
+
return this.eval(stack);
|
|
725
|
+
}
|
|
726
|
+
eval(stack) {
|
|
727
|
+
const strs = new Array(this.vals.length);
|
|
728
|
+
for (let i = 0;i < this.vals.length; i++)
|
|
729
|
+
strs[i] = this.vals[i]?.eval(stack, "");
|
|
730
|
+
return strs.join("");
|
|
731
|
+
}
|
|
732
|
+
toLiteralSource() {
|
|
733
|
+
let out = "";
|
|
734
|
+
for (const v of this.vals) {
|
|
735
|
+
if (!(v instanceof ConstVal) || v.fromMacroVar)
|
|
736
|
+
return null;
|
|
737
|
+
out += v.val;
|
|
738
|
+
}
|
|
739
|
+
return new ConstVal(out).toString();
|
|
740
|
+
}
|
|
741
|
+
static parse(s, px) {
|
|
742
|
+
const parts = unescapeStr(s).split(STR_TPL_SPLIT_RE);
|
|
743
|
+
const vals = new Array(parts.length);
|
|
744
|
+
for (let i = 0;i < parts.length; i++) {
|
|
745
|
+
const part = parts[i];
|
|
746
|
+
const isExpr = part[0] === "{" && part.at(-1) === "}";
|
|
747
|
+
vals[i] = isExpr ? vp.parseText(part.slice(1, -1), px) : new ConstVal(part);
|
|
748
|
+
}
|
|
749
|
+
let lo = 0;
|
|
750
|
+
let hi = vals.length;
|
|
751
|
+
const isTrimmable = (v) => v instanceof ConstVal && v.val === "" && !v.fromMacroVar;
|
|
752
|
+
while (lo < hi && isTrimmable(vals[lo]))
|
|
753
|
+
lo++;
|
|
754
|
+
while (hi > lo && isTrimmable(vals[hi - 1]))
|
|
755
|
+
hi--;
|
|
756
|
+
return new StrTplVal(lo === 0 && hi === vals.length ? vals : vals.slice(lo, hi));
|
|
757
|
+
}
|
|
758
|
+
}
|
|
759
|
+
|
|
760
|
+
class NameVal extends VarVal {
|
|
761
|
+
constructor(name) {
|
|
762
|
+
super();
|
|
763
|
+
this.name = name;
|
|
764
|
+
}
|
|
765
|
+
eval(stack) {
|
|
766
|
+
return stack.lookupName(this.name);
|
|
767
|
+
}
|
|
768
|
+
toString() {
|
|
769
|
+
return this.name;
|
|
770
|
+
}
|
|
771
|
+
}
|
|
772
|
+
|
|
773
|
+
class HandlerNameVal extends NameVal {
|
|
774
|
+
constructor(name, namespace) {
|
|
775
|
+
super(name);
|
|
776
|
+
this.namespace = namespace;
|
|
777
|
+
}
|
|
778
|
+
eval(stack) {
|
|
779
|
+
return stack.getHandlerFor(this.name, this.namespace) ?? mk404Handler(this.namespace, this.name);
|
|
780
|
+
}
|
|
781
|
+
}
|
|
782
|
+
var mk404Handler = (type, name) => function(...args) {
|
|
783
|
+
console.warn("handler not found", { type, name, args }, this);
|
|
784
|
+
return this;
|
|
785
|
+
};
|
|
786
|
+
|
|
787
|
+
class TypeVal extends NameVal {
|
|
788
|
+
eval(stack) {
|
|
789
|
+
return stack.lookupType(this.name);
|
|
790
|
+
}
|
|
791
|
+
}
|
|
792
|
+
|
|
793
|
+
class RequestVal extends NameVal {
|
|
794
|
+
eval(stack) {
|
|
795
|
+
return stack.lookupRequest(this.name);
|
|
796
|
+
}
|
|
797
|
+
toString() {
|
|
798
|
+
return `!${this.name}`;
|
|
799
|
+
}
|
|
800
|
+
}
|
|
801
|
+
|
|
802
|
+
class RenderVal extends BaseVal {
|
|
803
|
+
render(stack, _rx) {
|
|
804
|
+
return this.eval(stack);
|
|
805
|
+
}
|
|
806
|
+
}
|
|
807
|
+
|
|
808
|
+
class RenderNameVal extends RenderVal {
|
|
809
|
+
constructor(name) {
|
|
810
|
+
super();
|
|
811
|
+
this.name = name;
|
|
812
|
+
}
|
|
813
|
+
}
|
|
814
|
+
|
|
815
|
+
class BindVal extends RenderNameVal {
|
|
816
|
+
eval(stack) {
|
|
817
|
+
return stack.lookupBind(this.name);
|
|
818
|
+
}
|
|
819
|
+
toString() {
|
|
820
|
+
return `@${this.name}`;
|
|
821
|
+
}
|
|
822
|
+
}
|
|
823
|
+
|
|
824
|
+
class DynVal extends RenderNameVal {
|
|
825
|
+
eval(stack) {
|
|
826
|
+
return stack.lookupDynamic(this.name);
|
|
827
|
+
}
|
|
828
|
+
toPathItem() {
|
|
829
|
+
return null;
|
|
830
|
+
}
|
|
831
|
+
toString() {
|
|
832
|
+
return `*${this.name}`;
|
|
833
|
+
}
|
|
834
|
+
}
|
|
835
|
+
|
|
836
|
+
class FieldVal extends RenderNameVal {
|
|
837
|
+
eval(stack) {
|
|
838
|
+
return stack.lookupFieldRaw(this.name);
|
|
839
|
+
}
|
|
840
|
+
toPathItem() {
|
|
841
|
+
return new FieldStep(this.name);
|
|
842
|
+
}
|
|
843
|
+
toString() {
|
|
844
|
+
return `.${this.name}`;
|
|
845
|
+
}
|
|
846
|
+
}
|
|
847
|
+
|
|
848
|
+
class MethodVal extends RenderNameVal {
|
|
849
|
+
eval(stack) {
|
|
850
|
+
return stack.lookupMethod(this.name);
|
|
851
|
+
}
|
|
852
|
+
evalAsHandler(stack) {
|
|
853
|
+
return stack.lookupFieldRaw(this.name);
|
|
854
|
+
}
|
|
855
|
+
toString() {
|
|
856
|
+
return `$${this.name}`;
|
|
857
|
+
}
|
|
858
|
+
}
|
|
859
|
+
|
|
860
|
+
class SeqAccessVal extends RenderVal {
|
|
861
|
+
constructor(seqVal, keyVal) {
|
|
862
|
+
super();
|
|
863
|
+
this.seqVal = seqVal;
|
|
864
|
+
this.keyVal = keyVal;
|
|
865
|
+
}
|
|
866
|
+
toPathItem() {
|
|
867
|
+
return new SeqAccessStep(this.seqVal.name, this.keyVal.name);
|
|
868
|
+
}
|
|
869
|
+
eval(stack) {
|
|
870
|
+
const key = this.keyVal.eval(stack);
|
|
871
|
+
return this.seqVal.eval(stack)?.get(key, null);
|
|
872
|
+
}
|
|
873
|
+
toString() {
|
|
874
|
+
return `${this.seqVal}[${this.keyVal}]`;
|
|
875
|
+
}
|
|
876
|
+
}
|
|
877
|
+
var vp = new ValParser;
|
|
878
|
+
|
|
879
|
+
// src/attribute.js
|
|
880
|
+
class Attributes {
|
|
881
|
+
constructor(items) {
|
|
882
|
+
this.items = items;
|
|
883
|
+
}
|
|
884
|
+
eval(_stack) {
|
|
885
|
+
return {};
|
|
886
|
+
}
|
|
887
|
+
static parse(attributes, px, parseAll = false) {
|
|
888
|
+
return getAttrParser(px).parse(attributes, parseAll);
|
|
889
|
+
}
|
|
890
|
+
isConstant() {
|
|
891
|
+
return false;
|
|
892
|
+
}
|
|
893
|
+
}
|
|
894
|
+
var booleanAttrsRaw = "itemscope,allowfullscreen,formnovalidate,ismap,nomodule,novalidate,readonly,async,autofocus,autoplay,controls,default,defer,disabled,hidden,inert,loop,open,required,reversed,scoped,seamless,checked,muted,multiple,selected";
|
|
895
|
+
var booleanAttrs = new Set(booleanAttrsRaw.split(","));
|
|
896
|
+
|
|
897
|
+
class AttrParser {
|
|
898
|
+
constructor(px) {
|
|
899
|
+
this.clear(px);
|
|
900
|
+
}
|
|
901
|
+
clear(px) {
|
|
902
|
+
this.px = px;
|
|
903
|
+
this.attrs = null;
|
|
904
|
+
this.hasDynamic = false;
|
|
905
|
+
this.wrapperAttrs = null;
|
|
906
|
+
this.textChild = null;
|
|
907
|
+
this.eachAttr = null;
|
|
908
|
+
this.ifAttr = null;
|
|
909
|
+
this.events = null;
|
|
910
|
+
}
|
|
911
|
+
parseAttr(name, value, parseAll = false) {
|
|
912
|
+
const val = parseAll ? vp.parseMacroAttr(value, this.px) : vp.parseText(value, this.px);
|
|
913
|
+
if (val !== null) {
|
|
914
|
+
this.attrs ??= [];
|
|
915
|
+
this.attrs.push(new Attr(name, val));
|
|
916
|
+
this.hasDynamic ||= !(val instanceof ConstVal);
|
|
917
|
+
} else
|
|
918
|
+
this.px.onParseIssue("bad-value", { role: "attr", attr: name, value });
|
|
919
|
+
}
|
|
920
|
+
pushWrapper(name, raw, val) {
|
|
921
|
+
const node = { name, val, raw };
|
|
922
|
+
this.wrapperAttrs ??= [];
|
|
923
|
+
this.wrapperAttrs.push(node);
|
|
924
|
+
return node;
|
|
925
|
+
}
|
|
926
|
+
parseIf(directiveName, value) {
|
|
927
|
+
const dynVal = vp.parseBool(value, this.px);
|
|
928
|
+
if (dynVal) {
|
|
929
|
+
this.ifAttr = new IfAttr(directiveName.slice(3), dynVal);
|
|
930
|
+
this.attrs ??= [];
|
|
931
|
+
this.attrs.push(this.ifAttr);
|
|
932
|
+
this.hasDynamic = true;
|
|
933
|
+
} else {
|
|
934
|
+
const info = { role: "if", attr: directiveName.slice(3), value };
|
|
935
|
+
this.px.onParseIssue("bad-value", info);
|
|
936
|
+
}
|
|
937
|
+
}
|
|
938
|
+
parseThen(s) {
|
|
939
|
+
if (this.ifAttr)
|
|
940
|
+
this.ifAttr.thenVal = vp.parseText(s, this.px) ?? NOT_SET_VAL;
|
|
941
|
+
}
|
|
942
|
+
parseElse(value) {
|
|
943
|
+
if (this.ifAttr)
|
|
944
|
+
this.ifAttr.elseVal = vp.parseText(value, this.px) ?? NOT_SET_VAL;
|
|
945
|
+
}
|
|
946
|
+
parseEvent(directiveName, value) {
|
|
947
|
+
const [eventName, ...modifiers] = directiveName.slice(3).split("+");
|
|
948
|
+
const handler = EventHandler.parse(value, this.px);
|
|
949
|
+
if (handler) {
|
|
950
|
+
if (this.events === null) {
|
|
951
|
+
this.events = this.px.registerEvents();
|
|
952
|
+
this.attrs ??= [];
|
|
953
|
+
this.attrs.push(new ConstAttr("data-eid", vp.const(this.events.id)));
|
|
954
|
+
}
|
|
955
|
+
this.events.add(eventName, handler, modifiers);
|
|
956
|
+
}
|
|
957
|
+
}
|
|
958
|
+
_parseDirectiveValue(directiveName, s, parserFn) {
|
|
959
|
+
const val = parserFn.call(vp, s, this.px);
|
|
960
|
+
if (val === null) {
|
|
961
|
+
const info = { role: "directive", directive: directiveName, value: s };
|
|
962
|
+
this.px.onParseIssue("bad-value", info);
|
|
963
|
+
}
|
|
964
|
+
return val;
|
|
965
|
+
}
|
|
966
|
+
parseDirective(s, directiveName) {
|
|
967
|
+
switch (directiveName) {
|
|
968
|
+
case "dangerouslysetinnerhtml":
|
|
969
|
+
this.attrs ??= [];
|
|
970
|
+
this.attrs.push(new RawHtmlAttr(this._parseDirectiveValue(directiveName, s, vp.parseText)));
|
|
971
|
+
this.hasDynamic = true;
|
|
972
|
+
return;
|
|
973
|
+
case "slot":
|
|
974
|
+
this.pushWrapper("slot", s, vp.const(s));
|
|
975
|
+
return;
|
|
976
|
+
case "push-view":
|
|
977
|
+
this.pushWrapper("push-view", s, this._parseDirectiveValue(directiveName, s, vp.parseText));
|
|
978
|
+
return;
|
|
979
|
+
case "text":
|
|
980
|
+
this.textChild = this._parseDirectiveValue(directiveName, s, vp.parseText);
|
|
981
|
+
return;
|
|
982
|
+
case "show":
|
|
983
|
+
this.pushWrapper("show", s, this._parseDirectiveValue(directiveName, s, vp.parseBool));
|
|
984
|
+
return;
|
|
985
|
+
case "hide":
|
|
986
|
+
this.pushWrapper("hide", s, this._parseDirectiveValue(directiveName, s, vp.parseBool));
|
|
987
|
+
return;
|
|
988
|
+
case "each": {
|
|
989
|
+
const val = this._parseDirectiveValue(directiveName, s, vp.parseSequence);
|
|
990
|
+
this.eachAttr = this.pushWrapper("each", s, val);
|
|
991
|
+
return;
|
|
992
|
+
}
|
|
993
|
+
case "enrich-with":
|
|
994
|
+
if (this.eachAttr !== null)
|
|
995
|
+
this.eachAttr.enrichWithVal = this._parseDirectiveValue(directiveName, s, vp.parseAlterHandler);
|
|
996
|
+
else
|
|
997
|
+
this.pushWrapper("scope", s, this._parseDirectiveValue(directiveName, s, vp.parseAlterHandler));
|
|
998
|
+
return;
|
|
999
|
+
case "when":
|
|
1000
|
+
this._parseWhen(s);
|
|
1001
|
+
return;
|
|
1002
|
+
case "loop-with":
|
|
1003
|
+
this._parseLoopWith(s);
|
|
1004
|
+
return;
|
|
1005
|
+
case "then":
|
|
1006
|
+
this.parseThen(s);
|
|
1007
|
+
return;
|
|
1008
|
+
case "else":
|
|
1009
|
+
this.parseElse(s);
|
|
1010
|
+
return;
|
|
1011
|
+
}
|
|
1012
|
+
if (directiveName.startsWith("on."))
|
|
1013
|
+
this.parseEvent(directiveName, s);
|
|
1014
|
+
else if (directiveName.startsWith("if."))
|
|
1015
|
+
this.parseIf(directiveName, s);
|
|
1016
|
+
else if (directiveName.startsWith("then."))
|
|
1017
|
+
this.parseThen(s);
|
|
1018
|
+
else if (directiveName.startsWith("else."))
|
|
1019
|
+
this.parseElse(s);
|
|
1020
|
+
else {
|
|
1021
|
+
const info = { name: directiveName, value: s };
|
|
1022
|
+
this.px.onParseIssue("unknown-directive", info);
|
|
1023
|
+
}
|
|
1024
|
+
}
|
|
1025
|
+
_parseWhen(s) {
|
|
1026
|
+
if (this.eachAttr !== null)
|
|
1027
|
+
this.eachAttr.whenVal = this._parseDirectiveValue("when", s, vp.parseAlterHandler);
|
|
1028
|
+
}
|
|
1029
|
+
_parseLoopWith(s) {
|
|
1030
|
+
if (this.eachAttr !== null)
|
|
1031
|
+
this.eachAttr.loopWithVal = this._parseDirectiveValue("loop-with", s, vp.parseAlterHandler);
|
|
1032
|
+
}
|
|
1033
|
+
parse(attributes, parseAll = false) {
|
|
1034
|
+
for (const { name, value } of attributes) {
|
|
1035
|
+
const charCode = name.charCodeAt(0);
|
|
1036
|
+
if (charCode === 58)
|
|
1037
|
+
this.parseAttr(name === ":viewbox" ? "viewBox" : name.slice(1), value, parseAll);
|
|
1038
|
+
else if (charCode === 64)
|
|
1039
|
+
this.parseDirective(value, name.slice(1));
|
|
1040
|
+
else {
|
|
1041
|
+
this.attrs ??= [];
|
|
1042
|
+
const constVal = value === "" && booleanAttrs.has(name) ? true : value;
|
|
1043
|
+
this.attrs.push(new ConstAttr(name, vp.const(constVal)));
|
|
1044
|
+
}
|
|
1045
|
+
}
|
|
1046
|
+
const { attrs, hasDynamic } = this;
|
|
1047
|
+
const pAttrs = hasDynamic ? new DynAttrs(attrs) : ConstAttrs.fromAttrs(attrs ?? []);
|
|
1048
|
+
return [pAttrs, this.wrapperAttrs, this.textChild];
|
|
1049
|
+
}
|
|
1050
|
+
}
|
|
1051
|
+
|
|
1052
|
+
class ConstAttrs extends Attributes {
|
|
1053
|
+
eval(_stack) {
|
|
1054
|
+
return this.items;
|
|
1055
|
+
}
|
|
1056
|
+
static fromAttrs(attrs) {
|
|
1057
|
+
const attrsObj = {};
|
|
1058
|
+
for (const attr of attrs)
|
|
1059
|
+
attrsObj[attr.name] = attr.val.eval(null);
|
|
1060
|
+
return new ConstAttrs(attrsObj);
|
|
1061
|
+
}
|
|
1062
|
+
setDataAttr(key, val) {
|
|
1063
|
+
this.items[key] = val;
|
|
1064
|
+
}
|
|
1065
|
+
toMacroVars() {
|
|
1066
|
+
const r = {};
|
|
1067
|
+
for (const name in this.items)
|
|
1068
|
+
r[name] = `'${this.items[name]}'`;
|
|
1069
|
+
return r;
|
|
1070
|
+
}
|
|
1071
|
+
isConstant() {
|
|
1072
|
+
return true;
|
|
1073
|
+
}
|
|
1074
|
+
}
|
|
1075
|
+
|
|
1076
|
+
class DynAttrs extends Attributes {
|
|
1077
|
+
eval(stack) {
|
|
1078
|
+
const attrs = {};
|
|
1079
|
+
for (let i = 0;i < this.items.length; i++) {
|
|
1080
|
+
const attr = this.items[i];
|
|
1081
|
+
attrs[attr.name] = attr.eval(stack);
|
|
1082
|
+
}
|
|
1083
|
+
return attrs;
|
|
1084
|
+
}
|
|
1085
|
+
setDataAttr(key, val) {
|
|
1086
|
+
this.items.push(new ConstAttr(key, new ConstVal(val)));
|
|
1087
|
+
}
|
|
1088
|
+
toMacroVars() {
|
|
1089
|
+
const r = {};
|
|
1090
|
+
for (const attr of this.items)
|
|
1091
|
+
r[attr.name] = attr.val.toString();
|
|
1092
|
+
return r;
|
|
1093
|
+
}
|
|
1094
|
+
}
|
|
1095
|
+
|
|
1096
|
+
class BaseAttr {
|
|
1097
|
+
constructor(name) {
|
|
1098
|
+
this.name = name;
|
|
1099
|
+
}
|
|
1100
|
+
}
|
|
1101
|
+
|
|
1102
|
+
class Attr extends BaseAttr {
|
|
1103
|
+
constructor(name, val) {
|
|
1104
|
+
super(name);
|
|
1105
|
+
this.val = val;
|
|
1106
|
+
}
|
|
1107
|
+
eval(stack) {
|
|
1108
|
+
return this.val.eval(stack);
|
|
1109
|
+
}
|
|
1110
|
+
}
|
|
1111
|
+
|
|
1112
|
+
class ConstAttr extends Attr {
|
|
1113
|
+
}
|
|
1114
|
+
|
|
1115
|
+
class RawHtmlAttr extends Attr {
|
|
1116
|
+
constructor(val) {
|
|
1117
|
+
super("dangerouslySetInnerHTML", val ?? vp.nullConstVal);
|
|
1118
|
+
}
|
|
1119
|
+
eval(stack) {
|
|
1120
|
+
return { __html: `${this.val.eval(stack)}` };
|
|
1121
|
+
}
|
|
1122
|
+
}
|
|
1123
|
+
var NOT_SET_VAL = vp.nullConstVal;
|
|
1124
|
+
|
|
1125
|
+
class IfAttr extends BaseAttr {
|
|
1126
|
+
constructor(name, condVal) {
|
|
1127
|
+
super(name);
|
|
1128
|
+
this.condVal = condVal;
|
|
1129
|
+
this.thenVal = this.elseVal = NOT_SET_VAL;
|
|
1130
|
+
}
|
|
1131
|
+
get anyBranchIsSet() {
|
|
1132
|
+
return this.thenVal !== NOT_SET_VAL || this.elseVal !== NOT_SET_VAL;
|
|
1133
|
+
}
|
|
1134
|
+
eval(stack) {
|
|
1135
|
+
return this.condVal.eval(stack) ? this.thenVal.eval(stack) : this.elseVal.eval(stack);
|
|
1136
|
+
}
|
|
1137
|
+
}
|
|
1138
|
+
var _attrParser = null;
|
|
1139
|
+
function getAttrParser(px) {
|
|
1140
|
+
_attrParser ??= new AttrParser(px);
|
|
1141
|
+
_attrParser.clear(px);
|
|
1142
|
+
return _attrParser;
|
|
1143
|
+
}
|
|
1144
|
+
|
|
1145
|
+
class EventHandler {
|
|
1146
|
+
constructor(handlerVal, args = []) {
|
|
1147
|
+
this.handlerVal = handlerVal;
|
|
1148
|
+
this.args = args;
|
|
1149
|
+
}
|
|
1150
|
+
getHandlerAndArgs(stack, _event) {
|
|
1151
|
+
const argValues = new Array(this.args.length);
|
|
1152
|
+
for (let i = 0;i < argValues.length; i++)
|
|
1153
|
+
argValues[i] = this.args[i].eval(stack);
|
|
1154
|
+
return [this.handlerVal.evalAsHandler(stack), argValues];
|
|
1155
|
+
}
|
|
1156
|
+
static parse(s, px) {
|
|
1157
|
+
const r = vp.parseInputHandler(s, px);
|
|
1158
|
+
return r === null ? null : new EventHandler(r.handlerVal, r.args);
|
|
1159
|
+
}
|
|
1160
|
+
}
|
|
1161
|
+
|
|
1162
|
+
class RequestHandler {
|
|
1163
|
+
constructor(name, fn) {
|
|
1164
|
+
this.name = name;
|
|
1165
|
+
this.fn = fn;
|
|
1166
|
+
}
|
|
1167
|
+
toHandlerArg(disp) {
|
|
1168
|
+
const f = (...args) => disp.request(this.name, args);
|
|
1169
|
+
f.withOpts = (...args) => disp.request(this.name, args.slice(0, -1), args.at(-1));
|
|
1170
|
+
return f;
|
|
1171
|
+
}
|
|
1172
|
+
}
|
|
1173
|
+
|
|
1174
|
+
// src/vdom.js
|
|
1175
|
+
var HTML_NS = "http://www.w3.org/1999/xhtml";
|
|
1176
|
+
var isNamespaced = (node) => {
|
|
1177
|
+
const ns = node.namespaceURI;
|
|
1178
|
+
return ns !== null && ns !== HTML_NS;
|
|
1179
|
+
};
|
|
1180
|
+
var NEVER_ASSIGN = new Set([
|
|
1181
|
+
"width",
|
|
1182
|
+
"height",
|
|
1183
|
+
"href",
|
|
1184
|
+
"list",
|
|
1185
|
+
"form",
|
|
1186
|
+
"tabIndex",
|
|
1187
|
+
"download",
|
|
1188
|
+
"rowSpan",
|
|
1189
|
+
"colSpan",
|
|
1190
|
+
"role",
|
|
1191
|
+
"popover"
|
|
1192
|
+
]);
|
|
1193
|
+
function applyProperties(node, props, _previous) {
|
|
1194
|
+
const namespaced = isNamespaced(node);
|
|
1195
|
+
for (const name in props)
|
|
1196
|
+
setProp(node, name, props[name], namespaced);
|
|
1197
|
+
}
|
|
1198
|
+
function setProp(node, name, value, namespaced) {
|
|
1199
|
+
if (name === "dangerouslySetInnerHTML") {
|
|
1200
|
+
if (value === undefined)
|
|
1201
|
+
node.replaceChildren();
|
|
1202
|
+
else
|
|
1203
|
+
node.innerHTML = value.__html ?? "";
|
|
1204
|
+
return;
|
|
1205
|
+
}
|
|
1206
|
+
if (typeof value === "function")
|
|
1207
|
+
return;
|
|
1208
|
+
if (!namespaced && !NEVER_ASSIGN.has(name) && name in node) {
|
|
1209
|
+
try {
|
|
1210
|
+
node[name] = value == null ? "" : value;
|
|
1211
|
+
return;
|
|
1212
|
+
} catch {}
|
|
1213
|
+
}
|
|
1214
|
+
if (value == null || value === false && name[4] !== "-")
|
|
1215
|
+
node.removeAttribute(name);
|
|
1216
|
+
else
|
|
1217
|
+
node.setAttribute(name, value);
|
|
1218
|
+
}
|
|
1219
|
+
|
|
1220
|
+
class VBase {
|
|
1221
|
+
}
|
|
1222
|
+
var getKey = (child) => child instanceof VNode ? child.key : undefined;
|
|
1223
|
+
var isIterable = (obj) => obj != null && typeof obj !== "string" && typeof obj[Symbol.iterator] === "function";
|
|
1224
|
+
function childsEqual(a, b) {
|
|
1225
|
+
if (a === b)
|
|
1226
|
+
return true;
|
|
1227
|
+
for (let i = 0;i < a.length; i++)
|
|
1228
|
+
if (!a[i].isEqualTo(b[i]))
|
|
1229
|
+
return false;
|
|
1230
|
+
return true;
|
|
1231
|
+
}
|
|
1232
|
+
function appendChildNodes(parent, childs, opts) {
|
|
1233
|
+
for (const child of childs)
|
|
1234
|
+
parent.appendChild(child.toDom(opts));
|
|
1235
|
+
}
|
|
1236
|
+
function addChild(normalizedChildren, child) {
|
|
1237
|
+
if (child == null)
|
|
1238
|
+
return;
|
|
1239
|
+
if (isIterable(child)) {
|
|
1240
|
+
for (const c of child)
|
|
1241
|
+
addChild(normalizedChildren, c);
|
|
1242
|
+
} else if (child instanceof VBase) {
|
|
1243
|
+
if (child instanceof VFragment)
|
|
1244
|
+
normalizedChildren.push(...child.childs);
|
|
1245
|
+
else
|
|
1246
|
+
normalizedChildren.push(child);
|
|
1247
|
+
} else
|
|
1248
|
+
normalizedChildren.push(new VText(child));
|
|
1249
|
+
}
|
|
1250
|
+
|
|
1251
|
+
class VText extends VBase {
|
|
1252
|
+
constructor(text) {
|
|
1253
|
+
super();
|
|
1254
|
+
this.text = String(text);
|
|
1255
|
+
}
|
|
1256
|
+
get nodeType() {
|
|
1257
|
+
return 3;
|
|
1258
|
+
}
|
|
1259
|
+
isEqualTo(other) {
|
|
1260
|
+
return other instanceof VText && this.text === other.text;
|
|
1261
|
+
}
|
|
1262
|
+
toDom(opts) {
|
|
1263
|
+
return opts.document.createTextNode(this.text);
|
|
1264
|
+
}
|
|
1265
|
+
}
|
|
1266
|
+
|
|
1267
|
+
class VComment extends VBase {
|
|
1268
|
+
constructor(text) {
|
|
1269
|
+
super();
|
|
1270
|
+
this.text = text;
|
|
1271
|
+
}
|
|
1272
|
+
get nodeType() {
|
|
1273
|
+
return 8;
|
|
1274
|
+
}
|
|
1275
|
+
isEqualTo(other) {
|
|
1276
|
+
return other instanceof VComment && this.text === other.text;
|
|
1277
|
+
}
|
|
1278
|
+
toDom(opts) {
|
|
1279
|
+
return opts.document.createComment(this.text);
|
|
1280
|
+
}
|
|
1281
|
+
}
|
|
1282
|
+
|
|
1283
|
+
class VFragment extends VBase {
|
|
1284
|
+
constructor(childs) {
|
|
1285
|
+
super();
|
|
1286
|
+
this.childs = [];
|
|
1287
|
+
addChild(this.childs, childs);
|
|
1288
|
+
}
|
|
1289
|
+
get nodeType() {
|
|
1290
|
+
return 11;
|
|
1291
|
+
}
|
|
1292
|
+
isEqualTo(other) {
|
|
1293
|
+
if (!(other instanceof VFragment) || this.childs.length !== other.childs.length)
|
|
1294
|
+
return false;
|
|
1295
|
+
return childsEqual(this.childs, other.childs);
|
|
1296
|
+
}
|
|
1297
|
+
toDom(opts) {
|
|
1298
|
+
const fragment = opts.document.createDocumentFragment();
|
|
1299
|
+
appendChildNodes(fragment, this.childs, opts);
|
|
1300
|
+
return fragment;
|
|
1301
|
+
}
|
|
1302
|
+
}
|
|
1303
|
+
|
|
1304
|
+
class VNode extends VBase {
|
|
1305
|
+
constructor(tag, attrs, childs, key, namespace) {
|
|
1306
|
+
super();
|
|
1307
|
+
this.tag = tag;
|
|
1308
|
+
this.attrs = attrs ?? {};
|
|
1309
|
+
this.childs = childs ?? [];
|
|
1310
|
+
this.key = key != null ? String(key) : undefined;
|
|
1311
|
+
this.namespace = typeof namespace === "string" ? namespace : null;
|
|
1312
|
+
}
|
|
1313
|
+
get nodeType() {
|
|
1314
|
+
return 1;
|
|
1315
|
+
}
|
|
1316
|
+
isSameKind(other) {
|
|
1317
|
+
return this.tag === other.tag && this.namespace === other.namespace && this.key === other.key;
|
|
1318
|
+
}
|
|
1319
|
+
isEqualTo(other) {
|
|
1320
|
+
if (this === other)
|
|
1321
|
+
return true;
|
|
1322
|
+
if (!(other instanceof VNode) || !this.isSameKind(other) || this.childs.length !== other.childs.length) {
|
|
1323
|
+
return false;
|
|
1324
|
+
}
|
|
1325
|
+
if (this.attrs !== other.attrs) {
|
|
1326
|
+
for (const key in this.attrs)
|
|
1327
|
+
if (this.attrs[key] !== other.attrs[key])
|
|
1328
|
+
return false;
|
|
1329
|
+
for (const key in other.attrs)
|
|
1330
|
+
if (!Object.hasOwn(this.attrs, key))
|
|
1331
|
+
return false;
|
|
1332
|
+
}
|
|
1333
|
+
return childsEqual(this.childs, other.childs);
|
|
1334
|
+
}
|
|
1335
|
+
toDom(opts) {
|
|
1336
|
+
const doc = opts.document;
|
|
1337
|
+
const node = this.namespace === null ? doc.createElement(this.tag) : doc.createElementNS(this.namespace, this.tag);
|
|
1338
|
+
if (this.tag === "SELECT" && "value" in this.attrs) {
|
|
1339
|
+
const { value, ...rest } = this.attrs;
|
|
1340
|
+
applyProperties(node, rest, {});
|
|
1341
|
+
appendChildNodes(node, this.childs, opts);
|
|
1342
|
+
applyProperties(node, { value }, {});
|
|
1343
|
+
} else {
|
|
1344
|
+
applyProperties(node, this.attrs, {});
|
|
1345
|
+
appendChildNodes(node, this.childs, opts);
|
|
1346
|
+
}
|
|
1347
|
+
return node;
|
|
1348
|
+
}
|
|
1349
|
+
}
|
|
1350
|
+
function diffProps(a, b) {
|
|
1351
|
+
if (a === b)
|
|
1352
|
+
return null;
|
|
1353
|
+
let diff = null;
|
|
1354
|
+
for (const aKey in a) {
|
|
1355
|
+
if (!Object.hasOwn(b, aKey)) {
|
|
1356
|
+
diff ??= {};
|
|
1357
|
+
diff[aKey] = undefined;
|
|
1358
|
+
} else if (a[aKey] !== b[aKey]) {
|
|
1359
|
+
diff ??= {};
|
|
1360
|
+
diff[aKey] = b[aKey];
|
|
1361
|
+
}
|
|
1362
|
+
}
|
|
1363
|
+
for (const bKey in b) {
|
|
1364
|
+
if (!Object.hasOwn(a, bKey)) {
|
|
1365
|
+
diff ??= {};
|
|
1366
|
+
diff[bKey] = b[bKey];
|
|
1367
|
+
}
|
|
1368
|
+
}
|
|
1369
|
+
return diff;
|
|
1370
|
+
}
|
|
1371
|
+
function morphNode(domNode, source, target, opts) {
|
|
1372
|
+
if (source === target || source.isEqualTo(target))
|
|
1373
|
+
return domNode;
|
|
1374
|
+
const type = source.nodeType;
|
|
1375
|
+
if (type === target.nodeType) {
|
|
1376
|
+
if (type === 3 || type === 8) {
|
|
1377
|
+
domNode.data = target.text;
|
|
1378
|
+
return domNode;
|
|
1379
|
+
}
|
|
1380
|
+
if (type === 1 && source.isSameKind(target)) {
|
|
1381
|
+
const propsDiff = diffProps(source.attrs, target.attrs);
|
|
1382
|
+
const isSelect = source.tag === "SELECT";
|
|
1383
|
+
if (propsDiff) {
|
|
1384
|
+
if (isSelect && "value" in propsDiff) {
|
|
1385
|
+
const { value: _v, ...rest } = propsDiff;
|
|
1386
|
+
applyProperties(domNode, rest, source.attrs);
|
|
1387
|
+
} else
|
|
1388
|
+
applyProperties(domNode, propsDiff, source.attrs);
|
|
1389
|
+
}
|
|
1390
|
+
if (!target.attrs.dangerouslySetInnerHTML)
|
|
1391
|
+
morphChildren(domNode, source.childs, target.childs, opts);
|
|
1392
|
+
if (isSelect && target.attrs.value !== undefined)
|
|
1393
|
+
applyProperties(domNode, { value: target.attrs.value }, source.attrs);
|
|
1394
|
+
return domNode;
|
|
1395
|
+
}
|
|
1396
|
+
if (type === 11) {
|
|
1397
|
+
morphChildren(domNode, source.childs, target.childs, opts);
|
|
1398
|
+
return domNode;
|
|
1399
|
+
}
|
|
1400
|
+
}
|
|
1401
|
+
const newNode = target.toDom(opts);
|
|
1402
|
+
domNode.parentNode?.replaceChild(newNode, domNode);
|
|
1403
|
+
return newNode;
|
|
1404
|
+
}
|
|
1405
|
+
function morphChildren(parentDom, oldChilds, newChilds, opts) {
|
|
1406
|
+
if (oldChilds.length === 0) {
|
|
1407
|
+
appendChildNodes(parentDom, newChilds, opts);
|
|
1408
|
+
return;
|
|
1409
|
+
}
|
|
1410
|
+
if (newChilds.length === 0) {
|
|
1411
|
+
parentDom.replaceChildren();
|
|
1412
|
+
return;
|
|
1413
|
+
}
|
|
1414
|
+
if (oldChilds.length === newChilds.length) {
|
|
1415
|
+
let hasKey = false;
|
|
1416
|
+
for (let i = 0;i < oldChilds.length; i++) {
|
|
1417
|
+
if (getKey(oldChilds[i]) != null || getKey(newChilds[i]) != null) {
|
|
1418
|
+
hasKey = true;
|
|
1419
|
+
break;
|
|
1420
|
+
}
|
|
1421
|
+
}
|
|
1422
|
+
if (!hasKey) {
|
|
1423
|
+
let dom = parentDom.firstChild;
|
|
1424
|
+
for (let i = 0;i < oldChilds.length; i++) {
|
|
1425
|
+
const next = dom.nextSibling;
|
|
1426
|
+
morphNode(dom, oldChilds[i], newChilds[i], opts);
|
|
1427
|
+
dom = next;
|
|
1428
|
+
}
|
|
1429
|
+
return;
|
|
1430
|
+
}
|
|
1431
|
+
}
|
|
1432
|
+
const domNodes = Array.from(parentDom.childNodes);
|
|
1433
|
+
const oldKeyMap = Object.create(null);
|
|
1434
|
+
for (let i = 0;i < oldChilds.length; i++) {
|
|
1435
|
+
const key = getKey(oldChilds[i]);
|
|
1436
|
+
if (key != null)
|
|
1437
|
+
oldKeyMap[key] = i;
|
|
1438
|
+
}
|
|
1439
|
+
const used = new Uint8Array(oldChilds.length);
|
|
1440
|
+
let unkeyedCursor = 0;
|
|
1441
|
+
for (let j = 0;j < newChilds.length; j++) {
|
|
1442
|
+
const newChild = newChilds[j];
|
|
1443
|
+
const newKey = getKey(newChild);
|
|
1444
|
+
let oldIdx = -1;
|
|
1445
|
+
if (newKey != null) {
|
|
1446
|
+
if (newKey in oldKeyMap && !used[oldKeyMap[newKey]])
|
|
1447
|
+
oldIdx = oldKeyMap[newKey];
|
|
1448
|
+
} else {
|
|
1449
|
+
while (unkeyedCursor < oldChilds.length) {
|
|
1450
|
+
if (!used[unkeyedCursor] && getKey(oldChilds[unkeyedCursor]) == null) {
|
|
1451
|
+
oldIdx = unkeyedCursor++;
|
|
1452
|
+
break;
|
|
1453
|
+
}
|
|
1454
|
+
unkeyedCursor++;
|
|
1455
|
+
}
|
|
1456
|
+
}
|
|
1457
|
+
if (oldIdx >= 0) {
|
|
1458
|
+
used[oldIdx] = 1;
|
|
1459
|
+
const newDom = morphNode(domNodes[oldIdx], oldChilds[oldIdx], newChild, opts);
|
|
1460
|
+
const ref = parentDom.childNodes[j] ?? null;
|
|
1461
|
+
if (newDom !== ref)
|
|
1462
|
+
parentDom.insertBefore(newDom, ref);
|
|
1463
|
+
} else {
|
|
1464
|
+
const ref = parentDom.childNodes[j] ?? null;
|
|
1465
|
+
parentDom.insertBefore(newChild.toDom(opts), ref);
|
|
1466
|
+
}
|
|
1467
|
+
}
|
|
1468
|
+
for (let i = oldChilds.length - 1;i >= 0; i--)
|
|
1469
|
+
if (!used[i] && domNodes[i].parentNode === parentDom)
|
|
1470
|
+
parentDom.removeChild(domNodes[i]);
|
|
1471
|
+
}
|
|
1472
|
+
function render(vnode, container, options, prev) {
|
|
1473
|
+
const isFragment = vnode instanceof VFragment;
|
|
1474
|
+
if (prev && prev.vnode instanceof VFragment === isFragment) {
|
|
1475
|
+
const oldDom = isFragment ? container : prev.dom;
|
|
1476
|
+
const newDom = morphNode(oldDom, prev.vnode, vnode, options);
|
|
1477
|
+
return { vnode, dom: isFragment ? container : newDom };
|
|
1478
|
+
}
|
|
1479
|
+
const domNode = vnode.toDom(options);
|
|
1480
|
+
container.replaceChildren(domNode);
|
|
1481
|
+
return { vnode, dom: isFragment ? container : domNode };
|
|
1482
|
+
}
|
|
1483
|
+
function h(tagName, properties, children, namespace) {
|
|
1484
|
+
const props = {};
|
|
1485
|
+
let key;
|
|
1486
|
+
if (properties) {
|
|
1487
|
+
for (const propName in properties) {
|
|
1488
|
+
const propVal = properties[propName];
|
|
1489
|
+
if (propName === "key")
|
|
1490
|
+
key = propVal;
|
|
1491
|
+
else if (propName === "namespace")
|
|
1492
|
+
namespace = namespace ?? propVal;
|
|
1493
|
+
else
|
|
1494
|
+
props[propName] = propVal;
|
|
1495
|
+
}
|
|
1496
|
+
}
|
|
1497
|
+
const c = tagName.charCodeAt(0);
|
|
1498
|
+
const tag = namespace == null && c >= 97 && c <= 122 ? tagName.toUpperCase() : tagName;
|
|
1499
|
+
const normalizedChildren = [];
|
|
1500
|
+
addChild(normalizedChildren, children);
|
|
1501
|
+
return new VNode(tag, props, normalizedChildren, key, namespace);
|
|
1502
|
+
}
|
|
1503
|
+
|
|
1504
|
+
// src/anode.js
|
|
1505
|
+
function resolveDynProducer(comp, dynName) {
|
|
1506
|
+
const dyn = comp?.dynamic?.[dynName];
|
|
1507
|
+
if (dyn == null)
|
|
1508
|
+
return null;
|
|
1509
|
+
let producerComp, producerDyn;
|
|
1510
|
+
if (dyn.compName != null) {
|
|
1511
|
+
producerComp = comp.scope?.lookupComponent(dyn.compName);
|
|
1512
|
+
producerDyn = producerComp?.dynamic?.[dyn.dynName];
|
|
1513
|
+
} else {
|
|
1514
|
+
producerComp = comp;
|
|
1515
|
+
producerDyn = dyn;
|
|
1516
|
+
}
|
|
1517
|
+
if (producerComp == null || producerDyn == null)
|
|
1518
|
+
return null;
|
|
1519
|
+
const pi = producerDyn.val?.toPathItem?.() ?? null;
|
|
1520
|
+
return { producerCompId: producerComp.id, producerSteps: pi ? [pi] : [] };
|
|
1521
|
+
}
|
|
1522
|
+
|
|
1523
|
+
class BaseNode {
|
|
1524
|
+
render(_stack, _rx) {
|
|
1525
|
+
return null;
|
|
1526
|
+
}
|
|
1527
|
+
setDataAttr(key, val) {
|
|
1528
|
+
console.warn("setDataAttr not implemented for", this, { key, val });
|
|
1529
|
+
}
|
|
1530
|
+
isConstant() {
|
|
1531
|
+
return false;
|
|
1532
|
+
}
|
|
1533
|
+
optimize() {}
|
|
1534
|
+
}
|
|
1535
|
+
|
|
1536
|
+
class TextNode extends BaseNode {
|
|
1537
|
+
constructor(val) {
|
|
1538
|
+
super();
|
|
1539
|
+
this.val = val;
|
|
1540
|
+
}
|
|
1541
|
+
render(_stack, _rx) {
|
|
1542
|
+
return this.val;
|
|
1543
|
+
}
|
|
1544
|
+
isWhiteSpace() {
|
|
1545
|
+
for (let i = 0;i < this.val.length; i++) {
|
|
1546
|
+
const c = this.val.charCodeAt(i);
|
|
1547
|
+
if (!(c === 32 || c === 10 || c === 9 || c === 13))
|
|
1548
|
+
return false;
|
|
1549
|
+
}
|
|
1550
|
+
return true;
|
|
1551
|
+
}
|
|
1552
|
+
hasNewLine() {
|
|
1553
|
+
for (let i = 0;i < this.val.length; i++) {
|
|
1554
|
+
const c = this.val.charCodeAt(i);
|
|
1555
|
+
if (c === 10 || c === 13)
|
|
1556
|
+
return true;
|
|
1557
|
+
}
|
|
1558
|
+
return false;
|
|
1559
|
+
}
|
|
1560
|
+
condenseWhiteSpace(replacement = "") {
|
|
1561
|
+
this.val = replacement;
|
|
1562
|
+
}
|
|
1563
|
+
isConstant() {
|
|
1564
|
+
return true;
|
|
1565
|
+
}
|
|
1566
|
+
setDataAttr(_key, _val) {}
|
|
1567
|
+
}
|
|
1568
|
+
|
|
1569
|
+
class CommentNode extends TextNode {
|
|
1570
|
+
render(_stack, rx) {
|
|
1571
|
+
return rx.renderComment(this.val);
|
|
1572
|
+
}
|
|
1573
|
+
}
|
|
1574
|
+
function optimizeChilds(childs) {
|
|
1575
|
+
for (let i = 0;i < childs.length; i++) {
|
|
1576
|
+
const child = childs[i];
|
|
1577
|
+
if (child.isConstant())
|
|
1578
|
+
childs[i] = new RenderOnceNode(child);
|
|
1579
|
+
else
|
|
1580
|
+
child.optimize();
|
|
1581
|
+
}
|
|
1582
|
+
}
|
|
1583
|
+
function optimizeNode(node) {
|
|
1584
|
+
if (node.isConstant())
|
|
1585
|
+
return new RenderOnceNode(node);
|
|
1586
|
+
node.optimize();
|
|
1587
|
+
return node;
|
|
1588
|
+
}
|
|
1589
|
+
|
|
1590
|
+
class ChildsNode extends BaseNode {
|
|
1591
|
+
constructor(childs) {
|
|
1592
|
+
super();
|
|
1593
|
+
this.childs = childs;
|
|
1594
|
+
}
|
|
1595
|
+
isConstant() {
|
|
1596
|
+
return this.childs.every((v) => v.isConstant());
|
|
1597
|
+
}
|
|
1598
|
+
optimize() {
|
|
1599
|
+
optimizeChilds(this.childs);
|
|
1600
|
+
}
|
|
1601
|
+
}
|
|
1602
|
+
|
|
1603
|
+
class DomNode extends ChildsNode {
|
|
1604
|
+
constructor(tagName, attrs, childs, namespace = null) {
|
|
1605
|
+
super(childs);
|
|
1606
|
+
this.tagName = tagName;
|
|
1607
|
+
this.attrs = attrs;
|
|
1608
|
+
this.namespace = namespace;
|
|
1609
|
+
}
|
|
1610
|
+
render(stack, rx) {
|
|
1611
|
+
const childNodes = new Array(this.childs.length);
|
|
1612
|
+
for (let i = 0;i < childNodes.length; i++)
|
|
1613
|
+
childNodes[i] = this.childs[i]?.render?.(stack, rx) ?? null;
|
|
1614
|
+
return rx.renderTag(this.tagName, this.attrs.eval(stack), childNodes, this.namespace);
|
|
1615
|
+
}
|
|
1616
|
+
setDataAttr(key, val) {
|
|
1617
|
+
this.attrs.setDataAttr(key, val);
|
|
1618
|
+
}
|
|
1619
|
+
isConstant() {
|
|
1620
|
+
return this.attrs.isConstant() && super.isConstant();
|
|
1621
|
+
}
|
|
1622
|
+
}
|
|
1623
|
+
|
|
1624
|
+
class FragmentNode extends ChildsNode {
|
|
1625
|
+
render(stack, rx) {
|
|
1626
|
+
return rx.renderFragment(this.childs.map((c) => c?.render(stack, rx)));
|
|
1627
|
+
}
|
|
1628
|
+
setDataAttr(key, val) {
|
|
1629
|
+
for (const child of this.childs)
|
|
1630
|
+
child.setDataAttr(key, val);
|
|
1631
|
+
}
|
|
1632
|
+
}
|
|
1633
|
+
var maybeFragment = (xs) => xs.length === 1 ? xs[0] : new FragmentNode(xs);
|
|
1634
|
+
var VALID_NODE_RE = /^[a-zA-Z][a-zA-Z0-9-]*$/;
|
|
1635
|
+
|
|
1636
|
+
class ANode extends BaseNode {
|
|
1637
|
+
constructor(nodeId, val) {
|
|
1638
|
+
super();
|
|
1639
|
+
this.nodeId = nodeId;
|
|
1640
|
+
this.val = val;
|
|
1641
|
+
}
|
|
1642
|
+
toPathStep(ctx) {
|
|
1643
|
+
return ctx.applyKey(this.val?.toPathItem?.() ?? null);
|
|
1644
|
+
}
|
|
1645
|
+
static parse(html, px) {
|
|
1646
|
+
const nodes = px.parseHTML(html);
|
|
1647
|
+
if (nodes.length === 0)
|
|
1648
|
+
return new CommentNode("Empty View in ANode.parse");
|
|
1649
|
+
if (nodes.length === 1)
|
|
1650
|
+
return ANode.fromDOM(nodes[0], px);
|
|
1651
|
+
const childs = [];
|
|
1652
|
+
for (let i = 0;i < nodes.length; i++) {
|
|
1653
|
+
const child = ANode.fromDOM(nodes[i], px);
|
|
1654
|
+
if (child !== null)
|
|
1655
|
+
childs.push(child);
|
|
1656
|
+
}
|
|
1657
|
+
const trimmed = condenseChildsWhites(childs);
|
|
1658
|
+
if (trimmed.length === 0)
|
|
1659
|
+
return new CommentNode("Empty View in ANode.parse");
|
|
1660
|
+
return maybeFragment(trimmed);
|
|
1661
|
+
}
|
|
1662
|
+
static fromDOM(node, px) {
|
|
1663
|
+
if (node instanceof px.Text)
|
|
1664
|
+
return new TextNode(node.textContent);
|
|
1665
|
+
else if (node instanceof px.Comment)
|
|
1666
|
+
return new CommentNode(node.textContent);
|
|
1667
|
+
const { childNodes, attributes: attrs, tagName: tag } = node;
|
|
1668
|
+
const childs = [];
|
|
1669
|
+
for (let i = 0;i < childNodes.length; i++) {
|
|
1670
|
+
const child = ANode.fromDOM(childNodes[i], px);
|
|
1671
|
+
if (child !== null)
|
|
1672
|
+
childs.push(child);
|
|
1673
|
+
}
|
|
1674
|
+
const prevTag = px.currentTag;
|
|
1675
|
+
px.currentTag = tag;
|
|
1676
|
+
try {
|
|
1677
|
+
const isPseudoX = attrs[0]?.name === "@x";
|
|
1678
|
+
if (tag === "X" || isPseudoX)
|
|
1679
|
+
return parseXOp(attrs, childs, isPseudoX ? 1 : 0, px);
|
|
1680
|
+
else if (tag.charCodeAt(1) === 58 && tag.charCodeAt(0) === 88) {
|
|
1681
|
+
const macroName = tag.slice(2).toLowerCase();
|
|
1682
|
+
if (macroName === "slot") {
|
|
1683
|
+
const slotName = attrs.getNamedItem("name")?.value ?? "_";
|
|
1684
|
+
return px.frame.macroSlots[slotName] ?? maybeFragment(childs);
|
|
1685
|
+
}
|
|
1686
|
+
const [nAttrs, wrappers] = Attributes.parse(attrs, px, true);
|
|
1687
|
+
px.onAttributes(nAttrs, wrappers, null, true, tag);
|
|
1688
|
+
return wrap(px.newMacroNode(macroName, nAttrs.toMacroVars(), childs), px, wrappers);
|
|
1689
|
+
} else if (VALID_NODE_RE.test(tag)) {
|
|
1690
|
+
const [nAttrs, wrappers, textChild] = Attributes.parse(attrs, px);
|
|
1691
|
+
px.onAttributes(nAttrs, wrappers, textChild, false, tag);
|
|
1692
|
+
if (textChild)
|
|
1693
|
+
childs.unshift(new RenderTextNode(null, textChild));
|
|
1694
|
+
const domChilds = tag !== "PRE" ? condenseChildsWhites(childs) : childs;
|
|
1695
|
+
const ns = node.namespaceURI;
|
|
1696
|
+
const namespace = ns && ns !== HTML_NS ? ns : null;
|
|
1697
|
+
return wrap(new DomNode(tag, nAttrs, domChilds, namespace), px, wrappers);
|
|
1698
|
+
}
|
|
1699
|
+
return new CommentNode(`Error: InvalidTagName ${tag}`);
|
|
1700
|
+
} finally {
|
|
1701
|
+
px.currentTag = prevTag;
|
|
1702
|
+
}
|
|
1703
|
+
}
|
|
1704
|
+
}
|
|
1705
|
+
function parseXOp(attrs, childs, opIdx, px) {
|
|
1706
|
+
if (attrs.length === 0)
|
|
1707
|
+
return maybeFragment(childs);
|
|
1708
|
+
const { name, value } = attrs[opIdx];
|
|
1709
|
+
const as = attrs.getNamedItem("as")?.value ?? null;
|
|
1710
|
+
let node;
|
|
1711
|
+
switch (name) {
|
|
1712
|
+
case "slot":
|
|
1713
|
+
node = new SlotNode(null, vp.const(value), maybeFragment(childs));
|
|
1714
|
+
break;
|
|
1715
|
+
case "text":
|
|
1716
|
+
node = px.addNodeIf(RenderTextNode, parseXOpVal(name, value, px, vp.parseText));
|
|
1717
|
+
break;
|
|
1718
|
+
case "render":
|
|
1719
|
+
node = px.addNodeIf(RenderNode, parseXOpVal(name, value, px, vp.parseComponent), as);
|
|
1720
|
+
break;
|
|
1721
|
+
case "render-it":
|
|
1722
|
+
node = px.addNodeIf(RenderItNode, vp.bindValIt, as);
|
|
1723
|
+
break;
|
|
1724
|
+
case "render-each":
|
|
1725
|
+
node = RenderEachNode.parse(px, vp, value, as, attrs);
|
|
1726
|
+
break;
|
|
1727
|
+
case "show": {
|
|
1728
|
+
const val = parseXOpVal(name, value, px, vp.parseBool);
|
|
1729
|
+
node = px.addNodeIf(ShowNode, val, maybeFragment(childs));
|
|
1730
|
+
break;
|
|
1731
|
+
}
|
|
1732
|
+
case "hide": {
|
|
1733
|
+
const val = parseXOpVal(name, value, px, vp.parseBool);
|
|
1734
|
+
node = px.addNodeIf(HideNode, val, maybeFragment(childs));
|
|
1735
|
+
break;
|
|
1736
|
+
}
|
|
1737
|
+
default:
|
|
1738
|
+
px.onParseIssue("unknown-x-op", { name, value });
|
|
1739
|
+
return new CommentNode(`Error: InvalidSpecialTagOp ${name}=${value}`);
|
|
1740
|
+
}
|
|
1741
|
+
return processXExtras(node, attrs, name, opIdx + 1, px);
|
|
1742
|
+
}
|
|
1743
|
+
function parseXOpVal(opName, value, px, parserFn) {
|
|
1744
|
+
const val = parserFn.call(vp, value, px);
|
|
1745
|
+
if (val === null)
|
|
1746
|
+
px.onParseIssue("bad-value", { role: "x-op", op: opName, value });
|
|
1747
|
+
return val;
|
|
1748
|
+
}
|
|
1749
|
+
function processXExtras(node, attrs, opName, startIdx, px) {
|
|
1750
|
+
const consumed = X_OP_CONSUMED[opName];
|
|
1751
|
+
const wrappable = X_OP_WRAPPABLE.has(opName);
|
|
1752
|
+
const wrappers = [];
|
|
1753
|
+
for (let i = startIdx;i < attrs.length; i++) {
|
|
1754
|
+
const a = attrs[i];
|
|
1755
|
+
const aName = a.name;
|
|
1756
|
+
if (consumed.has(aName))
|
|
1757
|
+
continue;
|
|
1758
|
+
if (wrappable && X_ATTR_WRAPPERS[aName]) {
|
|
1759
|
+
wrappers.push([X_ATTR_WRAPPERS[aName], vp.parseBool(a.value, px)]);
|
|
1760
|
+
continue;
|
|
1761
|
+
}
|
|
1762
|
+
const issueInfo = { op: opName, name: aName, value: a.value };
|
|
1763
|
+
px.onParseIssue("unknown-x-attr", issueInfo);
|
|
1764
|
+
}
|
|
1765
|
+
for (let i = wrappers.length - 1;i >= 0; i--) {
|
|
1766
|
+
const [Cls, val] = wrappers[i];
|
|
1767
|
+
const wrapper = px.addNodeIf(Cls, val, node);
|
|
1768
|
+
if (wrapper !== null)
|
|
1769
|
+
node = wrapper;
|
|
1770
|
+
}
|
|
1771
|
+
return node;
|
|
1772
|
+
}
|
|
1773
|
+
function wrap(node, px, wrappers) {
|
|
1774
|
+
if (wrappers) {
|
|
1775
|
+
for (let i = wrappers.length - 1;i >= 0; i--) {
|
|
1776
|
+
const wrapperNode = makeWrapperNode(wrappers[i], px);
|
|
1777
|
+
if (wrapperNode) {
|
|
1778
|
+
wrapperNode.wrapNode(node);
|
|
1779
|
+
node = wrapperNode;
|
|
1780
|
+
}
|
|
1781
|
+
}
|
|
1782
|
+
}
|
|
1783
|
+
return node;
|
|
1784
|
+
}
|
|
1785
|
+
function makeWrapperNode(data, px) {
|
|
1786
|
+
const Cls = WRAPPER_NODES[data.name];
|
|
1787
|
+
const node = Cls.register ? px.addNodeIf(Cls, data.val) : data.val && new Cls(null, data.val);
|
|
1788
|
+
if (node !== null && data.name === "each") {
|
|
1789
|
+
node.iterInfo.enrichWithVal = data.enrichWithVal ?? null;
|
|
1790
|
+
node.iterInfo.whenVal = data.whenVal ?? null;
|
|
1791
|
+
node.iterInfo.loopWithVal = data.loopWithVal ?? null;
|
|
1792
|
+
}
|
|
1793
|
+
return node;
|
|
1794
|
+
}
|
|
1795
|
+
|
|
1796
|
+
class MacroNode extends BaseNode {
|
|
1797
|
+
constructor(name, attrs, slots, px) {
|
|
1798
|
+
super();
|
|
1799
|
+
this.name = name;
|
|
1800
|
+
this.attrs = attrs;
|
|
1801
|
+
this.slots = slots;
|
|
1802
|
+
this.px = px;
|
|
1803
|
+
this.node = null;
|
|
1804
|
+
this.dataAttrs = {};
|
|
1805
|
+
}
|
|
1806
|
+
compile(scope) {
|
|
1807
|
+
const { name, attrs, slots } = this;
|
|
1808
|
+
if (this.px.isInsideMacro(name))
|
|
1809
|
+
throw new Error(`Recursive macro expansion: ${name}`);
|
|
1810
|
+
const macro = scope.lookupMacro(name);
|
|
1811
|
+
if (macro === null)
|
|
1812
|
+
this.node = new CommentNode(`bad macro: ${name}`);
|
|
1813
|
+
else {
|
|
1814
|
+
const vars = { ...macro.defaults, ...attrs };
|
|
1815
|
+
this.node = macro.expand(this.px.enterMacro(name, vars, slots));
|
|
1816
|
+
for (const key in this.dataAttrs)
|
|
1817
|
+
this.node.setDataAttr(key, this.dataAttrs[key]);
|
|
1818
|
+
}
|
|
1819
|
+
}
|
|
1820
|
+
render(stack, rx) {
|
|
1821
|
+
return this.node.render(stack, rx);
|
|
1822
|
+
}
|
|
1823
|
+
setDataAttr(key, val) {
|
|
1824
|
+
this.dataAttrs[key] = val;
|
|
1825
|
+
}
|
|
1826
|
+
isConstant() {
|
|
1827
|
+
return this.node.isConstant();
|
|
1828
|
+
}
|
|
1829
|
+
optimize() {
|
|
1830
|
+
this.node = optimizeNode(this.node);
|
|
1831
|
+
}
|
|
1832
|
+
}
|
|
1833
|
+
|
|
1834
|
+
class Macro {
|
|
1835
|
+
constructor(defaults, rawView) {
|
|
1836
|
+
this.defaults = defaults;
|
|
1837
|
+
this.rawView = rawView;
|
|
1838
|
+
}
|
|
1839
|
+
expand(px) {
|
|
1840
|
+
return ANode.parse(this.rawView, px);
|
|
1841
|
+
}
|
|
1842
|
+
}
|
|
1843
|
+
|
|
1844
|
+
class RenderViewId extends ANode {
|
|
1845
|
+
constructor(nodeId, val, viewId) {
|
|
1846
|
+
super(nodeId, val);
|
|
1847
|
+
this.viewId = viewId;
|
|
1848
|
+
}
|
|
1849
|
+
setDataAttr(_key, _val) {}
|
|
1850
|
+
}
|
|
1851
|
+
|
|
1852
|
+
class RenderNode extends RenderViewId {
|
|
1853
|
+
render(stack, rx) {
|
|
1854
|
+
const newStack = stack.enter(this.val.eval(stack), {}, true);
|
|
1855
|
+
return rx.renderIt(newStack, this, "", this.viewId);
|
|
1856
|
+
}
|
|
1857
|
+
toPathStep(ctx) {
|
|
1858
|
+
if (this.val instanceof DynVal) {
|
|
1859
|
+
const p = resolveDynProducer(ctx.comp, this.val.name);
|
|
1860
|
+
return p ? new DynStep(p.producerCompId, p.producerSteps) : null;
|
|
1861
|
+
}
|
|
1862
|
+
return super.toPathStep(ctx);
|
|
1863
|
+
}
|
|
1864
|
+
}
|
|
1865
|
+
|
|
1866
|
+
class RenderItNode extends RenderViewId {
|
|
1867
|
+
render(stack, rx) {
|
|
1868
|
+
const newStack = stack.enter(stack.it, {}, true);
|
|
1869
|
+
return rx.renderIt(newStack, this, "", this.viewId);
|
|
1870
|
+
}
|
|
1871
|
+
toPathStep(ctx) {
|
|
1872
|
+
const next = ctx.next();
|
|
1873
|
+
if (next === null)
|
|
1874
|
+
return null;
|
|
1875
|
+
const nextNode = next.resolveNode();
|
|
1876
|
+
if (nextNode instanceof EachNode && next.hasKey) {
|
|
1877
|
+
if (nextNode.val instanceof DynVal) {
|
|
1878
|
+
const p = resolveDynProducer(ctx.comp, nextNode.val.name);
|
|
1879
|
+
return p ? new DynEachStep(p.producerCompId, p.producerSteps, next.key) : null;
|
|
1880
|
+
}
|
|
1881
|
+
return new EachRenderItStep(nextNode.val.name, next.key);
|
|
1882
|
+
}
|
|
1883
|
+
return null;
|
|
1884
|
+
}
|
|
1885
|
+
}
|
|
1886
|
+
|
|
1887
|
+
class RenderEachNode extends RenderViewId {
|
|
1888
|
+
constructor(nodeId, val, viewId) {
|
|
1889
|
+
super(nodeId, val, viewId);
|
|
1890
|
+
this.iterInfo = new IterInfo(val, null, null, null);
|
|
1891
|
+
}
|
|
1892
|
+
render(stack, rx) {
|
|
1893
|
+
return rx.renderEach(stack, this.iterInfo, this, this.viewId);
|
|
1894
|
+
}
|
|
1895
|
+
toPathStep(ctx) {
|
|
1896
|
+
if (this.val instanceof DynVal) {
|
|
1897
|
+
if (!ctx.hasKey)
|
|
1898
|
+
return null;
|
|
1899
|
+
const p = resolveDynProducer(ctx.comp, this.val.name);
|
|
1900
|
+
return p ? new DynEachStep(p.producerCompId, p.producerSteps, ctx.key) : null;
|
|
1901
|
+
}
|
|
1902
|
+
return super.toPathStep(ctx);
|
|
1903
|
+
}
|
|
1904
|
+
static parse(px, vp2, s, as, attrs) {
|
|
1905
|
+
const node = px.addNodeIf(RenderEachNode, parseXOpVal("render-each", s, px, vp2.parseSequence), as);
|
|
1906
|
+
if (node !== null) {
|
|
1907
|
+
const attrParser = getAttrParser(px);
|
|
1908
|
+
attrParser.eachAttr = attrParser.pushWrapper("each", s, node.val);
|
|
1909
|
+
const when = attrs.getNamedItem("when");
|
|
1910
|
+
if (when)
|
|
1911
|
+
attrParser._parseWhen(when.value);
|
|
1912
|
+
const lWith = attrs.getNamedItem("loop-with");
|
|
1913
|
+
if (lWith)
|
|
1914
|
+
attrParser._parseLoopWith(lWith.value);
|
|
1915
|
+
node.iterInfo.whenVal = attrParser.eachAttr.whenVal ?? null;
|
|
1916
|
+
node.iterInfo.loopWithVal = attrParser.eachAttr.loopWithVal ?? null;
|
|
1917
|
+
}
|
|
1918
|
+
return node;
|
|
1919
|
+
}
|
|
1920
|
+
}
|
|
1921
|
+
|
|
1922
|
+
class RenderTextNode extends ANode {
|
|
1923
|
+
render(stack, _rx) {
|
|
1924
|
+
return this.val.eval(stack);
|
|
1925
|
+
}
|
|
1926
|
+
setDataAttr(_key, _val) {}
|
|
1927
|
+
}
|
|
1928
|
+
|
|
1929
|
+
class RenderOnceNode extends BaseNode {
|
|
1930
|
+
constructor(node) {
|
|
1931
|
+
super();
|
|
1932
|
+
this.node = node;
|
|
1933
|
+
this._render = (stack, rx) => {
|
|
1934
|
+
const dom = node.render(stack, rx);
|
|
1935
|
+
this._render = (_stack, _rx) => dom;
|
|
1936
|
+
return dom;
|
|
1937
|
+
};
|
|
1938
|
+
}
|
|
1939
|
+
render(stack, rx) {
|
|
1940
|
+
return this._render(stack, rx);
|
|
1941
|
+
}
|
|
1942
|
+
}
|
|
1943
|
+
|
|
1944
|
+
class WrapperNode extends ANode {
|
|
1945
|
+
constructor(nodeId, val, node = null) {
|
|
1946
|
+
super(nodeId, val);
|
|
1947
|
+
this.node = node;
|
|
1948
|
+
}
|
|
1949
|
+
wrapNode(node) {
|
|
1950
|
+
this.node = node;
|
|
1951
|
+
}
|
|
1952
|
+
setDataAttr(key, val) {
|
|
1953
|
+
this.node.setDataAttr(key, val);
|
|
1954
|
+
}
|
|
1955
|
+
optimize() {
|
|
1956
|
+
this.node = optimizeNode(this.node);
|
|
1957
|
+
}
|
|
1958
|
+
static register = false;
|
|
1959
|
+
}
|
|
1960
|
+
|
|
1961
|
+
class ShowNode extends WrapperNode {
|
|
1962
|
+
render(stack, rx) {
|
|
1963
|
+
return this.val.eval(stack) ? this.node.render(stack, rx) : null;
|
|
1964
|
+
}
|
|
1965
|
+
}
|
|
1966
|
+
|
|
1967
|
+
class HideNode extends WrapperNode {
|
|
1968
|
+
render(stack, rx) {
|
|
1969
|
+
return this.val.eval(stack) ? null : this.node.render(stack, rx);
|
|
1970
|
+
}
|
|
1971
|
+
}
|
|
1972
|
+
|
|
1973
|
+
class PushViewNameNode extends WrapperNode {
|
|
1974
|
+
render(stack, rx) {
|
|
1975
|
+
return this.node.render(stack.pushViewName(this.val.eval(stack)), rx);
|
|
1976
|
+
}
|
|
1977
|
+
}
|
|
1978
|
+
|
|
1979
|
+
class SlotNode extends WrapperNode {
|
|
1980
|
+
optimize() {
|
|
1981
|
+
this.node.optimize();
|
|
1982
|
+
}
|
|
1983
|
+
}
|
|
1984
|
+
|
|
1985
|
+
class ScopeNode extends WrapperNode {
|
|
1986
|
+
render(stack, rx) {
|
|
1987
|
+
const binds = this.val.evalAsHandler(stack)?.call(stack.it) ?? {};
|
|
1988
|
+
return this.node.render(stack.enter(stack.it, binds, false), rx);
|
|
1989
|
+
}
|
|
1990
|
+
toPathStep(_ctx) {
|
|
1991
|
+
return new BindStep({});
|
|
1992
|
+
}
|
|
1993
|
+
wrapNode(node) {
|
|
1994
|
+
this.node = node;
|
|
1995
|
+
this.node.setDataAttr("data-nid", this.nodeId);
|
|
1996
|
+
}
|
|
1997
|
+
static register = true;
|
|
1998
|
+
}
|
|
1999
|
+
|
|
2000
|
+
class EachNode extends WrapperNode {
|
|
2001
|
+
constructor(nodeId, val) {
|
|
2002
|
+
super(nodeId, val);
|
|
2003
|
+
this.iterInfo = new IterInfo(val, null, null, null);
|
|
2004
|
+
}
|
|
2005
|
+
render(stack, rx) {
|
|
2006
|
+
return rx.renderEachWhen(stack, this.iterInfo, this.node, this.nodeId);
|
|
2007
|
+
}
|
|
2008
|
+
toPathStep(ctx) {
|
|
2009
|
+
return ctx.hasKey ? new EachBindStep(this.val, ctx.key) : null;
|
|
2010
|
+
}
|
|
2011
|
+
static register = true;
|
|
2012
|
+
}
|
|
2013
|
+
|
|
2014
|
+
class IterInfo {
|
|
2015
|
+
constructor(val, whenVal, loopWithVal, enrichWithVal) {
|
|
2016
|
+
this.val = val;
|
|
2017
|
+
this.whenVal = whenVal;
|
|
2018
|
+
this.loopWithVal = loopWithVal;
|
|
2019
|
+
this.enrichWithVal = enrichWithVal;
|
|
2020
|
+
}
|
|
2021
|
+
eval(stack) {
|
|
2022
|
+
const seq = this.val.eval(stack) ?? [];
|
|
2023
|
+
const filter = this.whenVal?.evalAsHandler(stack) ?? filterAlwaysTrue;
|
|
2024
|
+
const loopWith = this.loopWithVal?.evalAsHandler(stack) ?? nullLoopWith;
|
|
2025
|
+
const enricher = this.enrichWithVal?.evalAsHandler(stack) ?? null;
|
|
2026
|
+
return { seq, filter, loopWith, enricher };
|
|
2027
|
+
}
|
|
2028
|
+
}
|
|
2029
|
+
var filterAlwaysTrue = (_v, _k, _seq) => true;
|
|
2030
|
+
var nullLoopWith = (seq) => ({ iterData: { seq } });
|
|
2031
|
+
var X_OP_CONSUMED = {
|
|
2032
|
+
slot: new Set,
|
|
2033
|
+
text: new Set,
|
|
2034
|
+
render: new Set(["as"]),
|
|
2035
|
+
"render-it": new Set(["as"]),
|
|
2036
|
+
"render-each": new Set(["as", "when", "loop-with"]),
|
|
2037
|
+
show: new Set,
|
|
2038
|
+
hide: new Set
|
|
2039
|
+
};
|
|
2040
|
+
var X_OP_WRAPPABLE = new Set(["text", "render", "render-it", "render-each"]);
|
|
2041
|
+
var X_ATTR_WRAPPERS = { show: ShowNode, hide: HideNode };
|
|
2042
|
+
var WRAPPER_NODES = {
|
|
2043
|
+
slot: SlotNode,
|
|
2044
|
+
show: ShowNode,
|
|
2045
|
+
hide: HideNode,
|
|
2046
|
+
each: EachNode,
|
|
2047
|
+
scope: ScopeNode,
|
|
2048
|
+
"push-view": PushViewNameNode
|
|
2049
|
+
};
|
|
2050
|
+
|
|
2051
|
+
class ParseContext {
|
|
2052
|
+
constructor(document2, Text, Comment, nodes, events, macroNodes, frame, parent) {
|
|
2053
|
+
this.nodes = nodes ?? [];
|
|
2054
|
+
this.events = events ?? [];
|
|
2055
|
+
this.macroNodes = macroNodes ?? [];
|
|
2056
|
+
this.parent = parent ?? null;
|
|
2057
|
+
this.frame = frame ?? {};
|
|
2058
|
+
this.document = document2 ?? globalThis.document;
|
|
2059
|
+
this.Text = Text ?? globalThis.Text;
|
|
2060
|
+
this.Comment = Comment ?? globalThis.Comment;
|
|
2061
|
+
this.cacheConstNodes = true;
|
|
2062
|
+
this.currentTag = null;
|
|
2063
|
+
}
|
|
2064
|
+
isInsideMacro(name) {
|
|
2065
|
+
return this.frame.macroName === name || this.parent?.isInsideMacro(name);
|
|
2066
|
+
}
|
|
2067
|
+
enterMacro(macroName, macroVars, macroSlots) {
|
|
2068
|
+
const { document: document2, Text, Comment, nodes, events, macroNodes } = this;
|
|
2069
|
+
const frame = { macroName, macroVars, macroSlots };
|
|
2070
|
+
return new ParseContext(document2, Text, Comment, nodes, events, macroNodes, frame, this);
|
|
2071
|
+
}
|
|
2072
|
+
parseHTML(html) {
|
|
2073
|
+
const t = this.document.createElement("template");
|
|
2074
|
+
t.innerHTML = html;
|
|
2075
|
+
return t.content.childNodes;
|
|
2076
|
+
}
|
|
2077
|
+
addNodeIf(Class, val, extra) {
|
|
2078
|
+
if (val !== null) {
|
|
2079
|
+
const nodeId = this.nodes.length;
|
|
2080
|
+
const node = new Class(nodeId, val, extra);
|
|
2081
|
+
this.nodes.push(node);
|
|
2082
|
+
return node;
|
|
2083
|
+
}
|
|
2084
|
+
return null;
|
|
2085
|
+
}
|
|
2086
|
+
registerEvents() {
|
|
2087
|
+
const id = this.events.length;
|
|
2088
|
+
const events = new NodeEvents(id);
|
|
2089
|
+
this.events.push(events);
|
|
2090
|
+
return events;
|
|
2091
|
+
}
|
|
2092
|
+
newMacroNode(macroName, mAttrs, childs) {
|
|
2093
|
+
const anySlot = [];
|
|
2094
|
+
const slots = { _: new FragmentNode(anySlot) };
|
|
2095
|
+
for (const child of childs)
|
|
2096
|
+
if (child instanceof SlotNode)
|
|
2097
|
+
slots[child.val.val] = child.node;
|
|
2098
|
+
else if (!(child instanceof TextNode) || !child.isWhiteSpace())
|
|
2099
|
+
anySlot.push(child);
|
|
2100
|
+
const node = new MacroNode(macroName, mAttrs, slots, this);
|
|
2101
|
+
this.macroNodes.push(node);
|
|
2102
|
+
return node;
|
|
2103
|
+
}
|
|
2104
|
+
compile(scope) {
|
|
2105
|
+
for (let i = 0;i < this.macroNodes.length; i++)
|
|
2106
|
+
this.macroNodes[i].compile(scope);
|
|
2107
|
+
}
|
|
2108
|
+
*genEventNames() {
|
|
2109
|
+
for (const event of this.events)
|
|
2110
|
+
yield* event.genEventNames();
|
|
2111
|
+
}
|
|
2112
|
+
getEventForId(id) {
|
|
2113
|
+
return this.events[id] ?? null;
|
|
2114
|
+
}
|
|
2115
|
+
getNodeForId(id) {
|
|
2116
|
+
return this.nodes[id] ?? null;
|
|
2117
|
+
}
|
|
2118
|
+
onAttributes(_attrs, _wrapperAttrs, _textChild, _isMacroCall, _tag) {}
|
|
2119
|
+
onParseIssue(kind, info) {
|
|
2120
|
+
console.warn(`tutuca parse issue [${kind}]`, info);
|
|
2121
|
+
}
|
|
2122
|
+
}
|
|
2123
|
+
var _htmlBlockTags = "ADDRESS,ARTICLE,ASIDE,BLOCKQUOTE,CAPTION,COL,COLGROUP,DETAILS,DIALOG,DIV,DD,DL,DT,FIELDSET,FIGCAPTION,FIGURE,FOOTER,FORM,H1,H2,H3,H4,H5,H6,HEADER,HGROUP,HR,LEGEND,LI,MAIN,MENU,NAV,OL,P,PRE,SECTION,SUMMARY,TABLE,TBODY,TD,TFOOT,TH,THEAD,TR,UL";
|
|
2124
|
+
var HTML_BLOCK_TAGS = new Set(_htmlBlockTags.split(","));
|
|
2125
|
+
var isBlockDomNode = (n) => {
|
|
2126
|
+
const node = n instanceof FragmentNode ? n.childs[0] : n;
|
|
2127
|
+
return node instanceof DomNode && HTML_BLOCK_TAGS.has(node.tagName);
|
|
2128
|
+
};
|
|
2129
|
+
function condenseChildsWhites(childs) {
|
|
2130
|
+
if (childs.length === 0)
|
|
2131
|
+
return childs;
|
|
2132
|
+
let changed = false;
|
|
2133
|
+
if (childs[0].isWhiteSpace?.()) {
|
|
2134
|
+
childs[0].condenseWhiteSpace();
|
|
2135
|
+
changed = true;
|
|
2136
|
+
}
|
|
2137
|
+
const last = childs.length - 1;
|
|
2138
|
+
if (last > 0 && childs[last].isWhiteSpace?.()) {
|
|
2139
|
+
childs[last].condenseWhiteSpace();
|
|
2140
|
+
changed = true;
|
|
2141
|
+
}
|
|
2142
|
+
for (let i = 1;i < last; i++) {
|
|
2143
|
+
const cur = childs[i];
|
|
2144
|
+
if (cur.isWhiteSpace?.() && cur.hasNewLine()) {
|
|
2145
|
+
const bothBlock = isBlockDomNode(childs[i - 1]) && isBlockDomNode(childs[i + 1]);
|
|
2146
|
+
cur.condenseWhiteSpace(bothBlock ? "" : " ");
|
|
2147
|
+
if (bothBlock)
|
|
2148
|
+
changed = true;
|
|
2149
|
+
}
|
|
2150
|
+
}
|
|
2151
|
+
return changed ? childs.filter((c) => !(c instanceof TextNode && c.val === "")) : childs;
|
|
2152
|
+
}
|
|
2153
|
+
|
|
2154
|
+
class View {
|
|
2155
|
+
constructor(name, rawView = "No View Defined", style = "", anode = null, ctx = null) {
|
|
2156
|
+
this.name = name;
|
|
2157
|
+
this.anode = anode;
|
|
2158
|
+
this.style = style;
|
|
2159
|
+
this.ctx = ctx;
|
|
2160
|
+
this.rawView = rawView;
|
|
2161
|
+
}
|
|
2162
|
+
compile(ctx, scope, cid) {
|
|
2163
|
+
this.ctx = ctx;
|
|
2164
|
+
this.anode = ANode.parse(this.rawView, ctx);
|
|
2165
|
+
this.anode.setDataAttr("data-cid", cid);
|
|
2166
|
+
this.anode.setDataAttr("data-vid", this.name);
|
|
2167
|
+
this.ctx.compile(scope);
|
|
2168
|
+
if (ctx.cacheConstNodes)
|
|
2169
|
+
this.anode = optimizeNode(this.anode);
|
|
2170
|
+
}
|
|
2171
|
+
render(stack, rx) {
|
|
2172
|
+
return this.anode.render(stack, rx);
|
|
2173
|
+
}
|
|
2174
|
+
}
|
|
2175
|
+
|
|
2176
|
+
class NodeEvents {
|
|
2177
|
+
constructor(id) {
|
|
2178
|
+
this.id = id;
|
|
2179
|
+
this.handlers = [];
|
|
2180
|
+
}
|
|
2181
|
+
add(name, handlerCall, modifiers) {
|
|
2182
|
+
this.handlers.push(new NodeEvent(name, handlerCall, modifiers));
|
|
2183
|
+
}
|
|
2184
|
+
*genEventNames() {
|
|
2185
|
+
for (const handler of this.handlers)
|
|
2186
|
+
yield handler.name;
|
|
2187
|
+
}
|
|
2188
|
+
getHandlersFor(eventName) {
|
|
2189
|
+
let r = null;
|
|
2190
|
+
for (const handler of this.handlers)
|
|
2191
|
+
if (handler.handlesEventName(eventName)) {
|
|
2192
|
+
r ??= [];
|
|
2193
|
+
r.push(handler);
|
|
2194
|
+
}
|
|
2195
|
+
return r;
|
|
2196
|
+
}
|
|
2197
|
+
}
|
|
2198
|
+
|
|
2199
|
+
class NodeEvent {
|
|
2200
|
+
constructor(name, handlerCall, modifiers) {
|
|
2201
|
+
this.name = name;
|
|
2202
|
+
this.handlerCall = handlerCall;
|
|
2203
|
+
this.modifierWrapper = compileModifiers(name, modifiers);
|
|
2204
|
+
this.modifiers = modifiers;
|
|
2205
|
+
}
|
|
2206
|
+
handlesEventName(name) {
|
|
2207
|
+
return this.name === name;
|
|
2208
|
+
}
|
|
2209
|
+
getHandlerAndArgs(stack, event) {
|
|
2210
|
+
const r = this.handlerCall.getHandlerAndArgs(stack, event);
|
|
2211
|
+
r[0] = this.modifierWrapper(r[0], event);
|
|
2212
|
+
return r;
|
|
2213
|
+
}
|
|
2214
|
+
}
|
|
2215
|
+
var isMac = (globalThis.navigator?.userAgent ?? "").toLowerCase().includes("mac");
|
|
2216
|
+
var fwdIfCtxPred = (pred) => (w) => (that, f, args, ctx) => pred(ctx) ? w(that, f, args, ctx) : that;
|
|
2217
|
+
var fwdIfKey = (keyName) => fwdIfCtxPred((ctx) => ctx.e.key === keyName);
|
|
2218
|
+
var fwdCtrl = fwdIfCtxPred(({ e }) => isMac && e.metaKey || e.ctrlKey);
|
|
2219
|
+
var fwdMeta = fwdIfCtxPred(({ e }) => e.metaKey);
|
|
2220
|
+
var fwdAlt = fwdIfCtxPred(({ e }) => e.altKey);
|
|
2221
|
+
var metaWraps = { ctrl: fwdCtrl, cmd: fwdCtrl, meta: fwdMeta, alt: fwdAlt };
|
|
2222
|
+
var MOD_WRAPPERS_BY_EVENT = {
|
|
2223
|
+
keydown: {
|
|
2224
|
+
send: fwdIfKey("Enter"),
|
|
2225
|
+
cancel: fwdIfKey("Escape"),
|
|
2226
|
+
...metaWraps
|
|
2227
|
+
},
|
|
2228
|
+
click: { ...metaWraps }
|
|
2229
|
+
};
|
|
2230
|
+
var identityModifierWrapper = (f, _ctx) => f;
|
|
2231
|
+
function compileModifiers(eventName, names) {
|
|
2232
|
+
if (names.length === 0)
|
|
2233
|
+
return identityModifierWrapper;
|
|
2234
|
+
const wrappers = MOD_WRAPPERS_BY_EVENT[eventName] ?? {};
|
|
2235
|
+
let w = (that, f, args, _ctx) => f.apply(that, args);
|
|
2236
|
+
for (const name of names) {
|
|
2237
|
+
const wrapper = wrappers[name];
|
|
2238
|
+
if (wrapper !== undefined)
|
|
2239
|
+
w = wrapper(w);
|
|
2240
|
+
}
|
|
2241
|
+
return (f, ctx) => function(...args) {
|
|
2242
|
+
return w(this, f, args, ctx);
|
|
2243
|
+
};
|
|
2244
|
+
}
|
|
2245
|
+
|
|
2246
|
+
// src/components.js
|
|
2247
|
+
class Components {
|
|
2248
|
+
constructor() {
|
|
2249
|
+
this.getComponentSymbol = Symbol("getComponent");
|
|
2250
|
+
this.byId = new Map;
|
|
2251
|
+
}
|
|
2252
|
+
registerComponent(comp) {
|
|
2253
|
+
comp.Class.prototype[this.getComponentSymbol] = () => comp;
|
|
2254
|
+
this.byId.set(comp.id, comp);
|
|
2255
|
+
}
|
|
2256
|
+
getComponentForId(id) {
|
|
2257
|
+
return this.byId.get(id) ?? null;
|
|
2258
|
+
}
|
|
2259
|
+
getCompFor(v) {
|
|
2260
|
+
return v?.[this.getComponentSymbol]?.() ?? null;
|
|
2261
|
+
}
|
|
2262
|
+
getOnEnterFor(v) {
|
|
2263
|
+
return this.getCompFor(v)?.on.stackEnter ?? defaultOnStackEnter;
|
|
2264
|
+
}
|
|
2265
|
+
getHandlerFor(v, name, key) {
|
|
2266
|
+
return this.getCompFor(v)?.[key][name] ?? null;
|
|
2267
|
+
}
|
|
2268
|
+
getRequestFor(v, name) {
|
|
2269
|
+
return this.getCompFor(v)?.scope.lookupRequest(name) ?? null;
|
|
2270
|
+
}
|
|
2271
|
+
compileStyles() {
|
|
2272
|
+
const styles = [];
|
|
2273
|
+
for (const comp of this.byId.values())
|
|
2274
|
+
styles.push(comp.compileStyle());
|
|
2275
|
+
return styles.join(`
|
|
2276
|
+
`);
|
|
2277
|
+
}
|
|
2278
|
+
}
|
|
2279
|
+
|
|
2280
|
+
class ComponentStack {
|
|
2281
|
+
constructor(comps = new Components, parent = null) {
|
|
2282
|
+
this.comps = comps;
|
|
2283
|
+
this.parent = parent;
|
|
2284
|
+
this.byName = {};
|
|
2285
|
+
this.reqsByName = {};
|
|
2286
|
+
this.macros = {};
|
|
2287
|
+
}
|
|
2288
|
+
enter() {
|
|
2289
|
+
return new ComponentStack(this.comps, this);
|
|
2290
|
+
}
|
|
2291
|
+
registerComponents(comps, opts) {
|
|
2292
|
+
const { aliases = {} } = opts ?? {};
|
|
2293
|
+
for (let i = 0;i < comps.length; i++) {
|
|
2294
|
+
const comp = comps[i];
|
|
2295
|
+
comp.scope = this.enter();
|
|
2296
|
+
this.comps.registerComponent(comp);
|
|
2297
|
+
this.byName[comp.name] = comp;
|
|
2298
|
+
}
|
|
2299
|
+
for (const alias in aliases) {
|
|
2300
|
+
const comp = this.byName[aliases[alias]];
|
|
2301
|
+
console.assert(this.byName[alias] === undefined, "alias overrides component", alias);
|
|
2302
|
+
if (comp !== undefined)
|
|
2303
|
+
this.byName[alias] = comp;
|
|
2304
|
+
else
|
|
2305
|
+
console.warn("alias", alias, "to inexistent component", aliases[alias]);
|
|
2306
|
+
}
|
|
2307
|
+
}
|
|
2308
|
+
registerMacros(macros) {
|
|
2309
|
+
for (const key in macros) {
|
|
2310
|
+
const lower = key.toLowerCase();
|
|
2311
|
+
console.assert(this.macros[lower] === undefined, "macro key collision", lower);
|
|
2312
|
+
this.macros[lower] = macros[key];
|
|
2313
|
+
}
|
|
2314
|
+
}
|
|
2315
|
+
getCompFor(v) {
|
|
2316
|
+
return this.comps.getCompFor(v);
|
|
2317
|
+
}
|
|
2318
|
+
registerRequestHandlers(handlers) {
|
|
2319
|
+
for (const name in handlers)
|
|
2320
|
+
this.reqsByName[name] = new RequestHandler(name, handlers[name]);
|
|
2321
|
+
}
|
|
2322
|
+
lookupRequest(name) {
|
|
2323
|
+
return this.reqsByName[name] ?? this.parent?.lookupRequest(name) ?? null;
|
|
2324
|
+
}
|
|
2325
|
+
lookupComponent(name) {
|
|
2326
|
+
return this.byName[name] ?? this.parent?.lookupComponent(name) ?? null;
|
|
2327
|
+
}
|
|
2328
|
+
lookupMacro(name) {
|
|
2329
|
+
return this.macros[name] ?? this.parent?.lookupMacro(name) ?? null;
|
|
2330
|
+
}
|
|
2331
|
+
}
|
|
2332
|
+
|
|
2333
|
+
class Dynamic {
|
|
2334
|
+
constructor(name, val, symbol) {
|
|
2335
|
+
this.name = name;
|
|
2336
|
+
this.val = val;
|
|
2337
|
+
this.symbol = symbol;
|
|
2338
|
+
}
|
|
2339
|
+
getSymbol(_stack) {
|
|
2340
|
+
return this.symbol;
|
|
2341
|
+
}
|
|
2342
|
+
evalAndBind(stack, binds) {
|
|
2343
|
+
binds[this.getSymbol(stack)] = this.val.eval(stack);
|
|
2344
|
+
}
|
|
2345
|
+
}
|
|
2346
|
+
|
|
2347
|
+
class DynamicAlias extends Dynamic {
|
|
2348
|
+
constructor(name, val, compName, dynName) {
|
|
2349
|
+
super(name, val, null);
|
|
2350
|
+
this.compName = compName;
|
|
2351
|
+
this.dynName = dynName;
|
|
2352
|
+
}
|
|
2353
|
+
_resolveSymbol(stack) {
|
|
2354
|
+
return stack.lookupType(this.compName)?.dynamic[this.dynName]?.symbol ?? null;
|
|
2355
|
+
}
|
|
2356
|
+
getSymbol(stack) {
|
|
2357
|
+
this.symbol ??= this._resolveSymbol(stack);
|
|
2358
|
+
return this.symbol;
|
|
2359
|
+
}
|
|
2360
|
+
}
|
|
2361
|
+
var isString = (v) => typeof v === "string";
|
|
2362
|
+
var _rawSpecKeys = "name view style commonStyle globalStyle input receive bubble response alter on views dynamic fields methods statics";
|
|
2363
|
+
var KNOWN_SPEC_KEYS = new Set(_rawSpecKeys.split(" "));
|
|
2364
|
+
var _compId = 0;
|
|
2365
|
+
|
|
2366
|
+
class Component {
|
|
2367
|
+
constructor(Class, o) {
|
|
2368
|
+
this.id = _compId++;
|
|
2369
|
+
this.name = o.name ?? "UnkComp";
|
|
2370
|
+
this.Class = Class;
|
|
2371
|
+
this.views = { main: new View("main", o.view, o.style) };
|
|
2372
|
+
this.commonStyle = o.commonStyle ?? "";
|
|
2373
|
+
this.globalStyle = o.globalStyle ?? "";
|
|
2374
|
+
this.input = o.input ?? {};
|
|
2375
|
+
this.receive = o.receive ?? {};
|
|
2376
|
+
this.bubble = o.bubble ?? {};
|
|
2377
|
+
this.response = o.response ?? {};
|
|
2378
|
+
this.alter = o.alter ?? {};
|
|
2379
|
+
this.on = { stackEnter: o.on?.stackEnter ?? defaultOnStackEnter };
|
|
2380
|
+
for (const name in o.views ?? {}) {
|
|
2381
|
+
const v = o.views[name];
|
|
2382
|
+
const { view, style } = isString(v) ? { view: v } : v;
|
|
2383
|
+
this.views[name] = new View(name, view, style);
|
|
2384
|
+
}
|
|
2385
|
+
this._rawDynamic = o.dynamic ?? {};
|
|
2386
|
+
this.dynamic = {};
|
|
2387
|
+
this.scope = null;
|
|
2388
|
+
this.extra = {};
|
|
2389
|
+
for (const key of Object.keys(o))
|
|
2390
|
+
if (!KNOWN_SPEC_KEYS.has(key))
|
|
2391
|
+
this.extra[key] = o[key];
|
|
2392
|
+
}
|
|
2393
|
+
compile(ParseContext2) {
|
|
2394
|
+
for (const name in this.views)
|
|
2395
|
+
this.views[name].compile(new ParseContext2, this.scope, this.id);
|
|
2396
|
+
for (const key in this._rawDynamic) {
|
|
2397
|
+
const dinfo = this._rawDynamic[key];
|
|
2398
|
+
if (isString(dinfo)) {
|
|
2399
|
+
const val = vp.parseField(dinfo, this.views.main.ctx);
|
|
2400
|
+
this.dynamic[key] = new Dynamic(key, val, Symbol(key));
|
|
2401
|
+
} else if (isString(dinfo?.default) && isString(dinfo?.for)) {
|
|
2402
|
+
const val = vp.parseField(dinfo.default, this.views.main.ctx);
|
|
2403
|
+
const [compName, dynName] = dinfo.for.split(".");
|
|
2404
|
+
if (isString(compName) && isString(dynName))
|
|
2405
|
+
this.dynamic[key] = new DynamicAlias(key, val, compName, dynName);
|
|
2406
|
+
}
|
|
2407
|
+
}
|
|
2408
|
+
}
|
|
2409
|
+
make(args, opts) {
|
|
2410
|
+
return this.Class.make(args, opts ?? { scope: this.scope });
|
|
2411
|
+
}
|
|
2412
|
+
getView(name) {
|
|
2413
|
+
return this.views[name] ?? this.views.main;
|
|
2414
|
+
}
|
|
2415
|
+
getEventForId(id, name = "main") {
|
|
2416
|
+
return this.getView(name).ctx.getEventForId(id);
|
|
2417
|
+
}
|
|
2418
|
+
getNodeForId(id, name = "main") {
|
|
2419
|
+
return this.getView(name).ctx.getNodeForId(id);
|
|
2420
|
+
}
|
|
2421
|
+
compileStyle() {
|
|
2422
|
+
const { id, commonStyle, globalStyle, views } = this;
|
|
2423
|
+
const styles = commonStyle ? [`[data-cid="${id}"]{${commonStyle}}`] : [];
|
|
2424
|
+
if (globalStyle !== "")
|
|
2425
|
+
styles.push(globalStyle);
|
|
2426
|
+
for (const name in views) {
|
|
2427
|
+
const { style } = views[name];
|
|
2428
|
+
if (style !== "")
|
|
2429
|
+
styles.push(`[data-cid="${id}"][data-vid="${name}"]{${style}}`);
|
|
2430
|
+
}
|
|
2431
|
+
return styles.join(`
|
|
2432
|
+
`);
|
|
2433
|
+
}
|
|
2434
|
+
}
|
|
2435
|
+
function defaultOnStackEnter() {
|
|
2436
|
+
return null;
|
|
2437
|
+
}
|
|
2438
|
+
|
|
2439
|
+
// src/stack.js
|
|
2440
|
+
var STOP = Symbol("STOP");
|
|
2441
|
+
var NEXT = Symbol("NEXT");
|
|
2442
|
+
function lookup(chain, name, dv = null) {
|
|
2443
|
+
let n = chain;
|
|
2444
|
+
while (n !== null) {
|
|
2445
|
+
const r = n[0].lookup(name);
|
|
2446
|
+
if (r === STOP)
|
|
2447
|
+
return dv;
|
|
2448
|
+
if (r !== NEXT)
|
|
2449
|
+
return r;
|
|
2450
|
+
n = n[1];
|
|
2451
|
+
}
|
|
2452
|
+
return dv;
|
|
2453
|
+
}
|
|
2454
|
+
|
|
2455
|
+
class BindFrame {
|
|
2456
|
+
constructor(it, binds, isFrame) {
|
|
2457
|
+
this.it = it;
|
|
2458
|
+
this.binds = binds;
|
|
2459
|
+
this.isFrame = isFrame;
|
|
2460
|
+
}
|
|
2461
|
+
lookup(name) {
|
|
2462
|
+
const v = this.binds[name];
|
|
2463
|
+
return v === undefined ? this.isFrame ? STOP : NEXT : v;
|
|
2464
|
+
}
|
|
2465
|
+
}
|
|
2466
|
+
|
|
2467
|
+
class ObjectFrame {
|
|
2468
|
+
constructor(binds) {
|
|
2469
|
+
this.binds = binds;
|
|
2470
|
+
}
|
|
2471
|
+
lookup(key) {
|
|
2472
|
+
const v = this.binds[key];
|
|
2473
|
+
return v === undefined ? NEXT : v;
|
|
2474
|
+
}
|
|
2475
|
+
}
|
|
2476
|
+
function computeViewsId(views) {
|
|
2477
|
+
let s = "";
|
|
2478
|
+
let n = views;
|
|
2479
|
+
while (n !== null) {
|
|
2480
|
+
s += n[0];
|
|
2481
|
+
n = n[1];
|
|
2482
|
+
}
|
|
2483
|
+
return s === "main" ? "" : s;
|
|
2484
|
+
}
|
|
2485
|
+
|
|
2486
|
+
class Stack {
|
|
2487
|
+
constructor(comps, it, binds, dynBinds, views, viewsId, ctx = null) {
|
|
2488
|
+
this.comps = comps;
|
|
2489
|
+
this.it = it;
|
|
2490
|
+
this.binds = binds;
|
|
2491
|
+
this.dynBinds = dynBinds;
|
|
2492
|
+
this.views = views;
|
|
2493
|
+
this.viewsId = viewsId;
|
|
2494
|
+
this.ctx = ctx;
|
|
2495
|
+
}
|
|
2496
|
+
_enrichOnEnter() {
|
|
2497
|
+
return this.withDynamicBinds(this.comps.getOnEnterFor(this.it).call(this.it));
|
|
2498
|
+
}
|
|
2499
|
+
static root(comps, it, ctx) {
|
|
2500
|
+
const binds = [new BindFrame(it, { it }, true), null];
|
|
2501
|
+
const dynBinds = [new ObjectFrame({}), null];
|
|
2502
|
+
const views = ["main", null];
|
|
2503
|
+
return new Stack(comps, it, binds, dynBinds, views, "", ctx)._enrichOnEnter();
|
|
2504
|
+
}
|
|
2505
|
+
enter(it, bindings = {}, isFrame = true) {
|
|
2506
|
+
const { comps, binds, dynBinds, views, viewsId, ctx } = this;
|
|
2507
|
+
const newBinds = [new BindFrame(it, bindings, isFrame), binds];
|
|
2508
|
+
const stack = new Stack(comps, it, newBinds, dynBinds, views, viewsId, ctx);
|
|
2509
|
+
return isFrame ? stack._enrichOnEnter() : stack;
|
|
2510
|
+
}
|
|
2511
|
+
pushViewName(name) {
|
|
2512
|
+
const { comps, it, binds, dynBinds, views, ctx } = this;
|
|
2513
|
+
const newViews = [name, views];
|
|
2514
|
+
return new Stack(comps, it, binds, dynBinds, newViews, computeViewsId(newViews), ctx);
|
|
2515
|
+
}
|
|
2516
|
+
withDynamicBinds(dynamics) {
|
|
2517
|
+
if (dynamics == null || dynamics.length === 0)
|
|
2518
|
+
return this;
|
|
2519
|
+
const dynObj = {};
|
|
2520
|
+
const comp = this.comps.getCompFor(this.it);
|
|
2521
|
+
for (const dynName of dynamics)
|
|
2522
|
+
comp.dynamic[dynName].evalAndBind(this, dynObj);
|
|
2523
|
+
const newDynBinds = [new ObjectFrame(dynObj), this.dynBinds];
|
|
2524
|
+
const { comps, it, binds, views, viewsId, ctx } = this;
|
|
2525
|
+
return new Stack(comps, it, binds, newDynBinds, views, viewsId, ctx);
|
|
2526
|
+
}
|
|
2527
|
+
_pushDynBindValuesToArray(arr, dyns) {
|
|
2528
|
+
for (const k in dyns)
|
|
2529
|
+
arr.push(this._lookupDynamicWithDynVal(dyns[k]));
|
|
2530
|
+
}
|
|
2531
|
+
_lookupDynamicWithDynVal(d) {
|
|
2532
|
+
return lookup(this.dynBinds, d.getSymbol(this)) ?? d.val?.eval(this) ?? null;
|
|
2533
|
+
}
|
|
2534
|
+
lookupDynamic(name) {
|
|
2535
|
+
const d = this.comps.getCompFor(this.it)?.dynamic[name];
|
|
2536
|
+
return d ? this._lookupDynamicWithDynVal(d) : null;
|
|
2537
|
+
}
|
|
2538
|
+
lookupBind(name) {
|
|
2539
|
+
return lookup(this.binds, name);
|
|
2540
|
+
}
|
|
2541
|
+
lookupType(name) {
|
|
2542
|
+
return this.comps.getCompFor(this.it).scope.lookupComponent(name);
|
|
2543
|
+
}
|
|
2544
|
+
lookupFieldRaw(name) {
|
|
2545
|
+
return this.it[name] ?? null;
|
|
2546
|
+
}
|
|
2547
|
+
lookupMethod(name) {
|
|
2548
|
+
const fn = this.it[name];
|
|
2549
|
+
return fn instanceof Function ? fn.call(this.it) : null;
|
|
2550
|
+
}
|
|
2551
|
+
lookupName(name) {
|
|
2552
|
+
return this.ctx.lookupName(name);
|
|
2553
|
+
}
|
|
2554
|
+
getHandlerFor(name, key) {
|
|
2555
|
+
return this.comps.getHandlerFor(this.it, name, key);
|
|
2556
|
+
}
|
|
2557
|
+
lookupRequest(name) {
|
|
2558
|
+
return this.comps.getRequestFor(this.it, name);
|
|
2559
|
+
}
|
|
2560
|
+
lookupBestView(views, defaultViewName) {
|
|
2561
|
+
let n = this.views;
|
|
2562
|
+
while (n !== null) {
|
|
2563
|
+
const view = views[n[0]];
|
|
2564
|
+
if (view !== undefined)
|
|
2565
|
+
return view;
|
|
2566
|
+
n = n[1];
|
|
2567
|
+
}
|
|
2568
|
+
return views[defaultViewName];
|
|
2569
|
+
}
|
|
2570
|
+
}
|
|
2571
|
+
|
|
2572
|
+
// src/transactor.js
|
|
2573
|
+
class State {
|
|
2574
|
+
constructor(val) {
|
|
2575
|
+
this.val = val;
|
|
2576
|
+
this.changeSubs = [];
|
|
2577
|
+
}
|
|
2578
|
+
onChange(cb) {
|
|
2579
|
+
this.changeSubs.push(cb);
|
|
2580
|
+
}
|
|
2581
|
+
set(val, info) {
|
|
2582
|
+
const old = this.val;
|
|
2583
|
+
this.val = val;
|
|
2584
|
+
for (const sub of this.changeSubs)
|
|
2585
|
+
sub({ val, old, info, timestamp: Date.now() });
|
|
2586
|
+
}
|
|
2587
|
+
update(fn, info) {
|
|
2588
|
+
return this.set(fn(this.val), info);
|
|
2589
|
+
}
|
|
2590
|
+
}
|
|
2591
|
+
|
|
2592
|
+
class Transactor {
|
|
2593
|
+
constructor(comps, rootValue) {
|
|
2594
|
+
this.comps = comps;
|
|
2595
|
+
this.transactions = [];
|
|
2596
|
+
this.state = new State(rootValue);
|
|
2597
|
+
this.onTransactionPushed = () => {};
|
|
2598
|
+
}
|
|
2599
|
+
pushTransaction(t) {
|
|
2600
|
+
this.transactions.push(t);
|
|
2601
|
+
this.onTransactionPushed(t);
|
|
2602
|
+
}
|
|
2603
|
+
pushSend(path, name, args = [], opts = {}, parent = null) {
|
|
2604
|
+
this.pushTransaction(new SendEvent(path, this, name, args, parent, opts));
|
|
2605
|
+
}
|
|
2606
|
+
pushBubble(path, name, args = [], opts = {}, parent = null, targetPath = null) {
|
|
2607
|
+
const newOpts = opts.skipSelf ? { ...opts, skipSelf: false } : opts;
|
|
2608
|
+
this.pushTransaction(new BubbleEvent(path, this, name, args, parent, newOpts, targetPath));
|
|
2609
|
+
}
|
|
2610
|
+
async pushRequest(path, name, args = [], opts = {}, parent = null) {
|
|
2611
|
+
const curRoot = this.state.val;
|
|
2612
|
+
const curLeaf = path.toTransactionPath().lookup(curRoot);
|
|
2613
|
+
const handler = this.comps.getRequestFor(curLeaf, name) ?? mkReq404(name);
|
|
2614
|
+
const resHandlerName = opts?.onResName ?? name;
|
|
2615
|
+
const push = (specificName, baseName, singleArg, result, error) => {
|
|
2616
|
+
const resArgs = specificName ? [singleArg] : [result, error];
|
|
2617
|
+
const t = new ResponseEvent(path, this, specificName ?? baseName, resArgs, parent);
|
|
2618
|
+
this.pushTransaction(t);
|
|
2619
|
+
};
|
|
2620
|
+
try {
|
|
2621
|
+
const result = await handler.fn.apply(null, args);
|
|
2622
|
+
push(opts?.onOkName, resHandlerName, result, result, null);
|
|
2623
|
+
} catch (error) {
|
|
2624
|
+
push(opts?.onErrorName, resHandlerName, error, null, error);
|
|
2625
|
+
}
|
|
2626
|
+
}
|
|
2627
|
+
get hasPendingTransactions() {
|
|
2628
|
+
return this.transactions.length > 0;
|
|
2629
|
+
}
|
|
2630
|
+
transactNext() {
|
|
2631
|
+
if (this.hasPendingTransactions)
|
|
2632
|
+
this.transact(this.transactions.shift());
|
|
2633
|
+
}
|
|
2634
|
+
transact(transaction) {
|
|
2635
|
+
const curState = this.state.val;
|
|
2636
|
+
const newState = transaction.run(curState, this.comps);
|
|
2637
|
+
if (newState !== undefined) {
|
|
2638
|
+
this.state.set(newState, { transaction });
|
|
2639
|
+
transaction.afterTransaction();
|
|
2640
|
+
} else
|
|
2641
|
+
console.warn("undefined new state", { curState, transaction });
|
|
2642
|
+
}
|
|
2643
|
+
transactInputNow(path, event, eventHandler, dragInfo) {
|
|
2644
|
+
this.transact(new InputEvent(path, event, eventHandler, this, dragInfo));
|
|
2645
|
+
}
|
|
2646
|
+
}
|
|
2647
|
+
function mkReq404(name) {
|
|
2648
|
+
const fn = () => {
|
|
2649
|
+
throw new Error(`Request not found: ${name}`);
|
|
2650
|
+
};
|
|
2651
|
+
return { fn };
|
|
2652
|
+
}
|
|
2653
|
+
function nullHandler() {
|
|
2654
|
+
return this;
|
|
2655
|
+
}
|
|
2656
|
+
|
|
2657
|
+
class Transaction {
|
|
2658
|
+
constructor(path, transactor, parentTransaction = null) {
|
|
2659
|
+
this.path = path;
|
|
2660
|
+
this.transactor = transactor;
|
|
2661
|
+
this.parentTransaction = parentTransaction;
|
|
2662
|
+
this._task = null;
|
|
2663
|
+
}
|
|
2664
|
+
get task() {
|
|
2665
|
+
this._task ??= new Task;
|
|
2666
|
+
return this._task;
|
|
2667
|
+
}
|
|
2668
|
+
getCompletionPromise() {
|
|
2669
|
+
return this.task.promise;
|
|
2670
|
+
}
|
|
2671
|
+
setParent(parentTransaction) {
|
|
2672
|
+
this.parentTransaction = parentTransaction;
|
|
2673
|
+
parentTransaction.task.addDep(this.task);
|
|
2674
|
+
}
|
|
2675
|
+
run(rootValue, comps) {
|
|
2676
|
+
return this.updateRootValue(rootValue, comps);
|
|
2677
|
+
}
|
|
2678
|
+
afterTransaction() {}
|
|
2679
|
+
buildRootStack(root, comps) {
|
|
2680
|
+
return Stack.root(comps, root);
|
|
2681
|
+
}
|
|
2682
|
+
buildStack(root, comps) {
|
|
2683
|
+
return this.path.toTransactionPath().buildStack(this.buildRootStack(root, comps));
|
|
2684
|
+
}
|
|
2685
|
+
callHandler(root, instance, comps) {
|
|
2686
|
+
const [handler, args] = this.getHandlerAndArgs(root, instance, comps);
|
|
2687
|
+
return handler.apply(instance, args);
|
|
2688
|
+
}
|
|
2689
|
+
getHandlerAndArgs(_root, _instance, _comps) {
|
|
2690
|
+
return null;
|
|
2691
|
+
}
|
|
2692
|
+
updateRootValue(curRoot, comps) {
|
|
2693
|
+
const txnPath = this.path.toTransactionPath();
|
|
2694
|
+
const curLeaf = txnPath.lookup(curRoot);
|
|
2695
|
+
const newLeaf = this.callHandler(curRoot, curLeaf, comps);
|
|
2696
|
+
this._task?.complete?.({ value: newLeaf, old: curLeaf });
|
|
2697
|
+
return curLeaf !== newLeaf ? txnPath.setValue(curRoot, newLeaf) : curRoot;
|
|
2698
|
+
}
|
|
2699
|
+
lookupName(_name) {
|
|
2700
|
+
return null;
|
|
2701
|
+
}
|
|
2702
|
+
}
|
|
2703
|
+
var isMac2 = (globalThis.navigator?.userAgent ?? "").toLowerCase().includes("mac");
|
|
2704
|
+
var toNullIfNaN = (v) => Number.isNaN(v) ? null : v;
|
|
2705
|
+
function getValue(e) {
|
|
2706
|
+
return e.target.type === "checkbox" ? e.target.checked : (e instanceof CustomEvent ? e.detail : e.target.value) ?? null;
|
|
2707
|
+
}
|
|
2708
|
+
|
|
2709
|
+
class InputEvent extends Transaction {
|
|
2710
|
+
constructor(path, e, handler, transactor, dragInfo) {
|
|
2711
|
+
super(path, transactor);
|
|
2712
|
+
this.e = e;
|
|
2713
|
+
this.handler = handler;
|
|
2714
|
+
this.dragInfo = dragInfo;
|
|
2715
|
+
this._dispatchPath = null;
|
|
2716
|
+
}
|
|
2717
|
+
get dispatchPath() {
|
|
2718
|
+
this._dispatchPath ??= this.path.compact();
|
|
2719
|
+
return this._dispatchPath;
|
|
2720
|
+
}
|
|
2721
|
+
buildRootStack(root, comps) {
|
|
2722
|
+
return Stack.root(comps, root, this);
|
|
2723
|
+
}
|
|
2724
|
+
getHandlerAndArgs(root, _instance, comps) {
|
|
2725
|
+
const stack = this.buildStack(root, comps);
|
|
2726
|
+
const [handler, args] = this.handler.getHandlerAndArgs(stack, this);
|
|
2727
|
+
const path = this.dispatchPath;
|
|
2728
|
+
let dispatcher;
|
|
2729
|
+
for (let i = 0;i < args.length; i++) {
|
|
2730
|
+
if (args[i]?.toHandlerArg) {
|
|
2731
|
+
dispatcher ??= new Dispatcher(path, this.transactor, this);
|
|
2732
|
+
args[i] = args[i].toHandlerArg(dispatcher);
|
|
2733
|
+
}
|
|
2734
|
+
}
|
|
2735
|
+
args.push(new EventContext(path, this.transactor, this));
|
|
2736
|
+
return [handler, args];
|
|
2737
|
+
}
|
|
2738
|
+
lookupName(name) {
|
|
2739
|
+
const { e } = this;
|
|
2740
|
+
switch (name) {
|
|
2741
|
+
case "value":
|
|
2742
|
+
return getValue(e);
|
|
2743
|
+
case "valueAsInt":
|
|
2744
|
+
return toNullIfNaN(parseInt(getValue(e), 10));
|
|
2745
|
+
case "valueAsFloat":
|
|
2746
|
+
return toNullIfNaN(parseFloat(getValue(e)));
|
|
2747
|
+
case "target":
|
|
2748
|
+
return e.target;
|
|
2749
|
+
case "event":
|
|
2750
|
+
return e;
|
|
2751
|
+
case "isAlt":
|
|
2752
|
+
return e.altKey;
|
|
2753
|
+
case "isShift":
|
|
2754
|
+
return e.shiftKey;
|
|
2755
|
+
case "isCtrl":
|
|
2756
|
+
case "isCmd":
|
|
2757
|
+
return isMac2 && e.metaKey || e.ctrlKey;
|
|
2758
|
+
case "key":
|
|
2759
|
+
return e.key;
|
|
2760
|
+
case "keyCode":
|
|
2761
|
+
return e.keyCode;
|
|
2762
|
+
case "isUpKey":
|
|
2763
|
+
return e.key === "ArrowUp";
|
|
2764
|
+
case "isDownKey":
|
|
2765
|
+
return e.key === "ArrowDown";
|
|
2766
|
+
case "isSend":
|
|
2767
|
+
return e.key === "Enter";
|
|
2768
|
+
case "isCancel":
|
|
2769
|
+
return e.key === "Escape";
|
|
2770
|
+
case "isTabKey":
|
|
2771
|
+
return e.key === "Tab";
|
|
2772
|
+
case "ctx":
|
|
2773
|
+
return new EventContext(this.dispatchPath, this.transactor, this);
|
|
2774
|
+
case "dragInfo":
|
|
2775
|
+
return this.dragInfo;
|
|
2776
|
+
}
|
|
2777
|
+
return null;
|
|
2778
|
+
}
|
|
2779
|
+
}
|
|
2780
|
+
|
|
2781
|
+
class NameArgsTransaction extends Transaction {
|
|
2782
|
+
constructor(path, transactor, name, args, parentTransaction, opts = {}) {
|
|
2783
|
+
super(path, transactor, parentTransaction);
|
|
2784
|
+
this.name = name;
|
|
2785
|
+
this.args = args;
|
|
2786
|
+
this.opts = opts;
|
|
2787
|
+
this.targetPath = path;
|
|
2788
|
+
}
|
|
2789
|
+
handlerProp = null;
|
|
2790
|
+
getHandlerForName(comp) {
|
|
2791
|
+
const handlers = comp?.[this.handlerProp];
|
|
2792
|
+
return handlers?.[this.name] ?? handlers?.$unknown ?? nullHandler;
|
|
2793
|
+
}
|
|
2794
|
+
getHandlerAndArgs(_root, instance, comps) {
|
|
2795
|
+
const handler = this.getHandlerForName(comps.getCompFor(instance));
|
|
2796
|
+
return [handler, [...this.args, new EventContext(this.path, this.transactor, this)]];
|
|
2797
|
+
}
|
|
2798
|
+
}
|
|
2799
|
+
|
|
2800
|
+
class ResponseEvent extends NameArgsTransaction {
|
|
2801
|
+
handlerProp = "response";
|
|
2802
|
+
}
|
|
2803
|
+
|
|
2804
|
+
class SendEvent extends NameArgsTransaction {
|
|
2805
|
+
handlerProp = "receive";
|
|
2806
|
+
run(rootVal, comps) {
|
|
2807
|
+
return this.opts.skipSelf ? rootVal : this.updateRootValue(rootVal, comps);
|
|
2808
|
+
}
|
|
2809
|
+
afterTransaction() {
|
|
2810
|
+
const { path, name, args, opts, targetPath } = this;
|
|
2811
|
+
if (opts.bubbles && path.steps.length > 0)
|
|
2812
|
+
this.transactor.pushBubble(path.popStep(), name, args, opts, this, targetPath);
|
|
2813
|
+
}
|
|
2814
|
+
}
|
|
2815
|
+
|
|
2816
|
+
class BubbleEvent extends SendEvent {
|
|
2817
|
+
handlerProp = "bubble";
|
|
2818
|
+
constructor(path, transactor, name, args, parent, opts, targetPath) {
|
|
2819
|
+
super(path, transactor, name, args, parent, opts);
|
|
2820
|
+
this.targetPath = targetPath ?? path;
|
|
2821
|
+
}
|
|
2822
|
+
stopPropagation() {
|
|
2823
|
+
this.opts.bubbles = false;
|
|
2824
|
+
}
|
|
2825
|
+
}
|
|
2826
|
+
|
|
2827
|
+
class Task {
|
|
2828
|
+
constructor() {
|
|
2829
|
+
this.deps = [];
|
|
2830
|
+
this.val = this.resolve = this.reject = null;
|
|
2831
|
+
this.promise = new Promise((res, rej) => {
|
|
2832
|
+
this.resolve = res;
|
|
2833
|
+
this.reject = rej;
|
|
2834
|
+
});
|
|
2835
|
+
this.isCompleted = false;
|
|
2836
|
+
}
|
|
2837
|
+
addDep(task) {
|
|
2838
|
+
console.assert(!this.isCompleted, "addDep for completed task", this, task);
|
|
2839
|
+
this.deps.push(task);
|
|
2840
|
+
task.promise.then((_) => this._check());
|
|
2841
|
+
}
|
|
2842
|
+
complete(val) {
|
|
2843
|
+
this.val = val;
|
|
2844
|
+
this._check();
|
|
2845
|
+
}
|
|
2846
|
+
_check() {
|
|
2847
|
+
if (this.deps.every((task) => task.isCompleted)) {
|
|
2848
|
+
this.isCompleted = true;
|
|
2849
|
+
this.resolve(this);
|
|
2850
|
+
}
|
|
2851
|
+
}
|
|
2852
|
+
}
|
|
2853
|
+
|
|
2854
|
+
class Dispatcher {
|
|
2855
|
+
constructor(path, transactor, parentTransaction) {
|
|
2856
|
+
this.path = path;
|
|
2857
|
+
this.transactor = transactor;
|
|
2858
|
+
this.parent = parentTransaction;
|
|
2859
|
+
}
|
|
2860
|
+
get at() {
|
|
2861
|
+
return new PathChanges(this);
|
|
2862
|
+
}
|
|
2863
|
+
send(name, args, opts) {
|
|
2864
|
+
return this.sendAtPath(this.path, name, args, opts);
|
|
2865
|
+
}
|
|
2866
|
+
bubble(name, args, opts) {
|
|
2867
|
+
return this.send(name, args, { skipSelf: true, bubbles: true, ...opts });
|
|
2868
|
+
}
|
|
2869
|
+
sendAtPath(path, name, args, opts) {
|
|
2870
|
+
return this.transactor.pushSend(path, name, args, opts, this.parent);
|
|
2871
|
+
}
|
|
2872
|
+
request(name, args, opts) {
|
|
2873
|
+
return this.requestAtPath(this.path, name, args, opts);
|
|
2874
|
+
}
|
|
2875
|
+
requestAtPath(path, name, args, opts) {
|
|
2876
|
+
return this.transactor.pushRequest(path, name, args, opts, this.parent);
|
|
2877
|
+
}
|
|
2878
|
+
lookupTypeFor(name, inst) {
|
|
2879
|
+
return this.transactor.comps.getCompFor(inst).scope.lookupComponent(name);
|
|
2880
|
+
}
|
|
2881
|
+
}
|
|
2882
|
+
|
|
2883
|
+
class EventContext extends Dispatcher {
|
|
2884
|
+
get name() {
|
|
2885
|
+
return this.parent?.name ?? null;
|
|
2886
|
+
}
|
|
2887
|
+
get targetPath() {
|
|
2888
|
+
return this.parent.targetPath;
|
|
2889
|
+
}
|
|
2890
|
+
stopPropagation() {
|
|
2891
|
+
return this.parent.stopPropagation();
|
|
2892
|
+
}
|
|
2893
|
+
}
|
|
2894
|
+
|
|
2895
|
+
class PathChanges extends PathBuilder {
|
|
2896
|
+
constructor(dispatcher) {
|
|
2897
|
+
super();
|
|
2898
|
+
this.dispatcher = dispatcher;
|
|
2899
|
+
}
|
|
2900
|
+
send(name, args, opts) {
|
|
2901
|
+
return this.dispatcher.sendAtPath(this.buildPath(), name, args, opts);
|
|
2902
|
+
}
|
|
2903
|
+
bubble(name, args, opts) {
|
|
2904
|
+
return this.send(name, args, { skipSelf: true, bubbles: true, ...opts });
|
|
2905
|
+
}
|
|
2906
|
+
buildPath() {
|
|
2907
|
+
return this.dispatcher.path.concat(this.pathChanges);
|
|
2908
|
+
}
|
|
2909
|
+
}
|
|
2910
|
+
|
|
2911
|
+
// src/app.js
|
|
2912
|
+
var _evs = "dragstart dragover dragend touchstart touchmove touchend touchcancel".split(" ");
|
|
2913
|
+
|
|
2914
|
+
class App {
|
|
2915
|
+
constructor(rootNode, comps, renderer, ParseContext2) {
|
|
2916
|
+
this.rootNode = rootNode;
|
|
2917
|
+
this.comps = comps;
|
|
2918
|
+
this.compStack = new ComponentStack(comps);
|
|
2919
|
+
this.transactor = new Transactor(comps, null);
|
|
2920
|
+
this.ParseContext = ParseContext2;
|
|
2921
|
+
this.renderer = renderer;
|
|
2922
|
+
this.maxEventNodeDepth = Infinity;
|
|
2923
|
+
this._transactNextBatchId = this._evictCacheId = null;
|
|
2924
|
+
this._eventNames = new Set(_evs);
|
|
2925
|
+
this.dragInfo = this.curDragOver = null;
|
|
2926
|
+
this._touch = null;
|
|
2927
|
+
this.transactor.onTransactionPushed = (_transaction) => {
|
|
2928
|
+
if (this._transactNextBatchId === null)
|
|
2929
|
+
this._scheduleNextTransactionBatchExecution();
|
|
2930
|
+
};
|
|
2931
|
+
this._compiled = false;
|
|
2932
|
+
this._renderOpts = { document: rootNode.ownerDocument };
|
|
2933
|
+
this._renderState = null;
|
|
2934
|
+
}
|
|
2935
|
+
get state() {
|
|
2936
|
+
return this.transactor.state;
|
|
2937
|
+
}
|
|
2938
|
+
handleEvent(e) {
|
|
2939
|
+
const { type } = e;
|
|
2940
|
+
if (type[0] === "t" && type.startsWith("touch")) {
|
|
2941
|
+
this._handleTouchEvent(e);
|
|
2942
|
+
return;
|
|
2943
|
+
}
|
|
2944
|
+
this._dispatchEvent(e);
|
|
2945
|
+
}
|
|
2946
|
+
_dispatchEvent(e) {
|
|
2947
|
+
const { type } = e;
|
|
2948
|
+
const isDrag = type === "dragover" || type === "dragstart" || type === "dragend" || type === "drop";
|
|
2949
|
+
const { rootNode: root, maxEventNodeDepth: maxDepth, comps, transactor } = this;
|
|
2950
|
+
const [path, handlers] = Path.fromEvent(e, root, maxDepth, comps, !isDrag);
|
|
2951
|
+
if (isDrag)
|
|
2952
|
+
this._handleDragEvent(e, type, path);
|
|
2953
|
+
if (path !== null && handlers !== null)
|
|
2954
|
+
for (const handler of handlers)
|
|
2955
|
+
transactor.transactInputNow(path, e, handler, this.dragInfo);
|
|
2956
|
+
}
|
|
2957
|
+
_handleTouchEvent(e) {
|
|
2958
|
+
const { type } = e;
|
|
2959
|
+
if (type === "touchstart") {
|
|
2960
|
+
if (this._touch !== null || e.touches.length !== 1)
|
|
2961
|
+
return;
|
|
2962
|
+
const t = e.touches[0];
|
|
2963
|
+
const draggable = t.target?.closest?.('[draggable="true"]');
|
|
2964
|
+
if (!draggable)
|
|
2965
|
+
return;
|
|
2966
|
+
this._touch = makeTouchInfo(t.identifier, t.clientX, t.clientY, draggable, false);
|
|
2967
|
+
return;
|
|
2968
|
+
}
|
|
2969
|
+
if (this._touch === null)
|
|
2970
|
+
return;
|
|
2971
|
+
const touch = findTouch(e, this._touch.id);
|
|
2972
|
+
if (touch === null)
|
|
2973
|
+
return;
|
|
2974
|
+
const { rootNode, _touch } = this;
|
|
2975
|
+
const { clientX, clientY } = touch;
|
|
2976
|
+
const fire = (type2, target) => {
|
|
2977
|
+
const e2 = { type: type2, target, clientX, clientY, preventDefault: NOOP };
|
|
2978
|
+
this._dispatchEvent(e2);
|
|
2979
|
+
};
|
|
2980
|
+
if (type === "touchmove") {
|
|
2981
|
+
if (!_touch.active) {
|
|
2982
|
+
const dx = clientX - _touch.startX;
|
|
2983
|
+
const dy = clientY - _touch.startY;
|
|
2984
|
+
if (dx * dx + dy * dy < 100)
|
|
2985
|
+
return;
|
|
2986
|
+
_touch.active = true;
|
|
2987
|
+
e.preventDefault();
|
|
2988
|
+
fire("dragstart", _touch.target);
|
|
2989
|
+
} else {
|
|
2990
|
+
e.preventDefault();
|
|
2991
|
+
fire("dragover", hitTest(rootNode, clientX, clientY));
|
|
2992
|
+
}
|
|
2993
|
+
return;
|
|
2994
|
+
}
|
|
2995
|
+
if (type === "touchend" || type === "touchcancel") {
|
|
2996
|
+
if (_touch.active) {
|
|
2997
|
+
if (type === "touchend")
|
|
2998
|
+
fire("drop", hitTest(rootNode, clientX, clientY));
|
|
2999
|
+
fire("dragend", _touch.target);
|
|
3000
|
+
}
|
|
3001
|
+
this._touch = null;
|
|
3002
|
+
}
|
|
3003
|
+
}
|
|
3004
|
+
_handleDragEvent(e, type, path) {
|
|
3005
|
+
if (type === "dragover") {
|
|
3006
|
+
const dropTarget = getClosestDropTarget(e.target, this.rootNode, Infinity);
|
|
3007
|
+
if (dropTarget !== null) {
|
|
3008
|
+
e.preventDefault();
|
|
3009
|
+
this._cleanDragOverAttrs();
|
|
3010
|
+
this.curDragOver = dropTarget;
|
|
3011
|
+
dropTarget.dataset.draggingover = this.dragInfo?.type ?? "_external";
|
|
3012
|
+
}
|
|
3013
|
+
} else if (type === "dragstart") {
|
|
3014
|
+
e.target.dataset.dragging = 1;
|
|
3015
|
+
const rootValue = this.state.val;
|
|
3016
|
+
const txnPath = path.compact().toTransactionPath();
|
|
3017
|
+
const value = txnPath.lookup(rootValue);
|
|
3018
|
+
const dragType = e.target.dataset.dragtype ?? "?";
|
|
3019
|
+
const stack = path.toTransactionPath().buildStack(this.makeStack(rootValue));
|
|
3020
|
+
this.dragInfo = new DragInfo(txnPath, stack, e, value, dragType, e.target);
|
|
3021
|
+
} else if (type === "drop") {
|
|
3022
|
+
e.preventDefault();
|
|
3023
|
+
this._cleanDragOverAttrs();
|
|
3024
|
+
} else {
|
|
3025
|
+
if (this.dragInfo !== null) {
|
|
3026
|
+
delete this.dragInfo.node.dataset.dragging;
|
|
3027
|
+
this.dragInfo = null;
|
|
3028
|
+
}
|
|
3029
|
+
this._cleanDragOverAttrs();
|
|
3030
|
+
}
|
|
3031
|
+
}
|
|
3032
|
+
makeStack(rootValue) {
|
|
3033
|
+
return Stack.root(this.comps, rootValue);
|
|
3034
|
+
}
|
|
3035
|
+
_cleanDragOverAttrs() {
|
|
3036
|
+
if (this.curDragOver !== null) {
|
|
3037
|
+
delete this.curDragOver.dataset.draggingover;
|
|
3038
|
+
this.curDragOver = null;
|
|
3039
|
+
}
|
|
3040
|
+
}
|
|
3041
|
+
render() {
|
|
3042
|
+
const root = this.state.val;
|
|
3043
|
+
const stack = this.makeStack(root);
|
|
3044
|
+
const { renderer, rootNode, _renderOpts, _renderState } = this;
|
|
3045
|
+
const newState = render(renderer.renderRoot(stack, root), rootNode, _renderOpts, _renderState);
|
|
3046
|
+
this._renderState = newState;
|
|
3047
|
+
return newState.dom;
|
|
3048
|
+
}
|
|
3049
|
+
onChange(callback) {
|
|
3050
|
+
this.transactor.state.onChange(callback);
|
|
3051
|
+
}
|
|
3052
|
+
compile() {
|
|
3053
|
+
for (const Comp of this.comps.byId.values()) {
|
|
3054
|
+
Comp.compile(this.ParseContext);
|
|
3055
|
+
for (const key in Comp.views)
|
|
3056
|
+
for (const name of Comp.views[key].ctx.genEventNames())
|
|
3057
|
+
this._eventNames.add(name);
|
|
3058
|
+
}
|
|
3059
|
+
this._compiled = true;
|
|
3060
|
+
}
|
|
3061
|
+
subscribeToEvents(eventNames) {
|
|
3062
|
+
for (const name of eventNames)
|
|
3063
|
+
this.rootNode.addEventListener(name, this, listenerOpts(name));
|
|
3064
|
+
}
|
|
3065
|
+
recompileStyles(opts) {
|
|
3066
|
+
injectCss("tutuca-app", this.comps.compileStyles(), opts?.head ?? document.head);
|
|
3067
|
+
}
|
|
3068
|
+
start(opts) {
|
|
3069
|
+
if (!this._compiled)
|
|
3070
|
+
this.compile();
|
|
3071
|
+
this.subscribeToEvents(this._eventNames);
|
|
3072
|
+
this.onChange((info) => {
|
|
3073
|
+
if (info.val !== info.old)
|
|
3074
|
+
this.render();
|
|
3075
|
+
});
|
|
3076
|
+
this.recompileStyles(opts);
|
|
3077
|
+
if (opts?.noCache)
|
|
3078
|
+
this.renderer.setNullCache();
|
|
3079
|
+
else
|
|
3080
|
+
this.startCacheEvictionInterval();
|
|
3081
|
+
this.render();
|
|
3082
|
+
}
|
|
3083
|
+
stop() {
|
|
3084
|
+
this.stopCacheEvictionInterval();
|
|
3085
|
+
for (const name of this._eventNames)
|
|
3086
|
+
this.rootNode.removeEventListener(name, this, listenerOpts(name));
|
|
3087
|
+
}
|
|
3088
|
+
sendAtRoot(name, args, opts) {
|
|
3089
|
+
this.transactor.pushSend(new Path([]), name, args, opts);
|
|
3090
|
+
}
|
|
3091
|
+
registerComponents(comps, opts) {
|
|
3092
|
+
const scope = this.compStack.enter();
|
|
3093
|
+
scope.registerComponents(comps, opts);
|
|
3094
|
+
return scope;
|
|
3095
|
+
}
|
|
3096
|
+
_transactNextBatch(maxRunTimeMs = 10) {
|
|
3097
|
+
this._transactNextBatchId = null;
|
|
3098
|
+
const startTs = Date.now();
|
|
3099
|
+
const t = this.transactor;
|
|
3100
|
+
while (t.hasPendingTransactions && Date.now() - startTs < maxRunTimeMs)
|
|
3101
|
+
t.transactNext();
|
|
3102
|
+
if (t.hasPendingTransactions)
|
|
3103
|
+
this._scheduleNextTransactionBatchExecution();
|
|
3104
|
+
}
|
|
3105
|
+
_scheduleNextTransactionBatchExecution() {
|
|
3106
|
+
this._transactNextBatchId = setTimeout(() => this._transactNextBatch(), 0);
|
|
3107
|
+
}
|
|
3108
|
+
startCacheEvictionInterval(intervalMs = 30000) {
|
|
3109
|
+
this._evictCacheId = setInterval(() => this.renderer.cache.evict(), intervalMs);
|
|
3110
|
+
}
|
|
3111
|
+
stopCacheEvictionInterval() {
|
|
3112
|
+
clearInterval(this._evictCacheId);
|
|
3113
|
+
this._evictCacheId = null;
|
|
3114
|
+
}
|
|
3115
|
+
}
|
|
3116
|
+
function injectCss(nodeId, style, styleTarget = document.head) {
|
|
3117
|
+
const styleNode = document.createElement("style");
|
|
3118
|
+
const currentNodeWithId = styleTarget.querySelector(`#${nodeId}`);
|
|
3119
|
+
if (currentNodeWithId)
|
|
3120
|
+
styleTarget.removeChild(currentNodeWithId);
|
|
3121
|
+
styleNode.id = nodeId;
|
|
3122
|
+
styleNode.innerHTML = style;
|
|
3123
|
+
styleTarget.appendChild(styleNode);
|
|
3124
|
+
}
|
|
3125
|
+
var NOOP = () => {};
|
|
3126
|
+
function findTouch(e, id) {
|
|
3127
|
+
for (const t of e.changedTouches)
|
|
3128
|
+
if (t.identifier === id)
|
|
3129
|
+
return t;
|
|
3130
|
+
for (const t of e.touches)
|
|
3131
|
+
if (t.identifier === id)
|
|
3132
|
+
return t;
|
|
3133
|
+
return null;
|
|
3134
|
+
}
|
|
3135
|
+
var listenerOpts = (name) => name === "touchmove" ? { passive: false } : undefined;
|
|
3136
|
+
function makeTouchInfo(id, startX, startY, target, active) {
|
|
3137
|
+
return { id, startX, startY, target, active };
|
|
3138
|
+
}
|
|
3139
|
+
function hitTest(rootNode, x, y) {
|
|
3140
|
+
const root = rootNode.getRootNode();
|
|
3141
|
+
let el = root.elementFromPoint?.(x, y) ?? null;
|
|
3142
|
+
while (el?.shadowRoot) {
|
|
3143
|
+
const next = el.shadowRoot.elementFromPoint(x, y);
|
|
3144
|
+
if (next === null || next === el)
|
|
3145
|
+
break;
|
|
3146
|
+
el = next;
|
|
3147
|
+
}
|
|
3148
|
+
return el ?? rootNode;
|
|
3149
|
+
}
|
|
3150
|
+
function getClosestDropTarget(target, rootNode, count) {
|
|
3151
|
+
let node = target;
|
|
3152
|
+
while (count-- > 0 && node !== rootNode) {
|
|
3153
|
+
if (node.dataset?.droptarget !== undefined)
|
|
3154
|
+
return node;
|
|
3155
|
+
node = node.parentNode;
|
|
3156
|
+
}
|
|
3157
|
+
return null;
|
|
3158
|
+
}
|
|
3159
|
+
|
|
3160
|
+
class DragInfo {
|
|
3161
|
+
constructor(path, stack, e, val, type, node) {
|
|
3162
|
+
this.path = path;
|
|
3163
|
+
this.stack = stack;
|
|
3164
|
+
this.e = e;
|
|
3165
|
+
this.val = val;
|
|
3166
|
+
this.type = type;
|
|
3167
|
+
this.node = node;
|
|
3168
|
+
}
|
|
3169
|
+
lookupBind(name) {
|
|
3170
|
+
return this.stack.lookupBind(name);
|
|
3171
|
+
}
|
|
3172
|
+
}
|
|
3173
|
+
|
|
3174
|
+
// src/renderer.js
|
|
3175
|
+
import { isIndexed, isKeyed } from "immutable";
|
|
3176
|
+
|
|
3177
|
+
// src/cache.js
|
|
3178
|
+
class NullDomCache {
|
|
3179
|
+
get(_keys, _cacheKey) {}
|
|
3180
|
+
set(_keys, _cacheKey, _v) {}
|
|
3181
|
+
evict() {
|
|
3182
|
+
return { hit: 0, miss: 0, badKey: 0 };
|
|
3183
|
+
}
|
|
3184
|
+
}
|
|
3185
|
+
|
|
3186
|
+
class WeakMapDomCache {
|
|
3187
|
+
constructor() {
|
|
3188
|
+
this.hit = this.miss = this.badKey = 0;
|
|
3189
|
+
this.keysByLen = new Map;
|
|
3190
|
+
}
|
|
3191
|
+
_returnValue(r) {
|
|
3192
|
+
if (r === undefined)
|
|
3193
|
+
this.miss += 1;
|
|
3194
|
+
else
|
|
3195
|
+
this.hit += 1;
|
|
3196
|
+
return r;
|
|
3197
|
+
}
|
|
3198
|
+
get(keys, cacheKey) {
|
|
3199
|
+
const len = keys.length;
|
|
3200
|
+
let cur = this.keysByLen.get(len);
|
|
3201
|
+
if (!cur)
|
|
3202
|
+
return this._returnValue(undefined);
|
|
3203
|
+
for (let i = 0;i < len - 1; i++) {
|
|
3204
|
+
cur = cur.get(keys[i]);
|
|
3205
|
+
if (!cur)
|
|
3206
|
+
return this._returnValue(undefined);
|
|
3207
|
+
}
|
|
3208
|
+
return this._returnValue(cur.get(keys[len - 1])?.[cacheKey]);
|
|
3209
|
+
}
|
|
3210
|
+
set(keys, cacheKey, v) {
|
|
3211
|
+
const len = keys.length;
|
|
3212
|
+
let cur = this.keysByLen.get(len);
|
|
3213
|
+
if (!cur) {
|
|
3214
|
+
cur = new WeakMap;
|
|
3215
|
+
this.keysByLen.set(len, cur);
|
|
3216
|
+
}
|
|
3217
|
+
for (let i = 0;i < len - 1; i++) {
|
|
3218
|
+
const key = keys[i];
|
|
3219
|
+
let next = cur.get(key);
|
|
3220
|
+
if (!next) {
|
|
3221
|
+
if (typeof key !== "object") {
|
|
3222
|
+
this.badKey += 1;
|
|
3223
|
+
return;
|
|
3224
|
+
}
|
|
3225
|
+
next = new WeakMap;
|
|
3226
|
+
cur.set(key, next);
|
|
3227
|
+
}
|
|
3228
|
+
cur = next;
|
|
3229
|
+
}
|
|
3230
|
+
const lastKey = keys[len - 1];
|
|
3231
|
+
const leaf = cur.get(lastKey);
|
|
3232
|
+
if (leaf)
|
|
3233
|
+
leaf[cacheKey] = v;
|
|
3234
|
+
else if (typeof lastKey === "object")
|
|
3235
|
+
cur.set(lastKey, { [cacheKey]: v });
|
|
3236
|
+
else
|
|
3237
|
+
this.badKey += 1;
|
|
3238
|
+
}
|
|
3239
|
+
evict() {
|
|
3240
|
+
const { hit, miss, badKey } = this;
|
|
3241
|
+
this.hit = this.miss = this.badKey = 0;
|
|
3242
|
+
this.keysByLen = new Map;
|
|
3243
|
+
return { hit, miss, badKey };
|
|
3244
|
+
}
|
|
3245
|
+
}
|
|
3246
|
+
|
|
3247
|
+
// src/renderer.js
|
|
3248
|
+
var DATASET_ATTRS = ["nid", "cid", "eid", "vid", "si", "sk"];
|
|
3249
|
+
|
|
3250
|
+
class Renderer {
|
|
3251
|
+
constructor(comps) {
|
|
3252
|
+
this.comps = comps;
|
|
3253
|
+
this.cache = new WeakMapDomCache;
|
|
3254
|
+
this.renderTag = h;
|
|
3255
|
+
}
|
|
3256
|
+
renderFragment(childs) {
|
|
3257
|
+
return new VFragment(childs);
|
|
3258
|
+
}
|
|
3259
|
+
renderComment(text) {
|
|
3260
|
+
return new VComment(text);
|
|
3261
|
+
}
|
|
3262
|
+
setNullCache() {
|
|
3263
|
+
this.cache = new NullDomCache;
|
|
3264
|
+
}
|
|
3265
|
+
renderToDOM(stack, val) {
|
|
3266
|
+
const rootNode = document.createElement("div");
|
|
3267
|
+
const rOpts = { document };
|
|
3268
|
+
render(h("DIV", null, [this.renderRoot(stack, val)]), rootNode, rOpts);
|
|
3269
|
+
return rootNode.childNodes[0];
|
|
3270
|
+
}
|
|
3271
|
+
renderToString(stack, val, cleanAttrs = true) {
|
|
3272
|
+
const dom = this.renderToDOM(stack, val);
|
|
3273
|
+
if (cleanAttrs) {
|
|
3274
|
+
const nodes = dom.querySelectorAll("[data-nid],[data-cid],[data-eid]");
|
|
3275
|
+
for (const { dataset } of nodes)
|
|
3276
|
+
for (const name of DATASET_ATTRS)
|
|
3277
|
+
delete dataset[name];
|
|
3278
|
+
}
|
|
3279
|
+
return dom.innerHTML;
|
|
3280
|
+
}
|
|
3281
|
+
renderRoot(stack, val, viewName = null) {
|
|
3282
|
+
const comp = this.comps.getCompFor(val);
|
|
3283
|
+
if (comp === null)
|
|
3284
|
+
return null;
|
|
3285
|
+
return this._rValComp(stack, val, comp, comp.getView(viewName).anode, "ROOT", viewName);
|
|
3286
|
+
}
|
|
3287
|
+
renderIt(stack, node, key, viewName) {
|
|
3288
|
+
const comp = this.comps.getCompFor(stack.it);
|
|
3289
|
+
return comp ? this._rValComp(stack, stack.it, comp, node, key, viewName) : null;
|
|
3290
|
+
}
|
|
3291
|
+
_rValComp(stack, val, comp, node, key, viewName) {
|
|
3292
|
+
const cacheKey = `${viewName ?? stack.viewsId ?? ""}-${key}`;
|
|
3293
|
+
const cachePath = [node, val];
|
|
3294
|
+
stack._pushDynBindValuesToArray(cachePath, comp.dynamic);
|
|
3295
|
+
const cachedNode = this.cache.get(cachePath, cacheKey);
|
|
3296
|
+
if (cachedNode)
|
|
3297
|
+
return cachedNode;
|
|
3298
|
+
const view = viewName ? comp.getView(viewName) : stack.lookupBestView(comp.views, "main");
|
|
3299
|
+
const meta = this._renderMetadata({
|
|
3300
|
+
$: "Comp",
|
|
3301
|
+
nid: node?.nodeId ?? null,
|
|
3302
|
+
cid: comp.id,
|
|
3303
|
+
vid: view.name
|
|
3304
|
+
});
|
|
3305
|
+
const dom = new VFragment([meta, this.renderView(view, stack)]);
|
|
3306
|
+
this.cache.set(cachePath, cacheKey, dom);
|
|
3307
|
+
return dom;
|
|
3308
|
+
}
|
|
3309
|
+
pushEachEntry(r, nid, attrName, key, dom) {
|
|
3310
|
+
r.push(this._renderMetadata({ $: "Each", nid, [attrName]: key }), dom);
|
|
3311
|
+
}
|
|
3312
|
+
renderEach(stack, iterInfo, node, viewName) {
|
|
3313
|
+
const { seq, filter, loopWith } = iterInfo.eval(stack);
|
|
3314
|
+
const r = [];
|
|
3315
|
+
const { iterData, start, end } = unpackLoopResult(loopWith.call(stack.it, seq), seq);
|
|
3316
|
+
getSeqInfo(seq)(seq, (key, value, attrName) => {
|
|
3317
|
+
if (filter.call(stack.it, key, value, iterData)) {
|
|
3318
|
+
const dom = this.renderIt(stack.enter(value, { key }, true), node, key, viewName);
|
|
3319
|
+
this.pushEachEntry(r, node.nodeId, attrName, key, dom);
|
|
3320
|
+
}
|
|
3321
|
+
}, start, end);
|
|
3322
|
+
return r;
|
|
3323
|
+
}
|
|
3324
|
+
renderEachWhen(stack, iterInfo, view, nid) {
|
|
3325
|
+
const { seq, filter, loopWith, enricher } = iterInfo.eval(stack);
|
|
3326
|
+
const r = [];
|
|
3327
|
+
const it = stack.it;
|
|
3328
|
+
const { iterData, start, end } = unpackLoopResult(loopWith.call(it, seq), seq);
|
|
3329
|
+
getSeqInfo(seq)(seq, (key, value, attrName) => {
|
|
3330
|
+
if (filter.call(it, key, value, iterData)) {
|
|
3331
|
+
const cachePath = enricher ? [view, it, value] : [view, value];
|
|
3332
|
+
const binds = { key, value };
|
|
3333
|
+
const cacheKey = `${nid}-${key}`;
|
|
3334
|
+
if (enricher)
|
|
3335
|
+
enricher.call(it, binds, key, value, iterData);
|
|
3336
|
+
const cachedNode = this.cache.get(cachePath, cacheKey);
|
|
3337
|
+
if (cachedNode)
|
|
3338
|
+
this.pushEachEntry(r, nid, attrName, key, cachedNode);
|
|
3339
|
+
else {
|
|
3340
|
+
const dom = this.renderView(view, stack.enter(value, binds, false));
|
|
3341
|
+
this.pushEachEntry(r, nid, attrName, key, dom);
|
|
3342
|
+
this.cache.set(cachePath, cacheKey, dom);
|
|
3343
|
+
}
|
|
3344
|
+
}
|
|
3345
|
+
}, start, end);
|
|
3346
|
+
return r;
|
|
3347
|
+
}
|
|
3348
|
+
renderView(view, stack) {
|
|
3349
|
+
let n = stack.binds[1];
|
|
3350
|
+
while (n !== null) {
|
|
3351
|
+
const b = n[0];
|
|
3352
|
+
if (b.isFrame) {
|
|
3353
|
+
if (stack.it !== b.it)
|
|
3354
|
+
break;
|
|
3355
|
+
console.error("recursion detected", stack.it, b.it);
|
|
3356
|
+
return new VComment("RECURSION AVOIDED");
|
|
3357
|
+
}
|
|
3358
|
+
n = n[1];
|
|
3359
|
+
}
|
|
3360
|
+
return view.render(stack, this);
|
|
3361
|
+
}
|
|
3362
|
+
_renderMetadata(info) {
|
|
3363
|
+
return new VComment(`§${JSON.stringify(info)}§`);
|
|
3364
|
+
}
|
|
3365
|
+
}
|
|
3366
|
+
var getSeqInfo = (seq) => isIndexed(seq) ? imIndexedIter : isKeyed(seq) ? imKeyedIter : seq?.[SEQ_INFO] ?? unkIter;
|
|
3367
|
+
var normalizeRange = (start, end, size) => {
|
|
3368
|
+
let s = start == null ? 0 : start < 0 ? size + start : start;
|
|
3369
|
+
let e = end == null ? size : end < 0 ? size + end : end;
|
|
3370
|
+
s = s < 0 ? 0 : s > size ? size : s;
|
|
3371
|
+
e = e < 0 ? 0 : e > size ? size : e;
|
|
3372
|
+
return [s, e < s ? s : e];
|
|
3373
|
+
};
|
|
3374
|
+
var unpackLoopResult = (result, seq) => {
|
|
3375
|
+
const r = result ?? {};
|
|
3376
|
+
return { iterData: r.iterData ?? { seq }, start: r.start, end: r.end };
|
|
3377
|
+
};
|
|
3378
|
+
var imIndexedIter = (seq, visit, start, end) => {
|
|
3379
|
+
const [s, e] = normalizeRange(start, end, seq.size);
|
|
3380
|
+
for (let i = s;i < e; i++)
|
|
3381
|
+
visit(i, seq.get(i), "si");
|
|
3382
|
+
};
|
|
3383
|
+
var imKeyedIter = (seq, visit, start, end) => {
|
|
3384
|
+
const [s, e] = normalizeRange(start, end, seq.size);
|
|
3385
|
+
let i = 0;
|
|
3386
|
+
for (const [k, v] of seq.toSeq().entries()) {
|
|
3387
|
+
if (i >= e)
|
|
3388
|
+
break;
|
|
3389
|
+
if (i >= s)
|
|
3390
|
+
visit(k, v, "sk");
|
|
3391
|
+
i++;
|
|
3392
|
+
}
|
|
3393
|
+
};
|
|
3394
|
+
var unkIter = () => {};
|
|
3395
|
+
var SEQ_INFO = Symbol.for("tutuca.seqInfo");
|
|
3396
|
+
|
|
3397
|
+
// index.js
|
|
3398
|
+
export * from "immutable";
|
|
3399
|
+
import {
|
|
3400
|
+
isMap,
|
|
3401
|
+
isOrderedMap,
|
|
3402
|
+
Map as Map2,
|
|
3403
|
+
OrderedMap as OrderedMap2,
|
|
3404
|
+
Set as Set2
|
|
3405
|
+
} from "immutable";
|
|
3406
|
+
|
|
3407
|
+
// src/oo.js
|
|
3408
|
+
import { Map as IMap, Set as ISet, List, OrderedMap, Record } from "immutable";
|
|
3409
|
+
var BAD_VALUE = Symbol("BadValue");
|
|
3410
|
+
var nullCoercer = (v) => v;
|
|
3411
|
+
|
|
3412
|
+
class Field {
|
|
3413
|
+
constructor(type, name, typeCheck, coercer, defaultValue = null) {
|
|
3414
|
+
this.type = type;
|
|
3415
|
+
this.name = name;
|
|
3416
|
+
this.typeCheck = typeCheck;
|
|
3417
|
+
this.coercer = coercer;
|
|
3418
|
+
this.checks = [];
|
|
3419
|
+
this.defaultValue = defaultValue;
|
|
3420
|
+
}
|
|
3421
|
+
toDataDef() {
|
|
3422
|
+
const { type, defaultValue: dv } = this;
|
|
3423
|
+
return { type, defaultValue: dv?.toJS ? dv.toJS() : dv };
|
|
3424
|
+
}
|
|
3425
|
+
getFirstFailingCheck(v) {
|
|
3426
|
+
if (!this.typeCheck.isValid(v))
|
|
3427
|
+
return this.typeCheck;
|
|
3428
|
+
for (const check of this.checks)
|
|
3429
|
+
if (!check.isValid(v))
|
|
3430
|
+
return check;
|
|
3431
|
+
return null;
|
|
3432
|
+
}
|
|
3433
|
+
isValid(v) {
|
|
3434
|
+
return this.getFirstFailingCheck(v) === null;
|
|
3435
|
+
}
|
|
3436
|
+
addCheck(check) {
|
|
3437
|
+
this.checks.push(check);
|
|
3438
|
+
return this;
|
|
3439
|
+
}
|
|
3440
|
+
coerceOr(v, defaultValue = null) {
|
|
3441
|
+
if (this.isValid(v))
|
|
3442
|
+
return v;
|
|
3443
|
+
const v1 = this.coercer(v);
|
|
3444
|
+
return this.isValid(v1) ? v1 : defaultValue;
|
|
3445
|
+
}
|
|
3446
|
+
coerceOrDefault(v) {
|
|
3447
|
+
return this.coerceOr(v, this.defaultValue);
|
|
3448
|
+
}
|
|
3449
|
+
extendProtoForType(_proto, _uname) {}
|
|
3450
|
+
extendProto(proto) {
|
|
3451
|
+
const { name } = this;
|
|
3452
|
+
const uname = name[0].toUpperCase() + name.slice(1);
|
|
3453
|
+
const setName = `set${uname}`;
|
|
3454
|
+
const that = this;
|
|
3455
|
+
proto[setName] = function(v) {
|
|
3456
|
+
const v1 = that.coerceOr(v, BAD_VALUE);
|
|
3457
|
+
if (v1 === BAD_VALUE) {
|
|
3458
|
+
console.warn("invalid value", v);
|
|
3459
|
+
return this;
|
|
3460
|
+
}
|
|
3461
|
+
return this.set(name, v1);
|
|
3462
|
+
};
|
|
3463
|
+
proto[`update${uname}`] = function(fn) {
|
|
3464
|
+
return this[setName](fn(this.get(name)));
|
|
3465
|
+
};
|
|
3466
|
+
proto[`reset${uname}`] = function() {
|
|
3467
|
+
return this.set(name, that.defaultValue);
|
|
3468
|
+
};
|
|
3469
|
+
this.extendProtoForType(proto, uname);
|
|
3470
|
+
}
|
|
3471
|
+
}
|
|
3472
|
+
|
|
3473
|
+
class Check {
|
|
3474
|
+
isValid(_v) {
|
|
3475
|
+
return true;
|
|
3476
|
+
}
|
|
3477
|
+
getMessage(_v) {
|
|
3478
|
+
return "Invalid";
|
|
3479
|
+
}
|
|
3480
|
+
}
|
|
3481
|
+
|
|
3482
|
+
class CheckTypeAny extends Check {
|
|
3483
|
+
}
|
|
3484
|
+
var CHECK_TYPE_ANY = new CheckTypeAny;
|
|
3485
|
+
|
|
3486
|
+
class FnCheck extends Check {
|
|
3487
|
+
constructor(isValidFn, getMessageFn) {
|
|
3488
|
+
super();
|
|
3489
|
+
this._isValid = isValidFn;
|
|
3490
|
+
this._getMessage = getMessageFn;
|
|
3491
|
+
}
|
|
3492
|
+
isValid(v) {
|
|
3493
|
+
return this._isValid(v);
|
|
3494
|
+
}
|
|
3495
|
+
getMessage(v) {
|
|
3496
|
+
return this._getMessage(v);
|
|
3497
|
+
}
|
|
3498
|
+
}
|
|
3499
|
+
var CHECK_TYPE_INT = new FnCheck((v) => Number.isInteger(v), () => "Integer expected");
|
|
3500
|
+
var CHECK_TYPE_FLOAT = new FnCheck((v) => Number.isFinite(v), () => "Float expected");
|
|
3501
|
+
var CHECK_TYPE_BOOL = new FnCheck((v) => typeof v === "boolean", () => "Boolean expected");
|
|
3502
|
+
var CHECK_TYPE_STRING = new FnCheck((v) => typeof v === "string", () => "String expected");
|
|
3503
|
+
var CHECK_TYPE_LIST = new FnCheck((v) => List.isList(v), () => "List expected");
|
|
3504
|
+
var CHECK_TYPE_MAP = new FnCheck((v) => IMap.isMap(v), () => "Map expected");
|
|
3505
|
+
var CHECK_TYPE_OMAP = new FnCheck((v) => OrderedMap.isOrderedMap(v), () => "OrderedMap expected");
|
|
3506
|
+
var CHECK_TYPE_SET = new FnCheck((v) => ISet.isSet(v), () => "Set expected");
|
|
3507
|
+
var boolCoercer = (v) => !!v;
|
|
3508
|
+
|
|
3509
|
+
class FieldBool extends Field {
|
|
3510
|
+
constructor(name, defaultValue = false) {
|
|
3511
|
+
super("bool", name, CHECK_TYPE_BOOL, boolCoercer, defaultValue);
|
|
3512
|
+
}
|
|
3513
|
+
extendProtoForType(proto, uname) {
|
|
3514
|
+
const { name } = this;
|
|
3515
|
+
proto[`toggle${uname}`] = function() {
|
|
3516
|
+
return this.set(name, !this.get(name, false));
|
|
3517
|
+
};
|
|
3518
|
+
proto[`set${uname}`] = function(v) {
|
|
3519
|
+
return this.set(name, !!v);
|
|
3520
|
+
};
|
|
3521
|
+
}
|
|
3522
|
+
}
|
|
3523
|
+
|
|
3524
|
+
class FieldAny extends Field {
|
|
3525
|
+
constructor(name, defaultValue = null) {
|
|
3526
|
+
super("any", name, CHECK_TYPE_ANY, nullCoercer, defaultValue);
|
|
3527
|
+
}
|
|
3528
|
+
toDataDef() {
|
|
3529
|
+
const { defaultValue: dv } = this;
|
|
3530
|
+
const type = getTypeName(dv) ?? "any";
|
|
3531
|
+
return { type, defaultValue: dv?.toJS ? dv.toJS() : dv };
|
|
3532
|
+
}
|
|
3533
|
+
}
|
|
3534
|
+
var stringCoercer = (v) => v?.toString?.() ?? "";
|
|
3535
|
+
|
|
3536
|
+
class FieldString extends Field {
|
|
3537
|
+
constructor(name, defaultValue = "") {
|
|
3538
|
+
super("text", name, CHECK_TYPE_STRING, stringCoercer, defaultValue);
|
|
3539
|
+
}
|
|
3540
|
+
extendProtoForType(proto, _uname) {
|
|
3541
|
+
extendProtoSized(proto, this.name, "", "length");
|
|
3542
|
+
}
|
|
3543
|
+
}
|
|
3544
|
+
var intCoercer = (v) => Number.isFinite(v) ? Math.trunc(v) : null;
|
|
3545
|
+
|
|
3546
|
+
class FieldInt extends Field {
|
|
3547
|
+
constructor(name, defaultValue = 0) {
|
|
3548
|
+
super("int", name, CHECK_TYPE_INT, intCoercer, defaultValue);
|
|
3549
|
+
}
|
|
3550
|
+
}
|
|
3551
|
+
var floatCoercer = (_) => null;
|
|
3552
|
+
|
|
3553
|
+
class FieldFloat extends Field {
|
|
3554
|
+
constructor(name, defaultValue = 0) {
|
|
3555
|
+
super("float", name, CHECK_TYPE_FLOAT, floatCoercer, defaultValue);
|
|
3556
|
+
}
|
|
3557
|
+
}
|
|
3558
|
+
var getTypeName = (v) => v?.constructor?.getMetaClass?.()?.name;
|
|
3559
|
+
|
|
3560
|
+
class CheckTypeName {
|
|
3561
|
+
constructor(typeName) {
|
|
3562
|
+
this.typeName = typeName;
|
|
3563
|
+
}
|
|
3564
|
+
isValid(v) {
|
|
3565
|
+
return getTypeName(v) === this.typeName;
|
|
3566
|
+
}
|
|
3567
|
+
getMessage(v) {
|
|
3568
|
+
const got = getTypeName(v);
|
|
3569
|
+
return `Expected "${this.typeName}", got "${got}"`;
|
|
3570
|
+
}
|
|
3571
|
+
}
|
|
3572
|
+
|
|
3573
|
+
class FieldComp extends Field {
|
|
3574
|
+
constructor(type, name, args) {
|
|
3575
|
+
super(type, name, new CheckTypeName(type), nullCoercer, null);
|
|
3576
|
+
this.args = args;
|
|
3577
|
+
}
|
|
3578
|
+
toDataDef() {
|
|
3579
|
+
return { component: this.typeName, args: this.args };
|
|
3580
|
+
}
|
|
3581
|
+
}
|
|
3582
|
+
var NONE2 = Symbol("NONE");
|
|
3583
|
+
function extendProtoForKeyed(proto, name, uname) {
|
|
3584
|
+
extendProtoSized(proto, name, EMPTY_LIST);
|
|
3585
|
+
proto[`setIn${uname}At`] = function(i, v) {
|
|
3586
|
+
return this.set(name, this.get(name).set(i, v));
|
|
3587
|
+
};
|
|
3588
|
+
proto[`getIn${uname}At`] = function(i, dval) {
|
|
3589
|
+
return this.get(name).get(i, dval);
|
|
3590
|
+
};
|
|
3591
|
+
proto[`updateIn${uname}At`] = function(i, fn) {
|
|
3592
|
+
const col = this.get(name);
|
|
3593
|
+
const v = col.get(i, NONE2);
|
|
3594
|
+
if (v !== NONE2)
|
|
3595
|
+
return this.set(name, col.set(i, fn(v)));
|
|
3596
|
+
console.warn("key", i, "not found in", name, col);
|
|
3597
|
+
return this;
|
|
3598
|
+
};
|
|
3599
|
+
extendDeleteInAt(proto, name, `${uname}At`);
|
|
3600
|
+
}
|
|
3601
|
+
function extendDeleteInAt(proto, name, uname) {
|
|
3602
|
+
proto[`deleteIn${uname}`] = function(v) {
|
|
3603
|
+
return this.set(name, this.get(name).delete(v));
|
|
3604
|
+
};
|
|
3605
|
+
proto[`removeIn${uname}`] = proto[`deleteIn${uname}`];
|
|
3606
|
+
}
|
|
3607
|
+
var EMPTY_LIST = List();
|
|
3608
|
+
var listCoercer = (v) => Array.isArray(v) ? List(v) : null;
|
|
3609
|
+
|
|
3610
|
+
class FieldList extends Field {
|
|
3611
|
+
constructor(name, defaultValue = EMPTY_LIST) {
|
|
3612
|
+
super("list", name, CHECK_TYPE_LIST, listCoercer, defaultValue);
|
|
3613
|
+
}
|
|
3614
|
+
extendProtoForType(proto, uname) {
|
|
3615
|
+
const { name } = this;
|
|
3616
|
+
extendProtoForKeyed(proto, name, uname);
|
|
3617
|
+
proto[`pushIn${uname}`] = function(v) {
|
|
3618
|
+
return this.set(name, this.get(name).push(v));
|
|
3619
|
+
};
|
|
3620
|
+
proto[`insertIn${uname}At`] = function(i, v) {
|
|
3621
|
+
return this.set(name, this.get(name).insert(i, v));
|
|
3622
|
+
};
|
|
3623
|
+
}
|
|
3624
|
+
}
|
|
3625
|
+
var imapCoercer = (v) => IMap(v);
|
|
3626
|
+
|
|
3627
|
+
class FieldMap extends Field {
|
|
3628
|
+
constructor(name, defaultValue = IMap()) {
|
|
3629
|
+
super("map", name, CHECK_TYPE_MAP, imapCoercer, defaultValue);
|
|
3630
|
+
}
|
|
3631
|
+
extendProtoForType(proto, uname) {
|
|
3632
|
+
extendProtoForKeyed(proto, this.name, uname);
|
|
3633
|
+
}
|
|
3634
|
+
}
|
|
3635
|
+
var omapCoercer = (v) => OrderedMap(v);
|
|
3636
|
+
|
|
3637
|
+
class FieldOMap extends Field {
|
|
3638
|
+
constructor(name, defaultValue = OrderedMap()) {
|
|
3639
|
+
super("omap", name, CHECK_TYPE_OMAP, omapCoercer, defaultValue);
|
|
3640
|
+
}
|
|
3641
|
+
extendProtoForType(proto, uname) {
|
|
3642
|
+
extendProtoForKeyed(proto, this.name, uname);
|
|
3643
|
+
}
|
|
3644
|
+
}
|
|
3645
|
+
function extendProtoSized(proto, name, defaultEmpty, propName = "size") {
|
|
3646
|
+
proto[`${name}Len`] = function() {
|
|
3647
|
+
return this.get(name, defaultEmpty)[propName];
|
|
3648
|
+
};
|
|
3649
|
+
}
|
|
3650
|
+
var EMPTY_SET = ISet();
|
|
3651
|
+
var isetCoercer = (v) => Array.isArray(v) ? ISet(v) : v instanceof Set ? ISet(v) : null;
|
|
3652
|
+
|
|
3653
|
+
class FieldSet extends Field {
|
|
3654
|
+
constructor(name, defaultValue = EMPTY_SET) {
|
|
3655
|
+
super("set", name, CHECK_TYPE_SET, isetCoercer, defaultValue);
|
|
3656
|
+
}
|
|
3657
|
+
extendProtoForType(proto, uname) {
|
|
3658
|
+
const { name } = this;
|
|
3659
|
+
extendProtoSized(proto, name, EMPTY_SET);
|
|
3660
|
+
proto[`addIn${uname}`] = function(v) {
|
|
3661
|
+
return this.set(name, this.get(name).add(v));
|
|
3662
|
+
};
|
|
3663
|
+
extendDeleteInAt(proto, name, uname);
|
|
3664
|
+
proto[`hasIn${uname}`] = function(v) {
|
|
3665
|
+
return this.get(name).has(v);
|
|
3666
|
+
};
|
|
3667
|
+
proto[`toggleIn${uname}`] = function(v) {
|
|
3668
|
+
const current = this.get(name);
|
|
3669
|
+
return this.set(name, current.has(v) ? current.delete(v) : current.add(v));
|
|
3670
|
+
};
|
|
3671
|
+
}
|
|
3672
|
+
}
|
|
3673
|
+
function mkCompField(field, scope, args) {
|
|
3674
|
+
const Comp = scope.lookupComponent(field.type);
|
|
3675
|
+
console.assert(Comp !== null, "component not found", { field });
|
|
3676
|
+
return Comp?.make({ ...field.args, ...args }, { scope }) ?? null;
|
|
3677
|
+
}
|
|
3678
|
+
|
|
3679
|
+
class ClassBuilder {
|
|
3680
|
+
constructor(name) {
|
|
3681
|
+
const fields = {};
|
|
3682
|
+
const compFields = new Set;
|
|
3683
|
+
this.name = name;
|
|
3684
|
+
this.fields = fields;
|
|
3685
|
+
this.compFields = compFields;
|
|
3686
|
+
this._methods = {};
|
|
3687
|
+
this._statics = {
|
|
3688
|
+
make: function(inArgs = {}, opts = {}) {
|
|
3689
|
+
const args = {};
|
|
3690
|
+
for (const key in inArgs) {
|
|
3691
|
+
const field = fields[key];
|
|
3692
|
+
if (compFields.has(key))
|
|
3693
|
+
args[key] = mkCompField(field, opts.scope, inArgs[key]);
|
|
3694
|
+
else if (field === undefined)
|
|
3695
|
+
console.warn("extra argument to constructor:", name, key, inArgs);
|
|
3696
|
+
else
|
|
3697
|
+
args[key] = field.coerceOrDefault(inArgs[key]);
|
|
3698
|
+
}
|
|
3699
|
+
for (const key of compFields)
|
|
3700
|
+
if (args[key] === undefined)
|
|
3701
|
+
args[key] = mkCompField(fields[key], opts.scope, inArgs[key]);
|
|
3702
|
+
return this(args);
|
|
3703
|
+
}
|
|
3704
|
+
};
|
|
3705
|
+
}
|
|
3706
|
+
build() {
|
|
3707
|
+
const fieldVals = {};
|
|
3708
|
+
const proto = {};
|
|
3709
|
+
const { name, _methods, fields } = this;
|
|
3710
|
+
for (const fieldName in fields) {
|
|
3711
|
+
const field = fields[fieldName];
|
|
3712
|
+
fieldVals[fieldName] = field.defaultValue;
|
|
3713
|
+
field.extendProto(proto);
|
|
3714
|
+
}
|
|
3715
|
+
const Class = { [name]: Record(fieldVals, name) }[name];
|
|
3716
|
+
Object.assign(Class.prototype, proto, _methods);
|
|
3717
|
+
const metaClass = { fields, name, methods: _methods };
|
|
3718
|
+
Object.assign(Class, this._statics, { getMetaClass: () => metaClass });
|
|
3719
|
+
return Class;
|
|
3720
|
+
}
|
|
3721
|
+
methods(proto) {
|
|
3722
|
+
for (const k in proto)
|
|
3723
|
+
this._methods[k] = proto[k];
|
|
3724
|
+
}
|
|
3725
|
+
statics(proto) {
|
|
3726
|
+
for (const k in proto)
|
|
3727
|
+
this._statics[k] = proto[k];
|
|
3728
|
+
}
|
|
3729
|
+
addField(name, dval, FieldCls) {
|
|
3730
|
+
const field = new FieldCls(name, dval);
|
|
3731
|
+
this.fields[name] = field;
|
|
3732
|
+
return field;
|
|
3733
|
+
}
|
|
3734
|
+
addCompField(name, type, args) {
|
|
3735
|
+
const field = new FieldComp(type, name, args);
|
|
3736
|
+
this.compFields.add(name);
|
|
3737
|
+
this.fields[name] = field;
|
|
3738
|
+
return field;
|
|
3739
|
+
}
|
|
3740
|
+
}
|
|
3741
|
+
var FIELD_CLASS = Symbol.for("tutuca.fieldClass");
|
|
3742
|
+
var fieldsByTypeName = {
|
|
3743
|
+
text: FieldString,
|
|
3744
|
+
int: FieldInt,
|
|
3745
|
+
float: FieldFloat,
|
|
3746
|
+
bool: FieldBool,
|
|
3747
|
+
list: FieldList,
|
|
3748
|
+
map: FieldMap,
|
|
3749
|
+
omap: FieldOMap,
|
|
3750
|
+
set: FieldSet,
|
|
3751
|
+
any: FieldAny
|
|
3752
|
+
};
|
|
3753
|
+
function classFromData(name, { fields = {}, methods, statics }) {
|
|
3754
|
+
const b = new ClassBuilder(name);
|
|
3755
|
+
for (const field in fields) {
|
|
3756
|
+
const value = fields[field];
|
|
3757
|
+
const type = typeof value;
|
|
3758
|
+
if (type === "string")
|
|
3759
|
+
b.addField(field, value, FieldString);
|
|
3760
|
+
else if (type === "number")
|
|
3761
|
+
b.addField(field, value, FieldFloat);
|
|
3762
|
+
else if (type === "boolean")
|
|
3763
|
+
b.addField(field, value, FieldBool);
|
|
3764
|
+
else if (List.isList(value) || Array.isArray(value))
|
|
3765
|
+
b.addField(field, List(value), FieldList);
|
|
3766
|
+
else if (ISet.isSet(value) || value instanceof Set)
|
|
3767
|
+
b.addField(field, ISet(value), FieldSet);
|
|
3768
|
+
else if (OrderedMap.isOrderedMap(value))
|
|
3769
|
+
b.addField(field, value, FieldOMap);
|
|
3770
|
+
else if (value?.type && value?.defaultValue !== undefined) {
|
|
3771
|
+
const Field2 = fieldsByTypeName[value.type] ?? FieldAny;
|
|
3772
|
+
b.addField(field, new Field2().coerceOr(value.defaultValue), Field2);
|
|
3773
|
+
} else if (value?.component && value?.args !== undefined)
|
|
3774
|
+
b.addCompField(field, value.component, value.args);
|
|
3775
|
+
else if (IMap.isMap(value) || value?.constructor === Object)
|
|
3776
|
+
b.addField(field, IMap(value), FieldMap);
|
|
3777
|
+
else {
|
|
3778
|
+
const Field2 = value?.[FIELD_CLASS] ?? FieldAny;
|
|
3779
|
+
b.addField(field, value, Field2);
|
|
3780
|
+
}
|
|
3781
|
+
}
|
|
3782
|
+
if (methods)
|
|
3783
|
+
b.methods(methods);
|
|
3784
|
+
if (statics)
|
|
3785
|
+
b.statics(statics);
|
|
3786
|
+
return b.build();
|
|
3787
|
+
}
|
|
3788
|
+
var component = (opts) => new Component(classFromData(opts.name, opts), opts);
|
|
3789
|
+
|
|
3790
|
+
// index.js
|
|
3791
|
+
var css = String.raw;
|
|
3792
|
+
var html = String.raw;
|
|
3793
|
+
var macro = (defaults, rawView) => new Macro(defaults, rawView);
|
|
3794
|
+
function check(_app) {
|
|
3795
|
+
return { error: 0, warn: 0, hint: 0, dummyCheck: true };
|
|
3796
|
+
}
|
|
3797
|
+
async function test(_opts) {
|
|
3798
|
+
return null;
|
|
3799
|
+
}
|
|
3800
|
+
function collectIterBindings() {
|
|
3801
|
+
console.warn("collectIterBindings is a no-op in the core tutuca build; use the tutuca-dev build for a functional implementation");
|
|
3802
|
+
return [];
|
|
3803
|
+
}
|
|
3804
|
+
function tutuca(nodeOrSelector) {
|
|
3805
|
+
const rootNode = typeof nodeOrSelector === "string" ? document.querySelector(nodeOrSelector) : nodeOrSelector;
|
|
3806
|
+
const comps = new Components;
|
|
3807
|
+
const renderer = new Renderer(comps);
|
|
3808
|
+
return new App(rootNode, comps, renderer, ParseContext);
|
|
3809
|
+
}
|
|
3810
|
+
export {
|
|
3811
|
+
tutuca,
|
|
3812
|
+
test,
|
|
3813
|
+
macro,
|
|
3814
|
+
isOrderedMap as isOMap,
|
|
3815
|
+
isMap as isIMap,
|
|
3816
|
+
injectCss,
|
|
3817
|
+
html,
|
|
3818
|
+
css,
|
|
3819
|
+
component,
|
|
3820
|
+
collectIterBindings,
|
|
3821
|
+
check,
|
|
3822
|
+
SEQ_INFO,
|
|
3823
|
+
ParseContext,
|
|
3824
|
+
OrderedMap2 as OMap,
|
|
3825
|
+
Set2 as ISet,
|
|
3826
|
+
Map2 as IMap,
|
|
3827
|
+
FIELD_CLASS
|
|
3828
|
+
};
|