@odoo/owl 3.0.0-alpha.20 → 3.0.0-alpha.21
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +100 -110
- package/dist/compile_templates.mjs +84 -80
- package/dist/compiler.js +2356 -2356
- package/dist/owl-devtools.zip +0 -0
- package/dist/owl.cjs.js +1596 -1459
- package/dist/owl.cjs.runtime.js +4362 -3952
- package/dist/owl.es.js +1596 -1459
- package/dist/owl.es.runtime.js +4360 -3950
- package/dist/owl.iife.js +1596 -1459
- package/dist/owl.iife.min.js +1 -1
- package/dist/owl.iife.runtime.js +4363 -3953
- package/dist/owl.iife.runtime.min.js +1 -1
- package/dist/types/common/types.d.ts +1 -1
- package/dist/types/compiler/code_generator.d.ts +1 -2
- package/dist/types/compiler/inline_expressions.d.ts +7 -24
- package/dist/types/compiler/parser.d.ts +3 -8
- package/dist/types/owl.d.ts +33 -27
- package/dist/types/runtime/blockdom/attributes.d.ts +2 -0
- package/dist/types/runtime/component_node.d.ts +2 -0
- package/dist/types/runtime/error_handling.d.ts +13 -13
- package/dist/types/runtime/fibers.d.ts +37 -37
- package/dist/types/runtime/plugin_manager.d.ts +2 -0
- package/dist/types/runtime/portal.d.ts +15 -12
- package/dist/types/runtime/reactivity/computations.d.ts +1 -0
- package/dist/types/runtime/reactivity.d.ts +57 -57
- package/dist/types/runtime/rendering/scheduler.d.ts +1 -1
- package/dist/types/runtime/rendering/template_helpers.d.ts +2 -2
- package/dist/types/runtime/scheduler.d.ts +21 -21
- package/dist/types/runtime/types.d.ts +3 -0
- package/dist/types/version.d.ts +1 -1
- package/package.json +24 -1
- package/dist/types/runtime/cancellableContext.d.ts +0 -15
- package/dist/types/runtime/cancellablePromise.d.ts +0 -15
- package/dist/types/runtime/executionContext.d.ts +0 -0
- package/dist/types/runtime/listOperation.d.ts +0 -1
- package/dist/types/runtime/model.d.ts +0 -48
- package/dist/types/runtime/plugins.d.ts +0 -36
- package/dist/types/runtime/reactivity/async_computed.d.ts +0 -1
- package/dist/types/runtime/reactivity/derived.d.ts +0 -7
- package/dist/types/runtime/reactivity/reactivity.d.ts +0 -46
- package/dist/types/runtime/reactivity/signals.d.ts +0 -30
- package/dist/types/runtime/reactivity/state.d.ts +0 -48
- package/dist/types/runtime/relationalModel/discussModel.d.ts +0 -19
- package/dist/types/runtime/relationalModel/discussModelTypes.d.ts +0 -22
- package/dist/types/runtime/relationalModel/field.d.ts +0 -20
- package/dist/types/runtime/relationalModel/model.d.ts +0 -59
- package/dist/types/runtime/relationalModel/modelData.d.ts +0 -18
- package/dist/types/runtime/relationalModel/modelRegistry.d.ts +0 -3
- package/dist/types/runtime/relationalModel/modelUtils.d.ts +0 -4
- package/dist/types/runtime/relationalModel/store.d.ts +0 -16
- package/dist/types/runtime/relationalModel/types.d.ts +0 -83
- package/dist/types/runtime/relationalModel/util.d.ts +0 -1
- package/dist/types/runtime/relationalModel/web/WebDataPoint.d.ts +0 -25
- package/dist/types/runtime/relationalModel/web/WebRecord.d.ts +0 -131
- package/dist/types/runtime/relationalModel/web/WebStaticList.d.ts +0 -63
- package/dist/types/runtime/relationalModel/web/webModel.d.ts +0 -5
- package/dist/types/runtime/relationalModel/web/webModelTypes.d.ts +0 -139
- package/dist/types/runtime/signals.d.ts +0 -19
- package/dist/types/runtime/suspense.d.ts +0 -5
- package/dist/types/runtime/task.d.ts +0 -12
- package/dist/types/utils/registry.d.ts +0 -15
package/dist/owl.es.js
CHANGED
|
@@ -127,10 +127,16 @@ function toggler(key, child) {
|
|
|
127
127
|
return new VToggler(key, child);
|
|
128
128
|
}
|
|
129
129
|
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
130
|
+
let elemSetAttribute;
|
|
131
|
+
let removeAttribute;
|
|
132
|
+
let tokenListAdd;
|
|
133
|
+
let tokenListRemove;
|
|
134
|
+
if (typeof Element !== "undefined") {
|
|
135
|
+
({ setAttribute: elemSetAttribute, removeAttribute } = Element.prototype);
|
|
136
|
+
const tokenList = DOMTokenList.prototype;
|
|
137
|
+
tokenListAdd = tokenList.add;
|
|
138
|
+
tokenListRemove = tokenList.remove;
|
|
139
|
+
}
|
|
134
140
|
const isArray = Array.isArray;
|
|
135
141
|
const { split, trim } = String.prototype;
|
|
136
142
|
const wordRegexp = /\s+/;
|
|
@@ -162,6 +168,9 @@ function attrsSetter(attrs) {
|
|
|
162
168
|
if (attrs[0] === "class") {
|
|
163
169
|
setClass.call(this, attrs[1]);
|
|
164
170
|
}
|
|
171
|
+
else if (attrs[0] === "style") {
|
|
172
|
+
setStyle.call(this, attrs[1]);
|
|
173
|
+
}
|
|
165
174
|
else {
|
|
166
175
|
setAttribute.call(this, attrs[0], attrs[1]);
|
|
167
176
|
}
|
|
@@ -171,6 +180,9 @@ function attrsSetter(attrs) {
|
|
|
171
180
|
if (k === "class") {
|
|
172
181
|
setClass.call(this, attrs[k]);
|
|
173
182
|
}
|
|
183
|
+
else if (k === "style") {
|
|
184
|
+
setStyle.call(this, attrs[k]);
|
|
185
|
+
}
|
|
174
186
|
else {
|
|
175
187
|
setAttribute.call(this, k, attrs[k]);
|
|
176
188
|
}
|
|
@@ -188,6 +200,9 @@ function attrsUpdater(attrs, oldAttrs) {
|
|
|
188
200
|
if (name === "class") {
|
|
189
201
|
updateClass.call(this, val, oldAttrs[1]);
|
|
190
202
|
}
|
|
203
|
+
else if (name === "style") {
|
|
204
|
+
updateStyle.call(this, val, oldAttrs[1]);
|
|
205
|
+
}
|
|
191
206
|
else {
|
|
192
207
|
setAttribute.call(this, name, val);
|
|
193
208
|
}
|
|
@@ -203,6 +218,9 @@ function attrsUpdater(attrs, oldAttrs) {
|
|
|
203
218
|
if (k === "class") {
|
|
204
219
|
updateClass.call(this, "", oldAttrs[k]);
|
|
205
220
|
}
|
|
221
|
+
else if (k === "style") {
|
|
222
|
+
updateStyle.call(this, "", oldAttrs[k]);
|
|
223
|
+
}
|
|
206
224
|
else {
|
|
207
225
|
removeAttribute.call(this, k);
|
|
208
226
|
}
|
|
@@ -214,6 +232,9 @@ function attrsUpdater(attrs, oldAttrs) {
|
|
|
214
232
|
if (k === "class") {
|
|
215
233
|
updateClass.call(this, val, oldAttrs[k]);
|
|
216
234
|
}
|
|
235
|
+
else if (k === "style") {
|
|
236
|
+
updateStyle.call(this, val, oldAttrs[k]);
|
|
237
|
+
}
|
|
217
238
|
else {
|
|
218
239
|
setAttribute.call(this, k, val);
|
|
219
240
|
}
|
|
@@ -286,6 +307,80 @@ function updateClass(val, oldVal) {
|
|
|
286
307
|
}
|
|
287
308
|
}
|
|
288
309
|
}
|
|
310
|
+
// ---------------------------------------------------------------------------
|
|
311
|
+
// Style
|
|
312
|
+
// ---------------------------------------------------------------------------
|
|
313
|
+
const CSS_PROP_CACHE = {};
|
|
314
|
+
function toKebabCase(prop) {
|
|
315
|
+
if (prop in CSS_PROP_CACHE) {
|
|
316
|
+
return CSS_PROP_CACHE[prop];
|
|
317
|
+
}
|
|
318
|
+
const result = prop.replace(/[A-Z]/g, (m) => "-" + m.toLowerCase());
|
|
319
|
+
CSS_PROP_CACHE[prop] = result;
|
|
320
|
+
return result;
|
|
321
|
+
}
|
|
322
|
+
function toStyleObj(expr) {
|
|
323
|
+
const result = {};
|
|
324
|
+
switch (typeof expr) {
|
|
325
|
+
case "string": {
|
|
326
|
+
const str = trim.call(expr);
|
|
327
|
+
if (!str) {
|
|
328
|
+
return {};
|
|
329
|
+
}
|
|
330
|
+
const parts = str.split(";");
|
|
331
|
+
for (let part of parts) {
|
|
332
|
+
part = trim.call(part);
|
|
333
|
+
if (!part) {
|
|
334
|
+
continue;
|
|
335
|
+
}
|
|
336
|
+
const colonIdx = part.indexOf(":");
|
|
337
|
+
if (colonIdx === -1) {
|
|
338
|
+
continue;
|
|
339
|
+
}
|
|
340
|
+
const prop = trim.call(part.slice(0, colonIdx));
|
|
341
|
+
const value = trim.call(part.slice(colonIdx + 1));
|
|
342
|
+
if (prop && value) {
|
|
343
|
+
result[prop] = value;
|
|
344
|
+
}
|
|
345
|
+
}
|
|
346
|
+
return result;
|
|
347
|
+
}
|
|
348
|
+
case "object":
|
|
349
|
+
for (let prop in expr) {
|
|
350
|
+
const value = expr[prop];
|
|
351
|
+
if (value || value === 0) {
|
|
352
|
+
result[toKebabCase(prop)] = String(value);
|
|
353
|
+
}
|
|
354
|
+
}
|
|
355
|
+
return result;
|
|
356
|
+
default:
|
|
357
|
+
return {};
|
|
358
|
+
}
|
|
359
|
+
}
|
|
360
|
+
function setStyle(val) {
|
|
361
|
+
val = val === "" ? {} : toStyleObj(val);
|
|
362
|
+
const style = this.style;
|
|
363
|
+
for (let prop in val) {
|
|
364
|
+
style.setProperty(prop, val[prop]);
|
|
365
|
+
}
|
|
366
|
+
}
|
|
367
|
+
function updateStyle(val, oldVal) {
|
|
368
|
+
oldVal = oldVal === "" ? {} : toStyleObj(oldVal);
|
|
369
|
+
val = val === "" ? {} : toStyleObj(val);
|
|
370
|
+
const style = this.style;
|
|
371
|
+
// remove old styles
|
|
372
|
+
for (let prop in oldVal) {
|
|
373
|
+
if (!(prop in val)) {
|
|
374
|
+
style.removeProperty(prop);
|
|
375
|
+
}
|
|
376
|
+
}
|
|
377
|
+
// set new/changed styles
|
|
378
|
+
for (let prop in val) {
|
|
379
|
+
if (val[prop] !== oldVal[prop]) {
|
|
380
|
+
style.setProperty(prop, val[prop]);
|
|
381
|
+
}
|
|
382
|
+
}
|
|
383
|
+
}
|
|
289
384
|
|
|
290
385
|
/**
|
|
291
386
|
* Creates a batched version of a callback so that all calls to it in the same
|
|
@@ -421,16 +516,17 @@ function markup(valueOrStrings, ...placeholders) {
|
|
|
421
516
|
function createEventHandler(rawEvent) {
|
|
422
517
|
const eventName = rawEvent.split(".")[0];
|
|
423
518
|
const capture = rawEvent.includes(".capture");
|
|
519
|
+
const passive = rawEvent.includes(".passive");
|
|
424
520
|
if (rawEvent.includes(".synthetic")) {
|
|
425
|
-
return createSyntheticHandler(eventName, capture);
|
|
521
|
+
return createSyntheticHandler(eventName, capture, passive);
|
|
426
522
|
}
|
|
427
523
|
else {
|
|
428
|
-
return createElementHandler(eventName, capture);
|
|
524
|
+
return createElementHandler(eventName, capture, passive);
|
|
429
525
|
}
|
|
430
526
|
}
|
|
431
527
|
// Native listener
|
|
432
528
|
let nextNativeEventId = 1;
|
|
433
|
-
function createElementHandler(evName, capture = false) {
|
|
529
|
+
function createElementHandler(evName, capture = false, passive = false) {
|
|
434
530
|
let eventKey = `__event__${evName}_${nextNativeEventId++}`;
|
|
435
531
|
if (capture) {
|
|
436
532
|
eventKey = `${eventKey}_capture`;
|
|
@@ -444,13 +540,14 @@ function createElementHandler(evName, capture = false) {
|
|
|
444
540
|
return;
|
|
445
541
|
config$1.mainEventHandler(data, ev, currentTarget);
|
|
446
542
|
}
|
|
543
|
+
const options = { capture, passive };
|
|
447
544
|
function setup(data) {
|
|
448
545
|
this[eventKey] = data;
|
|
449
|
-
this.addEventListener(evName, listener,
|
|
546
|
+
this.addEventListener(evName, listener, options);
|
|
450
547
|
}
|
|
451
548
|
function remove() {
|
|
452
549
|
delete this[eventKey];
|
|
453
|
-
this.removeEventListener(evName, listener,
|
|
550
|
+
this.removeEventListener(evName, listener, options);
|
|
454
551
|
}
|
|
455
552
|
function update(data) {
|
|
456
553
|
this[eventKey] = data;
|
|
@@ -460,12 +557,12 @@ function createElementHandler(evName, capture = false) {
|
|
|
460
557
|
// Synthetic handler: a form of event delegation that allows placing only one
|
|
461
558
|
// listener per event type.
|
|
462
559
|
let nextSyntheticEventId = 1;
|
|
463
|
-
function createSyntheticHandler(evName, capture = false) {
|
|
560
|
+
function createSyntheticHandler(evName, capture = false, passive = false) {
|
|
464
561
|
let eventKey = `__event__synthetic_${evName}`;
|
|
465
562
|
if (capture) {
|
|
466
563
|
eventKey = `${eventKey}_capture`;
|
|
467
564
|
}
|
|
468
|
-
setupSyntheticEvent(evName, eventKey, capture);
|
|
565
|
+
setupSyntheticEvent(evName, eventKey, capture, passive);
|
|
469
566
|
const currentId = nextSyntheticEventId++;
|
|
470
567
|
function setup(data) {
|
|
471
568
|
const _data = this[eventKey] || {};
|
|
@@ -492,21 +589,27 @@ function nativeToSyntheticEvent(eventKey, event) {
|
|
|
492
589
|
}
|
|
493
590
|
}
|
|
494
591
|
const CONFIGURED_SYNTHETIC_EVENTS = {};
|
|
495
|
-
function setupSyntheticEvent(evName, eventKey, capture = false) {
|
|
592
|
+
function setupSyntheticEvent(evName, eventKey, capture = false, passive = false) {
|
|
496
593
|
if (CONFIGURED_SYNTHETIC_EVENTS[eventKey]) {
|
|
497
594
|
return;
|
|
498
595
|
}
|
|
499
596
|
document.addEventListener(evName, (event) => nativeToSyntheticEvent(eventKey, event), {
|
|
500
597
|
capture,
|
|
598
|
+
passive,
|
|
501
599
|
});
|
|
502
600
|
CONFIGURED_SYNTHETIC_EVENTS[eventKey] = true;
|
|
503
601
|
}
|
|
504
602
|
|
|
505
603
|
const getDescriptor$3 = (o, p) => Object.getOwnPropertyDescriptor(o, p);
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
604
|
+
let nodeInsertBefore$3;
|
|
605
|
+
let nodeSetTextContent$1;
|
|
606
|
+
let nodeRemoveChild$3;
|
|
607
|
+
if (typeof Node !== "undefined") {
|
|
608
|
+
const nodeProto = Node.prototype;
|
|
609
|
+
nodeInsertBefore$3 = nodeProto.insertBefore;
|
|
610
|
+
nodeSetTextContent$1 = getDescriptor$3(nodeProto, "textContent").set;
|
|
611
|
+
nodeRemoveChild$3 = nodeProto.removeChild;
|
|
612
|
+
}
|
|
510
613
|
// -----------------------------------------------------------------------------
|
|
511
614
|
// Multi NODE
|
|
512
615
|
// -----------------------------------------------------------------------------
|
|
@@ -646,11 +749,15 @@ function multi(children) {
|
|
|
646
749
|
}
|
|
647
750
|
|
|
648
751
|
const getDescriptor$2 = (o, p) => Object.getOwnPropertyDescriptor(o, p);
|
|
649
|
-
|
|
650
|
-
|
|
651
|
-
|
|
652
|
-
|
|
653
|
-
const
|
|
752
|
+
let nodeInsertBefore$2;
|
|
753
|
+
let characterDataSetData$1;
|
|
754
|
+
let nodeRemoveChild$2;
|
|
755
|
+
if (typeof Node !== "undefined") {
|
|
756
|
+
const nodeProto = Node.prototype;
|
|
757
|
+
nodeInsertBefore$2 = nodeProto.insertBefore;
|
|
758
|
+
nodeRemoveChild$2 = nodeProto.removeChild;
|
|
759
|
+
characterDataSetData$1 = getDescriptor$2(CharacterData.prototype, "data").set;
|
|
760
|
+
}
|
|
654
761
|
class VSimpleNode {
|
|
655
762
|
text;
|
|
656
763
|
parentEl;
|
|
@@ -681,7 +788,7 @@ class VSimpleNode {
|
|
|
681
788
|
return this.text;
|
|
682
789
|
}
|
|
683
790
|
}
|
|
684
|
-
|
|
791
|
+
class VText extends VSimpleNode {
|
|
685
792
|
mount(parent, afterNode) {
|
|
686
793
|
this.mountNode(document.createTextNode(toText(this.text)), parent, afterNode);
|
|
687
794
|
}
|
|
@@ -692,7 +799,7 @@ let VText$1 = class VText extends VSimpleNode {
|
|
|
692
799
|
this.text = text2;
|
|
693
800
|
}
|
|
694
801
|
}
|
|
695
|
-
}
|
|
802
|
+
}
|
|
696
803
|
class VComment extends VSimpleNode {
|
|
697
804
|
mount(parent, afterNode) {
|
|
698
805
|
this.mountNode(document.createComment(toText(this.text)), parent, afterNode);
|
|
@@ -700,7 +807,7 @@ class VComment extends VSimpleNode {
|
|
|
700
807
|
patch() { }
|
|
701
808
|
}
|
|
702
809
|
function text(str) {
|
|
703
|
-
return new VText
|
|
810
|
+
return new VText(str);
|
|
704
811
|
}
|
|
705
812
|
function comment(str) {
|
|
706
813
|
return new VComment(str);
|
|
@@ -719,12 +826,18 @@ function toText(value) {
|
|
|
719
826
|
}
|
|
720
827
|
|
|
721
828
|
const getDescriptor$1 = (o, p) => Object.getOwnPropertyDescriptor(o, p);
|
|
722
|
-
|
|
723
|
-
|
|
724
|
-
|
|
725
|
-
|
|
726
|
-
|
|
727
|
-
|
|
829
|
+
let nodeProto;
|
|
830
|
+
let elementProto;
|
|
831
|
+
let characterDataSetData;
|
|
832
|
+
let nodeGetFirstChild;
|
|
833
|
+
let nodeGetNextSibling;
|
|
834
|
+
if (typeof Node !== "undefined") {
|
|
835
|
+
nodeProto = Node.prototype;
|
|
836
|
+
elementProto = Element.prototype;
|
|
837
|
+
characterDataSetData = getDescriptor$1(CharacterData.prototype, "data").set;
|
|
838
|
+
nodeGetFirstChild = getDescriptor$1(nodeProto, "firstChild").get;
|
|
839
|
+
nodeGetNextSibling = getDescriptor$1(nodeProto, "nextSibling").get;
|
|
840
|
+
}
|
|
728
841
|
const NO_OP = () => { };
|
|
729
842
|
function makePropSetter(name) {
|
|
730
843
|
return function setProp(value) {
|
|
@@ -1019,6 +1132,10 @@ function updateCtx(ctx, tree) {
|
|
|
1019
1132
|
setter = setClass;
|
|
1020
1133
|
updater = updateClass;
|
|
1021
1134
|
}
|
|
1135
|
+
else if (info.name === "style") {
|
|
1136
|
+
setter = setStyle;
|
|
1137
|
+
updater = updateStyle;
|
|
1138
|
+
}
|
|
1022
1139
|
else {
|
|
1023
1140
|
setter = createAttrUpdater(info.name);
|
|
1024
1141
|
updater = setter;
|
|
@@ -1104,8 +1221,8 @@ function createBlockClass(template, ctx) {
|
|
|
1104
1221
|
(((c.afterRefIdx ?? 0) & 0x7fff) << 16));
|
|
1105
1222
|
// these values are defined here to make them faster to lookup in the class
|
|
1106
1223
|
// block scope
|
|
1107
|
-
const nodeCloneNode = nodeProto
|
|
1108
|
-
const nodeInsertBefore = nodeProto
|
|
1224
|
+
const nodeCloneNode = nodeProto.cloneNode;
|
|
1225
|
+
const nodeInsertBefore = nodeProto.insertBefore;
|
|
1109
1226
|
const elementRemove = elementProto.remove;
|
|
1110
1227
|
class Block {
|
|
1111
1228
|
el;
|
|
@@ -1161,8 +1278,7 @@ function createBlockClass(template, ctx) {
|
|
|
1161
1278
|
locSetters[i].call(refs[locRefIdxs[i]], data[i]);
|
|
1162
1279
|
}
|
|
1163
1280
|
}
|
|
1164
|
-
|
|
1165
|
-
// preparing all children
|
|
1281
|
+
// preparing all children (off-DOM, before inserting el into the live document)
|
|
1166
1282
|
if (childN) {
|
|
1167
1283
|
const children = this.children;
|
|
1168
1284
|
for (let i = 0; i < childN; i++) {
|
|
@@ -1176,6 +1292,7 @@ function createBlockClass(template, ctx) {
|
|
|
1176
1292
|
}
|
|
1177
1293
|
}
|
|
1178
1294
|
}
|
|
1295
|
+
nodeInsertBefore.call(parent, el, afterNode);
|
|
1179
1296
|
this.el = el;
|
|
1180
1297
|
this.parentEl = parent;
|
|
1181
1298
|
if (cbRefs.length) {
|
|
@@ -1253,11 +1370,17 @@ function setText(value) {
|
|
|
1253
1370
|
}
|
|
1254
1371
|
|
|
1255
1372
|
const getDescriptor = (o, p) => Object.getOwnPropertyDescriptor(o, p);
|
|
1256
|
-
|
|
1257
|
-
|
|
1258
|
-
|
|
1259
|
-
|
|
1260
|
-
|
|
1373
|
+
let nodeInsertBefore$1;
|
|
1374
|
+
let nodeAppendChild;
|
|
1375
|
+
let nodeRemoveChild$1;
|
|
1376
|
+
let nodeSetTextContent;
|
|
1377
|
+
if (typeof Node !== "undefined") {
|
|
1378
|
+
const nodeProto = Node.prototype;
|
|
1379
|
+
nodeInsertBefore$1 = nodeProto.insertBefore;
|
|
1380
|
+
nodeAppendChild = nodeProto.appendChild;
|
|
1381
|
+
nodeRemoveChild$1 = nodeProto.removeChild;
|
|
1382
|
+
nodeSetTextContent = getDescriptor(nodeProto, "textContent").set;
|
|
1383
|
+
}
|
|
1261
1384
|
// -----------------------------------------------------------------------------
|
|
1262
1385
|
// List Node
|
|
1263
1386
|
// -----------------------------------------------------------------------------
|
|
@@ -1473,9 +1596,13 @@ function createMapping(ch1, startIdx1, endIdx2) {
|
|
|
1473
1596
|
return mapping;
|
|
1474
1597
|
}
|
|
1475
1598
|
|
|
1476
|
-
|
|
1477
|
-
|
|
1478
|
-
|
|
1599
|
+
let nodeInsertBefore;
|
|
1600
|
+
let nodeRemoveChild;
|
|
1601
|
+
if (typeof Node !== "undefined") {
|
|
1602
|
+
const nodeProto = Node.prototype;
|
|
1603
|
+
nodeInsertBefore = nodeProto.insertBefore;
|
|
1604
|
+
nodeRemoveChild = nodeProto.removeChild;
|
|
1605
|
+
}
|
|
1479
1606
|
class VHtml {
|
|
1480
1607
|
html;
|
|
1481
1608
|
parentEl;
|
|
@@ -1731,127 +1858,6 @@ class Component {
|
|
|
1731
1858
|
setup() { }
|
|
1732
1859
|
}
|
|
1733
1860
|
|
|
1734
|
-
// Maps fibers to thrown errors
|
|
1735
|
-
const fibersInError = new WeakMap();
|
|
1736
|
-
const nodeErrorHandlers = new WeakMap();
|
|
1737
|
-
function destroyApp(app, error) {
|
|
1738
|
-
try {
|
|
1739
|
-
app.destroy();
|
|
1740
|
-
}
|
|
1741
|
-
catch (e) {
|
|
1742
|
-
// mute all errors here because we are in a corrupted state anyway
|
|
1743
|
-
}
|
|
1744
|
-
const e = Object.assign(new OwlError(`[Owl] Unhandled error. Destroying the root component`), {
|
|
1745
|
-
cause: error,
|
|
1746
|
-
});
|
|
1747
|
-
return e;
|
|
1748
|
-
}
|
|
1749
|
-
function _handleError(node, error) {
|
|
1750
|
-
if (!node) {
|
|
1751
|
-
return false;
|
|
1752
|
-
}
|
|
1753
|
-
const fiber = node.fiber;
|
|
1754
|
-
if (fiber) {
|
|
1755
|
-
fibersInError.set(fiber, error);
|
|
1756
|
-
}
|
|
1757
|
-
const errorHandlers = nodeErrorHandlers.get(node);
|
|
1758
|
-
if (errorHandlers) {
|
|
1759
|
-
let handled = false;
|
|
1760
|
-
// execute in the opposite order
|
|
1761
|
-
const finalize = () => destroyApp(node.app, error);
|
|
1762
|
-
for (let i = errorHandlers.length - 1; i >= 0; i--) {
|
|
1763
|
-
try {
|
|
1764
|
-
errorHandlers[i](error, finalize);
|
|
1765
|
-
handled = true;
|
|
1766
|
-
break;
|
|
1767
|
-
}
|
|
1768
|
-
catch (e) {
|
|
1769
|
-
error = e;
|
|
1770
|
-
}
|
|
1771
|
-
}
|
|
1772
|
-
if (handled) {
|
|
1773
|
-
return true;
|
|
1774
|
-
}
|
|
1775
|
-
}
|
|
1776
|
-
return _handleError(node.parent, error);
|
|
1777
|
-
}
|
|
1778
|
-
function handleError(params) {
|
|
1779
|
-
let { error } = params;
|
|
1780
|
-
const node = "node" in params ? params.node : params.fiber.node;
|
|
1781
|
-
const fiber = "fiber" in params ? params.fiber : node.fiber;
|
|
1782
|
-
if (fiber) {
|
|
1783
|
-
// resets the fibers on components if possible. This is important so that
|
|
1784
|
-
// new renderings can be properly included in the initial one, if any.
|
|
1785
|
-
let current = fiber;
|
|
1786
|
-
do {
|
|
1787
|
-
current.node.fiber = current;
|
|
1788
|
-
fibersInError.set(current, error);
|
|
1789
|
-
current = current.parent;
|
|
1790
|
-
} while (current);
|
|
1791
|
-
fibersInError.set(fiber.root, error);
|
|
1792
|
-
}
|
|
1793
|
-
const handled = _handleError(node, error);
|
|
1794
|
-
if (!handled) {
|
|
1795
|
-
throw destroyApp(node.app, error);
|
|
1796
|
-
}
|
|
1797
|
-
}
|
|
1798
|
-
|
|
1799
|
-
// -----------------------------------------------------------------------------
|
|
1800
|
-
// hooks
|
|
1801
|
-
// -----------------------------------------------------------------------------
|
|
1802
|
-
function decorate(node, f, hookName) {
|
|
1803
|
-
const result = f.bind(node.component);
|
|
1804
|
-
if (node.app.dev) {
|
|
1805
|
-
const suffix = f.name ? ` <${f.name}>` : "";
|
|
1806
|
-
Reflect.defineProperty(result, "name", {
|
|
1807
|
-
value: hookName + suffix,
|
|
1808
|
-
});
|
|
1809
|
-
}
|
|
1810
|
-
return result;
|
|
1811
|
-
}
|
|
1812
|
-
function onWillStart(fn) {
|
|
1813
|
-
const { node } = getContext("component");
|
|
1814
|
-
node.willStart.push(decorate(node, fn, "onWillStart"));
|
|
1815
|
-
}
|
|
1816
|
-
function onWillUpdateProps(fn) {
|
|
1817
|
-
const { node } = getContext("component");
|
|
1818
|
-
node.willUpdateProps.push(decorate(node, fn, "onWillUpdateProps"));
|
|
1819
|
-
}
|
|
1820
|
-
function onMounted(fn) {
|
|
1821
|
-
const { node } = getContext("component");
|
|
1822
|
-
node.mounted.push(decorate(node, fn, "onMounted"));
|
|
1823
|
-
}
|
|
1824
|
-
function onWillPatch(fn) {
|
|
1825
|
-
const { node } = getContext("component");
|
|
1826
|
-
node.willPatch.unshift(decorate(node, fn, "onWillPatch"));
|
|
1827
|
-
}
|
|
1828
|
-
function onPatched(fn) {
|
|
1829
|
-
const { node } = getContext("component");
|
|
1830
|
-
node.patched.push(decorate(node, fn, "onPatched"));
|
|
1831
|
-
}
|
|
1832
|
-
function onWillUnmount(fn) {
|
|
1833
|
-
const { node } = getContext("component");
|
|
1834
|
-
node.willUnmount.unshift(decorate(node, fn, "onWillUnmount"));
|
|
1835
|
-
}
|
|
1836
|
-
function onWillDestroy(fn) {
|
|
1837
|
-
const context = getContext();
|
|
1838
|
-
if (context.type === "component") {
|
|
1839
|
-
context.node.willDestroy.unshift(decorate(context.node, fn, "onWillDestroy"));
|
|
1840
|
-
}
|
|
1841
|
-
else {
|
|
1842
|
-
context.manager.onDestroyCb.push(fn);
|
|
1843
|
-
}
|
|
1844
|
-
}
|
|
1845
|
-
function onError(callback) {
|
|
1846
|
-
const { node } = getContext("component");
|
|
1847
|
-
let handlers = nodeErrorHandlers.get(node);
|
|
1848
|
-
if (!handlers) {
|
|
1849
|
-
handlers = [];
|
|
1850
|
-
nodeErrorHandlers.set(node, handlers);
|
|
1851
|
-
}
|
|
1852
|
-
handlers.push(callback.bind(node.component));
|
|
1853
|
-
}
|
|
1854
|
-
|
|
1855
1861
|
var ComputationState;
|
|
1856
1862
|
(function (ComputationState) {
|
|
1857
1863
|
ComputationState[ComputationState["EXECUTED"] = 0] = "EXECUTED";
|
|
@@ -1944,6 +1950,20 @@ function removeSources(computation) {
|
|
|
1944
1950
|
}
|
|
1945
1951
|
sources.clear();
|
|
1946
1952
|
}
|
|
1953
|
+
function disposeComputation(computation) {
|
|
1954
|
+
for (const source of computation.sources) {
|
|
1955
|
+
source.observers.delete(computation);
|
|
1956
|
+
// Recursively dispose derived computations that lost all observers
|
|
1957
|
+
if ("compute" in source &&
|
|
1958
|
+
source.isDerived &&
|
|
1959
|
+
source.observers.size === 0) {
|
|
1960
|
+
disposeComputation(source);
|
|
1961
|
+
}
|
|
1962
|
+
}
|
|
1963
|
+
computation.sources.clear();
|
|
1964
|
+
// Mark as stale so it recomputes correctly if ever re-used (shared computed case)
|
|
1965
|
+
computation.state = ComputationState.STALE;
|
|
1966
|
+
}
|
|
1947
1967
|
function markDownstream(computation) {
|
|
1948
1968
|
const stack = [computation];
|
|
1949
1969
|
let current;
|
|
@@ -1975,668 +1995,332 @@ function untrack(fn) {
|
|
|
1975
1995
|
return result;
|
|
1976
1996
|
}
|
|
1977
1997
|
|
|
1978
|
-
|
|
1979
|
-
const
|
|
1980
|
-
|
|
1981
|
-
|
|
1982
|
-
|
|
1983
|
-
|
|
1984
|
-
const numberType = function validateNumber(context) {
|
|
1985
|
-
if (typeof context.value !== "number") {
|
|
1986
|
-
context.addIssue({ message: "value is not a number" });
|
|
1998
|
+
// Maps fibers to thrown errors
|
|
1999
|
+
const fibersInError = new WeakMap();
|
|
2000
|
+
const nodeErrorHandlers = new WeakMap();
|
|
2001
|
+
function destroyApp(app, error) {
|
|
2002
|
+
try {
|
|
2003
|
+
app.destroy();
|
|
1987
2004
|
}
|
|
1988
|
-
|
|
1989
|
-
|
|
1990
|
-
if (typeof context.value !== "string") {
|
|
1991
|
-
context.addIssue({ message: "value is not a string" });
|
|
2005
|
+
catch (e) {
|
|
2006
|
+
// mute all errors here because we are in a corrupted state anyway
|
|
1992
2007
|
}
|
|
1993
|
-
|
|
1994
|
-
|
|
1995
|
-
|
|
1996
|
-
|
|
1997
|
-
context.addIssue({ message: "value is not an array" });
|
|
1998
|
-
return;
|
|
1999
|
-
}
|
|
2000
|
-
if (!elementType) {
|
|
2001
|
-
return;
|
|
2002
|
-
}
|
|
2003
|
-
for (let index = 0; index < context.value.length; index++) {
|
|
2004
|
-
context.withKey(index).validate(elementType);
|
|
2005
|
-
}
|
|
2006
|
-
};
|
|
2007
|
-
}
|
|
2008
|
-
function constructorType(constructor) {
|
|
2009
|
-
return function validateConstructor(context) {
|
|
2010
|
-
if (!(typeof context.value === "function") ||
|
|
2011
|
-
!(context.value === constructor || context.value.prototype instanceof constructor)) {
|
|
2012
|
-
context.addIssue({ message: `value is not '${constructor.name}' or an extension` });
|
|
2013
|
-
}
|
|
2014
|
-
};
|
|
2008
|
+
const e = Object.assign(new OwlError(`[Owl] Unhandled error. Destroying the root component`), {
|
|
2009
|
+
cause: error,
|
|
2010
|
+
});
|
|
2011
|
+
return e;
|
|
2015
2012
|
}
|
|
2016
|
-
function
|
|
2017
|
-
|
|
2018
|
-
|
|
2019
|
-
|
|
2020
|
-
|
|
2013
|
+
function _handleError(node, error) {
|
|
2014
|
+
if (!node) {
|
|
2015
|
+
return false;
|
|
2016
|
+
}
|
|
2017
|
+
const fiber = node.fiber;
|
|
2018
|
+
if (fiber) {
|
|
2019
|
+
fibersInError.set(fiber, error);
|
|
2020
|
+
}
|
|
2021
|
+
const errorHandlers = nodeErrorHandlers.get(node);
|
|
2022
|
+
if (errorHandlers) {
|
|
2023
|
+
let handled = false;
|
|
2024
|
+
// execute in the opposite order
|
|
2025
|
+
const finalize = () => destroyApp(node.app, error);
|
|
2026
|
+
for (let i = errorHandlers.length - 1; i >= 0; i--) {
|
|
2027
|
+
try {
|
|
2028
|
+
errorHandlers[i](error, finalize);
|
|
2029
|
+
handled = true;
|
|
2030
|
+
break;
|
|
2031
|
+
}
|
|
2032
|
+
catch (e) {
|
|
2033
|
+
error = e;
|
|
2034
|
+
}
|
|
2021
2035
|
}
|
|
2022
|
-
if (
|
|
2023
|
-
|
|
2036
|
+
if (handled) {
|
|
2037
|
+
return true;
|
|
2024
2038
|
}
|
|
2025
|
-
}
|
|
2039
|
+
}
|
|
2040
|
+
return _handleError(node.parent, error);
|
|
2026
2041
|
}
|
|
2027
|
-
function
|
|
2028
|
-
|
|
2029
|
-
|
|
2030
|
-
|
|
2031
|
-
|
|
2032
|
-
|
|
2033
|
-
|
|
2034
|
-
|
|
2035
|
-
|
|
2036
|
-
|
|
2037
|
-
|
|
2038
|
-
|
|
2039
|
-
|
|
2040
|
-
|
|
2041
|
-
|
|
2042
|
-
|
|
2043
|
-
|
|
2044
|
-
|
|
2045
|
-
|
|
2046
|
-
};
|
|
2047
|
-
}
|
|
2048
|
-
function literalType(literal) {
|
|
2049
|
-
return function validateLiteral(context) {
|
|
2050
|
-
if (context.value !== literal) {
|
|
2051
|
-
context.addIssue({
|
|
2052
|
-
message: `value is not equal to ${typeof literal === "string" ? `'${literal}'` : literal}`,
|
|
2053
|
-
});
|
|
2054
|
-
}
|
|
2055
|
-
};
|
|
2042
|
+
function handleError(params) {
|
|
2043
|
+
let { error } = params;
|
|
2044
|
+
const node = "node" in params ? params.node : params.fiber.node;
|
|
2045
|
+
const fiber = "fiber" in params ? params.fiber : node.fiber;
|
|
2046
|
+
if (fiber) {
|
|
2047
|
+
// resets the fibers on components if possible. This is important so that
|
|
2048
|
+
// new renderings can be properly included in the initial one, if any.
|
|
2049
|
+
let current = fiber;
|
|
2050
|
+
do {
|
|
2051
|
+
current.node.fiber = current;
|
|
2052
|
+
fibersInError.set(current, error);
|
|
2053
|
+
current = current.parent;
|
|
2054
|
+
} while (current);
|
|
2055
|
+
fibersInError.set(fiber.root, error);
|
|
2056
|
+
}
|
|
2057
|
+
const handled = _handleError(node, error);
|
|
2058
|
+
if (!handled) {
|
|
2059
|
+
throw destroyApp(node.app, error);
|
|
2060
|
+
}
|
|
2056
2061
|
}
|
|
2057
|
-
|
|
2058
|
-
|
|
2062
|
+
|
|
2063
|
+
function makeChildFiber(node, parent) {
|
|
2064
|
+
let current = node.fiber;
|
|
2065
|
+
if (current) {
|
|
2066
|
+
cancelFibers(current.children);
|
|
2067
|
+
current.root = null;
|
|
2068
|
+
}
|
|
2069
|
+
return new Fiber(node, parent);
|
|
2059
2070
|
}
|
|
2060
|
-
function
|
|
2061
|
-
|
|
2062
|
-
|
|
2063
|
-
|
|
2064
|
-
|
|
2065
|
-
|
|
2066
|
-
|
|
2071
|
+
function makeRootFiber(node) {
|
|
2072
|
+
let current = node.fiber;
|
|
2073
|
+
if (current) {
|
|
2074
|
+
let root = current.root;
|
|
2075
|
+
// lock root fiber because canceling children fibers may destroy components,
|
|
2076
|
+
// which means any arbitrary code can be run in onWillDestroy, which may
|
|
2077
|
+
// trigger new renderings
|
|
2078
|
+
root.locked = true;
|
|
2079
|
+
root.setCounter(root.counter + 1 - cancelFibers(current.children));
|
|
2080
|
+
root.locked = false;
|
|
2081
|
+
current.children = [];
|
|
2082
|
+
current.childrenMap = {};
|
|
2083
|
+
current.bdom = null;
|
|
2084
|
+
if (fibersInError.has(current)) {
|
|
2085
|
+
fibersInError.delete(current);
|
|
2086
|
+
fibersInError.delete(root);
|
|
2087
|
+
current.appliedToDom = false;
|
|
2088
|
+
if (current instanceof RootFiber) {
|
|
2089
|
+
// it is possible that this fiber is a fiber that crashed while being
|
|
2090
|
+
// mounted, so the mounted list is possibly corrupted. We restore it to
|
|
2091
|
+
// its normal initial state (which is empty list or a list with a mount
|
|
2092
|
+
// fiber.
|
|
2093
|
+
current.mounted = current instanceof MountFiber ? [current] : [];
|
|
2067
2094
|
}
|
|
2068
|
-
continue;
|
|
2069
2095
|
}
|
|
2070
|
-
|
|
2096
|
+
return current;
|
|
2071
2097
|
}
|
|
2072
|
-
|
|
2073
|
-
|
|
2074
|
-
|
|
2075
|
-
missingKeys,
|
|
2076
|
-
});
|
|
2098
|
+
const fiber = new RootFiber(node, null);
|
|
2099
|
+
if (node.willPatch.length) {
|
|
2100
|
+
fiber.willPatch.push(fiber);
|
|
2077
2101
|
}
|
|
2078
|
-
|
|
2079
|
-
|
|
2080
|
-
const missingKeys = keys.filter((key) => {
|
|
2081
|
-
if (key.endsWith("?")) {
|
|
2082
|
-
return false;
|
|
2083
|
-
}
|
|
2084
|
-
return !(key in context.value);
|
|
2085
|
-
});
|
|
2086
|
-
if (missingKeys.length) {
|
|
2087
|
-
context.addIssue({
|
|
2088
|
-
message: "object value have missing keys",
|
|
2089
|
-
missingKeys,
|
|
2090
|
-
});
|
|
2102
|
+
if (node.patched.length) {
|
|
2103
|
+
fiber.patched.push(fiber);
|
|
2091
2104
|
}
|
|
2105
|
+
return fiber;
|
|
2092
2106
|
}
|
|
2093
|
-
function
|
|
2094
|
-
|
|
2095
|
-
if (typeof context.value !== "object" ||
|
|
2096
|
-
Array.isArray(context.value) ||
|
|
2097
|
-
context.value === null) {
|
|
2098
|
-
context.addIssue({ message: "value is not an object" });
|
|
2099
|
-
return;
|
|
2100
|
-
}
|
|
2101
|
-
if (!schema) {
|
|
2102
|
-
return;
|
|
2103
|
-
}
|
|
2104
|
-
if (Array.isArray(schema)) {
|
|
2105
|
-
validateObjectKeys(context, schema);
|
|
2106
|
-
}
|
|
2107
|
-
else {
|
|
2108
|
-
validateObjectShape(context, schema);
|
|
2109
|
-
}
|
|
2110
|
-
};
|
|
2111
|
-
}
|
|
2112
|
-
function promiseType(type) {
|
|
2113
|
-
return function validatePromise(context) {
|
|
2114
|
-
if (!(context.value instanceof Promise)) {
|
|
2115
|
-
context.addIssue({ message: "value is not a promise" });
|
|
2116
|
-
}
|
|
2117
|
-
};
|
|
2107
|
+
function throwOnRender() {
|
|
2108
|
+
throw new OwlError("Attempted to render cancelled fiber");
|
|
2118
2109
|
}
|
|
2119
|
-
|
|
2120
|
-
|
|
2121
|
-
|
|
2122
|
-
|
|
2123
|
-
|
|
2124
|
-
|
|
2125
|
-
|
|
2110
|
+
/**
|
|
2111
|
+
* @returns number of not-yet rendered fibers cancelled
|
|
2112
|
+
*/
|
|
2113
|
+
function cancelFibers(fibers) {
|
|
2114
|
+
let result = 0;
|
|
2115
|
+
for (let fiber of fibers) {
|
|
2116
|
+
let node = fiber.node;
|
|
2117
|
+
fiber.render = throwOnRender;
|
|
2118
|
+
if (node.status === 0 /* STATUS.NEW */) {
|
|
2119
|
+
node.cancel();
|
|
2126
2120
|
}
|
|
2127
|
-
|
|
2128
|
-
|
|
2121
|
+
node.fiber = null;
|
|
2122
|
+
if (fiber.bdom) {
|
|
2123
|
+
// if fiber has been rendered, this means that the component props have
|
|
2124
|
+
// been updated. however, this fiber will not be patched to the dom, so
|
|
2125
|
+
// it could happen that the next render compare the current props with
|
|
2126
|
+
// the same props, and skip the render completely. With the next line,
|
|
2127
|
+
// we kindly request the component code to force a render, so it works as
|
|
2128
|
+
// expected.
|
|
2129
|
+
node.forceNextRender = true;
|
|
2129
2130
|
}
|
|
2130
|
-
|
|
2131
|
-
|
|
2131
|
+
else {
|
|
2132
|
+
result++;
|
|
2132
2133
|
}
|
|
2133
|
-
|
|
2134
|
+
result += cancelFibers(fiber.children);
|
|
2135
|
+
}
|
|
2136
|
+
return result;
|
|
2134
2137
|
}
|
|
2135
|
-
|
|
2136
|
-
|
|
2137
|
-
|
|
2138
|
-
|
|
2139
|
-
|
|
2140
|
-
|
|
2141
|
-
|
|
2142
|
-
|
|
2143
|
-
|
|
2138
|
+
class Fiber {
|
|
2139
|
+
node;
|
|
2140
|
+
bdom = null;
|
|
2141
|
+
root; // A Fiber that has been replaced by another has no root
|
|
2142
|
+
parent;
|
|
2143
|
+
children = [];
|
|
2144
|
+
appliedToDom = false;
|
|
2145
|
+
deep = false;
|
|
2146
|
+
childrenMap = {};
|
|
2147
|
+
constructor(node, parent) {
|
|
2148
|
+
this.node = node;
|
|
2149
|
+
this.parent = parent;
|
|
2150
|
+
if (parent) {
|
|
2151
|
+
this.deep = parent.deep;
|
|
2152
|
+
const root = parent.root;
|
|
2153
|
+
root.setCounter(root.counter + 1);
|
|
2154
|
+
this.root = root;
|
|
2155
|
+
parent.children.push(this);
|
|
2144
2156
|
}
|
|
2145
|
-
|
|
2146
|
-
|
|
2157
|
+
else {
|
|
2158
|
+
this.root = this;
|
|
2147
2159
|
}
|
|
2148
|
-
}
|
|
2149
|
-
|
|
2150
|
-
|
|
2151
|
-
|
|
2152
|
-
let
|
|
2153
|
-
|
|
2154
|
-
|
|
2155
|
-
|
|
2156
|
-
|
|
2157
|
-
|
|
2158
|
-
|
|
2159
|
-
|
|
2160
|
+
}
|
|
2161
|
+
render() {
|
|
2162
|
+
// if some parent has a fiber => register in followup
|
|
2163
|
+
let prev = this.root.node;
|
|
2164
|
+
let scheduler = prev.app.scheduler;
|
|
2165
|
+
let current = prev.parent;
|
|
2166
|
+
while (current) {
|
|
2167
|
+
if (current.fiber) {
|
|
2168
|
+
let root = current.fiber.root;
|
|
2169
|
+
if (root.counter === 0 && prev.parentKey in current.fiber.childrenMap) {
|
|
2170
|
+
current = root.node;
|
|
2171
|
+
}
|
|
2172
|
+
else {
|
|
2173
|
+
scheduler.delayedRenders.push(this);
|
|
2174
|
+
return;
|
|
2175
|
+
}
|
|
2160
2176
|
}
|
|
2161
|
-
|
|
2177
|
+
prev = current;
|
|
2178
|
+
current = current.parent;
|
|
2162
2179
|
}
|
|
2163
|
-
|
|
2164
|
-
|
|
2165
|
-
|
|
2166
|
-
|
|
2167
|
-
|
|
2168
|
-
|
|
2169
|
-
|
|
2170
|
-
|
|
2171
|
-
|
|
2172
|
-
|
|
2180
|
+
// there are no current rendering from above => we can render
|
|
2181
|
+
this._render();
|
|
2182
|
+
}
|
|
2183
|
+
_render() {
|
|
2184
|
+
const node = this.node;
|
|
2185
|
+
const root = this.root;
|
|
2186
|
+
if (root) {
|
|
2187
|
+
// todo: should use updateComputation somewhere else.
|
|
2188
|
+
const c = getCurrentComputation();
|
|
2189
|
+
removeSources(node.signalComputation);
|
|
2190
|
+
setComputation(node.signalComputation);
|
|
2191
|
+
try {
|
|
2192
|
+
this.bdom = true;
|
|
2193
|
+
this.bdom = node.renderFn();
|
|
2194
|
+
}
|
|
2195
|
+
catch (e) {
|
|
2196
|
+
node.app.handleError({ node, error: e });
|
|
2197
|
+
}
|
|
2198
|
+
setComputation(c);
|
|
2199
|
+
root.setCounter(root.counter - 1);
|
|
2173
2200
|
}
|
|
2174
|
-
}
|
|
2201
|
+
}
|
|
2175
2202
|
}
|
|
2176
|
-
|
|
2177
|
-
|
|
2178
|
-
|
|
2179
|
-
|
|
2180
|
-
|
|
2181
|
-
|
|
2182
|
-
|
|
2183
|
-
|
|
2184
|
-
|
|
2185
|
-
|
|
2186
|
-
|
|
2187
|
-
|
|
2188
|
-
|
|
2189
|
-
|
|
2190
|
-
|
|
2191
|
-
|
|
2192
|
-
|
|
2193
|
-
|
|
2194
|
-
|
|
2195
|
-
|
|
2196
|
-
|
|
2197
|
-
|
|
2198
|
-
|
|
2199
|
-
|
|
2200
|
-
|
|
2201
|
-
|
|
2202
|
-
|
|
2203
|
-
if (issues.length) {
|
|
2204
|
-
const issueStrings = JSON.stringify(issues, (key, value) => {
|
|
2205
|
-
if (typeof value === "function") {
|
|
2206
|
-
return value.name;
|
|
2203
|
+
class RootFiber extends Fiber {
|
|
2204
|
+
counter = 1;
|
|
2205
|
+
// only add stuff in this if they have registered some hooks
|
|
2206
|
+
willPatch = [];
|
|
2207
|
+
patched = [];
|
|
2208
|
+
mounted = [];
|
|
2209
|
+
// A fiber is typically locked when it is completing and the patch has not, or is being applied.
|
|
2210
|
+
// i.e.: render triggered in onWillUnmount or in willPatch will be delayed
|
|
2211
|
+
locked = false;
|
|
2212
|
+
complete() {
|
|
2213
|
+
const node = this.node;
|
|
2214
|
+
this.locked = true;
|
|
2215
|
+
let current = undefined;
|
|
2216
|
+
let mountedFibers = this.mounted;
|
|
2217
|
+
try {
|
|
2218
|
+
// Step 1: calling all willPatch lifecycle hooks
|
|
2219
|
+
for (current of this.willPatch) {
|
|
2220
|
+
// because of the asynchronous nature of the rendering, some parts of the
|
|
2221
|
+
// UI may have been rendered, then deleted in a followup rendering, and we
|
|
2222
|
+
// do not want to call onWillPatch in that case.
|
|
2223
|
+
let node = current.node;
|
|
2224
|
+
if (node.fiber === current) {
|
|
2225
|
+
const component = node.component;
|
|
2226
|
+
for (let cb of node.willPatch) {
|
|
2227
|
+
cb.call(component);
|
|
2228
|
+
}
|
|
2229
|
+
}
|
|
2207
2230
|
}
|
|
2208
|
-
|
|
2209
|
-
|
|
2210
|
-
|
|
2211
|
-
|
|
2212
|
-
|
|
2213
|
-
|
|
2214
|
-
|
|
2215
|
-
|
|
2216
|
-
|
|
2217
|
-
|
|
2218
|
-
|
|
2219
|
-
|
|
2220
|
-
},
|
|
2221
|
-
addIssue(issue) {
|
|
2222
|
-
issues.push({
|
|
2223
|
-
received: this.value,
|
|
2224
|
-
path: this.path,
|
|
2225
|
-
...issue,
|
|
2226
|
-
});
|
|
2227
|
-
},
|
|
2228
|
-
mergeIssues(newIssues) {
|
|
2229
|
-
issues.push(...newIssues);
|
|
2230
|
-
},
|
|
2231
|
-
validate(type) {
|
|
2232
|
-
type(this);
|
|
2233
|
-
if (!this.isValid && parent) {
|
|
2234
|
-
parent.issueDepth = this.issueDepth + 1;
|
|
2231
|
+
current = undefined;
|
|
2232
|
+
// Step 2: patching the dom
|
|
2233
|
+
node._patch();
|
|
2234
|
+
this.locked = false;
|
|
2235
|
+
// Step 4: calling all mounted lifecycle hooks
|
|
2236
|
+
while ((current = mountedFibers.pop())) {
|
|
2237
|
+
current = current;
|
|
2238
|
+
if (current.appliedToDom) {
|
|
2239
|
+
for (let cb of current.node.mounted) {
|
|
2240
|
+
cb();
|
|
2241
|
+
}
|
|
2242
|
+
}
|
|
2235
2243
|
}
|
|
2236
|
-
|
|
2237
|
-
|
|
2238
|
-
|
|
2239
|
-
|
|
2240
|
-
|
|
2241
|
-
|
|
2242
|
-
|
|
2243
|
-
|
|
2244
|
-
}
|
|
2245
|
-
function validateType(value, validation) {
|
|
2246
|
-
const issues = [];
|
|
2247
|
-
validation(createContext$1(issues, value, []));
|
|
2248
|
-
return issues;
|
|
2249
|
-
}
|
|
2250
|
-
|
|
2251
|
-
function validateObjectWithDefaults(schema, defaultValues) {
|
|
2252
|
-
const keys = Array.isArray(schema) ? schema : Object.keys(schema);
|
|
2253
|
-
const mandatoryDefaultedKeys = keys.filter((key) => !key.endsWith("?") && key in defaultValues);
|
|
2254
|
-
return (context) => {
|
|
2255
|
-
if (mandatoryDefaultedKeys.length) {
|
|
2256
|
-
context.addIssue({
|
|
2257
|
-
message: "props have default values on mandatory keys",
|
|
2258
|
-
keys: mandatoryDefaultedKeys,
|
|
2259
|
-
});
|
|
2260
|
-
}
|
|
2261
|
-
context.validate(types.object(schema));
|
|
2262
|
-
};
|
|
2263
|
-
}
|
|
2264
|
-
function props(type, defaults) {
|
|
2265
|
-
const { node, app, componentName } = getContext("component");
|
|
2266
|
-
function getProp(key) {
|
|
2267
|
-
if (node.props[key] === undefined && defaults) {
|
|
2268
|
-
return defaults[key];
|
|
2269
|
-
}
|
|
2270
|
-
return node.props[key];
|
|
2271
|
-
}
|
|
2272
|
-
const result = Object.create(null);
|
|
2273
|
-
function applyPropGetters(keys) {
|
|
2274
|
-
for (const key of keys) {
|
|
2275
|
-
Reflect.defineProperty(result, key, {
|
|
2276
|
-
enumerable: true,
|
|
2277
|
-
get: getProp.bind(null, key),
|
|
2278
|
-
});
|
|
2279
|
-
}
|
|
2280
|
-
}
|
|
2281
|
-
if (type) {
|
|
2282
|
-
const keys = (Array.isArray(type) ? type : Object.keys(type)).map((key) => key.endsWith("?") ? key.slice(0, -1) : key);
|
|
2283
|
-
applyPropGetters(keys);
|
|
2284
|
-
if (app.dev) {
|
|
2285
|
-
const validation = defaults ? validateObjectWithDefaults(type, defaults) : types.object(type);
|
|
2286
|
-
assertType(node.props, validation, `Invalid component props (${componentName})`);
|
|
2287
|
-
node.willUpdateProps.push((np) => {
|
|
2288
|
-
assertType(np, validation, `Invalid component props (${componentName})`);
|
|
2289
|
-
});
|
|
2290
|
-
}
|
|
2291
|
-
}
|
|
2292
|
-
else {
|
|
2293
|
-
applyPropGetters(Object.keys(node.props));
|
|
2294
|
-
node.willUpdateProps.push((np) => {
|
|
2295
|
-
for (let key in result) {
|
|
2296
|
-
Reflect.deleteProperty(result, key);
|
|
2244
|
+
// Step 5: calling all patched hooks
|
|
2245
|
+
let patchedFibers = this.patched;
|
|
2246
|
+
while ((current = patchedFibers.pop())) {
|
|
2247
|
+
current = current;
|
|
2248
|
+
if (current.appliedToDom) {
|
|
2249
|
+
for (let cb of current.node.patched) {
|
|
2250
|
+
cb();
|
|
2251
|
+
}
|
|
2252
|
+
}
|
|
2297
2253
|
}
|
|
2298
|
-
applyPropGetters(Object.keys(np));
|
|
2299
|
-
});
|
|
2300
|
-
}
|
|
2301
|
-
return result;
|
|
2302
|
-
}
|
|
2303
|
-
|
|
2304
|
-
const VText = text("").constructor;
|
|
2305
|
-
class VPortal extends VText {
|
|
2306
|
-
content;
|
|
2307
|
-
selector;
|
|
2308
|
-
target = null;
|
|
2309
|
-
constructor(selector, content) {
|
|
2310
|
-
super("");
|
|
2311
|
-
this.selector = selector;
|
|
2312
|
-
this.content = content;
|
|
2313
|
-
}
|
|
2314
|
-
mount(parent, anchor) {
|
|
2315
|
-
super.mount(parent, anchor);
|
|
2316
|
-
this.target = document.querySelector(this.selector);
|
|
2317
|
-
if (this.target) {
|
|
2318
|
-
this.content.mount(this.target, null);
|
|
2319
|
-
}
|
|
2320
|
-
else {
|
|
2321
|
-
this.content.mount(parent, anchor);
|
|
2322
2254
|
}
|
|
2323
|
-
|
|
2324
|
-
|
|
2325
|
-
|
|
2326
|
-
|
|
2327
|
-
|
|
2328
|
-
|
|
2329
|
-
|
|
2330
|
-
|
|
2331
|
-
|
|
2255
|
+
catch (e) {
|
|
2256
|
+
// if mountedFibers is not empty, this means that a crash occured while
|
|
2257
|
+
// calling the mounted hooks of some component. So, there may still be
|
|
2258
|
+
// some component that have been mounted, but for which the mounted hooks
|
|
2259
|
+
// have not been called. Here, we remove the willUnmount hooks for these
|
|
2260
|
+
// specific component to prevent a worse situation (willUnmount being
|
|
2261
|
+
// called even though mounted has not been called)
|
|
2262
|
+
for (let fiber of mountedFibers) {
|
|
2263
|
+
fiber.node.willUnmount = [];
|
|
2264
|
+
}
|
|
2265
|
+
this.locked = false;
|
|
2266
|
+
node.app.handleError({ fiber: current || this, error: e });
|
|
2332
2267
|
}
|
|
2333
2268
|
}
|
|
2334
|
-
|
|
2335
|
-
|
|
2336
|
-
if (
|
|
2337
|
-
this.
|
|
2338
|
-
}
|
|
2339
|
-
else {
|
|
2340
|
-
this.content = other.content;
|
|
2341
|
-
this.content.mount(this.target, null);
|
|
2269
|
+
setCounter(newValue) {
|
|
2270
|
+
this.counter = newValue;
|
|
2271
|
+
if (newValue === 0) {
|
|
2272
|
+
this.node.app.scheduler.flush();
|
|
2342
2273
|
}
|
|
2343
2274
|
}
|
|
2344
2275
|
}
|
|
2345
|
-
|
|
2346
|
-
|
|
2347
|
-
|
|
2348
|
-
|
|
2349
|
-
|
|
2350
|
-
|
|
2351
|
-
|
|
2352
|
-
}
|
|
2353
|
-
|
|
2354
|
-
|
|
2355
|
-
|
|
2356
|
-
|
|
2357
|
-
|
|
2358
|
-
|
|
2359
|
-
|
|
2360
|
-
|
|
2361
|
-
|
|
2362
|
-
|
|
2363
|
-
|
|
2364
|
-
|
|
2365
|
-
|
|
2366
|
-
|
|
2276
|
+
class MountFiber extends RootFiber {
|
|
2277
|
+
target;
|
|
2278
|
+
position;
|
|
2279
|
+
constructor(node, target, options = {}) {
|
|
2280
|
+
super(node, null);
|
|
2281
|
+
this.target = target;
|
|
2282
|
+
this.position = options.position || "last-child";
|
|
2283
|
+
}
|
|
2284
|
+
complete() {
|
|
2285
|
+
let current = this;
|
|
2286
|
+
try {
|
|
2287
|
+
const node = this.node;
|
|
2288
|
+
node.children = this.childrenMap;
|
|
2289
|
+
node.app.constructor.validateTarget(this.target);
|
|
2290
|
+
if (node.bdom) {
|
|
2291
|
+
// this is a complicated situation: if we mount a fiber with an existing
|
|
2292
|
+
// bdom, this means that this same fiber was already completed, mounted,
|
|
2293
|
+
// but a crash occurred in some mounted hook. Then, it was handled and
|
|
2294
|
+
// the new rendering is being applied.
|
|
2295
|
+
node.updateDom();
|
|
2296
|
+
}
|
|
2297
|
+
else {
|
|
2298
|
+
node.bdom = this.bdom;
|
|
2299
|
+
if (this.position === "last-child" || this.target.childNodes.length === 0) {
|
|
2300
|
+
mount$1(node.bdom, this.target);
|
|
2367
2301
|
}
|
|
2368
2302
|
else {
|
|
2369
|
-
|
|
2303
|
+
const firstChild = this.target.childNodes[0];
|
|
2304
|
+
mount$1(node.bdom, this.target, firstChild);
|
|
2370
2305
|
}
|
|
2371
2306
|
}
|
|
2372
|
-
|
|
2373
|
-
|
|
2374
|
-
|
|
2375
|
-
|
|
2376
|
-
|
|
2377
|
-
|
|
2378
|
-
|
|
2379
|
-
|
|
2380
|
-
|
|
2381
|
-
|
|
2382
|
-
|
|
2383
|
-
|
|
2384
|
-
|
|
2385
|
-
|
|
2386
|
-
|
|
2387
|
-
}
|
|
2388
|
-
|
|
2389
|
-
let current = node.fiber;
|
|
2390
|
-
if (current) {
|
|
2391
|
-
let root = current.root;
|
|
2392
|
-
// lock root fiber because canceling children fibers may destroy components,
|
|
2393
|
-
// which means any arbitrary code can be run in onWillDestroy, which may
|
|
2394
|
-
// trigger new renderings
|
|
2395
|
-
root.locked = true;
|
|
2396
|
-
root.setCounter(root.counter + 1 - cancelFibers(current.children));
|
|
2397
|
-
root.locked = false;
|
|
2398
|
-
current.children = [];
|
|
2399
|
-
current.childrenMap = {};
|
|
2400
|
-
current.bdom = null;
|
|
2401
|
-
if (fibersInError.has(current)) {
|
|
2402
|
-
fibersInError.delete(current);
|
|
2403
|
-
fibersInError.delete(root);
|
|
2404
|
-
current.appliedToDom = false;
|
|
2405
|
-
if (current instanceof RootFiber) {
|
|
2406
|
-
// it is possible that this fiber is a fiber that crashed while being
|
|
2407
|
-
// mounted, so the mounted list is possibly corrupted. We restore it to
|
|
2408
|
-
// its normal initial state (which is empty list or a list with a mount
|
|
2409
|
-
// fiber.
|
|
2410
|
-
current.mounted = current instanceof MountFiber ? [current] : [];
|
|
2411
|
-
}
|
|
2412
|
-
}
|
|
2413
|
-
return current;
|
|
2414
|
-
}
|
|
2415
|
-
const fiber = new RootFiber(node, null);
|
|
2416
|
-
if (node.willPatch.length) {
|
|
2417
|
-
fiber.willPatch.push(fiber);
|
|
2418
|
-
}
|
|
2419
|
-
if (node.patched.length) {
|
|
2420
|
-
fiber.patched.push(fiber);
|
|
2421
|
-
}
|
|
2422
|
-
return fiber;
|
|
2423
|
-
}
|
|
2424
|
-
function throwOnRender() {
|
|
2425
|
-
throw new OwlError("Attempted to render cancelled fiber");
|
|
2426
|
-
}
|
|
2427
|
-
/**
|
|
2428
|
-
* @returns number of not-yet rendered fibers cancelled
|
|
2429
|
-
*/
|
|
2430
|
-
function cancelFibers(fibers) {
|
|
2431
|
-
let result = 0;
|
|
2432
|
-
for (let fiber of fibers) {
|
|
2433
|
-
let node = fiber.node;
|
|
2434
|
-
fiber.render = throwOnRender;
|
|
2435
|
-
if (node.status === 0 /* STATUS.NEW */) {
|
|
2436
|
-
node.cancel();
|
|
2437
|
-
}
|
|
2438
|
-
node.fiber = null;
|
|
2439
|
-
if (fiber.bdom) {
|
|
2440
|
-
// if fiber has been rendered, this means that the component props have
|
|
2441
|
-
// been updated. however, this fiber will not be patched to the dom, so
|
|
2442
|
-
// it could happen that the next render compare the current props with
|
|
2443
|
-
// the same props, and skip the render completely. With the next line,
|
|
2444
|
-
// we kindly request the component code to force a render, so it works as
|
|
2445
|
-
// expected.
|
|
2446
|
-
node.forceNextRender = true;
|
|
2447
|
-
}
|
|
2448
|
-
else {
|
|
2449
|
-
result++;
|
|
2450
|
-
}
|
|
2451
|
-
result += cancelFibers(fiber.children);
|
|
2452
|
-
}
|
|
2453
|
-
return result;
|
|
2454
|
-
}
|
|
2455
|
-
class Fiber {
|
|
2456
|
-
node;
|
|
2457
|
-
bdom = null;
|
|
2458
|
-
root; // A Fiber that has been replaced by another has no root
|
|
2459
|
-
parent;
|
|
2460
|
-
children = [];
|
|
2461
|
-
appliedToDom = false;
|
|
2462
|
-
deep = false;
|
|
2463
|
-
childrenMap = {};
|
|
2464
|
-
constructor(node, parent) {
|
|
2465
|
-
this.node = node;
|
|
2466
|
-
this.parent = parent;
|
|
2467
|
-
if (parent) {
|
|
2468
|
-
this.deep = parent.deep;
|
|
2469
|
-
const root = parent.root;
|
|
2470
|
-
root.setCounter(root.counter + 1);
|
|
2471
|
-
this.root = root;
|
|
2472
|
-
parent.children.push(this);
|
|
2473
|
-
}
|
|
2474
|
-
else {
|
|
2475
|
-
this.root = this;
|
|
2476
|
-
}
|
|
2477
|
-
}
|
|
2478
|
-
render() {
|
|
2479
|
-
// if some parent has a fiber => register in followup
|
|
2480
|
-
let prev = this.root.node;
|
|
2481
|
-
let scheduler = prev.app.scheduler;
|
|
2482
|
-
let current = prev.parent;
|
|
2483
|
-
while (current) {
|
|
2484
|
-
if (current.fiber) {
|
|
2485
|
-
let root = current.fiber.root;
|
|
2486
|
-
if (root.counter === 0 && prev.parentKey in current.fiber.childrenMap) {
|
|
2487
|
-
current = root.node;
|
|
2488
|
-
}
|
|
2489
|
-
else {
|
|
2490
|
-
scheduler.delayedRenders.push(this);
|
|
2491
|
-
return;
|
|
2492
|
-
}
|
|
2493
|
-
}
|
|
2494
|
-
prev = current;
|
|
2495
|
-
current = current.parent;
|
|
2496
|
-
}
|
|
2497
|
-
// there are no current rendering from above => we can render
|
|
2498
|
-
this._render();
|
|
2499
|
-
}
|
|
2500
|
-
_render() {
|
|
2501
|
-
const node = this.node;
|
|
2502
|
-
const root = this.root;
|
|
2503
|
-
if (root) {
|
|
2504
|
-
// todo: should use updateComputation somewhere else.
|
|
2505
|
-
const c = getCurrentComputation();
|
|
2506
|
-
setComputation(node.signalComputation);
|
|
2507
|
-
try {
|
|
2508
|
-
this.bdom = true;
|
|
2509
|
-
this.bdom = node.renderFn();
|
|
2510
|
-
}
|
|
2511
|
-
catch (e) {
|
|
2512
|
-
node.app.handleError({ node, error: e });
|
|
2513
|
-
}
|
|
2514
|
-
setComputation(c);
|
|
2515
|
-
root.setCounter(root.counter - 1);
|
|
2516
|
-
}
|
|
2517
|
-
}
|
|
2518
|
-
}
|
|
2519
|
-
class RootFiber extends Fiber {
|
|
2520
|
-
counter = 1;
|
|
2521
|
-
// only add stuff in this if they have registered some hooks
|
|
2522
|
-
willPatch = [];
|
|
2523
|
-
patched = [];
|
|
2524
|
-
mounted = [];
|
|
2525
|
-
// A fiber is typically locked when it is completing and the patch has not, or is being applied.
|
|
2526
|
-
// i.e.: render triggered in onWillUnmount or in willPatch will be delayed
|
|
2527
|
-
locked = false;
|
|
2528
|
-
complete() {
|
|
2529
|
-
const node = this.node;
|
|
2530
|
-
this.locked = true;
|
|
2531
|
-
let current = undefined;
|
|
2532
|
-
let mountedFibers = this.mounted;
|
|
2533
|
-
try {
|
|
2534
|
-
// Step 1: calling all willPatch lifecycle hooks
|
|
2535
|
-
for (current of this.willPatch) {
|
|
2536
|
-
// because of the asynchronous nature of the rendering, some parts of the
|
|
2537
|
-
// UI may have been rendered, then deleted in a followup rendering, and we
|
|
2538
|
-
// do not want to call onWillPatch in that case.
|
|
2539
|
-
let node = current.node;
|
|
2540
|
-
if (node.fiber === current) {
|
|
2541
|
-
const component = node.component;
|
|
2542
|
-
for (let cb of node.willPatch) {
|
|
2543
|
-
cb.call(component);
|
|
2544
|
-
}
|
|
2545
|
-
}
|
|
2546
|
-
}
|
|
2547
|
-
current = undefined;
|
|
2548
|
-
// Step 2: patching the dom
|
|
2549
|
-
node._patch();
|
|
2550
|
-
this.locked = false;
|
|
2551
|
-
// Step 4: calling all mounted lifecycle hooks
|
|
2552
|
-
while ((current = mountedFibers.pop())) {
|
|
2553
|
-
current = current;
|
|
2554
|
-
if (current.appliedToDom) {
|
|
2555
|
-
for (let cb of current.node.mounted) {
|
|
2556
|
-
cb();
|
|
2557
|
-
}
|
|
2558
|
-
}
|
|
2559
|
-
}
|
|
2560
|
-
// Step 5: calling all patched hooks
|
|
2561
|
-
let patchedFibers = this.patched;
|
|
2562
|
-
while ((current = patchedFibers.pop())) {
|
|
2563
|
-
current = current;
|
|
2564
|
-
if (current.appliedToDom) {
|
|
2565
|
-
for (let cb of current.node.patched) {
|
|
2566
|
-
cb();
|
|
2567
|
-
}
|
|
2568
|
-
}
|
|
2569
|
-
}
|
|
2570
|
-
}
|
|
2571
|
-
catch (e) {
|
|
2572
|
-
// if mountedFibers is not empty, this means that a crash occured while
|
|
2573
|
-
// calling the mounted hooks of some component. So, there may still be
|
|
2574
|
-
// some component that have been mounted, but for which the mounted hooks
|
|
2575
|
-
// have not been called. Here, we remove the willUnmount hooks for these
|
|
2576
|
-
// specific component to prevent a worse situation (willUnmount being
|
|
2577
|
-
// called even though mounted has not been called)
|
|
2578
|
-
for (let fiber of mountedFibers) {
|
|
2579
|
-
fiber.node.willUnmount = [];
|
|
2580
|
-
}
|
|
2581
|
-
this.locked = false;
|
|
2582
|
-
node.app.handleError({ fiber: current || this, error: e });
|
|
2583
|
-
}
|
|
2584
|
-
}
|
|
2585
|
-
setCounter(newValue) {
|
|
2586
|
-
this.counter = newValue;
|
|
2587
|
-
if (newValue === 0) {
|
|
2588
|
-
this.node.app.scheduler.flush();
|
|
2589
|
-
}
|
|
2590
|
-
}
|
|
2591
|
-
}
|
|
2592
|
-
class MountFiber extends RootFiber {
|
|
2593
|
-
target;
|
|
2594
|
-
position;
|
|
2595
|
-
constructor(node, target, options = {}) {
|
|
2596
|
-
super(node, null);
|
|
2597
|
-
this.target = target;
|
|
2598
|
-
this.position = options.position || "last-child";
|
|
2599
|
-
}
|
|
2600
|
-
complete() {
|
|
2601
|
-
let current = this;
|
|
2602
|
-
try {
|
|
2603
|
-
const node = this.node;
|
|
2604
|
-
node.children = this.childrenMap;
|
|
2605
|
-
node.app.constructor.validateTarget(this.target);
|
|
2606
|
-
if (node.bdom) {
|
|
2607
|
-
// this is a complicated situation: if we mount a fiber with an existing
|
|
2608
|
-
// bdom, this means that this same fiber was already completed, mounted,
|
|
2609
|
-
// but a crash occurred in some mounted hook. Then, it was handled and
|
|
2610
|
-
// the new rendering is being applied.
|
|
2611
|
-
node.updateDom();
|
|
2612
|
-
}
|
|
2613
|
-
else {
|
|
2614
|
-
node.bdom = this.bdom;
|
|
2615
|
-
if (this.position === "last-child" || this.target.childNodes.length === 0) {
|
|
2616
|
-
mount$1(node.bdom, this.target);
|
|
2617
|
-
}
|
|
2618
|
-
else {
|
|
2619
|
-
const firstChild = this.target.childNodes[0];
|
|
2620
|
-
mount$1(node.bdom, this.target, firstChild);
|
|
2621
|
-
}
|
|
2622
|
-
}
|
|
2623
|
-
// unregistering the fiber before mounted since it can do another render
|
|
2624
|
-
// and that the current rendering is obviously completed
|
|
2625
|
-
node.fiber = null;
|
|
2626
|
-
node.status = 1 /* STATUS.MOUNTED */;
|
|
2627
|
-
this.appliedToDom = true;
|
|
2628
|
-
let mountedFibers = this.mounted;
|
|
2629
|
-
while ((current = mountedFibers.pop())) {
|
|
2630
|
-
if (current.appliedToDom) {
|
|
2631
|
-
for (let cb of current.node.mounted) {
|
|
2632
|
-
cb();
|
|
2633
|
-
}
|
|
2634
|
-
}
|
|
2635
|
-
}
|
|
2636
|
-
}
|
|
2637
|
-
catch (e) {
|
|
2638
|
-
this.node.app.handleError({ fiber: current, error: e });
|
|
2639
|
-
}
|
|
2307
|
+
// unregistering the fiber before mounted since it can do another render
|
|
2308
|
+
// and that the current rendering is obviously completed
|
|
2309
|
+
node.fiber = null;
|
|
2310
|
+
node.status = 1 /* STATUS.MOUNTED */;
|
|
2311
|
+
this.appliedToDom = true;
|
|
2312
|
+
let mountedFibers = this.mounted;
|
|
2313
|
+
while ((current = mountedFibers.pop())) {
|
|
2314
|
+
if (current.appliedToDom) {
|
|
2315
|
+
for (let cb of current.node.mounted) {
|
|
2316
|
+
cb();
|
|
2317
|
+
}
|
|
2318
|
+
}
|
|
2319
|
+
}
|
|
2320
|
+
}
|
|
2321
|
+
catch (e) {
|
|
2322
|
+
this.node.app.handleError({ fiber: current, error: e });
|
|
2323
|
+
}
|
|
2640
2324
|
}
|
|
2641
2325
|
}
|
|
2642
2326
|
|
|
@@ -2650,6 +2334,7 @@ class ComponentNode {
|
|
|
2650
2334
|
forceNextRender = false;
|
|
2651
2335
|
parentKey;
|
|
2652
2336
|
props;
|
|
2337
|
+
defaultProps = {};
|
|
2653
2338
|
renderFn;
|
|
2654
2339
|
parent;
|
|
2655
2340
|
children = Object.create(null);
|
|
@@ -2661,6 +2346,7 @@ class ComponentNode {
|
|
|
2661
2346
|
patched = [];
|
|
2662
2347
|
willDestroy = [];
|
|
2663
2348
|
signalComputation;
|
|
2349
|
+
computations = [];
|
|
2664
2350
|
pluginManager;
|
|
2665
2351
|
constructor(C, props, app, parent, parentKey) {
|
|
2666
2352
|
this.app = app;
|
|
@@ -2668,7 +2354,7 @@ class ComponentNode {
|
|
|
2668
2354
|
this.parentKey = parentKey;
|
|
2669
2355
|
this.pluginManager = parent ? parent.pluginManager : app.pluginManager;
|
|
2670
2356
|
this.signalComputation = createComputation(() => this.render(false), false, ComputationState.EXECUTED);
|
|
2671
|
-
this.props =
|
|
2357
|
+
this.props = props;
|
|
2672
2358
|
contextStack.push({
|
|
2673
2359
|
type: "component",
|
|
2674
2360
|
app,
|
|
@@ -2804,10 +2490,19 @@ class ComponentNode {
|
|
|
2804
2490
|
this.app.handleError({ error: e, node: this });
|
|
2805
2491
|
}
|
|
2806
2492
|
}
|
|
2493
|
+
for (const computation of this.computations) {
|
|
2494
|
+
disposeComputation(computation);
|
|
2495
|
+
}
|
|
2496
|
+
disposeComputation(this.signalComputation);
|
|
2807
2497
|
this.status = 3 /* STATUS.DESTROYED */;
|
|
2808
2498
|
}
|
|
2809
2499
|
async updateAndRender(props, parentFiber) {
|
|
2810
2500
|
props = Object.assign({}, props);
|
|
2501
|
+
for (const key in this.defaultProps) {
|
|
2502
|
+
if (props[key] === undefined) {
|
|
2503
|
+
props[key] = this.defaultProps[key];
|
|
2504
|
+
}
|
|
2505
|
+
}
|
|
2811
2506
|
// update
|
|
2812
2507
|
const fiber = makeChildFiber(this, parentFiber);
|
|
2813
2508
|
this.fiber = fiber;
|
|
@@ -3436,6 +3131,13 @@ function createRef(ref) {
|
|
|
3436
3131
|
}
|
|
3437
3132
|
};
|
|
3438
3133
|
}
|
|
3134
|
+
function callHandler(fn, ctx, ev) {
|
|
3135
|
+
if (typeof fn !== "function") {
|
|
3136
|
+
throw new OwlError(`Invalid handler expression: the \`t-on\` expression should evaluate to a function, but got '${typeof fn}'. ` +
|
|
3137
|
+
`Did you mean to use an arrow function? (e.g. \`t-on-click="() => expr"\`)`);
|
|
3138
|
+
}
|
|
3139
|
+
fn.call(ctx["this"], ev);
|
|
3140
|
+
}
|
|
3439
3141
|
function modelExpr(value) {
|
|
3440
3142
|
if (typeof value !== "function" || typeof value.set !== "function") {
|
|
3441
3143
|
throw new OwlError(`Invalid t-model expression: expression should evaluate to a function with a 'set' method defined on it`);
|
|
@@ -3530,8 +3232,8 @@ const helpers = {
|
|
|
3530
3232
|
createRef,
|
|
3531
3233
|
modelExpr,
|
|
3532
3234
|
createComponent,
|
|
3533
|
-
Portal,
|
|
3534
3235
|
callTemplate,
|
|
3236
|
+
callHandler,
|
|
3535
3237
|
};
|
|
3536
3238
|
|
|
3537
3239
|
const bdom = { text, createBlock, list, multi, html, toggler, comment };
|
|
@@ -3639,7 +3341,6 @@ function xml(...args) {
|
|
|
3639
3341
|
return name;
|
|
3640
3342
|
}
|
|
3641
3343
|
xml.nextId = 1;
|
|
3642
|
-
TemplateSet.registerTemplate("__portal__", portalTemplate);
|
|
3643
3344
|
|
|
3644
3345
|
/**
|
|
3645
3346
|
* Owl QWeb Expression Parser
|
|
@@ -3851,11 +3552,18 @@ const isRightSeparator = (token) => token && (token.type === "RIGHT_BRACE" || to
|
|
|
3851
3552
|
* the arrow operator, then we add the current (or some previous tokens) token to
|
|
3852
3553
|
* the list of variables so it does not get replaced by a lookup in the context
|
|
3853
3554
|
*/
|
|
3854
|
-
|
|
3555
|
+
// Leading spaces are trimmed during tokenization, so they need to be added back for some values
|
|
3556
|
+
const paddedValues = new Map([["in ", " in "]]);
|
|
3557
|
+
/**
|
|
3558
|
+
* Processes a javascript expression: compiles variable lookups and detects
|
|
3559
|
+
* top-level arrow functions with their free variables, all in a single pass.
|
|
3560
|
+
*/
|
|
3561
|
+
function processExpr(expr) {
|
|
3855
3562
|
const localVars = new Set();
|
|
3856
3563
|
const tokens = tokenize(expr);
|
|
3857
3564
|
let i = 0;
|
|
3858
3565
|
let stack = []; // to track last opening (, [ or {
|
|
3566
|
+
let topLevelArrowIndex = -1;
|
|
3859
3567
|
while (i < tokens.length) {
|
|
3860
3568
|
let token = tokens[i];
|
|
3861
3569
|
let prevToken = tokens[i - 1];
|
|
@@ -3896,18 +3604,21 @@ function compileExprToArray(expr) {
|
|
|
3896
3604
|
token.value = token.replace((expr) => compileExpr(expr));
|
|
3897
3605
|
}
|
|
3898
3606
|
if (nextToken && nextToken.type === "OPERATOR" && nextToken.value === "=>") {
|
|
3607
|
+
if (stack.length === 0) {
|
|
3608
|
+
topLevelArrowIndex = i + 1;
|
|
3609
|
+
}
|
|
3899
3610
|
if (token.type === "RIGHT_PAREN") {
|
|
3900
3611
|
let j = i - 1;
|
|
3901
3612
|
while (j > 0 && tokens[j].type !== "LEFT_PAREN") {
|
|
3902
3613
|
if (tokens[j].type === "SYMBOL" && tokens[j].originalValue) {
|
|
3903
3614
|
tokens[j].value = tokens[j].originalValue;
|
|
3904
|
-
localVars.add(tokens[j].value);
|
|
3615
|
+
localVars.add(tokens[j].value);
|
|
3905
3616
|
}
|
|
3906
3617
|
j--;
|
|
3907
3618
|
}
|
|
3908
3619
|
}
|
|
3909
3620
|
else {
|
|
3910
|
-
localVars.add(token.value);
|
|
3621
|
+
localVars.add(token.value);
|
|
3911
3622
|
}
|
|
3912
3623
|
}
|
|
3913
3624
|
if (isVar) {
|
|
@@ -3928,14 +3639,24 @@ function compileExprToArray(expr) {
|
|
|
3928
3639
|
token.isLocal = true;
|
|
3929
3640
|
}
|
|
3930
3641
|
}
|
|
3931
|
-
|
|
3642
|
+
// Collect free variables from arrow function body
|
|
3643
|
+
let freeVariables = null;
|
|
3644
|
+
if (topLevelArrowIndex !== -1) {
|
|
3645
|
+
freeVariables = [];
|
|
3646
|
+
const seen = new Set();
|
|
3647
|
+
for (let i = topLevelArrowIndex + 1; i < tokens.length; i++) {
|
|
3648
|
+
const t = tokens[i];
|
|
3649
|
+
if (t.varName && !t.isLocal && t.varName !== "this" && !seen.has(t.varName)) {
|
|
3650
|
+
seen.add(t.varName);
|
|
3651
|
+
freeVariables.push(t.varName);
|
|
3652
|
+
}
|
|
3653
|
+
}
|
|
3654
|
+
}
|
|
3655
|
+
const compiled = tokens.map((t) => paddedValues.get(t.value) || t.value).join("");
|
|
3656
|
+
return { expr: compiled, freeVariables };
|
|
3932
3657
|
}
|
|
3933
|
-
// Leading spaces are trimmed during tokenization, so they need to be added back for some values
|
|
3934
|
-
const paddedValues = new Map([["in ", " in "]]);
|
|
3935
3658
|
function compileExpr(expr) {
|
|
3936
|
-
return
|
|
3937
|
-
.map((t) => paddedValues.get(t.value) || t.value)
|
|
3938
|
-
.join("");
|
|
3659
|
+
return processExpr(expr).expr;
|
|
3939
3660
|
}
|
|
3940
3661
|
const INTERP_REGEXP = /\{\{.*?\}\}|\#\{.*?\}/g;
|
|
3941
3662
|
function replaceDynamicParts(s, replacer) {
|
|
@@ -3954,8 +3675,11 @@ const zero = Symbol("zero");
|
|
|
3954
3675
|
const whitespaceRE = /\s+/g;
|
|
3955
3676
|
// using a non-html document so that <inner/outer>HTML serializes as XML instead
|
|
3956
3677
|
// of HTML (as we will parse it as xml later)
|
|
3957
|
-
|
|
3958
|
-
|
|
3678
|
+
let xmlDoc;
|
|
3679
|
+
if (typeof document !== "undefined") {
|
|
3680
|
+
xmlDoc = document.implementation.createDocument(null, null, null);
|
|
3681
|
+
}
|
|
3682
|
+
const MODS = new Set(["stop", "capture", "prevent", "self", "synthetic", "passive"]);
|
|
3959
3683
|
let nextDataIds = {};
|
|
3960
3684
|
function generateId(prefix = "") {
|
|
3961
3685
|
nextDataIds[prefix] = (nextDataIds[prefix] || 0) + 1;
|
|
@@ -4053,7 +3777,7 @@ class BlockDescription {
|
|
|
4053
3777
|
return t.innerHTML;
|
|
4054
3778
|
}
|
|
4055
3779
|
}
|
|
4056
|
-
function createContext(parentCtx, params) {
|
|
3780
|
+
function createContext$1(parentCtx, params) {
|
|
4057
3781
|
return Object.assign({
|
|
4058
3782
|
block: null,
|
|
4059
3783
|
index: 0,
|
|
@@ -4221,7 +3945,7 @@ class CodeGenerator {
|
|
|
4221
3945
|
const target = new CodeTarget(name, on);
|
|
4222
3946
|
this.targets.push(target);
|
|
4223
3947
|
this.target = target;
|
|
4224
|
-
this.compileAST(ast, createContext(ctx));
|
|
3948
|
+
this.compileAST(ast, createContext$1(ctx));
|
|
4225
3949
|
this.target = initialTarget;
|
|
4226
3950
|
return name;
|
|
4227
3951
|
}
|
|
@@ -4315,8 +4039,6 @@ class CodeGenerator {
|
|
|
4315
4039
|
return this.compileTTranslation(ast, ctx);
|
|
4316
4040
|
case 16 /* ASTType.TTranslationContext */:
|
|
4317
4041
|
return this.compileTTranslationContext(ast, ctx);
|
|
4318
|
-
case 17 /* ASTType.TPortal */:
|
|
4319
|
-
return this.compileTPortal(ast, ctx);
|
|
4320
4042
|
}
|
|
4321
4043
|
}
|
|
4322
4044
|
compileDebug(ast, ctx) {
|
|
@@ -4402,7 +4124,8 @@ class CodeGenerator {
|
|
|
4402
4124
|
hoistedExpr = `(ctx,${bareArrowMatch[1]})=>${rest}`;
|
|
4403
4125
|
}
|
|
4404
4126
|
else {
|
|
4405
|
-
|
|
4127
|
+
this.helpers.add("callHandler");
|
|
4128
|
+
hoistedExpr = `(ctx, ev) => callHandler(${compiled}, ctx, ev)`;
|
|
4406
4129
|
}
|
|
4407
4130
|
const id = generateId("hdlr_fn");
|
|
4408
4131
|
this.staticDefs.push({ id, expr: hoistedExpr });
|
|
@@ -4480,11 +4203,22 @@ class CodeGenerator {
|
|
|
4480
4203
|
// t-model
|
|
4481
4204
|
let tModelSelectedExpr;
|
|
4482
4205
|
if (ast.model) {
|
|
4483
|
-
const { hasDynamicChildren, expr, eventType, shouldNumberize, shouldTrim, targetAttr, specialInitTargetAttr, } = ast.model;
|
|
4484
|
-
|
|
4485
|
-
|
|
4486
|
-
|
|
4487
|
-
|
|
4206
|
+
const { hasDynamicChildren, expr, eventType, shouldNumberize, shouldTrim, targetAttr, specialInitTargetAttr, isProxy, } = ast.model;
|
|
4207
|
+
let readExpr;
|
|
4208
|
+
let writeExpr;
|
|
4209
|
+
if (isProxy) {
|
|
4210
|
+
const expression = compileExpr(expr);
|
|
4211
|
+
readExpr = expression;
|
|
4212
|
+
writeExpr = (value) => `${expression} = ${value}`;
|
|
4213
|
+
}
|
|
4214
|
+
else {
|
|
4215
|
+
const exprId = generateId("expr");
|
|
4216
|
+
const expression = compileExpr(expr);
|
|
4217
|
+
this.helpers.add("modelExpr");
|
|
4218
|
+
this.define(exprId, `modelExpr(${expression})`);
|
|
4219
|
+
readExpr = `${exprId}()`;
|
|
4220
|
+
writeExpr = (value) => `${exprId}.set(${value})`;
|
|
4221
|
+
}
|
|
4488
4222
|
let idx;
|
|
4489
4223
|
if (specialInitTargetAttr) {
|
|
4490
4224
|
let targetExpr = targetAttr in attrs && `'${attrs[targetAttr]}'`;
|
|
@@ -4495,23 +4229,23 @@ class CodeGenerator {
|
|
|
4495
4229
|
targetExpr = compileExpr(dynamicTgExpr);
|
|
4496
4230
|
}
|
|
4497
4231
|
}
|
|
4498
|
-
idx = block.insertData(`${
|
|
4232
|
+
idx = block.insertData(`${readExpr} === ${targetExpr}`, "prop");
|
|
4499
4233
|
attrs[`block-property-${idx}`] = specialInitTargetAttr;
|
|
4500
4234
|
}
|
|
4501
4235
|
else if (hasDynamicChildren) {
|
|
4502
4236
|
const bValueId = generateId("bValue");
|
|
4503
4237
|
tModelSelectedExpr = `${bValueId}`;
|
|
4504
|
-
this.define(tModelSelectedExpr,
|
|
4238
|
+
this.define(tModelSelectedExpr, readExpr);
|
|
4505
4239
|
}
|
|
4506
4240
|
else {
|
|
4507
|
-
idx = block.insertData(
|
|
4241
|
+
idx = block.insertData(readExpr, "prop");
|
|
4508
4242
|
attrs[`block-property-${idx}`] = targetAttr;
|
|
4509
4243
|
}
|
|
4510
4244
|
this.helpers.add("toNumber");
|
|
4511
4245
|
let valueCode = `ev.target.${targetAttr}`;
|
|
4512
4246
|
valueCode = shouldTrim ? `${valueCode}.trim()` : valueCode;
|
|
4513
4247
|
valueCode = shouldNumberize ? `toNumber(${valueCode})` : valueCode;
|
|
4514
|
-
const handler = `[(ctx, ev) => { ${
|
|
4248
|
+
const handler = `[(ctx, ev) => { ${writeExpr(valueCode)}; }, ctx]`;
|
|
4515
4249
|
idx = block.insertData(handler, "hdlr");
|
|
4516
4250
|
attrs[`block-handler-${idx}`] = eventType;
|
|
4517
4251
|
}
|
|
@@ -4545,7 +4279,7 @@ class CodeGenerator {
|
|
|
4545
4279
|
const children = ast.content;
|
|
4546
4280
|
for (let i = 0; i < children.length; i++) {
|
|
4547
4281
|
const child = ast.content[i];
|
|
4548
|
-
const subCtx = createContext(ctx, {
|
|
4282
|
+
const subCtx = createContext$1(ctx, {
|
|
4549
4283
|
block,
|
|
4550
4284
|
index: block.childNumber,
|
|
4551
4285
|
forceNewBlock: false,
|
|
@@ -4601,7 +4335,7 @@ class CodeGenerator {
|
|
|
4601
4335
|
else if (ast.body) {
|
|
4602
4336
|
let bodyValue = null;
|
|
4603
4337
|
bodyValue = BlockDescription.nextBlockId;
|
|
4604
|
-
const subCtx = createContext(ctx);
|
|
4338
|
+
const subCtx = createContext$1(ctx);
|
|
4605
4339
|
this.compileAST({ type: 3 /* ASTType.Multi */, content: ast.body }, subCtx);
|
|
4606
4340
|
this.helpers.add("safeOutput");
|
|
4607
4341
|
blockStr = `safeOutput(${compileExpr(ast.expr)}, b${bodyValue})`;
|
|
@@ -4616,7 +4350,7 @@ class CodeGenerator {
|
|
|
4616
4350
|
compileTIfBranch(content, block, ctx) {
|
|
4617
4351
|
this.target.indentLevel++;
|
|
4618
4352
|
let childN = block.children.length;
|
|
4619
|
-
this.compileAST(content, createContext(ctx, { block, index: ctx.index }));
|
|
4353
|
+
this.compileAST(content, createContext$1(ctx, { block, index: ctx.index }));
|
|
4620
4354
|
if (block.children.length > childN) {
|
|
4621
4355
|
// we have some content => need to insert an anchor at correct index
|
|
4622
4356
|
this.insertAnchor(block, childN);
|
|
@@ -4712,7 +4446,7 @@ class CodeGenerator {
|
|
|
4712
4446
|
this.addLine(`if (keys${block.id}.has(String(key${this.target.loopLevel}))) { throw new OwlError(\`Got duplicate key in t-foreach: \${key${this.target.loopLevel}}\`)}`);
|
|
4713
4447
|
this.addLine(`keys${block.id}.add(String(key${this.target.loopLevel}));`);
|
|
4714
4448
|
}
|
|
4715
|
-
const subCtx = createContext(ctx, { block, index: loopVar });
|
|
4449
|
+
const subCtx = createContext$1(ctx, { block, index: loopVar });
|
|
4716
4450
|
this.compileAST(ast.body, subCtx);
|
|
4717
4451
|
this.target.indentLevel--;
|
|
4718
4452
|
this.target.loopLevel--;
|
|
@@ -4724,7 +4458,7 @@ class CodeGenerator {
|
|
|
4724
4458
|
compileTKey(ast, ctx) {
|
|
4725
4459
|
const tKeyExpr = generateId("tKey_");
|
|
4726
4460
|
this.define(tKeyExpr, compileExpr(ast.expr));
|
|
4727
|
-
ctx = createContext(ctx, {
|
|
4461
|
+
ctx = createContext$1(ctx, {
|
|
4728
4462
|
tKeyExpr,
|
|
4729
4463
|
block: ctx.block,
|
|
4730
4464
|
index: ctx.index,
|
|
@@ -4751,7 +4485,7 @@ class CodeGenerator {
|
|
|
4751
4485
|
for (let i = 0, l = ast.content.length; i < l; i++) {
|
|
4752
4486
|
const child = ast.content[i];
|
|
4753
4487
|
const forceNewBlock = !child.hasNoRepresentation;
|
|
4754
|
-
const subCtx = createContext(ctx, {
|
|
4488
|
+
const subCtx = createContext$1(ctx, {
|
|
4755
4489
|
block,
|
|
4756
4490
|
index,
|
|
4757
4491
|
forceNewBlock,
|
|
@@ -4804,12 +4538,11 @@ class CodeGenerator {
|
|
|
4804
4538
|
if (ast.context) {
|
|
4805
4539
|
const dynCtxVar = generateId("ctx");
|
|
4806
4540
|
this.addLine(`const ${dynCtxVar} = ${compileExpr(ast.context)};`);
|
|
4807
|
-
if (
|
|
4808
|
-
ctxExpr = `Object.assign({
|
|
4541
|
+
if (attrs.length) {
|
|
4542
|
+
ctxExpr = `Object.assign({this: ${dynCtxVar}}, ${ctxString})`;
|
|
4809
4543
|
}
|
|
4810
4544
|
else {
|
|
4811
|
-
|
|
4812
|
-
ctxExpr = `Object.assign({}, ${dynCtxVar}, ${thisCtx}${attrs.length ? ", " + ctxString : ""})`;
|
|
4545
|
+
ctxExpr = `{this: ${dynCtxVar}}`;
|
|
4813
4546
|
}
|
|
4814
4547
|
}
|
|
4815
4548
|
else {
|
|
@@ -4954,9 +4687,29 @@ class CodeGenerator {
|
|
|
4954
4687
|
let { block } = ctx;
|
|
4955
4688
|
// props
|
|
4956
4689
|
const hasSlotsProp = "slots" in (ast.props || {});
|
|
4957
|
-
const props =
|
|
4958
|
-
|
|
4959
|
-
|
|
4690
|
+
const props = [];
|
|
4691
|
+
const propList = [];
|
|
4692
|
+
for (let p in ast.props || {}) {
|
|
4693
|
+
let [name, suffix] = p.split(".");
|
|
4694
|
+
if (suffix) {
|
|
4695
|
+
// .alike, .bind, .translate — delegate to formatProp, no propList entry
|
|
4696
|
+
props.push(this.formatProp(p, ast.props[p], ast.propsTranslationCtx, ctx.translationCtx));
|
|
4697
|
+
continue;
|
|
4698
|
+
}
|
|
4699
|
+
const { expr: compiledValue, freeVariables } = processExpr(ast.props[p]);
|
|
4700
|
+
const propName = /^[a-z_]+$/i.test(name) ? name : `'${name}'`;
|
|
4701
|
+
props.push(`${propName}: ${compiledValue || undefined}`);
|
|
4702
|
+
if (freeVariables) {
|
|
4703
|
+
for (const varName of freeVariables) {
|
|
4704
|
+
const syntheticKey = `\x01${name}.${varName}`;
|
|
4705
|
+
propList.push(`"${syntheticKey}"`);
|
|
4706
|
+
props.push(`"${syntheticKey}": ctx['${varName}']`);
|
|
4707
|
+
}
|
|
4708
|
+
}
|
|
4709
|
+
else {
|
|
4710
|
+
propList.push(`"${name}"`);
|
|
4711
|
+
}
|
|
4712
|
+
}
|
|
4960
4713
|
// slots
|
|
4961
4714
|
let slotDef = "";
|
|
4962
4715
|
if (ast.slots) {
|
|
@@ -5013,13 +4766,6 @@ class CodeGenerator {
|
|
|
5013
4766
|
keyArg = `${ctx.tKeyExpr} + ${keyArg}`;
|
|
5014
4767
|
}
|
|
5015
4768
|
let id = generateId("comp");
|
|
5016
|
-
const propList = [];
|
|
5017
|
-
for (let p in ast.props || {}) {
|
|
5018
|
-
let [name, suffix] = p.split(".");
|
|
5019
|
-
if (!suffix) {
|
|
5020
|
-
propList.push(`"${name}"`);
|
|
5021
|
-
}
|
|
5022
|
-
}
|
|
5023
4769
|
this.helpers.add("createComponent");
|
|
5024
4770
|
this.staticDefs.push({
|
|
5025
4771
|
id,
|
|
@@ -5124,26 +4870,6 @@ class CodeGenerator {
|
|
|
5124
4870
|
}
|
|
5125
4871
|
return null;
|
|
5126
4872
|
}
|
|
5127
|
-
compileTPortal(ast, ctx) {
|
|
5128
|
-
this.helpers.add("Portal");
|
|
5129
|
-
let { block } = ctx;
|
|
5130
|
-
const name = this.compileInNewTarget("slot", ast.content, ctx);
|
|
5131
|
-
let id = generateId("comp");
|
|
5132
|
-
this.helpers.add("createComponent");
|
|
5133
|
-
this.staticDefs.push({
|
|
5134
|
-
id,
|
|
5135
|
-
expr: `createComponent(app, null, false, true, false, false)`,
|
|
5136
|
-
});
|
|
5137
|
-
const target = compileExpr(ast.target);
|
|
5138
|
-
const key = this.generateComponentKey();
|
|
5139
|
-
const blockString = `${id}({target: ${target},slots: {'default': {__render: ${name}.bind(this), __ctx: ctx}}}, ${key}, node, ctx, Portal)`;
|
|
5140
|
-
if (block) {
|
|
5141
|
-
this.insertAnchor(block);
|
|
5142
|
-
}
|
|
5143
|
-
block = this.createBlock(block, "multi", ctx);
|
|
5144
|
-
this.insertBlock(blockString, block, { ...ctx, forceNewBlock: false });
|
|
5145
|
-
return block.varName;
|
|
5146
|
-
}
|
|
5147
4873
|
}
|
|
5148
4874
|
|
|
5149
4875
|
// -----------------------------------------------------------------------------
|
|
@@ -5179,7 +4905,6 @@ function parseNode(node, ctx) {
|
|
|
5179
4905
|
parseTDebugLog(node, ctx) ||
|
|
5180
4906
|
parseTForEach(node, ctx) ||
|
|
5181
4907
|
parseTIf(node, ctx) ||
|
|
5182
|
-
parseTPortal(node, ctx) ||
|
|
5183
4908
|
parseTTranslation(node, ctx) ||
|
|
5184
4909
|
parseTTranslationContext(node, ctx) ||
|
|
5185
4910
|
parseTCall(node, ctx) ||
|
|
@@ -5326,6 +5051,7 @@ function parseDOMNode(node, ctx) {
|
|
|
5326
5051
|
const hasTrimMod = attr.includes(".trim");
|
|
5327
5052
|
const hasLazyMod = hasTrimMod || attr.includes(".lazy");
|
|
5328
5053
|
const hasNumberMod = attr.includes(".number");
|
|
5054
|
+
const hasProxyMod = attr.includes(".proxy");
|
|
5329
5055
|
const eventType = isRadioInput ? "click" : isSelect || hasLazyMod ? "change" : "input";
|
|
5330
5056
|
model = {
|
|
5331
5057
|
expr: value,
|
|
@@ -5335,6 +5061,7 @@ function parseDOMNode(node, ctx) {
|
|
|
5335
5061
|
hasDynamicChildren: false,
|
|
5336
5062
|
shouldTrim: hasTrimMod,
|
|
5337
5063
|
shouldNumberize: hasNumberMod,
|
|
5064
|
+
isProxy: hasProxyMod,
|
|
5338
5065
|
};
|
|
5339
5066
|
if (isSelect) {
|
|
5340
5067
|
// don't pollute the original ctx
|
|
@@ -5806,694 +5533,1104 @@ function parseTTranslationContext(node, ctx) {
|
|
|
5806
5533
|
return wrapInTTranslationContextAST(result, translationCtx);
|
|
5807
5534
|
}
|
|
5808
5535
|
// -----------------------------------------------------------------------------
|
|
5809
|
-
//
|
|
5536
|
+
// helpers
|
|
5810
5537
|
// -----------------------------------------------------------------------------
|
|
5811
|
-
|
|
5812
|
-
|
|
5813
|
-
|
|
5538
|
+
/**
|
|
5539
|
+
* Parse all the child nodes of a given node and return a list of ast elements
|
|
5540
|
+
*/
|
|
5541
|
+
function parseChildren(node, ctx) {
|
|
5542
|
+
const children = [];
|
|
5543
|
+
for (let child of node.childNodes) {
|
|
5544
|
+
const childAst = parseNode(child, ctx);
|
|
5545
|
+
if (childAst) {
|
|
5546
|
+
if (childAst.type === 3 /* ASTType.Multi */) {
|
|
5547
|
+
children.push(...childAst.content);
|
|
5548
|
+
}
|
|
5549
|
+
else {
|
|
5550
|
+
children.push(childAst);
|
|
5551
|
+
}
|
|
5552
|
+
}
|
|
5814
5553
|
}
|
|
5815
|
-
|
|
5816
|
-
|
|
5817
|
-
|
|
5818
|
-
|
|
5819
|
-
|
|
5820
|
-
|
|
5821
|
-
|
|
5822
|
-
|
|
5554
|
+
return children;
|
|
5555
|
+
}
|
|
5556
|
+
function makeASTMulti(children) {
|
|
5557
|
+
const ast = { type: 3 /* ASTType.Multi */, content: children };
|
|
5558
|
+
if (children.every((c) => c.hasNoRepresentation)) {
|
|
5559
|
+
ast.hasNoRepresentation = true;
|
|
5560
|
+
}
|
|
5561
|
+
return ast;
|
|
5562
|
+
}
|
|
5563
|
+
/**
|
|
5564
|
+
* Parse all the child nodes of a given node and return an ast if possible.
|
|
5565
|
+
* In the case there are multiple children, they are wrapped in a astmulti.
|
|
5566
|
+
*/
|
|
5567
|
+
function parseChildNodes(node, ctx) {
|
|
5568
|
+
const children = parseChildren(node, ctx);
|
|
5569
|
+
switch (children.length) {
|
|
5570
|
+
case 0:
|
|
5571
|
+
return null;
|
|
5572
|
+
case 1:
|
|
5573
|
+
return children[0];
|
|
5574
|
+
default:
|
|
5575
|
+
return makeASTMulti(children);
|
|
5576
|
+
}
|
|
5577
|
+
}
|
|
5578
|
+
/**
|
|
5579
|
+
* Normalizes the content of an Element so that t-if/t-elif/t-else directives
|
|
5580
|
+
* immediately follow one another (by removing empty text nodes or comments).
|
|
5581
|
+
* Throws an error when a conditional branching statement is malformed. This
|
|
5582
|
+
* function modifies the Element in place.
|
|
5583
|
+
*
|
|
5584
|
+
* @param el the element containing the tree that should be normalized
|
|
5585
|
+
*/
|
|
5586
|
+
function normalizeTIf(el) {
|
|
5587
|
+
let tbranch = el.querySelectorAll("[t-elif], [t-else]");
|
|
5588
|
+
for (let i = 0, ilen = tbranch.length; i < ilen; i++) {
|
|
5589
|
+
let node = tbranch[i];
|
|
5590
|
+
let prevElem = node.previousElementSibling;
|
|
5591
|
+
let pattr = (name) => prevElem.getAttribute(name);
|
|
5592
|
+
let nattr = (name) => +!!node.getAttribute(name);
|
|
5593
|
+
if (prevElem && (pattr("t-if") || pattr("t-elif"))) {
|
|
5594
|
+
if (pattr("t-foreach")) {
|
|
5595
|
+
throw new OwlError("t-if cannot stay at the same level as t-foreach when using t-elif or t-else");
|
|
5596
|
+
}
|
|
5597
|
+
if (["t-if", "t-elif", "t-else"].map(nattr).reduce(function (a, b) {
|
|
5598
|
+
return a + b;
|
|
5599
|
+
}) > 1) {
|
|
5600
|
+
throw new OwlError("Only one conditional branching directive is allowed per node");
|
|
5601
|
+
}
|
|
5602
|
+
// All text (with only spaces) and comment nodes (nodeType 8) between
|
|
5603
|
+
// branch nodes are removed
|
|
5604
|
+
let textNode;
|
|
5605
|
+
while ((textNode = node.previousSibling) !== prevElem) {
|
|
5606
|
+
if (textNode.nodeValue.trim().length && textNode.nodeType !== 8) {
|
|
5607
|
+
throw new OwlError("text is not allowed between branching directives");
|
|
5608
|
+
}
|
|
5609
|
+
textNode.remove();
|
|
5610
|
+
}
|
|
5611
|
+
}
|
|
5612
|
+
else {
|
|
5613
|
+
throw new OwlError("t-elif and t-else directives must be preceded by a t-if or t-elif directive");
|
|
5614
|
+
}
|
|
5615
|
+
}
|
|
5616
|
+
}
|
|
5617
|
+
/**
|
|
5618
|
+
* Normalizes the content of an Element so that t-out directives on components
|
|
5619
|
+
* are removed and instead places a <t t-out=""> as the default slot of the
|
|
5620
|
+
* component. Also throws if the component already has content. This function
|
|
5621
|
+
* modifies the Element in place.
|
|
5622
|
+
*
|
|
5623
|
+
* @param el the element containing the tree that should be normalized
|
|
5624
|
+
*/
|
|
5625
|
+
function normalizeTOut(el) {
|
|
5626
|
+
const elements = [...el.querySelectorAll(`[t-out]`)].filter((el) => el.tagName[0] === el.tagName[0].toUpperCase() || el.hasAttribute("t-component"));
|
|
5627
|
+
for (const el of elements) {
|
|
5628
|
+
if (el.childNodes.length) {
|
|
5629
|
+
throw new OwlError(`Cannot have t-out on a component that already has content`);
|
|
5630
|
+
}
|
|
5631
|
+
const value = el.getAttribute("t-out");
|
|
5632
|
+
el.removeAttribute("t-out");
|
|
5633
|
+
const t = el.ownerDocument.createElement("t");
|
|
5634
|
+
if (value != null) {
|
|
5635
|
+
t.setAttribute("t-out", value);
|
|
5636
|
+
}
|
|
5637
|
+
el.appendChild(t);
|
|
5638
|
+
}
|
|
5639
|
+
}
|
|
5640
|
+
/**
|
|
5641
|
+
* Normalizes the tree inside a given element and do some preliminary validation
|
|
5642
|
+
* on it. This function modifies the Element in place.
|
|
5643
|
+
*
|
|
5644
|
+
* @param el the element containing the tree that should be normalized
|
|
5645
|
+
*/
|
|
5646
|
+
function normalizeXML(el) {
|
|
5647
|
+
normalizeTIf(el);
|
|
5648
|
+
normalizeTOut(el);
|
|
5649
|
+
}
|
|
5650
|
+
|
|
5651
|
+
function compile(template, options = {
|
|
5652
|
+
hasGlobalValues: false,
|
|
5653
|
+
}) {
|
|
5654
|
+
// parsing
|
|
5655
|
+
const ast = parse(template, options.customDirectives);
|
|
5656
|
+
// code generation
|
|
5657
|
+
const codeGenerator = new CodeGenerator(ast, options);
|
|
5658
|
+
const code = codeGenerator.generateCode();
|
|
5659
|
+
// template function
|
|
5660
|
+
try {
|
|
5661
|
+
return new Function("app, bdom, helpers", code);
|
|
5662
|
+
}
|
|
5663
|
+
catch (originalError) {
|
|
5664
|
+
const { name } = options;
|
|
5665
|
+
const nameStr = name ? `template "${name}"` : "anonymous template";
|
|
5666
|
+
const err = new OwlError(`Failed to compile ${nameStr}: ${originalError.message}\n\ngenerated code:\nfunction(app, bdom, helpers) {\n${code}\n}`);
|
|
5667
|
+
err.cause = originalError;
|
|
5668
|
+
throw err;
|
|
5669
|
+
}
|
|
5670
|
+
}
|
|
5671
|
+
|
|
5672
|
+
// do not modify manually. This file is generated by the release script.
|
|
5673
|
+
const version = "3.0.0-alpha.21";
|
|
5674
|
+
|
|
5675
|
+
function effect(fn) {
|
|
5676
|
+
const computation = createComputation(() => {
|
|
5677
|
+
// In case the cleanup read an atom.
|
|
5678
|
+
// todo: test it
|
|
5679
|
+
setComputation(undefined);
|
|
5680
|
+
unsubscribeEffect(computation);
|
|
5681
|
+
setComputation(computation);
|
|
5682
|
+
return fn();
|
|
5683
|
+
}, false);
|
|
5684
|
+
getCurrentComputation()?.observers.add(computation);
|
|
5685
|
+
updateComputation(computation);
|
|
5686
|
+
// Remove sources and unsubscribe
|
|
5687
|
+
return function cleanupEffect() {
|
|
5688
|
+
// In case the cleanup read an atom.
|
|
5689
|
+
// todo: test it
|
|
5690
|
+
const previousComputation = getCurrentComputation();
|
|
5691
|
+
setComputation(undefined);
|
|
5692
|
+
unsubscribeEffect(computation);
|
|
5693
|
+
setComputation(previousComputation);
|
|
5694
|
+
};
|
|
5695
|
+
}
|
|
5696
|
+
function unsubscribeEffect(effect) {
|
|
5697
|
+
removeSources(effect);
|
|
5698
|
+
cleanupEffect(effect);
|
|
5699
|
+
for (const childEffect of effect.observers) {
|
|
5700
|
+
// Consider it executed to avoid it's re-execution
|
|
5701
|
+
// todo: make a test for it
|
|
5702
|
+
childEffect.state = ComputationState.EXECUTED;
|
|
5703
|
+
removeSources(childEffect);
|
|
5704
|
+
unsubscribeEffect(childEffect);
|
|
5705
|
+
}
|
|
5706
|
+
effect.observers.clear();
|
|
5707
|
+
}
|
|
5708
|
+
function cleanupEffect(effect) {
|
|
5709
|
+
// the computation.value of an effect is a cleanup function
|
|
5710
|
+
const cleanupFn = effect.value;
|
|
5711
|
+
if (cleanupFn && typeof cleanupFn === "function") {
|
|
5712
|
+
cleanupFn();
|
|
5713
|
+
effect.value = undefined;
|
|
5714
|
+
}
|
|
5715
|
+
}
|
|
5716
|
+
|
|
5717
|
+
class Plugin {
|
|
5718
|
+
static _shadowId;
|
|
5719
|
+
static get id() {
|
|
5720
|
+
return this._shadowId ?? this.name;
|
|
5721
|
+
}
|
|
5722
|
+
static set id(shadowId) {
|
|
5723
|
+
this._shadowId = shadowId;
|
|
5724
|
+
}
|
|
5725
|
+
__owl__;
|
|
5726
|
+
constructor(manager) {
|
|
5727
|
+
this.__owl__ = manager;
|
|
5728
|
+
}
|
|
5729
|
+
setup() { }
|
|
5730
|
+
}
|
|
5731
|
+
class PluginManager {
|
|
5732
|
+
app;
|
|
5733
|
+
config;
|
|
5734
|
+
onDestroyCb = [];
|
|
5735
|
+
computations = [];
|
|
5736
|
+
plugins;
|
|
5737
|
+
status = 0 /* STATUS.NEW */;
|
|
5738
|
+
constructor(app, options = {}) {
|
|
5739
|
+
this.app = app;
|
|
5740
|
+
this.config = options.config ?? {};
|
|
5741
|
+
if (options.parent) {
|
|
5742
|
+
const parent = options.parent;
|
|
5743
|
+
parent.onDestroyCb.push(() => this.destroy());
|
|
5744
|
+
this.plugins = Object.create(parent.plugins);
|
|
5745
|
+
}
|
|
5746
|
+
else {
|
|
5747
|
+
this.plugins = {};
|
|
5748
|
+
}
|
|
5749
|
+
}
|
|
5750
|
+
destroy() {
|
|
5751
|
+
const cbs = this.onDestroyCb;
|
|
5752
|
+
while (cbs.length) {
|
|
5753
|
+
cbs.pop()();
|
|
5754
|
+
}
|
|
5755
|
+
for (const computation of this.computations) {
|
|
5756
|
+
disposeComputation(computation);
|
|
5757
|
+
}
|
|
5758
|
+
this.status = 3 /* STATUS.DESTROYED */;
|
|
5759
|
+
}
|
|
5760
|
+
getPluginById(id) {
|
|
5761
|
+
return this.plugins[id] || null;
|
|
5762
|
+
}
|
|
5763
|
+
getPlugin(pluginConstructor) {
|
|
5764
|
+
return this.getPluginById(pluginConstructor.id);
|
|
5765
|
+
}
|
|
5766
|
+
startPlugin(pluginConstructor) {
|
|
5767
|
+
if (!pluginConstructor.id) {
|
|
5768
|
+
throw new OwlError(`Plugin "${pluginConstructor.name}" has no id`);
|
|
5769
|
+
}
|
|
5770
|
+
if (this.plugins.hasOwnProperty(pluginConstructor.id)) {
|
|
5771
|
+
const existingPluginType = this.getPluginById(pluginConstructor.id).constructor;
|
|
5772
|
+
if (existingPluginType !== pluginConstructor) {
|
|
5773
|
+
throw new OwlError(`Trying to start a plugin with the same id as an other plugin (id: '${pluginConstructor.id}', existing plugin: '${existingPluginType.name}', starting plugin: '${pluginConstructor.name}')`);
|
|
5774
|
+
}
|
|
5775
|
+
return null;
|
|
5776
|
+
}
|
|
5777
|
+
const plugin = new pluginConstructor(this);
|
|
5778
|
+
this.plugins[pluginConstructor.id] = plugin;
|
|
5779
|
+
plugin.setup();
|
|
5780
|
+
return plugin;
|
|
5781
|
+
}
|
|
5782
|
+
startPlugins(pluginConstructors) {
|
|
5783
|
+
contextStack.push({
|
|
5784
|
+
type: "plugin",
|
|
5785
|
+
app: this.app,
|
|
5786
|
+
manager: this,
|
|
5787
|
+
get status() {
|
|
5788
|
+
return this.manager.status;
|
|
5789
|
+
},
|
|
5790
|
+
});
|
|
5791
|
+
try {
|
|
5792
|
+
for (const pluginConstructor of pluginConstructors) {
|
|
5793
|
+
this.startPlugin(pluginConstructor);
|
|
5794
|
+
}
|
|
5795
|
+
}
|
|
5796
|
+
finally {
|
|
5797
|
+
contextStack.pop();
|
|
5798
|
+
}
|
|
5799
|
+
this.status = 1 /* STATUS.MOUNTED */;
|
|
5800
|
+
}
|
|
5801
|
+
}
|
|
5802
|
+
function startPlugins(manager, plugins) {
|
|
5803
|
+
if (Array.isArray(plugins)) {
|
|
5804
|
+
manager.startPlugins(plugins);
|
|
5805
|
+
}
|
|
5806
|
+
else {
|
|
5807
|
+
manager.onDestroyCb.push(effect(() => {
|
|
5808
|
+
const pluginItems = plugins.items();
|
|
5809
|
+
untrack(() => manager.startPlugins(pluginItems));
|
|
5810
|
+
}));
|
|
5811
|
+
}
|
|
5812
|
+
}
|
|
5813
|
+
|
|
5814
|
+
// -----------------------------------------------------------------------------
|
|
5815
|
+
// Scheduler
|
|
5816
|
+
// -----------------------------------------------------------------------------
|
|
5817
|
+
let requestAnimationFrame;
|
|
5818
|
+
if (typeof window !== "undefined") {
|
|
5819
|
+
requestAnimationFrame = window.requestAnimationFrame.bind(window);
|
|
5820
|
+
}
|
|
5821
|
+
class Scheduler {
|
|
5822
|
+
// capture the value of requestAnimationFrame as soon as possible, to avoid
|
|
5823
|
+
// interactions with other code, such as test frameworks that override them
|
|
5824
|
+
static requestAnimationFrame = requestAnimationFrame;
|
|
5825
|
+
tasks = new Set();
|
|
5826
|
+
requestAnimationFrame;
|
|
5827
|
+
frame = 0;
|
|
5828
|
+
delayedRenders = [];
|
|
5829
|
+
cancelledNodes = new Set();
|
|
5830
|
+
processing = false;
|
|
5831
|
+
constructor() {
|
|
5832
|
+
this.requestAnimationFrame = Scheduler.requestAnimationFrame;
|
|
5833
|
+
}
|
|
5834
|
+
addFiber(fiber) {
|
|
5835
|
+
this.tasks.add(fiber.root);
|
|
5836
|
+
}
|
|
5837
|
+
scheduleDestroy(node) {
|
|
5838
|
+
this.cancelledNodes.add(node);
|
|
5839
|
+
if (this.frame === 0) {
|
|
5840
|
+
this.frame = this.requestAnimationFrame(() => this.processTasks());
|
|
5841
|
+
}
|
|
5842
|
+
}
|
|
5843
|
+
/**
|
|
5844
|
+
* Process all current tasks. This only applies to the fibers that are ready.
|
|
5845
|
+
* Other tasks are left unchanged.
|
|
5846
|
+
*/
|
|
5847
|
+
flush() {
|
|
5848
|
+
if (this.delayedRenders.length) {
|
|
5849
|
+
let renders = this.delayedRenders;
|
|
5850
|
+
this.delayedRenders = [];
|
|
5851
|
+
for (let f of renders) {
|
|
5852
|
+
if (f.root && f.node.status !== 3 /* STATUS.DESTROYED */ && f.node.fiber === f) {
|
|
5853
|
+
f.render();
|
|
5854
|
+
}
|
|
5855
|
+
}
|
|
5856
|
+
}
|
|
5857
|
+
if (this.frame === 0) {
|
|
5858
|
+
this.frame = this.requestAnimationFrame(() => this.processTasks());
|
|
5859
|
+
}
|
|
5860
|
+
}
|
|
5861
|
+
processTasks() {
|
|
5862
|
+
if (this.processing) {
|
|
5863
|
+
return;
|
|
5864
|
+
}
|
|
5865
|
+
this.processing = true;
|
|
5866
|
+
this.frame = 0;
|
|
5867
|
+
for (let node of this.cancelledNodes) {
|
|
5868
|
+
node._destroy();
|
|
5869
|
+
}
|
|
5870
|
+
this.cancelledNodes.clear();
|
|
5871
|
+
for (let task of this.tasks) {
|
|
5872
|
+
this.processFiber(task);
|
|
5873
|
+
}
|
|
5874
|
+
for (let task of this.tasks) {
|
|
5875
|
+
if (task.node.status === 3 /* STATUS.DESTROYED */) {
|
|
5876
|
+
this.tasks.delete(task);
|
|
5877
|
+
}
|
|
5878
|
+
}
|
|
5879
|
+
this.processing = false;
|
|
5880
|
+
}
|
|
5881
|
+
processFiber(fiber) {
|
|
5882
|
+
if (fiber.root !== fiber) {
|
|
5883
|
+
this.tasks.delete(fiber);
|
|
5884
|
+
return;
|
|
5885
|
+
}
|
|
5886
|
+
const hasError = fibersInError.has(fiber);
|
|
5887
|
+
if (hasError && fiber.counter !== 0) {
|
|
5888
|
+
this.tasks.delete(fiber);
|
|
5889
|
+
return;
|
|
5890
|
+
}
|
|
5891
|
+
if (fiber.node.status === 3 /* STATUS.DESTROYED */) {
|
|
5892
|
+
this.tasks.delete(fiber);
|
|
5893
|
+
return;
|
|
5894
|
+
}
|
|
5895
|
+
if (fiber.counter === 0) {
|
|
5896
|
+
if (!hasError) {
|
|
5897
|
+
fiber.complete();
|
|
5898
|
+
}
|
|
5899
|
+
// at this point, the fiber should have been applied to the DOM, so we can
|
|
5900
|
+
// remove it from the task list. If it is not the case, it means that there
|
|
5901
|
+
// was an error and an error handler triggered a new rendering that recycled
|
|
5902
|
+
// the fiber, so in that case, we actually want to keep the fiber around,
|
|
5903
|
+
// otherwise it will just be ignored.
|
|
5904
|
+
if (fiber.appliedToDom) {
|
|
5905
|
+
this.tasks.delete(fiber);
|
|
5906
|
+
}
|
|
5907
|
+
}
|
|
5908
|
+
}
|
|
5909
|
+
}
|
|
5910
|
+
|
|
5911
|
+
let hasBeenLogged = false;
|
|
5912
|
+
const apps = new Set();
|
|
5913
|
+
if (typeof window !== "undefined") {
|
|
5914
|
+
window.__OWL_DEVTOOLS__ ||= { apps, Fiber, RootFiber, toRaw, proxy };
|
|
5915
|
+
}
|
|
5916
|
+
class App extends TemplateSet {
|
|
5917
|
+
static validateTarget = validateTarget;
|
|
5918
|
+
static apps = apps;
|
|
5919
|
+
static version = version;
|
|
5920
|
+
name;
|
|
5921
|
+
scheduler = new Scheduler();
|
|
5922
|
+
roots = new Set();
|
|
5923
|
+
pluginManager;
|
|
5924
|
+
constructor(config = {}) {
|
|
5925
|
+
super(config);
|
|
5926
|
+
this.name = config.name || "";
|
|
5927
|
+
apps.add(this);
|
|
5928
|
+
this.pluginManager = new PluginManager(this, { config: config.config });
|
|
5929
|
+
if (config.plugins) {
|
|
5930
|
+
startPlugins(this.pluginManager, config.plugins);
|
|
5931
|
+
}
|
|
5932
|
+
if (config.test) {
|
|
5933
|
+
this.dev = true;
|
|
5934
|
+
}
|
|
5935
|
+
if (this.dev && !config.test && !hasBeenLogged) {
|
|
5936
|
+
console.info(`Owl is running in 'dev' mode.`);
|
|
5937
|
+
hasBeenLogged = true;
|
|
5938
|
+
}
|
|
5939
|
+
}
|
|
5940
|
+
createRoot(Root, config = {}) {
|
|
5941
|
+
const props = config.props || {};
|
|
5942
|
+
let resolve;
|
|
5943
|
+
let reject;
|
|
5944
|
+
const promise = new Promise((res, rej) => {
|
|
5945
|
+
resolve = res;
|
|
5946
|
+
reject = rej;
|
|
5947
|
+
});
|
|
5948
|
+
const restore = saveContext();
|
|
5949
|
+
let node;
|
|
5950
|
+
let error = null;
|
|
5951
|
+
try {
|
|
5952
|
+
node = new ComponentNode(Root, props, this, null, null);
|
|
5953
|
+
}
|
|
5954
|
+
catch (e) {
|
|
5955
|
+
error = e;
|
|
5956
|
+
reject(e);
|
|
5957
|
+
}
|
|
5958
|
+
finally {
|
|
5959
|
+
restore();
|
|
5960
|
+
}
|
|
5961
|
+
const root = {
|
|
5962
|
+
node: node,
|
|
5963
|
+
promise,
|
|
5964
|
+
mount: (target, options) => {
|
|
5965
|
+
if (error) {
|
|
5966
|
+
return promise;
|
|
5967
|
+
}
|
|
5968
|
+
App.validateTarget(target);
|
|
5969
|
+
this.mountNode(node, target, resolve, reject, options);
|
|
5970
|
+
return promise;
|
|
5971
|
+
},
|
|
5972
|
+
destroy: () => {
|
|
5973
|
+
this.roots.delete(root);
|
|
5974
|
+
node.destroy();
|
|
5975
|
+
this.scheduler.processTasks();
|
|
5976
|
+
},
|
|
5977
|
+
};
|
|
5978
|
+
this.roots.add(root);
|
|
5979
|
+
return root;
|
|
5980
|
+
}
|
|
5981
|
+
mountNode(node, target, resolve, reject, options) {
|
|
5982
|
+
// Manually add the last resort error handler on the node
|
|
5983
|
+
let handlers = nodeErrorHandlers.get(node);
|
|
5984
|
+
if (!handlers) {
|
|
5985
|
+
handlers = [];
|
|
5986
|
+
nodeErrorHandlers.set(node, handlers);
|
|
5987
|
+
}
|
|
5988
|
+
handlers.unshift((e, finalize) => {
|
|
5989
|
+
const finalError = finalize();
|
|
5990
|
+
reject(finalError);
|
|
5991
|
+
});
|
|
5992
|
+
// manually set a onMounted callback.
|
|
5993
|
+
// that way, we are independant from the current node.
|
|
5994
|
+
node.mounted.push(() => {
|
|
5995
|
+
resolve(node.component);
|
|
5996
|
+
handlers.shift();
|
|
5997
|
+
});
|
|
5998
|
+
node.mountComponent(target, options);
|
|
5999
|
+
}
|
|
6000
|
+
destroy() {
|
|
6001
|
+
for (let root of this.roots) {
|
|
6002
|
+
root.destroy();
|
|
6003
|
+
}
|
|
6004
|
+
this.pluginManager.destroy();
|
|
6005
|
+
this.scheduler.processTasks();
|
|
6006
|
+
apps.delete(this);
|
|
6007
|
+
}
|
|
6008
|
+
handleError(...args) {
|
|
6009
|
+
return handleError(...args);
|
|
6010
|
+
}
|
|
6011
|
+
}
|
|
6012
|
+
async function mount(C, target, config = {}) {
|
|
6013
|
+
const app = new App(config);
|
|
6014
|
+
const root = app.createRoot(C, config);
|
|
6015
|
+
return root.mount(target, config);
|
|
6016
|
+
}
|
|
6017
|
+
|
|
6018
|
+
const mainEventHandler = (data, ev, currentTarget) => {
|
|
6019
|
+
const { data: _data, modifiers } = filterOutModifiersFromData(data);
|
|
6020
|
+
data = _data;
|
|
6021
|
+
let stopped = false;
|
|
6022
|
+
if (modifiers.length) {
|
|
6023
|
+
let selfMode = false;
|
|
6024
|
+
const isSelf = ev.target === currentTarget;
|
|
6025
|
+
for (const mod of modifiers) {
|
|
6026
|
+
switch (mod) {
|
|
6027
|
+
case "self":
|
|
6028
|
+
selfMode = true;
|
|
6029
|
+
if (isSelf) {
|
|
6030
|
+
continue;
|
|
6031
|
+
}
|
|
6032
|
+
else {
|
|
6033
|
+
return stopped;
|
|
6034
|
+
}
|
|
6035
|
+
case "prevent":
|
|
6036
|
+
if ((selfMode && isSelf) || !selfMode)
|
|
6037
|
+
ev.preventDefault();
|
|
6038
|
+
continue;
|
|
6039
|
+
case "stop":
|
|
6040
|
+
if ((selfMode && isSelf) || !selfMode)
|
|
6041
|
+
ev.stopPropagation();
|
|
6042
|
+
stopped = true;
|
|
6043
|
+
continue;
|
|
6044
|
+
}
|
|
6045
|
+
}
|
|
6046
|
+
}
|
|
6047
|
+
// If handler is empty, the array slot 0 will also be empty, and data will not have the property 0
|
|
6048
|
+
// We check this rather than data[0] being truthy (or typeof function) so that it crashes
|
|
6049
|
+
// as expected when there is a handler expression that evaluates to a falsy value
|
|
6050
|
+
if (Object.hasOwnProperty.call(data, 0)) {
|
|
6051
|
+
const handler = data[0];
|
|
6052
|
+
if (typeof handler !== "function") {
|
|
6053
|
+
throw new OwlError(`Invalid handler (expected a function, received: '${handler}')`);
|
|
6054
|
+
}
|
|
6055
|
+
let node = data[1] ? data[1].__owl__ : null;
|
|
6056
|
+
if (node ? node.status === 1 /* STATUS.MOUNTED */ : true) {
|
|
6057
|
+
handler(data[1], ev);
|
|
6058
|
+
}
|
|
5823
6059
|
}
|
|
5824
|
-
return
|
|
5825
|
-
|
|
5826
|
-
|
|
5827
|
-
content,
|
|
5828
|
-
};
|
|
5829
|
-
}
|
|
6060
|
+
return stopped;
|
|
6061
|
+
};
|
|
6062
|
+
|
|
5830
6063
|
// -----------------------------------------------------------------------------
|
|
5831
|
-
//
|
|
6064
|
+
// hooks
|
|
5832
6065
|
// -----------------------------------------------------------------------------
|
|
5833
|
-
|
|
5834
|
-
|
|
5835
|
-
|
|
5836
|
-
|
|
5837
|
-
|
|
5838
|
-
|
|
5839
|
-
|
|
5840
|
-
if (childAst) {
|
|
5841
|
-
if (childAst.type === 3 /* ASTType.Multi */) {
|
|
5842
|
-
children.push(...childAst.content);
|
|
5843
|
-
}
|
|
5844
|
-
else {
|
|
5845
|
-
children.push(childAst);
|
|
5846
|
-
}
|
|
5847
|
-
}
|
|
6066
|
+
function decorate(node, f, hookName) {
|
|
6067
|
+
const result = f.bind(node.component);
|
|
6068
|
+
if (node.app.dev) {
|
|
6069
|
+
const suffix = f.name ? ` <${f.name}>` : "";
|
|
6070
|
+
Reflect.defineProperty(result, "name", {
|
|
6071
|
+
value: hookName + suffix,
|
|
6072
|
+
});
|
|
5848
6073
|
}
|
|
5849
|
-
return
|
|
6074
|
+
return result;
|
|
5850
6075
|
}
|
|
5851
|
-
function
|
|
5852
|
-
const
|
|
5853
|
-
|
|
5854
|
-
|
|
6076
|
+
function onWillStart(fn) {
|
|
6077
|
+
const { node } = getContext("component");
|
|
6078
|
+
node.willStart.push(decorate(node, fn, "onWillStart"));
|
|
6079
|
+
}
|
|
6080
|
+
function onWillUpdateProps(fn) {
|
|
6081
|
+
const { node } = getContext("component");
|
|
6082
|
+
node.willUpdateProps.push(decorate(node, fn, "onWillUpdateProps"));
|
|
6083
|
+
}
|
|
6084
|
+
function onMounted(fn) {
|
|
6085
|
+
const { node } = getContext("component");
|
|
6086
|
+
node.mounted.push(decorate(node, fn, "onMounted"));
|
|
6087
|
+
}
|
|
6088
|
+
function onWillPatch(fn) {
|
|
6089
|
+
const { node } = getContext("component");
|
|
6090
|
+
node.willPatch.unshift(decorate(node, fn, "onWillPatch"));
|
|
6091
|
+
}
|
|
6092
|
+
function onPatched(fn) {
|
|
6093
|
+
const { node } = getContext("component");
|
|
6094
|
+
node.patched.push(decorate(node, fn, "onPatched"));
|
|
6095
|
+
}
|
|
6096
|
+
function onWillUnmount(fn) {
|
|
6097
|
+
const { node } = getContext("component");
|
|
6098
|
+
node.willUnmount.unshift(decorate(node, fn, "onWillUnmount"));
|
|
6099
|
+
}
|
|
6100
|
+
function onWillDestroy(fn) {
|
|
6101
|
+
const context = getContext();
|
|
6102
|
+
if (context.type === "component") {
|
|
6103
|
+
context.node.willDestroy.unshift(decorate(context.node, fn, "onWillDestroy"));
|
|
6104
|
+
}
|
|
6105
|
+
else {
|
|
6106
|
+
context.manager.onDestroyCb.push(fn);
|
|
5855
6107
|
}
|
|
5856
|
-
return ast;
|
|
5857
6108
|
}
|
|
5858
|
-
|
|
5859
|
-
|
|
5860
|
-
|
|
5861
|
-
|
|
5862
|
-
|
|
5863
|
-
|
|
5864
|
-
switch (children.length) {
|
|
5865
|
-
case 0:
|
|
5866
|
-
return null;
|
|
5867
|
-
case 1:
|
|
5868
|
-
return children[0];
|
|
5869
|
-
default:
|
|
5870
|
-
return makeASTMulti(children);
|
|
6109
|
+
function onError(callback) {
|
|
6110
|
+
const { node } = getContext("component");
|
|
6111
|
+
let handlers = nodeErrorHandlers.get(node);
|
|
6112
|
+
if (!handlers) {
|
|
6113
|
+
handlers = [];
|
|
6114
|
+
nodeErrorHandlers.set(node, handlers);
|
|
5871
6115
|
}
|
|
6116
|
+
handlers.push(callback.bind(node.component));
|
|
5872
6117
|
}
|
|
5873
|
-
|
|
5874
|
-
|
|
5875
|
-
|
|
5876
|
-
|
|
5877
|
-
|
|
5878
|
-
|
|
5879
|
-
* @param el the element containing the tree that should be normalized
|
|
5880
|
-
*/
|
|
5881
|
-
function normalizeTIf(el) {
|
|
5882
|
-
let tbranch = el.querySelectorAll("[t-elif], [t-else]");
|
|
5883
|
-
for (let i = 0, ilen = tbranch.length; i < ilen; i++) {
|
|
5884
|
-
let node = tbranch[i];
|
|
5885
|
-
let prevElem = node.previousElementSibling;
|
|
5886
|
-
let pattr = (name) => prevElem.getAttribute(name);
|
|
5887
|
-
let nattr = (name) => +!!node.getAttribute(name);
|
|
5888
|
-
if (prevElem && (pattr("t-if") || pattr("t-elif"))) {
|
|
5889
|
-
if (pattr("t-foreach")) {
|
|
5890
|
-
throw new OwlError("t-if cannot stay at the same level as t-foreach when using t-elif or t-else");
|
|
5891
|
-
}
|
|
5892
|
-
if (["t-if", "t-elif", "t-else"].map(nattr).reduce(function (a, b) {
|
|
5893
|
-
return a + b;
|
|
5894
|
-
}) > 1) {
|
|
5895
|
-
throw new OwlError("Only one conditional branching directive is allowed per node");
|
|
5896
|
-
}
|
|
5897
|
-
// All text (with only spaces) and comment nodes (nodeType 8) between
|
|
5898
|
-
// branch nodes are removed
|
|
5899
|
-
let textNode;
|
|
5900
|
-
while ((textNode = node.previousSibling) !== prevElem) {
|
|
5901
|
-
if (textNode.nodeValue.trim().length && textNode.nodeType !== 8) {
|
|
5902
|
-
throw new OwlError("text is not allowed between branching directives");
|
|
5903
|
-
}
|
|
5904
|
-
textNode.remove();
|
|
5905
|
-
}
|
|
6118
|
+
|
|
6119
|
+
function computed(getter, options = {}) {
|
|
6120
|
+
const computation = createComputation(() => {
|
|
6121
|
+
const newValue = getter();
|
|
6122
|
+
if (!Object.is(computation.value, newValue)) {
|
|
6123
|
+
onWriteAtom(computation);
|
|
5906
6124
|
}
|
|
5907
|
-
|
|
5908
|
-
|
|
6125
|
+
return newValue;
|
|
6126
|
+
}, true);
|
|
6127
|
+
function readComputed() {
|
|
6128
|
+
if (computation.state !== ComputationState.EXECUTED) {
|
|
6129
|
+
updateComputation(computation);
|
|
5909
6130
|
}
|
|
6131
|
+
onReadAtom(computation);
|
|
6132
|
+
return computation.value;
|
|
5910
6133
|
}
|
|
5911
|
-
|
|
5912
|
-
|
|
5913
|
-
|
|
5914
|
-
|
|
5915
|
-
|
|
5916
|
-
|
|
5917
|
-
*
|
|
5918
|
-
* @param el the element containing the tree that should be normalized
|
|
5919
|
-
*/
|
|
5920
|
-
function normalizeTOut(el) {
|
|
5921
|
-
const elements = [...el.querySelectorAll(`[t-out]`)].filter((el) => el.tagName[0] === el.tagName[0].toUpperCase() || el.hasAttribute("t-component"));
|
|
5922
|
-
for (const el of elements) {
|
|
5923
|
-
if (el.childNodes.length) {
|
|
5924
|
-
throw new OwlError(`Cannot have t-out on a component that already has content`);
|
|
6134
|
+
readComputed[atomSymbol] = computation;
|
|
6135
|
+
readComputed.set = options.set ?? (() => { });
|
|
6136
|
+
const context = contextStack.at(-1);
|
|
6137
|
+
if (context) {
|
|
6138
|
+
if (context.type === "component") {
|
|
6139
|
+
context.node.computations.push(computation);
|
|
5925
6140
|
}
|
|
5926
|
-
|
|
5927
|
-
|
|
5928
|
-
const t = el.ownerDocument.createElement("t");
|
|
5929
|
-
if (value != null) {
|
|
5930
|
-
t.setAttribute("t-out", value);
|
|
6141
|
+
else if (context.type === "plugin") {
|
|
6142
|
+
context.manager.computations.push(computation);
|
|
5931
6143
|
}
|
|
5932
|
-
el.appendChild(t);
|
|
5933
|
-
}
|
|
5934
|
-
}
|
|
5935
|
-
/**
|
|
5936
|
-
* Normalizes the tree inside a given element and do some preliminary validation
|
|
5937
|
-
* on it. This function modifies the Element in place.
|
|
5938
|
-
*
|
|
5939
|
-
* @param el the element containing the tree that should be normalized
|
|
5940
|
-
*/
|
|
5941
|
-
function normalizeXML(el) {
|
|
5942
|
-
normalizeTIf(el);
|
|
5943
|
-
normalizeTOut(el);
|
|
5944
|
-
}
|
|
5945
|
-
|
|
5946
|
-
function compile(template, options = {
|
|
5947
|
-
hasGlobalValues: false,
|
|
5948
|
-
}) {
|
|
5949
|
-
// parsing
|
|
5950
|
-
const ast = parse(template, options.customDirectives);
|
|
5951
|
-
// code generation
|
|
5952
|
-
const codeGenerator = new CodeGenerator(ast, options);
|
|
5953
|
-
const code = codeGenerator.generateCode();
|
|
5954
|
-
// template function
|
|
5955
|
-
try {
|
|
5956
|
-
return new Function("app, bdom, helpers", code);
|
|
5957
|
-
}
|
|
5958
|
-
catch (originalError) {
|
|
5959
|
-
const { name } = options;
|
|
5960
|
-
const nameStr = name ? `template "${name}"` : "anonymous template";
|
|
5961
|
-
const err = new OwlError(`Failed to compile ${nameStr}: ${originalError.message}\n\ngenerated code:\nfunction(app, bdom, helpers) {\n${code}\n}`);
|
|
5962
|
-
err.cause = originalError;
|
|
5963
|
-
throw err;
|
|
5964
6144
|
}
|
|
6145
|
+
return readComputed;
|
|
5965
6146
|
}
|
|
5966
6147
|
|
|
5967
|
-
|
|
5968
|
-
const
|
|
5969
|
-
|
|
5970
|
-
|
|
5971
|
-
|
|
5972
|
-
|
|
5973
|
-
|
|
5974
|
-
|
|
5975
|
-
|
|
5976
|
-
|
|
5977
|
-
|
|
5978
|
-
|
|
5979
|
-
|
|
5980
|
-
|
|
5981
|
-
|
|
5982
|
-
|
|
5983
|
-
|
|
5984
|
-
|
|
5985
|
-
|
|
5986
|
-
setComputation(undefined);
|
|
5987
|
-
unsubscribeEffect(computation);
|
|
5988
|
-
setComputation(previousComputation);
|
|
6148
|
+
function buildSignal(value, set) {
|
|
6149
|
+
const atom = {
|
|
6150
|
+
type: "signal",
|
|
6151
|
+
value,
|
|
6152
|
+
observers: new Set(),
|
|
6153
|
+
};
|
|
6154
|
+
let readValue = set(atom);
|
|
6155
|
+
const readSignal = () => {
|
|
6156
|
+
onReadAtom(atom);
|
|
6157
|
+
return readValue;
|
|
6158
|
+
};
|
|
6159
|
+
readSignal[atomSymbol] = atom;
|
|
6160
|
+
readSignal.set = function writeSignal(newValue) {
|
|
6161
|
+
if (Object.is(atom.value, newValue)) {
|
|
6162
|
+
return;
|
|
6163
|
+
}
|
|
6164
|
+
atom.value = newValue;
|
|
6165
|
+
readValue = set(atom);
|
|
6166
|
+
onWriteAtom(atom);
|
|
5989
6167
|
};
|
|
6168
|
+
return readSignal;
|
|
5990
6169
|
}
|
|
5991
|
-
function
|
|
5992
|
-
|
|
5993
|
-
|
|
5994
|
-
for (const childEffect of effect.observers) {
|
|
5995
|
-
// Consider it executed to avoid it's re-execution
|
|
5996
|
-
// todo: make a test for it
|
|
5997
|
-
childEffect.state = ComputationState.EXECUTED;
|
|
5998
|
-
removeSources(childEffect);
|
|
5999
|
-
unsubscribeEffect(childEffect);
|
|
6170
|
+
function invalidateSignal(signal) {
|
|
6171
|
+
if (typeof signal !== "function" || signal[atomSymbol]?.type !== "signal") {
|
|
6172
|
+
throw new OwlError(`Value is not a signal (${signal})`);
|
|
6000
6173
|
}
|
|
6001
|
-
|
|
6174
|
+
onWriteAtom(signal[atomSymbol]);
|
|
6175
|
+
}
|
|
6176
|
+
function signalArray(initialValue) {
|
|
6177
|
+
return buildSignal(initialValue, (atom) => proxifyTarget(atom.value, atom));
|
|
6178
|
+
}
|
|
6179
|
+
function signalObject(initialValue) {
|
|
6180
|
+
return buildSignal(initialValue, (atom) => proxifyTarget(atom.value, atom));
|
|
6181
|
+
}
|
|
6182
|
+
function signalMap(initialValue) {
|
|
6183
|
+
return buildSignal(initialValue, (atom) => proxifyTarget(atom.value, atom));
|
|
6002
6184
|
}
|
|
6003
|
-
function
|
|
6004
|
-
|
|
6005
|
-
|
|
6006
|
-
|
|
6007
|
-
|
|
6008
|
-
effect.value = undefined;
|
|
6009
|
-
}
|
|
6185
|
+
function signalSet(initialValue) {
|
|
6186
|
+
return buildSignal(initialValue, (atom) => proxifyTarget(atom.value, atom));
|
|
6187
|
+
}
|
|
6188
|
+
function signal(value) {
|
|
6189
|
+
return buildSignal(value, (atom) => atom.value);
|
|
6010
6190
|
}
|
|
6191
|
+
signal.invalidate = invalidateSignal;
|
|
6192
|
+
signal.Array = signalArray;
|
|
6193
|
+
signal.Map = signalMap;
|
|
6194
|
+
signal.Object = signalObject;
|
|
6195
|
+
signal.Set = signalSet;
|
|
6011
6196
|
|
|
6012
|
-
|
|
6013
|
-
|
|
6014
|
-
|
|
6015
|
-
|
|
6016
|
-
|
|
6017
|
-
|
|
6018
|
-
|
|
6019
|
-
|
|
6020
|
-
|
|
6021
|
-
|
|
6022
|
-
this.__owl__ = manager;
|
|
6197
|
+
function assertType(value, validation, errorMessage = "Value does not match the type") {
|
|
6198
|
+
const issues = validateType(value, validation);
|
|
6199
|
+
if (issues.length) {
|
|
6200
|
+
const issueStrings = JSON.stringify(issues, (key, value) => {
|
|
6201
|
+
if (typeof value === "function") {
|
|
6202
|
+
return value.name;
|
|
6203
|
+
}
|
|
6204
|
+
return value;
|
|
6205
|
+
}, 2);
|
|
6206
|
+
throw new OwlError(`${errorMessage}\n${issueStrings}`);
|
|
6023
6207
|
}
|
|
6024
|
-
setup() { }
|
|
6025
6208
|
}
|
|
6026
|
-
|
|
6027
|
-
|
|
6028
|
-
|
|
6029
|
-
|
|
6030
|
-
|
|
6031
|
-
|
|
6032
|
-
|
|
6033
|
-
|
|
6034
|
-
|
|
6035
|
-
|
|
6036
|
-
|
|
6037
|
-
|
|
6038
|
-
|
|
6039
|
-
|
|
6040
|
-
|
|
6041
|
-
|
|
6042
|
-
|
|
6209
|
+
function createContext(issues, value, path, parent) {
|
|
6210
|
+
return {
|
|
6211
|
+
issueDepth: 0,
|
|
6212
|
+
path,
|
|
6213
|
+
value,
|
|
6214
|
+
get isValid() {
|
|
6215
|
+
return !issues.length;
|
|
6216
|
+
},
|
|
6217
|
+
addIssue(issue) {
|
|
6218
|
+
issues.push({
|
|
6219
|
+
received: this.value,
|
|
6220
|
+
path: this.path,
|
|
6221
|
+
...issue,
|
|
6222
|
+
});
|
|
6223
|
+
},
|
|
6224
|
+
mergeIssues(newIssues) {
|
|
6225
|
+
issues.push(...newIssues);
|
|
6226
|
+
},
|
|
6227
|
+
validate(type) {
|
|
6228
|
+
type(this);
|
|
6229
|
+
if (!this.isValid && parent) {
|
|
6230
|
+
parent.issueDepth = this.issueDepth + 1;
|
|
6231
|
+
}
|
|
6232
|
+
},
|
|
6233
|
+
withIssues(issues) {
|
|
6234
|
+
return createContext(issues, this.value, this.path, this);
|
|
6235
|
+
},
|
|
6236
|
+
withKey(key) {
|
|
6237
|
+
return createContext(issues, this.value[key], this.path.concat(key), this);
|
|
6238
|
+
},
|
|
6239
|
+
};
|
|
6240
|
+
}
|
|
6241
|
+
function validateType(value, validation) {
|
|
6242
|
+
const issues = [];
|
|
6243
|
+
validation(createContext(issues, value, []));
|
|
6244
|
+
return issues;
|
|
6245
|
+
}
|
|
6246
|
+
|
|
6247
|
+
class Resource {
|
|
6248
|
+
_items = signal.Array([]);
|
|
6249
|
+
_name;
|
|
6250
|
+
_validation;
|
|
6251
|
+
constructor(options = {}) {
|
|
6252
|
+
this._name = options.name;
|
|
6253
|
+
this._validation = options.validation;
|
|
6043
6254
|
}
|
|
6044
|
-
|
|
6045
|
-
|
|
6046
|
-
|
|
6047
|
-
|
|
6255
|
+
items = computed(() => {
|
|
6256
|
+
return this._items()
|
|
6257
|
+
.sort((el1, el2) => el1[0] - el2[0])
|
|
6258
|
+
.map((elem) => elem[1]);
|
|
6259
|
+
});
|
|
6260
|
+
add(item, options = {}) {
|
|
6261
|
+
if (this._validation) {
|
|
6262
|
+
const info = this._name ? ` (resource '${this._name}')` : "";
|
|
6263
|
+
assertType(item, this._validation, `Resource item does not match the type${info}`);
|
|
6048
6264
|
}
|
|
6049
|
-
this.
|
|
6050
|
-
|
|
6051
|
-
getPluginById(id) {
|
|
6052
|
-
return this.plugins[id] || null;
|
|
6053
|
-
}
|
|
6054
|
-
getPlugin(pluginConstructor) {
|
|
6055
|
-
return this.getPluginById(pluginConstructor.id);
|
|
6265
|
+
this._items().push([options.sequence ?? 50, item]);
|
|
6266
|
+
return this;
|
|
6056
6267
|
}
|
|
6057
|
-
|
|
6058
|
-
|
|
6059
|
-
|
|
6060
|
-
|
|
6061
|
-
if (this.plugins.hasOwnProperty(pluginConstructor.id)) {
|
|
6062
|
-
const existingPluginType = this.getPluginById(pluginConstructor.id).constructor;
|
|
6063
|
-
if (existingPluginType !== pluginConstructor) {
|
|
6064
|
-
throw new OwlError(`Trying to start a plugin with the same id as an other plugin (id: '${pluginConstructor.id}', existing plugin: '${existingPluginType.name}', starting plugin: '${pluginConstructor.name}')`);
|
|
6065
|
-
}
|
|
6066
|
-
return null;
|
|
6067
|
-
}
|
|
6068
|
-
const plugin = new pluginConstructor(this);
|
|
6069
|
-
this.plugins[pluginConstructor.id] = plugin;
|
|
6070
|
-
plugin.setup();
|
|
6071
|
-
return plugin;
|
|
6268
|
+
delete(item) {
|
|
6269
|
+
const items = this._items().filter(([seq, val]) => val !== item);
|
|
6270
|
+
this._items.set(items);
|
|
6271
|
+
return this;
|
|
6072
6272
|
}
|
|
6073
|
-
|
|
6074
|
-
|
|
6075
|
-
type: "plugin",
|
|
6076
|
-
app: this.app,
|
|
6077
|
-
manager: this,
|
|
6078
|
-
get status() {
|
|
6079
|
-
return this.manager.status;
|
|
6080
|
-
},
|
|
6081
|
-
});
|
|
6082
|
-
try {
|
|
6083
|
-
for (const pluginConstructor of pluginConstructors) {
|
|
6084
|
-
this.startPlugin(pluginConstructor);
|
|
6085
|
-
}
|
|
6086
|
-
}
|
|
6087
|
-
finally {
|
|
6088
|
-
contextStack.pop();
|
|
6089
|
-
}
|
|
6090
|
-
this.status = 1 /* STATUS.MOUNTED */;
|
|
6273
|
+
has(item) {
|
|
6274
|
+
return this._items().some(([s, value]) => value === item);
|
|
6091
6275
|
}
|
|
6092
6276
|
}
|
|
6093
|
-
function
|
|
6094
|
-
|
|
6095
|
-
|
|
6096
|
-
}
|
|
6097
|
-
else {
|
|
6098
|
-
manager.onDestroyCb.push(effect(() => {
|
|
6099
|
-
const pluginItems = plugins.items();
|
|
6100
|
-
untrack(() => manager.startPlugins(pluginItems));
|
|
6101
|
-
}));
|
|
6277
|
+
function useResource(r, elements) {
|
|
6278
|
+
for (let elem of elements) {
|
|
6279
|
+
r.add(elem);
|
|
6102
6280
|
}
|
|
6281
|
+
onWillDestroy(() => {
|
|
6282
|
+
for (let elem of elements) {
|
|
6283
|
+
r.delete(elem);
|
|
6284
|
+
}
|
|
6285
|
+
});
|
|
6103
6286
|
}
|
|
6104
6287
|
|
|
6105
|
-
|
|
6106
|
-
|
|
6107
|
-
|
|
6108
|
-
|
|
6109
|
-
|
|
6110
|
-
|
|
6111
|
-
|
|
6112
|
-
tasks = new Set();
|
|
6113
|
-
requestAnimationFrame;
|
|
6114
|
-
frame = 0;
|
|
6115
|
-
delayedRenders = [];
|
|
6116
|
-
cancelledNodes = new Set();
|
|
6117
|
-
processing = false;
|
|
6118
|
-
constructor() {
|
|
6119
|
-
this.requestAnimationFrame = Scheduler.requestAnimationFrame;
|
|
6120
|
-
}
|
|
6121
|
-
addFiber(fiber) {
|
|
6122
|
-
this.tasks.add(fiber.root);
|
|
6288
|
+
class Registry {
|
|
6289
|
+
_map = signal.Object(Object.create(null));
|
|
6290
|
+
_name;
|
|
6291
|
+
_validation;
|
|
6292
|
+
constructor(options = {}) {
|
|
6293
|
+
this._name = options.name || "registry";
|
|
6294
|
+
this._validation = options.validation;
|
|
6123
6295
|
}
|
|
6124
|
-
|
|
6125
|
-
this.
|
|
6126
|
-
|
|
6127
|
-
|
|
6296
|
+
entries = computed(() => {
|
|
6297
|
+
const entries = Object.entries(this._map())
|
|
6298
|
+
.sort((el1, el2) => el1[1][0] - el2[1][0])
|
|
6299
|
+
.map(([str, elem]) => [str, elem[1]]);
|
|
6300
|
+
return entries;
|
|
6301
|
+
});
|
|
6302
|
+
items = computed(() => this.entries().map((e) => e[1]));
|
|
6303
|
+
addById(item, options = {}) {
|
|
6304
|
+
if (!item.id) {
|
|
6305
|
+
throw new OwlError(`Item should have an id key (registry '${this._name}')`);
|
|
6128
6306
|
}
|
|
6307
|
+
return this.add(item.id, item, { sequence: options.sequence ?? 50 });
|
|
6129
6308
|
}
|
|
6130
|
-
|
|
6131
|
-
|
|
6132
|
-
|
|
6133
|
-
|
|
6134
|
-
flush() {
|
|
6135
|
-
if (this.delayedRenders.length) {
|
|
6136
|
-
let renders = this.delayedRenders;
|
|
6137
|
-
this.delayedRenders = [];
|
|
6138
|
-
for (let f of renders) {
|
|
6139
|
-
if (f.root && f.node.status !== 3 /* STATUS.DESTROYED */ && f.node.fiber === f) {
|
|
6140
|
-
f.render();
|
|
6141
|
-
}
|
|
6142
|
-
}
|
|
6143
|
-
}
|
|
6144
|
-
if (this.frame === 0) {
|
|
6145
|
-
this.frame = this.requestAnimationFrame(() => this.processTasks());
|
|
6309
|
+
add(key, value, options = {}) {
|
|
6310
|
+
if (this._validation) {
|
|
6311
|
+
const info = this._name ? ` (registry '${this._name}', key: '${key}')` : ` (key: '${key}')`;
|
|
6312
|
+
assertType(value, this._validation, `Registry entry does not match the type${info}`);
|
|
6146
6313
|
}
|
|
6314
|
+
this._map()[key] = [options.sequence ?? 50, value];
|
|
6315
|
+
return this;
|
|
6147
6316
|
}
|
|
6148
|
-
|
|
6149
|
-
|
|
6150
|
-
|
|
6151
|
-
|
|
6152
|
-
this.processing = true;
|
|
6153
|
-
this.frame = 0;
|
|
6154
|
-
for (let node of this.cancelledNodes) {
|
|
6155
|
-
node._destroy();
|
|
6156
|
-
}
|
|
6157
|
-
this.cancelledNodes.clear();
|
|
6158
|
-
for (let task of this.tasks) {
|
|
6159
|
-
this.processFiber(task);
|
|
6160
|
-
}
|
|
6161
|
-
for (let task of this.tasks) {
|
|
6162
|
-
if (task.node.status === 3 /* STATUS.DESTROYED */) {
|
|
6163
|
-
this.tasks.delete(task);
|
|
6164
|
-
}
|
|
6317
|
+
get(key, defaultValue) {
|
|
6318
|
+
const hasKey = key in this._map();
|
|
6319
|
+
if (!hasKey && arguments.length < 2) {
|
|
6320
|
+
throw new Error(`KeyNotFoundError: Cannot find key "${key}" (registry '${this._name}')`);
|
|
6165
6321
|
}
|
|
6166
|
-
this.
|
|
6322
|
+
return hasKey ? this._map()[key][1] : defaultValue;
|
|
6167
6323
|
}
|
|
6168
|
-
|
|
6169
|
-
|
|
6170
|
-
|
|
6324
|
+
delete(key) {
|
|
6325
|
+
delete this._map()[key];
|
|
6326
|
+
}
|
|
6327
|
+
has(key) {
|
|
6328
|
+
return key in this._map();
|
|
6329
|
+
}
|
|
6330
|
+
}
|
|
6331
|
+
|
|
6332
|
+
const anyType = function validateAny() { };
|
|
6333
|
+
const booleanType = function validateBoolean(context) {
|
|
6334
|
+
if (typeof context.value !== "boolean") {
|
|
6335
|
+
context.addIssue({ message: "value is not a boolean" });
|
|
6336
|
+
}
|
|
6337
|
+
};
|
|
6338
|
+
const numberType = function validateNumber(context) {
|
|
6339
|
+
if (typeof context.value !== "number") {
|
|
6340
|
+
context.addIssue({ message: "value is not a number" });
|
|
6341
|
+
}
|
|
6342
|
+
};
|
|
6343
|
+
const stringType = function validateString(context) {
|
|
6344
|
+
if (typeof context.value !== "string") {
|
|
6345
|
+
context.addIssue({ message: "value is not a string" });
|
|
6346
|
+
}
|
|
6347
|
+
};
|
|
6348
|
+
function arrayType(elementType) {
|
|
6349
|
+
return function validateArray(context) {
|
|
6350
|
+
if (!Array.isArray(context.value)) {
|
|
6351
|
+
context.addIssue({ message: "value is not an array" });
|
|
6171
6352
|
return;
|
|
6172
6353
|
}
|
|
6173
|
-
|
|
6174
|
-
if (hasError && fiber.counter !== 0) {
|
|
6175
|
-
this.tasks.delete(fiber);
|
|
6354
|
+
if (!elementType) {
|
|
6176
6355
|
return;
|
|
6177
6356
|
}
|
|
6178
|
-
|
|
6179
|
-
|
|
6180
|
-
return;
|
|
6357
|
+
for (let index = 0; index < context.value.length; index++) {
|
|
6358
|
+
context.withKey(index).validate(elementType);
|
|
6181
6359
|
}
|
|
6182
|
-
|
|
6183
|
-
|
|
6184
|
-
|
|
6185
|
-
|
|
6186
|
-
|
|
6187
|
-
|
|
6188
|
-
|
|
6189
|
-
// the fiber, so in that case, we actually want to keep the fiber around,
|
|
6190
|
-
// otherwise it will just be ignored.
|
|
6191
|
-
if (fiber.appliedToDom) {
|
|
6192
|
-
this.tasks.delete(fiber);
|
|
6193
|
-
}
|
|
6360
|
+
};
|
|
6361
|
+
}
|
|
6362
|
+
function constructorType(constructor) {
|
|
6363
|
+
return function validateConstructor(context) {
|
|
6364
|
+
if (!(typeof context.value === "function") ||
|
|
6365
|
+
!(context.value === constructor || context.value.prototype instanceof constructor)) {
|
|
6366
|
+
context.addIssue({ message: `value is not '${constructor.name}' or an extension` });
|
|
6194
6367
|
}
|
|
6195
|
-
}
|
|
6368
|
+
};
|
|
6196
6369
|
}
|
|
6197
|
-
|
|
6198
|
-
|
|
6199
|
-
|
|
6200
|
-
|
|
6201
|
-
|
|
6202
|
-
static validateTarget = validateTarget;
|
|
6203
|
-
static apps = apps;
|
|
6204
|
-
static version = version;
|
|
6205
|
-
name;
|
|
6206
|
-
scheduler = new Scheduler();
|
|
6207
|
-
roots = new Set();
|
|
6208
|
-
pluginManager;
|
|
6209
|
-
constructor(config = {}) {
|
|
6210
|
-
super(config);
|
|
6211
|
-
this.name = config.name || "";
|
|
6212
|
-
apps.add(this);
|
|
6213
|
-
this.pluginManager = new PluginManager(this, { config: config.config });
|
|
6214
|
-
if (config.plugins) {
|
|
6215
|
-
startPlugins(this.pluginManager, config.plugins);
|
|
6370
|
+
function customValidator(type, validator, errorMessage = "value does not match custom validation") {
|
|
6371
|
+
return function validateCustom(context) {
|
|
6372
|
+
context.validate(type);
|
|
6373
|
+
if (!context.isValid) {
|
|
6374
|
+
return;
|
|
6216
6375
|
}
|
|
6217
|
-
if (
|
|
6218
|
-
|
|
6376
|
+
if (!validator(context.value)) {
|
|
6377
|
+
context.addIssue({ message: errorMessage });
|
|
6219
6378
|
}
|
|
6220
|
-
|
|
6221
|
-
|
|
6222
|
-
|
|
6379
|
+
};
|
|
6380
|
+
}
|
|
6381
|
+
function functionType(parameters = [], result = undefined) {
|
|
6382
|
+
return function validateFunction(context) {
|
|
6383
|
+
if (typeof context.value !== "function") {
|
|
6384
|
+
context.addIssue({ message: "value is not a function" });
|
|
6223
6385
|
}
|
|
6224
|
-
}
|
|
6225
|
-
|
|
6226
|
-
|
|
6227
|
-
|
|
6228
|
-
|
|
6229
|
-
|
|
6230
|
-
resolve = res;
|
|
6231
|
-
reject = rej;
|
|
6232
|
-
});
|
|
6233
|
-
const restore = saveContext();
|
|
6234
|
-
let node;
|
|
6235
|
-
let error = null;
|
|
6236
|
-
try {
|
|
6237
|
-
node = new ComponentNode(Root, props, this, null, null);
|
|
6386
|
+
};
|
|
6387
|
+
}
|
|
6388
|
+
function instanceType(constructor) {
|
|
6389
|
+
return function validateInstanceType(context) {
|
|
6390
|
+
if (!(context.value instanceof constructor)) {
|
|
6391
|
+
context.addIssue({ message: `value is not an instance of '${constructor.name}'` });
|
|
6238
6392
|
}
|
|
6239
|
-
|
|
6240
|
-
|
|
6241
|
-
|
|
6393
|
+
};
|
|
6394
|
+
}
|
|
6395
|
+
function intersection(types) {
|
|
6396
|
+
return function validateIntersection(context) {
|
|
6397
|
+
for (const type of types) {
|
|
6398
|
+
context.validate(type);
|
|
6242
6399
|
}
|
|
6243
|
-
|
|
6244
|
-
|
|
6400
|
+
};
|
|
6401
|
+
}
|
|
6402
|
+
function literalType(literal) {
|
|
6403
|
+
return function validateLiteral(context) {
|
|
6404
|
+
if (context.value !== literal) {
|
|
6405
|
+
context.addIssue({
|
|
6406
|
+
message: `value is not equal to ${typeof literal === "string" ? `'${literal}'` : literal}`,
|
|
6407
|
+
});
|
|
6245
6408
|
}
|
|
6246
|
-
|
|
6247
|
-
|
|
6248
|
-
|
|
6249
|
-
|
|
6250
|
-
|
|
6251
|
-
|
|
6252
|
-
|
|
6253
|
-
|
|
6254
|
-
|
|
6255
|
-
return promise;
|
|
6256
|
-
},
|
|
6257
|
-
destroy: () => {
|
|
6258
|
-
this.roots.delete(root);
|
|
6259
|
-
node.destroy();
|
|
6260
|
-
this.scheduler.processTasks();
|
|
6261
|
-
},
|
|
6262
|
-
};
|
|
6263
|
-
this.roots.add(root);
|
|
6264
|
-
return root;
|
|
6409
|
+
};
|
|
6410
|
+
}
|
|
6411
|
+
function literalSelection(literals) {
|
|
6412
|
+
return union(literals.map(literalType));
|
|
6413
|
+
}
|
|
6414
|
+
function validateObject(context, schema, isStrict) {
|
|
6415
|
+
if (typeof context.value !== "object" || Array.isArray(context.value) || context.value === null) {
|
|
6416
|
+
context.addIssue({ message: "value is not an object" });
|
|
6417
|
+
return;
|
|
6265
6418
|
}
|
|
6266
|
-
|
|
6267
|
-
|
|
6268
|
-
let handlers = nodeErrorHandlers.get(node);
|
|
6269
|
-
if (!handlers) {
|
|
6270
|
-
handlers = [];
|
|
6271
|
-
nodeErrorHandlers.set(node, handlers);
|
|
6272
|
-
}
|
|
6273
|
-
handlers.unshift((e, finalize) => {
|
|
6274
|
-
const finalError = finalize();
|
|
6275
|
-
reject(finalError);
|
|
6276
|
-
});
|
|
6277
|
-
// manually set a onMounted callback.
|
|
6278
|
-
// that way, we are independant from the current node.
|
|
6279
|
-
node.mounted.push(() => {
|
|
6280
|
-
resolve(node.component);
|
|
6281
|
-
handlers.shift();
|
|
6282
|
-
});
|
|
6283
|
-
node.mountComponent(target, options);
|
|
6419
|
+
if (!schema) {
|
|
6420
|
+
return;
|
|
6284
6421
|
}
|
|
6285
|
-
|
|
6286
|
-
|
|
6287
|
-
|
|
6422
|
+
const isShape = !Array.isArray(schema);
|
|
6423
|
+
let shape = schema;
|
|
6424
|
+
if (Array.isArray(schema)) {
|
|
6425
|
+
shape = {};
|
|
6426
|
+
for (const key of schema) {
|
|
6427
|
+
shape[key] = null;
|
|
6288
6428
|
}
|
|
6289
|
-
this.pluginManager.destroy();
|
|
6290
|
-
this.scheduler.processTasks();
|
|
6291
|
-
apps.delete(this);
|
|
6292
|
-
}
|
|
6293
|
-
handleError(...args) {
|
|
6294
|
-
return handleError(...args);
|
|
6295
6429
|
}
|
|
6296
|
-
|
|
6297
|
-
|
|
6298
|
-
|
|
6299
|
-
|
|
6300
|
-
|
|
6301
|
-
|
|
6302
|
-
|
|
6303
|
-
const mainEventHandler = (data, ev, currentTarget) => {
|
|
6304
|
-
const { data: _data, modifiers } = filterOutModifiersFromData(data);
|
|
6305
|
-
data = _data;
|
|
6306
|
-
let stopped = false;
|
|
6307
|
-
if (modifiers.length) {
|
|
6308
|
-
let selfMode = false;
|
|
6309
|
-
const isSelf = ev.target === currentTarget;
|
|
6310
|
-
for (const mod of modifiers) {
|
|
6311
|
-
switch (mod) {
|
|
6312
|
-
case "self":
|
|
6313
|
-
selfMode = true;
|
|
6314
|
-
if (isSelf) {
|
|
6315
|
-
continue;
|
|
6316
|
-
}
|
|
6317
|
-
else {
|
|
6318
|
-
return stopped;
|
|
6319
|
-
}
|
|
6320
|
-
case "prevent":
|
|
6321
|
-
if ((selfMode && isSelf) || !selfMode)
|
|
6322
|
-
ev.preventDefault();
|
|
6323
|
-
continue;
|
|
6324
|
-
case "stop":
|
|
6325
|
-
if ((selfMode && isSelf) || !selfMode)
|
|
6326
|
-
ev.stopPropagation();
|
|
6327
|
-
stopped = true;
|
|
6328
|
-
continue;
|
|
6430
|
+
const missingKeys = [];
|
|
6431
|
+
for (const key in shape) {
|
|
6432
|
+
const property = key.endsWith("?") ? key.slice(0, -1) : key;
|
|
6433
|
+
if (context.value[property] === undefined) {
|
|
6434
|
+
if (!key.endsWith("?")) {
|
|
6435
|
+
missingKeys.push(property);
|
|
6329
6436
|
}
|
|
6437
|
+
continue;
|
|
6330
6438
|
}
|
|
6331
|
-
|
|
6332
|
-
|
|
6333
|
-
// We check this rather than data[0] being truthy (or typeof function) so that it crashes
|
|
6334
|
-
// as expected when there is a handler expression that evaluates to a falsy value
|
|
6335
|
-
if (Object.hasOwnProperty.call(data, 0)) {
|
|
6336
|
-
const handler = data[0];
|
|
6337
|
-
if (typeof handler !== "function") {
|
|
6338
|
-
throw new OwlError(`Invalid handler (expected a function, received: '${handler}')`);
|
|
6339
|
-
}
|
|
6340
|
-
let node = data[1] ? data[1].__owl__ : null;
|
|
6341
|
-
if (node ? node.status === 1 /* STATUS.MOUNTED */ : true) {
|
|
6342
|
-
handler(data[1], ev);
|
|
6439
|
+
if (isShape) {
|
|
6440
|
+
context.withKey(property).validate(shape[key]);
|
|
6343
6441
|
}
|
|
6344
6442
|
}
|
|
6345
|
-
|
|
6346
|
-
|
|
6347
|
-
|
|
6348
|
-
|
|
6349
|
-
|
|
6350
|
-
|
|
6351
|
-
|
|
6352
|
-
|
|
6353
|
-
|
|
6354
|
-
|
|
6355
|
-
|
|
6443
|
+
if (missingKeys.length) {
|
|
6444
|
+
context.addIssue({
|
|
6445
|
+
message: "object value has missing keys",
|
|
6446
|
+
missingKeys,
|
|
6447
|
+
});
|
|
6448
|
+
}
|
|
6449
|
+
if (isStrict) {
|
|
6450
|
+
const unknownKeys = [];
|
|
6451
|
+
for (const key in context.value) {
|
|
6452
|
+
if (!(key in shape) && !(`${key}?` in shape)) {
|
|
6453
|
+
unknownKeys.push(key);
|
|
6454
|
+
}
|
|
6455
|
+
}
|
|
6456
|
+
if (unknownKeys.length) {
|
|
6457
|
+
context.addIssue({
|
|
6458
|
+
message: "object value has unknown keys",
|
|
6459
|
+
unknownKeys,
|
|
6460
|
+
});
|
|
6356
6461
|
}
|
|
6357
|
-
onReadAtom(computation);
|
|
6358
|
-
return computation.value;
|
|
6359
6462
|
}
|
|
6360
|
-
readComputed[atomSymbol] = computation;
|
|
6361
|
-
readComputed.set = options.set ?? (() => { });
|
|
6362
|
-
return readComputed;
|
|
6363
6463
|
}
|
|
6364
|
-
|
|
6365
|
-
function
|
|
6366
|
-
|
|
6367
|
-
type: "signal",
|
|
6368
|
-
value,
|
|
6369
|
-
observers: new Set(),
|
|
6464
|
+
function objectType(schema = {}) {
|
|
6465
|
+
return function validateLooseObject(context) {
|
|
6466
|
+
validateObject(context, schema, false);
|
|
6370
6467
|
};
|
|
6371
|
-
|
|
6372
|
-
|
|
6373
|
-
|
|
6374
|
-
|
|
6468
|
+
}
|
|
6469
|
+
function strictObjectType(schema) {
|
|
6470
|
+
return function validateStrictObject(context) {
|
|
6471
|
+
validateObject(context, schema, true);
|
|
6375
6472
|
};
|
|
6376
|
-
|
|
6377
|
-
|
|
6378
|
-
|
|
6379
|
-
|
|
6473
|
+
}
|
|
6474
|
+
function promiseType(type) {
|
|
6475
|
+
return function validatePromise(context) {
|
|
6476
|
+
if (!(context.value instanceof Promise)) {
|
|
6477
|
+
context.addIssue({ message: "value is not a promise" });
|
|
6380
6478
|
}
|
|
6381
|
-
atom.value = newValue;
|
|
6382
|
-
readValue = set(atom);
|
|
6383
|
-
onWriteAtom(atom);
|
|
6384
6479
|
};
|
|
6385
|
-
return readSignal;
|
|
6386
|
-
}
|
|
6387
|
-
function invalidateSignal(signal) {
|
|
6388
|
-
if (typeof signal !== "function" || signal[atomSymbol]?.type !== "signal") {
|
|
6389
|
-
throw new OwlError(`Value is not a signal (${signal})`);
|
|
6390
|
-
}
|
|
6391
|
-
onWriteAtom(signal[atomSymbol]);
|
|
6392
6480
|
}
|
|
6393
|
-
function
|
|
6394
|
-
return
|
|
6481
|
+
function recordType(valueType) {
|
|
6482
|
+
return function validateRecord(context) {
|
|
6483
|
+
if (typeof context.value !== "object" ||
|
|
6484
|
+
Array.isArray(context.value) ||
|
|
6485
|
+
context.value === null) {
|
|
6486
|
+
context.addIssue({ message: "value is not an object" });
|
|
6487
|
+
return;
|
|
6488
|
+
}
|
|
6489
|
+
if (!valueType) {
|
|
6490
|
+
return;
|
|
6491
|
+
}
|
|
6492
|
+
for (const key in context.value) {
|
|
6493
|
+
context.withKey(key).validate(valueType);
|
|
6494
|
+
}
|
|
6495
|
+
};
|
|
6395
6496
|
}
|
|
6396
|
-
function
|
|
6397
|
-
return
|
|
6497
|
+
function tuple(types) {
|
|
6498
|
+
return function validateTuple(context) {
|
|
6499
|
+
if (!Array.isArray(context.value)) {
|
|
6500
|
+
context.addIssue({ message: "value is not an array" });
|
|
6501
|
+
return;
|
|
6502
|
+
}
|
|
6503
|
+
if (context.value.length !== types.length) {
|
|
6504
|
+
context.addIssue({ message: "tuple value does not have the correct length" });
|
|
6505
|
+
return;
|
|
6506
|
+
}
|
|
6507
|
+
for (let index = 0; index < types.length; index++) {
|
|
6508
|
+
context.withKey(index).validate(types[index]);
|
|
6509
|
+
}
|
|
6510
|
+
};
|
|
6398
6511
|
}
|
|
6399
|
-
function
|
|
6400
|
-
return
|
|
6512
|
+
function union(types) {
|
|
6513
|
+
return function validateUnion(context) {
|
|
6514
|
+
let firstIssueIndex = 0;
|
|
6515
|
+
const subIssues = [];
|
|
6516
|
+
for (const type of types) {
|
|
6517
|
+
const subContext = context.withIssues(subIssues);
|
|
6518
|
+
subContext.validate(type);
|
|
6519
|
+
if (subIssues.length === firstIssueIndex || subContext.issueDepth > 0) {
|
|
6520
|
+
context.mergeIssues(subIssues.slice(firstIssueIndex));
|
|
6521
|
+
return;
|
|
6522
|
+
}
|
|
6523
|
+
firstIssueIndex = subIssues.length;
|
|
6524
|
+
}
|
|
6525
|
+
context.addIssue({
|
|
6526
|
+
message: "value does not match union type",
|
|
6527
|
+
subIssues,
|
|
6528
|
+
});
|
|
6529
|
+
};
|
|
6401
6530
|
}
|
|
6402
|
-
function
|
|
6403
|
-
return
|
|
6531
|
+
function reactiveValueType(type) {
|
|
6532
|
+
return function validateReactiveValue(context) {
|
|
6533
|
+
if (typeof context.value !== "function" || !context.value[atomSymbol]) {
|
|
6534
|
+
context.addIssue({ message: "value is not a reactive value" });
|
|
6535
|
+
}
|
|
6536
|
+
};
|
|
6404
6537
|
}
|
|
6405
|
-
function
|
|
6406
|
-
return
|
|
6538
|
+
function ref(type) {
|
|
6539
|
+
return union([literalType(null), instanceType(type)]);
|
|
6407
6540
|
}
|
|
6408
|
-
|
|
6409
|
-
|
|
6410
|
-
|
|
6411
|
-
|
|
6412
|
-
|
|
6541
|
+
const types = {
|
|
6542
|
+
and: intersection,
|
|
6543
|
+
any: anyType,
|
|
6544
|
+
array: arrayType,
|
|
6545
|
+
boolean: booleanType,
|
|
6546
|
+
constructor: constructorType,
|
|
6547
|
+
customValidator: customValidator,
|
|
6548
|
+
function: functionType,
|
|
6549
|
+
instanceOf: instanceType,
|
|
6550
|
+
literal: literalType,
|
|
6551
|
+
number: numberType,
|
|
6552
|
+
object: objectType,
|
|
6553
|
+
or: union,
|
|
6554
|
+
promise: promiseType,
|
|
6555
|
+
record: recordType,
|
|
6556
|
+
ref,
|
|
6557
|
+
selection: literalSelection,
|
|
6558
|
+
signal: reactiveValueType,
|
|
6559
|
+
strictObject: strictObjectType,
|
|
6560
|
+
string: stringType,
|
|
6561
|
+
tuple: tuple,
|
|
6562
|
+
};
|
|
6413
6563
|
|
|
6414
|
-
|
|
6415
|
-
|
|
6416
|
-
|
|
6417
|
-
|
|
6418
|
-
|
|
6419
|
-
|
|
6420
|
-
|
|
6421
|
-
|
|
6422
|
-
|
|
6423
|
-
return this._items()
|
|
6424
|
-
.sort((el1, el2) => el1[0] - el2[0])
|
|
6425
|
-
.map((elem) => elem[1]);
|
|
6426
|
-
});
|
|
6427
|
-
add(item, options = {}) {
|
|
6428
|
-
if (this._validation) {
|
|
6429
|
-
const info = this._name ? ` (resource '${this._name}')` : "";
|
|
6430
|
-
assertType(item, this._validation, `Resource item does not match the type${info}`);
|
|
6431
|
-
}
|
|
6432
|
-
this._items().push([options.sequence ?? 50, item]);
|
|
6433
|
-
return this;
|
|
6434
|
-
}
|
|
6435
|
-
delete(item) {
|
|
6436
|
-
const items = this._items().filter(([seq, val]) => val !== item);
|
|
6437
|
-
this._items.set(items);
|
|
6438
|
-
return this;
|
|
6439
|
-
}
|
|
6440
|
-
has(item) {
|
|
6441
|
-
return this._items().some(([s, value]) => value === item);
|
|
6442
|
-
}
|
|
6443
|
-
}
|
|
6444
|
-
function useResource(r, elements) {
|
|
6445
|
-
for (let elem of elements) {
|
|
6446
|
-
r.add(elem);
|
|
6447
|
-
}
|
|
6448
|
-
onWillDestroy(() => {
|
|
6449
|
-
for (let elem of elements) {
|
|
6450
|
-
r.delete(elem);
|
|
6564
|
+
function validateObjectWithDefaults(schema, defaultValues) {
|
|
6565
|
+
const keys = Array.isArray(schema) ? schema : Object.keys(schema);
|
|
6566
|
+
const mandatoryDefaultedKeys = keys.filter((key) => !key.endsWith("?") && key in defaultValues);
|
|
6567
|
+
return (context) => {
|
|
6568
|
+
if (mandatoryDefaultedKeys.length) {
|
|
6569
|
+
context.addIssue({
|
|
6570
|
+
message: "props have default values on mandatory keys",
|
|
6571
|
+
keys: mandatoryDefaultedKeys,
|
|
6572
|
+
});
|
|
6451
6573
|
}
|
|
6452
|
-
|
|
6574
|
+
context.validate(types.object(schema));
|
|
6575
|
+
};
|
|
6453
6576
|
}
|
|
6454
|
-
|
|
6455
|
-
|
|
6456
|
-
|
|
6457
|
-
|
|
6458
|
-
|
|
6459
|
-
|
|
6460
|
-
this._name = options.name || "registry";
|
|
6461
|
-
this._validation = options.validation;
|
|
6462
|
-
}
|
|
6463
|
-
entries = computed(() => {
|
|
6464
|
-
const entries = Object.entries(this._map())
|
|
6465
|
-
.sort((el1, el2) => el1[1][0] - el2[1][0])
|
|
6466
|
-
.map(([str, elem]) => [str, elem[1]]);
|
|
6467
|
-
return entries;
|
|
6468
|
-
});
|
|
6469
|
-
items = computed(() => this.entries().map((e) => e[1]));
|
|
6470
|
-
addById(item, options = {}) {
|
|
6471
|
-
if (!item.id) {
|
|
6472
|
-
throw new OwlError(`Item should have an id key (registry '${this._name}')`);
|
|
6577
|
+
function props(type, defaults) {
|
|
6578
|
+
const { node, app, componentName } = getContext("component");
|
|
6579
|
+
Object.assign(node.defaultProps, defaults);
|
|
6580
|
+
function getProp(key) {
|
|
6581
|
+
if (node.props[key] === undefined && defaults) {
|
|
6582
|
+
return defaults[key];
|
|
6473
6583
|
}
|
|
6474
|
-
return
|
|
6584
|
+
return node.props[key];
|
|
6475
6585
|
}
|
|
6476
|
-
|
|
6477
|
-
|
|
6478
|
-
|
|
6479
|
-
|
|
6586
|
+
const result = Object.create(null);
|
|
6587
|
+
function applyPropGetters(keys) {
|
|
6588
|
+
for (const key of keys) {
|
|
6589
|
+
Reflect.defineProperty(result, key, {
|
|
6590
|
+
enumerable: true,
|
|
6591
|
+
get: getProp.bind(null, key),
|
|
6592
|
+
});
|
|
6480
6593
|
}
|
|
6481
|
-
this._map()[key] = [options.sequence ?? 50, value];
|
|
6482
|
-
return this;
|
|
6483
6594
|
}
|
|
6484
|
-
|
|
6485
|
-
const
|
|
6486
|
-
|
|
6487
|
-
|
|
6595
|
+
if (type) {
|
|
6596
|
+
const keys = (Array.isArray(type) ? type : Object.keys(type)).map((key) => key.endsWith("?") ? key.slice(0, -1) : key);
|
|
6597
|
+
applyPropGetters(keys);
|
|
6598
|
+
if (app.dev) {
|
|
6599
|
+
const validation = defaults ? validateObjectWithDefaults(type, defaults) : types.object(type);
|
|
6600
|
+
assertType(node.props, validation, `Invalid component props (${componentName})`);
|
|
6601
|
+
node.willUpdateProps.push((np) => {
|
|
6602
|
+
assertType(np, validation, `Invalid component props (${componentName})`);
|
|
6603
|
+
});
|
|
6488
6604
|
}
|
|
6489
|
-
return hasKey ? this._map()[key][1] : defaultValue;
|
|
6490
|
-
}
|
|
6491
|
-
delete(key) {
|
|
6492
|
-
delete this._map()[key];
|
|
6493
6605
|
}
|
|
6494
|
-
|
|
6495
|
-
|
|
6606
|
+
else {
|
|
6607
|
+
const getKeys = (props) => {
|
|
6608
|
+
const keys = [];
|
|
6609
|
+
for (const k in props) {
|
|
6610
|
+
if (k.charCodeAt(0) !== 1) {
|
|
6611
|
+
keys.push(k);
|
|
6612
|
+
}
|
|
6613
|
+
}
|
|
6614
|
+
if (defaults) {
|
|
6615
|
+
for (const k in defaults) {
|
|
6616
|
+
if (!(k in props)) {
|
|
6617
|
+
keys.push(k);
|
|
6618
|
+
}
|
|
6619
|
+
}
|
|
6620
|
+
}
|
|
6621
|
+
return keys;
|
|
6622
|
+
};
|
|
6623
|
+
let keys = getKeys(node.props);
|
|
6624
|
+
applyPropGetters(keys);
|
|
6625
|
+
node.willUpdateProps.push((np) => {
|
|
6626
|
+
for (const key of keys) {
|
|
6627
|
+
Reflect.deleteProperty(result, key);
|
|
6628
|
+
}
|
|
6629
|
+
keys = getKeys(np);
|
|
6630
|
+
applyPropGetters(keys);
|
|
6631
|
+
});
|
|
6496
6632
|
}
|
|
6633
|
+
return result;
|
|
6497
6634
|
}
|
|
6498
6635
|
|
|
6499
6636
|
function status(entity) {
|
|
@@ -6632,6 +6769,6 @@ TemplateSet.prototype._compileTemplate = function _compileTemplate(name, templat
|
|
|
6632
6769
|
export { App, Component, EventBus, OwlError, Plugin, Registry, Resource, __info__, assertType, batched, blockDom, computed, config, effect, htmlEscape, markRaw, markup, mount, onError, onMounted, onPatched, onWillDestroy, onWillPatch, onWillStart, onWillUnmount, onWillUpdateProps, plugin, props, providePlugins, proxy, signal, status, toRaw, types, untrack, useApp, useContext, useEffect, useListener, useResource, validateType, whenReady, xml };
|
|
6633
6770
|
|
|
6634
6771
|
|
|
6635
|
-
__info__.date = '2026-
|
|
6636
|
-
__info__.hash = '
|
|
6772
|
+
__info__.date = '2026-04-02T11:14:58.432Z';
|
|
6773
|
+
__info__.hash = '9ca35d8';
|
|
6637
6774
|
__info__.url = 'https://github.com/odoo/owl';
|