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