@odoo/owl 2.8.1 → 3.0.0-alpha.10
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/compile_templates.mjs +92 -189
- package/dist/compiler.js +2378 -0
- package/dist/owl-devtools.zip +0 -0
- package/dist/owl.cjs.js +1371 -1261
- package/dist/owl.cjs.runtime.js +4094 -0
- package/dist/owl.es.js +1358 -1252
- package/dist/owl.es.runtime.js +4050 -0
- package/dist/owl.iife.js +1371 -1261
- package/dist/owl.iife.min.js +1 -1
- package/dist/owl.iife.runtime.js +4098 -0
- package/dist/owl.iife.runtime.min.js +1 -0
- package/dist/types/compiler/code_generator.d.ts +3 -5
- package/dist/types/compiler/index.d.ts +4 -4
- package/dist/types/compiler/inline_expressions.d.ts +1 -1
- package/dist/types/compiler/parser.d.ts +21 -28
- package/dist/types/owl.d.ts +299 -205
- package/dist/types/runtime/app.d.ts +29 -31
- package/dist/types/runtime/blockdom/block_compiler.d.ts +3 -3
- package/dist/types/runtime/blockdom/config.d.ts +1 -1
- package/dist/types/runtime/blockdom/event_catcher.d.ts +2 -2
- package/dist/types/runtime/blockdom/events.d.ts +1 -1
- package/dist/types/runtime/blockdom/index.d.ts +1 -1
- package/dist/types/runtime/cancellableContext.d.ts +15 -0
- package/dist/types/runtime/cancellablePromise.d.ts +15 -0
- package/dist/types/runtime/component.d.ts +5 -13
- package/dist/types/runtime/component_node.d.ts +15 -35
- package/dist/types/runtime/event_handling.d.ts +1 -1
- package/dist/types/runtime/executionContext.d.ts +0 -0
- package/dist/types/runtime/hooks.d.ts +7 -33
- package/dist/types/runtime/index.d.ts +15 -5
- package/dist/types/runtime/lifecycle_hooks.d.ts +1 -3
- package/dist/types/runtime/listOperation.d.ts +1 -0
- package/dist/types/runtime/plugins.d.ts +23 -0
- package/dist/types/runtime/portal.d.ts +4 -6
- package/dist/types/runtime/props.d.ts +65 -0
- package/dist/types/runtime/reactivity/computations.d.ts +31 -0
- package/dist/types/runtime/reactivity/computed.d.ts +7 -0
- package/dist/types/runtime/reactivity/derived.d.ts +7 -0
- package/dist/types/runtime/reactivity/effect.d.ts +2 -0
- package/dist/types/runtime/reactivity/proxy.d.ts +46 -0
- package/dist/types/runtime/reactivity/reactivity.d.ts +46 -0
- package/dist/types/runtime/reactivity/signal.d.ts +17 -0
- package/dist/types/runtime/reactivity/signals.d.ts +30 -0
- package/dist/types/runtime/registry.d.ts +19 -0
- package/dist/types/runtime/relationalModel/discussModel.d.ts +19 -0
- package/dist/types/runtime/relationalModel/discussModelTypes.d.ts +22 -0
- package/dist/types/runtime/relationalModel/field.d.ts +20 -0
- package/dist/types/runtime/relationalModel/model.d.ts +59 -0
- package/dist/types/runtime/relationalModel/modelData.d.ts +18 -0
- package/dist/types/runtime/relationalModel/modelRegistry.d.ts +3 -0
- package/dist/types/runtime/relationalModel/modelUtils.d.ts +4 -0
- package/dist/types/runtime/relationalModel/store.d.ts +16 -0
- package/dist/types/runtime/relationalModel/types.d.ts +83 -0
- package/dist/types/runtime/relationalModel/util.d.ts +1 -0
- package/dist/types/runtime/relationalModel/web/WebDataPoint.d.ts +25 -0
- package/dist/types/runtime/relationalModel/web/WebRecord.d.ts +131 -0
- package/dist/types/runtime/relationalModel/web/WebStaticList.d.ts +63 -0
- package/dist/types/runtime/relationalModel/web/webModel.d.ts +5 -0
- package/dist/types/runtime/relationalModel/web/webModelTypes.d.ts +139 -0
- package/dist/types/runtime/rendering/error_handling.d.ts +13 -0
- package/dist/types/runtime/rendering/fibers.d.ts +37 -0
- package/dist/types/runtime/rendering/scheduler.d.ts +21 -0
- package/dist/types/runtime/rendering/template_helpers.d.ts +50 -0
- package/dist/types/runtime/resource.d.ts +12 -0
- package/dist/types/runtime/signals.d.ts +19 -0
- package/dist/types/runtime/status.d.ts +2 -3
- package/dist/types/runtime/task.d.ts +12 -0
- package/dist/types/runtime/template_set.d.ts +3 -4
- package/dist/types/runtime/utils.d.ts +1 -2
- package/dist/types/runtime/validation.d.ts +6 -6
- package/dist/types/utils/registry.d.ts +15 -0
- package/dist/types/version.d.ts +1 -1
- package/package.json +9 -9
package/dist/owl.cjs.js
CHANGED
|
@@ -33,6 +33,7 @@ const config = {
|
|
|
33
33
|
// -----------------------------------------------------------------------------
|
|
34
34
|
// Toggler node
|
|
35
35
|
// -----------------------------------------------------------------------------
|
|
36
|
+
const txt = document.createTextNode("");
|
|
36
37
|
class VToggler {
|
|
37
38
|
constructor(key, child) {
|
|
38
39
|
this.key = key;
|
|
@@ -58,11 +59,13 @@ class VToggler {
|
|
|
58
59
|
child1.patch(child2, withBeforeRemove);
|
|
59
60
|
}
|
|
60
61
|
else {
|
|
61
|
-
|
|
62
|
+
const firstNode = child1.firstNode();
|
|
63
|
+
firstNode.parentElement.insertBefore(txt, firstNode);
|
|
62
64
|
if (withBeforeRemove) {
|
|
63
65
|
child1.beforeRemove();
|
|
64
66
|
}
|
|
65
67
|
child1.remove();
|
|
68
|
+
child2.mount(this.parentEl, txt);
|
|
66
69
|
this.child = child2;
|
|
67
70
|
this.key = other.key;
|
|
68
71
|
}
|
|
@@ -335,13 +338,6 @@ function whenReady(fn) {
|
|
|
335
338
|
}
|
|
336
339
|
}).then(fn || function () { });
|
|
337
340
|
}
|
|
338
|
-
async function loadFile(url) {
|
|
339
|
-
const result = await fetch(url);
|
|
340
|
-
if (!result.ok) {
|
|
341
|
-
throw new OwlError("Error while fetching xml templates");
|
|
342
|
-
}
|
|
343
|
-
return await result.text();
|
|
344
|
-
}
|
|
345
341
|
/*
|
|
346
342
|
* This class just transports the fact that a string is safe
|
|
347
343
|
* to be injected as HTML. Overriding a JS primitive is quite painful though
|
|
@@ -902,7 +898,7 @@ function parentTree(tree) {
|
|
|
902
898
|
function buildContext(tree, ctx, fromIdx) {
|
|
903
899
|
if (!ctx) {
|
|
904
900
|
const children = new Array(tree.info.filter((v) => v.type === "child").length);
|
|
905
|
-
ctx = { collectors: [], locations: [], children, cbRefs: [], refN: tree.refN
|
|
901
|
+
ctx = { collectors: [], locations: [], children, cbRefs: [], refN: tree.refN };
|
|
906
902
|
fromIdx = 0;
|
|
907
903
|
}
|
|
908
904
|
if (tree.refN) {
|
|
@@ -1009,14 +1005,16 @@ function updateCtx(ctx, tree) {
|
|
|
1009
1005
|
});
|
|
1010
1006
|
break;
|
|
1011
1007
|
}
|
|
1012
|
-
case "ref":
|
|
1013
|
-
const index = ctx.cbRefs.push(info.idx) - 1;
|
|
1008
|
+
case "ref": {
|
|
1014
1009
|
ctx.locations.push({
|
|
1015
1010
|
idx: info.idx,
|
|
1016
1011
|
refIdx: info.refIdx,
|
|
1017
|
-
setData:
|
|
1012
|
+
setData: NO_OP,
|
|
1018
1013
|
updateData: NO_OP,
|
|
1019
1014
|
});
|
|
1015
|
+
ctx.cbRefs.push(info.idx);
|
|
1016
|
+
break;
|
|
1017
|
+
}
|
|
1020
1018
|
}
|
|
1021
1019
|
}
|
|
1022
1020
|
}
|
|
@@ -1025,27 +1023,6 @@ function updateCtx(ctx, tree) {
|
|
|
1025
1023
|
// -----------------------------------------------------------------------------
|
|
1026
1024
|
function buildBlock(template, ctx) {
|
|
1027
1025
|
let B = createBlockClass(template, ctx);
|
|
1028
|
-
if (ctx.cbRefs.length) {
|
|
1029
|
-
const cbRefs = ctx.cbRefs;
|
|
1030
|
-
const refList = ctx.refList;
|
|
1031
|
-
let cbRefsNumber = cbRefs.length;
|
|
1032
|
-
B = class extends B {
|
|
1033
|
-
mount(parent, afterNode) {
|
|
1034
|
-
refList.push(new Array(cbRefsNumber));
|
|
1035
|
-
super.mount(parent, afterNode);
|
|
1036
|
-
for (let cbRef of refList.pop()) {
|
|
1037
|
-
cbRef();
|
|
1038
|
-
}
|
|
1039
|
-
}
|
|
1040
|
-
remove() {
|
|
1041
|
-
super.remove();
|
|
1042
|
-
for (let cbRef of cbRefs) {
|
|
1043
|
-
let fn = this.data[cbRef];
|
|
1044
|
-
fn(null);
|
|
1045
|
-
}
|
|
1046
|
-
}
|
|
1047
|
-
};
|
|
1048
|
-
}
|
|
1049
1026
|
if (ctx.children.length) {
|
|
1050
1027
|
B = class extends B {
|
|
1051
1028
|
constructor(data, children) {
|
|
@@ -1059,14 +1036,9 @@ function buildBlock(template, ctx) {
|
|
|
1059
1036
|
return (data) => new B(data);
|
|
1060
1037
|
}
|
|
1061
1038
|
function createBlockClass(template, ctx) {
|
|
1062
|
-
const { refN, collectors, children } = ctx;
|
|
1039
|
+
const { refN, collectors, children, locations, cbRefs } = ctx;
|
|
1063
1040
|
const colN = collectors.length;
|
|
1064
|
-
|
|
1065
|
-
const locations = ctx.locations.map((loc) => ({
|
|
1066
|
-
refIdx: loc.refIdx,
|
|
1067
|
-
setData: loc.setData,
|
|
1068
|
-
updateData: loc.updateData,
|
|
1069
|
-
}));
|
|
1041
|
+
locations.sort((a, b) => a.idx - b.idx);
|
|
1070
1042
|
const locN = locations.length;
|
|
1071
1043
|
const childN = children.length;
|
|
1072
1044
|
const childrenLocs = children;
|
|
@@ -1142,6 +1114,15 @@ function createBlockClass(template, ctx) {
|
|
|
1142
1114
|
}
|
|
1143
1115
|
this.el = el;
|
|
1144
1116
|
this.parentEl = parent;
|
|
1117
|
+
if (cbRefs.length) {
|
|
1118
|
+
const data = this.data;
|
|
1119
|
+
const refs = this.refs;
|
|
1120
|
+
for (let cbRef of cbRefs) {
|
|
1121
|
+
const { idx, refIdx } = locations[cbRef];
|
|
1122
|
+
const fn = data[idx];
|
|
1123
|
+
fn(refs[refIdx], null);
|
|
1124
|
+
}
|
|
1125
|
+
}
|
|
1145
1126
|
};
|
|
1146
1127
|
Block.prototype.patch = function patch(other, withBeforeRemove) {
|
|
1147
1128
|
if (this === other) {
|
|
@@ -1190,16 +1171,23 @@ function createBlockClass(template, ctx) {
|
|
|
1190
1171
|
}
|
|
1191
1172
|
}
|
|
1192
1173
|
};
|
|
1174
|
+
Block.prototype.remove = function remove() {
|
|
1175
|
+
if (cbRefs.length) {
|
|
1176
|
+
const data = this.data;
|
|
1177
|
+
const refs = this.refs;
|
|
1178
|
+
for (let cbRef of cbRefs) {
|
|
1179
|
+
const { idx, refIdx } = locations[cbRef];
|
|
1180
|
+
const fn = data[idx];
|
|
1181
|
+
fn(null, refs[refIdx]);
|
|
1182
|
+
}
|
|
1183
|
+
}
|
|
1184
|
+
elementRemove.call(this.el);
|
|
1185
|
+
};
|
|
1193
1186
|
}
|
|
1194
1187
|
return Block;
|
|
1195
1188
|
}
|
|
1196
1189
|
function setText(value) {
|
|
1197
1190
|
characterDataSetData.call(this, toText(value));
|
|
1198
|
-
}
|
|
1199
|
-
function makeRefSetter(index, refs) {
|
|
1200
|
-
return function setRef(fn) {
|
|
1201
|
-
refs[refs.length - 1][index] = () => fn(this);
|
|
1202
|
-
};
|
|
1203
1191
|
}
|
|
1204
1192
|
|
|
1205
1193
|
const getDescriptor = (o, p) => Object.getOwnPropertyDescriptor(o, p);
|
|
@@ -1596,9 +1584,160 @@ function remove(vnode, withBeforeRemove = false) {
|
|
|
1596
1584
|
vnode.remove();
|
|
1597
1585
|
}
|
|
1598
1586
|
|
|
1587
|
+
var ComputationState;
|
|
1588
|
+
(function (ComputationState) {
|
|
1589
|
+
ComputationState[ComputationState["EXECUTED"] = 0] = "EXECUTED";
|
|
1590
|
+
ComputationState[ComputationState["STALE"] = 1] = "STALE";
|
|
1591
|
+
ComputationState[ComputationState["PENDING"] = 2] = "PENDING";
|
|
1592
|
+
})(ComputationState || (ComputationState = {}));
|
|
1593
|
+
let Effects;
|
|
1594
|
+
let CurrentComputation;
|
|
1595
|
+
// export function computed<T>(fn: () => T, opts?: Opts) {
|
|
1596
|
+
// // todo: handle cleanup
|
|
1597
|
+
// let computedComputation: Computation = {
|
|
1598
|
+
// state: ComputationState.STALE,
|
|
1599
|
+
// sources: new Set(),
|
|
1600
|
+
// isEager: true,
|
|
1601
|
+
// compute: () => {
|
|
1602
|
+
// return fn();
|
|
1603
|
+
// },
|
|
1604
|
+
// value: undefined,
|
|
1605
|
+
// name: opts?.name,
|
|
1606
|
+
// };
|
|
1607
|
+
// updateComputation(computedComputation);
|
|
1608
|
+
// }
|
|
1609
|
+
function onReadAtom(atom) {
|
|
1610
|
+
if (!CurrentComputation)
|
|
1611
|
+
return;
|
|
1612
|
+
CurrentComputation.sources.add(atom);
|
|
1613
|
+
atom.observers.add(CurrentComputation);
|
|
1614
|
+
}
|
|
1615
|
+
function onWriteAtom(atom) {
|
|
1616
|
+
collectEffects(() => {
|
|
1617
|
+
for (const ctx of atom.observers) {
|
|
1618
|
+
if (ctx.state === ComputationState.EXECUTED) {
|
|
1619
|
+
if (ctx.isDerived)
|
|
1620
|
+
markDownstream(ctx);
|
|
1621
|
+
else
|
|
1622
|
+
Effects.push(ctx);
|
|
1623
|
+
}
|
|
1624
|
+
ctx.state = ComputationState.STALE;
|
|
1625
|
+
}
|
|
1626
|
+
});
|
|
1627
|
+
batchProcessEffects();
|
|
1628
|
+
}
|
|
1629
|
+
function collectEffects(fn) {
|
|
1630
|
+
if (Effects)
|
|
1631
|
+
return fn();
|
|
1632
|
+
Effects = [];
|
|
1633
|
+
try {
|
|
1634
|
+
return fn();
|
|
1635
|
+
}
|
|
1636
|
+
finally {
|
|
1637
|
+
}
|
|
1638
|
+
}
|
|
1639
|
+
const batchProcessEffects = batched(processEffects);
|
|
1640
|
+
function processEffects() {
|
|
1641
|
+
if (!Effects)
|
|
1642
|
+
return;
|
|
1643
|
+
for (const computation of Effects) {
|
|
1644
|
+
updateComputation(computation);
|
|
1645
|
+
}
|
|
1646
|
+
Effects = undefined;
|
|
1647
|
+
}
|
|
1648
|
+
function untrack(fn) {
|
|
1649
|
+
return runWithComputation(undefined, fn);
|
|
1650
|
+
}
|
|
1651
|
+
function getCurrentComputation() {
|
|
1652
|
+
return CurrentComputation;
|
|
1653
|
+
}
|
|
1654
|
+
function setComputation(computation) {
|
|
1655
|
+
CurrentComputation = computation;
|
|
1656
|
+
}
|
|
1657
|
+
// todo: should probably use updateComputation instead.
|
|
1658
|
+
function runWithComputation(computation, fn) {
|
|
1659
|
+
const previousComputation = CurrentComputation;
|
|
1660
|
+
CurrentComputation = computation;
|
|
1661
|
+
let result;
|
|
1662
|
+
try {
|
|
1663
|
+
result = fn();
|
|
1664
|
+
}
|
|
1665
|
+
finally {
|
|
1666
|
+
CurrentComputation = previousComputation;
|
|
1667
|
+
}
|
|
1668
|
+
return result;
|
|
1669
|
+
}
|
|
1670
|
+
function updateComputation(computation) {
|
|
1671
|
+
var _a;
|
|
1672
|
+
const state = computation.state;
|
|
1673
|
+
if (computation.isDerived)
|
|
1674
|
+
onReadAtom(computation);
|
|
1675
|
+
if (state === ComputationState.EXECUTED)
|
|
1676
|
+
return;
|
|
1677
|
+
if (state === ComputationState.PENDING) {
|
|
1678
|
+
computeSources(computation);
|
|
1679
|
+
// If the state is still not stale after processing the sources, it means
|
|
1680
|
+
// none of the dependencies have changed.
|
|
1681
|
+
// todo: test it
|
|
1682
|
+
if (computation.state !== ComputationState.STALE) {
|
|
1683
|
+
computation.state = ComputationState.EXECUTED;
|
|
1684
|
+
return;
|
|
1685
|
+
}
|
|
1686
|
+
}
|
|
1687
|
+
// todo: test performance. We might want to avoid removing the atoms to
|
|
1688
|
+
// directly re-add them at compute. Especially as we are making them stale.
|
|
1689
|
+
removeSources(computation);
|
|
1690
|
+
const previousComputation = CurrentComputation;
|
|
1691
|
+
CurrentComputation = computation;
|
|
1692
|
+
computation.value = (_a = computation.compute) === null || _a === void 0 ? void 0 : _a.call(computation);
|
|
1693
|
+
computation.state = ComputationState.EXECUTED;
|
|
1694
|
+
CurrentComputation = previousComputation;
|
|
1695
|
+
}
|
|
1696
|
+
function removeSources(computation) {
|
|
1697
|
+
const sources = computation.sources;
|
|
1698
|
+
for (const source of sources) {
|
|
1699
|
+
const observers = source.observers;
|
|
1700
|
+
observers.delete(computation);
|
|
1701
|
+
// todo: if source has no effect observer anymore, remove its sources too
|
|
1702
|
+
// todo: test it
|
|
1703
|
+
}
|
|
1704
|
+
sources.clear();
|
|
1705
|
+
}
|
|
1706
|
+
function markDownstream(derived) {
|
|
1707
|
+
for (const observer of derived.observers) {
|
|
1708
|
+
// if the state has already been marked, skip it
|
|
1709
|
+
if (observer.state)
|
|
1710
|
+
continue;
|
|
1711
|
+
observer.state = ComputationState.PENDING;
|
|
1712
|
+
if (observer.isDerived)
|
|
1713
|
+
markDownstream(observer);
|
|
1714
|
+
else
|
|
1715
|
+
Effects.push(observer);
|
|
1716
|
+
}
|
|
1717
|
+
}
|
|
1718
|
+
function computeSources(derived) {
|
|
1719
|
+
for (const source of derived.sources) {
|
|
1720
|
+
if (!("compute" in source))
|
|
1721
|
+
continue;
|
|
1722
|
+
updateComputation(source);
|
|
1723
|
+
}
|
|
1724
|
+
}
|
|
1725
|
+
|
|
1599
1726
|
// Maps fibers to thrown errors
|
|
1600
1727
|
const fibersInError = new WeakMap();
|
|
1601
1728
|
const nodeErrorHandlers = new WeakMap();
|
|
1729
|
+
function destroyApp(app, error) {
|
|
1730
|
+
try {
|
|
1731
|
+
app.destroy();
|
|
1732
|
+
}
|
|
1733
|
+
catch (e) {
|
|
1734
|
+
// mute all errors here because we are in a corrupted state anyway
|
|
1735
|
+
}
|
|
1736
|
+
const e = Object.assign(new OwlError(`[Owl] Unhandled error. Destroying the root component`), {
|
|
1737
|
+
cause: error,
|
|
1738
|
+
});
|
|
1739
|
+
return e;
|
|
1740
|
+
}
|
|
1602
1741
|
function _handleError(node, error) {
|
|
1603
1742
|
if (!node) {
|
|
1604
1743
|
return false;
|
|
@@ -1611,9 +1750,10 @@ function _handleError(node, error) {
|
|
|
1611
1750
|
if (errorHandlers) {
|
|
1612
1751
|
let handled = false;
|
|
1613
1752
|
// execute in the opposite order
|
|
1753
|
+
const finalize = () => destroyApp(node.app, error);
|
|
1614
1754
|
for (let i = errorHandlers.length - 1; i >= 0; i--) {
|
|
1615
1755
|
try {
|
|
1616
|
-
errorHandlers[i](error);
|
|
1756
|
+
errorHandlers[i](error, finalize);
|
|
1617
1757
|
handled = true;
|
|
1618
1758
|
break;
|
|
1619
1759
|
}
|
|
@@ -1629,10 +1769,6 @@ function _handleError(node, error) {
|
|
|
1629
1769
|
}
|
|
1630
1770
|
function handleError(params) {
|
|
1631
1771
|
let { error } = params;
|
|
1632
|
-
// Wrap error if it wasn't wrapped by wrapError (ie when not in dev mode)
|
|
1633
|
-
if (!(error instanceof OwlError)) {
|
|
1634
|
-
error = Object.assign(new OwlError(`An error occured in the owl lifecycle (see this Error's "cause" property)`), { cause: error });
|
|
1635
|
-
}
|
|
1636
1772
|
const node = "node" in params ? params.node : params.fiber.node;
|
|
1637
1773
|
const fiber = "fiber" in params ? params.fiber : node.fiber;
|
|
1638
1774
|
if (fiber) {
|
|
@@ -1647,14 +1783,7 @@ function handleError(params) {
|
|
|
1647
1783
|
}
|
|
1648
1784
|
const handled = _handleError(node, error);
|
|
1649
1785
|
if (!handled) {
|
|
1650
|
-
|
|
1651
|
-
try {
|
|
1652
|
-
node.app.destroy();
|
|
1653
|
-
}
|
|
1654
|
-
catch (e) {
|
|
1655
|
-
console.error(e);
|
|
1656
|
-
}
|
|
1657
|
-
throw error;
|
|
1786
|
+
throw destroyApp(node.app, error);
|
|
1658
1787
|
}
|
|
1659
1788
|
}
|
|
1660
1789
|
|
|
@@ -1713,7 +1842,7 @@ function cancelFibers(fibers) {
|
|
|
1713
1842
|
for (let fiber of fibers) {
|
|
1714
1843
|
let node = fiber.node;
|
|
1715
1844
|
fiber.render = throwOnRender;
|
|
1716
|
-
if (node.status === 0 /* NEW */) {
|
|
1845
|
+
if (node.status === 0 /* STATUS.NEW */) {
|
|
1717
1846
|
node.cancel();
|
|
1718
1847
|
}
|
|
1719
1848
|
node.fiber = null;
|
|
@@ -1779,13 +1908,16 @@ class Fiber {
|
|
|
1779
1908
|
const node = this.node;
|
|
1780
1909
|
const root = this.root;
|
|
1781
1910
|
if (root) {
|
|
1782
|
-
|
|
1783
|
-
|
|
1784
|
-
|
|
1785
|
-
|
|
1786
|
-
|
|
1787
|
-
|
|
1788
|
-
|
|
1911
|
+
// todo: should use updateComputation somewhere else.
|
|
1912
|
+
runWithComputation(node.signalComputation, () => {
|
|
1913
|
+
try {
|
|
1914
|
+
this.bdom = true;
|
|
1915
|
+
this.bdom = node.renderFn();
|
|
1916
|
+
}
|
|
1917
|
+
catch (e) {
|
|
1918
|
+
node.app.handleError({ node, error: e });
|
|
1919
|
+
}
|
|
1920
|
+
});
|
|
1789
1921
|
root.setCounter(root.counter - 1);
|
|
1790
1922
|
}
|
|
1791
1923
|
}
|
|
@@ -1898,7 +2030,7 @@ class MountFiber extends RootFiber {
|
|
|
1898
2030
|
// unregistering the fiber before mounted since it can do another render
|
|
1899
2031
|
// and that the current rendering is obviously completed
|
|
1900
2032
|
node.fiber = null;
|
|
1901
|
-
node.status = 1 /* MOUNTED */;
|
|
2033
|
+
node.status = 1 /* STATUS.MOUNTED */;
|
|
1902
2034
|
this.appliedToDom = true;
|
|
1903
2035
|
let mountedFibers = this.mounted;
|
|
1904
2036
|
while ((current = mountedFibers.pop())) {
|
|
@@ -1915,567 +2047,104 @@ class MountFiber extends RootFiber {
|
|
|
1915
2047
|
}
|
|
1916
2048
|
}
|
|
1917
2049
|
|
|
1918
|
-
|
|
1919
|
-
|
|
1920
|
-
|
|
1921
|
-
|
|
1922
|
-
|
|
1923
|
-
|
|
1924
|
-
};
|
|
1925
|
-
const objectToString = Object.prototype.toString;
|
|
1926
|
-
const objectHasOwnProperty = Object.prototype.hasOwnProperty;
|
|
1927
|
-
// Use arrays because Array.includes is faster than Set.has for small arrays
|
|
1928
|
-
const SUPPORTED_RAW_TYPES = ["Object", "Array", "Set", "Map", "WeakMap"];
|
|
1929
|
-
const COLLECTION_RAW_TYPES = ["Set", "Map", "WeakMap"];
|
|
1930
|
-
/**
|
|
1931
|
-
* extract "RawType" from strings like "[object RawType]" => this lets us ignore
|
|
1932
|
-
* many native objects such as Promise (whose toString is [object Promise])
|
|
1933
|
-
* or Date ([object Date]), while also supporting collections without using
|
|
1934
|
-
* instanceof in a loop
|
|
1935
|
-
*
|
|
1936
|
-
* @param obj the object to check
|
|
1937
|
-
* @returns the raw type of the object
|
|
1938
|
-
*/
|
|
1939
|
-
function rawType(obj) {
|
|
1940
|
-
return objectToString.call(toRaw(obj)).slice(8, -1);
|
|
2050
|
+
let currentNode = null;
|
|
2051
|
+
function saveCurrent() {
|
|
2052
|
+
let n = currentNode;
|
|
2053
|
+
return () => {
|
|
2054
|
+
currentNode = n;
|
|
2055
|
+
};
|
|
1941
2056
|
}
|
|
1942
|
-
|
|
1943
|
-
|
|
1944
|
-
|
|
1945
|
-
* @param value the value to check
|
|
1946
|
-
* @returns whether the value can be made reactive
|
|
1947
|
-
*/
|
|
1948
|
-
function canBeMadeReactive(value) {
|
|
1949
|
-
if (typeof value !== "object") {
|
|
1950
|
-
return false;
|
|
2057
|
+
function getCurrent() {
|
|
2058
|
+
if (!currentNode) {
|
|
2059
|
+
throw new OwlError("No active component (a hook function should only be called in 'setup')");
|
|
1951
2060
|
}
|
|
1952
|
-
return
|
|
1953
|
-
}
|
|
1954
|
-
/**
|
|
1955
|
-
* Creates a reactive from the given object/callback if possible and returns it,
|
|
1956
|
-
* returns the original object otherwise.
|
|
1957
|
-
*
|
|
1958
|
-
* @param value the value make reactive
|
|
1959
|
-
* @returns a reactive for the given object when possible, the original otherwise
|
|
1960
|
-
*/
|
|
1961
|
-
function possiblyReactive(val, cb) {
|
|
1962
|
-
return canBeMadeReactive(val) ? reactive(val, cb) : val;
|
|
1963
|
-
}
|
|
1964
|
-
const skipped = new WeakSet();
|
|
1965
|
-
/**
|
|
1966
|
-
* Mark an object or array so that it is ignored by the reactivity system
|
|
1967
|
-
*
|
|
1968
|
-
* @param value the value to mark
|
|
1969
|
-
* @returns the object itself
|
|
1970
|
-
*/
|
|
1971
|
-
function markRaw(value) {
|
|
1972
|
-
skipped.add(value);
|
|
1973
|
-
return value;
|
|
1974
|
-
}
|
|
1975
|
-
/**
|
|
1976
|
-
* Given a reactive objet, return the raw (non reactive) underlying object
|
|
1977
|
-
*
|
|
1978
|
-
* @param value a reactive value
|
|
1979
|
-
* @returns the underlying value
|
|
1980
|
-
*/
|
|
1981
|
-
function toRaw(value) {
|
|
1982
|
-
return targets.has(value) ? targets.get(value) : value;
|
|
2061
|
+
return currentNode;
|
|
1983
2062
|
}
|
|
1984
|
-
|
|
1985
|
-
|
|
1986
|
-
* Observes a given key on a target with an callback. The callback will be
|
|
1987
|
-
* called when the given key changes on the target.
|
|
1988
|
-
*
|
|
1989
|
-
* @param target the target whose key should be observed
|
|
1990
|
-
* @param key the key to observe (or Symbol(KEYCHANGES) for key creation
|
|
1991
|
-
* or deletion)
|
|
1992
|
-
* @param callback the function to call when the key changes
|
|
1993
|
-
*/
|
|
1994
|
-
function observeTargetKey(target, key, callback) {
|
|
1995
|
-
if (callback === NO_CALLBACK) {
|
|
1996
|
-
return;
|
|
1997
|
-
}
|
|
1998
|
-
if (!targetToKeysToCallbacks.get(target)) {
|
|
1999
|
-
targetToKeysToCallbacks.set(target, new Map());
|
|
2000
|
-
}
|
|
2001
|
-
const keyToCallbacks = targetToKeysToCallbacks.get(target);
|
|
2002
|
-
if (!keyToCallbacks.get(key)) {
|
|
2003
|
-
keyToCallbacks.set(key, new Set());
|
|
2004
|
-
}
|
|
2005
|
-
keyToCallbacks.get(key).add(callback);
|
|
2006
|
-
if (!callbacksToTargets.has(callback)) {
|
|
2007
|
-
callbacksToTargets.set(callback, new Set());
|
|
2008
|
-
}
|
|
2009
|
-
callbacksToTargets.get(callback).add(target);
|
|
2063
|
+
function useComponent() {
|
|
2064
|
+
return currentNode.component;
|
|
2010
2065
|
}
|
|
2011
|
-
|
|
2012
|
-
|
|
2013
|
-
|
|
2014
|
-
|
|
2015
|
-
|
|
2016
|
-
|
|
2017
|
-
|
|
2018
|
-
|
|
2019
|
-
|
|
2020
|
-
|
|
2021
|
-
|
|
2022
|
-
|
|
2023
|
-
|
|
2024
|
-
|
|
2025
|
-
|
|
2026
|
-
|
|
2027
|
-
|
|
2028
|
-
|
|
2029
|
-
|
|
2030
|
-
|
|
2031
|
-
|
|
2032
|
-
|
|
2066
|
+
class ComponentNode {
|
|
2067
|
+
constructor(C, props, app, parent, parentKey) {
|
|
2068
|
+
this.fiber = null;
|
|
2069
|
+
this.bdom = null;
|
|
2070
|
+
this.status = 0 /* STATUS.NEW */;
|
|
2071
|
+
this.forceNextRender = false;
|
|
2072
|
+
this.children = Object.create(null);
|
|
2073
|
+
this.willStart = [];
|
|
2074
|
+
this.willUpdateProps = [];
|
|
2075
|
+
this.willUnmount = [];
|
|
2076
|
+
this.mounted = [];
|
|
2077
|
+
this.willPatch = [];
|
|
2078
|
+
this.patched = [];
|
|
2079
|
+
this.willDestroy = [];
|
|
2080
|
+
this.name = C.name;
|
|
2081
|
+
currentNode = this;
|
|
2082
|
+
this.app = app;
|
|
2083
|
+
this.parent = parent;
|
|
2084
|
+
this.parentKey = parentKey;
|
|
2085
|
+
this.pluginManager = parent ? parent.pluginManager : app.pluginManager;
|
|
2086
|
+
this.signalComputation = {
|
|
2087
|
+
value: undefined,
|
|
2088
|
+
compute: () => this.render(false),
|
|
2089
|
+
sources: new Set(),
|
|
2090
|
+
state: ComputationState.EXECUTED,
|
|
2091
|
+
};
|
|
2092
|
+
this.props = Object.assign({}, props);
|
|
2093
|
+
const previousComputation = getCurrentComputation();
|
|
2094
|
+
setComputation(this.signalComputation);
|
|
2095
|
+
this.component = new C(this);
|
|
2096
|
+
const ctx = { this: this.component, __owl__: this };
|
|
2097
|
+
this.renderFn = app.getTemplate(C.template).bind(this.component, ctx, this);
|
|
2098
|
+
this.component.setup();
|
|
2099
|
+
setComputation(previousComputation);
|
|
2100
|
+
currentNode = null;
|
|
2033
2101
|
}
|
|
2034
|
-
|
|
2035
|
-
const
|
|
2036
|
-
|
|
2037
|
-
|
|
2038
|
-
*
|
|
2039
|
-
* @param callback the callback for which the reactives need to be cleared
|
|
2040
|
-
*/
|
|
2041
|
-
function clearReactivesForCallback(callback) {
|
|
2042
|
-
const targetsToClear = callbacksToTargets.get(callback);
|
|
2043
|
-
if (!targetsToClear) {
|
|
2044
|
-
return;
|
|
2102
|
+
mountComponent(target, options) {
|
|
2103
|
+
const fiber = new MountFiber(this, target, options);
|
|
2104
|
+
this.app.scheduler.addFiber(fiber);
|
|
2105
|
+
this.initiateRender(fiber);
|
|
2045
2106
|
}
|
|
2046
|
-
|
|
2047
|
-
|
|
2048
|
-
if (
|
|
2049
|
-
|
|
2107
|
+
async initiateRender(fiber) {
|
|
2108
|
+
this.fiber = fiber;
|
|
2109
|
+
if (this.mounted.length) {
|
|
2110
|
+
fiber.root.mounted.push(fiber);
|
|
2050
2111
|
}
|
|
2051
|
-
|
|
2052
|
-
|
|
2053
|
-
|
|
2054
|
-
|
|
2055
|
-
|
|
2112
|
+
const component = this.component;
|
|
2113
|
+
try {
|
|
2114
|
+
let promises;
|
|
2115
|
+
runWithComputation(undefined, () => {
|
|
2116
|
+
promises = this.willStart.map((f) => f.call(component));
|
|
2117
|
+
});
|
|
2118
|
+
await Promise.all(promises);
|
|
2119
|
+
}
|
|
2120
|
+
catch (e) {
|
|
2121
|
+
this.app.handleError({ node: this, error: e });
|
|
2122
|
+
return;
|
|
2123
|
+
}
|
|
2124
|
+
if (this.status === 0 /* STATUS.NEW */ && this.fiber === fiber) {
|
|
2125
|
+
fiber.render();
|
|
2056
2126
|
}
|
|
2057
2127
|
}
|
|
2058
|
-
|
|
2059
|
-
|
|
2060
|
-
|
|
2061
|
-
|
|
2062
|
-
|
|
2063
|
-
|
|
2064
|
-
|
|
2065
|
-
|
|
2066
|
-
|
|
2067
|
-
|
|
2068
|
-
|
|
2128
|
+
async render(deep) {
|
|
2129
|
+
if (this.status >= 2 /* STATUS.CANCELLED */) {
|
|
2130
|
+
return;
|
|
2131
|
+
}
|
|
2132
|
+
let current = this.fiber;
|
|
2133
|
+
if (current && (current.root.locked || current.bdom === true)) {
|
|
2134
|
+
await Promise.resolve();
|
|
2135
|
+
// situation may have changed after the microtask tick
|
|
2136
|
+
current = this.fiber;
|
|
2137
|
+
}
|
|
2138
|
+
if (current) {
|
|
2139
|
+
if (!current.bdom && !fibersInError.has(current)) {
|
|
2140
|
+
if (deep) {
|
|
2141
|
+
// we want the render from this point on to be with deep=true
|
|
2142
|
+
current.deep = deep;
|
|
2069
2143
|
}
|
|
2144
|
+
return;
|
|
2070
2145
|
}
|
|
2071
|
-
|
|
2072
|
-
|
|
2073
|
-
});
|
|
2074
|
-
}
|
|
2075
|
-
// Maps reactive objects to the underlying target
|
|
2076
|
-
const targets = new WeakMap();
|
|
2077
|
-
const reactiveCache = new WeakMap();
|
|
2078
|
-
/**
|
|
2079
|
-
* Creates a reactive proxy for an object. Reading data on the reactive object
|
|
2080
|
-
* subscribes to changes to the data. Writing data on the object will cause the
|
|
2081
|
-
* notify callback to be called if there are suscriptions to that data. Nested
|
|
2082
|
-
* objects and arrays are automatically made reactive as well.
|
|
2083
|
-
*
|
|
2084
|
-
* Whenever you are notified of a change, all subscriptions are cleared, and if
|
|
2085
|
-
* you would like to be notified of any further changes, you should go read
|
|
2086
|
-
* the underlying data again. We assume that if you don't go read it again after
|
|
2087
|
-
* being notified, it means that you are no longer interested in that data.
|
|
2088
|
-
*
|
|
2089
|
-
* Subscriptions:
|
|
2090
|
-
* + Reading a property on an object will subscribe you to changes in the value
|
|
2091
|
-
* of that property.
|
|
2092
|
-
* + Accessing an object's keys (eg with Object.keys or with `for..in`) will
|
|
2093
|
-
* subscribe you to the creation/deletion of keys. Checking the presence of a
|
|
2094
|
-
* key on the object with 'in' has the same effect.
|
|
2095
|
-
* - getOwnPropertyDescriptor does not currently subscribe you to the property.
|
|
2096
|
-
* This is a choice that was made because changing a key's value will trigger
|
|
2097
|
-
* this trap and we do not want to subscribe by writes. This also means that
|
|
2098
|
-
* Object.hasOwnProperty doesn't subscribe as it goes through this trap.
|
|
2099
|
-
*
|
|
2100
|
-
* @param target the object for which to create a reactive proxy
|
|
2101
|
-
* @param callback the function to call when an observed property of the
|
|
2102
|
-
* reactive has changed
|
|
2103
|
-
* @returns a proxy that tracks changes to it
|
|
2104
|
-
*/
|
|
2105
|
-
function reactive(target, callback = NO_CALLBACK) {
|
|
2106
|
-
if (!canBeMadeReactive(target)) {
|
|
2107
|
-
throw new OwlError(`Cannot make the given value reactive`);
|
|
2108
|
-
}
|
|
2109
|
-
if (skipped.has(target)) {
|
|
2110
|
-
return target;
|
|
2111
|
-
}
|
|
2112
|
-
if (targets.has(target)) {
|
|
2113
|
-
// target is reactive, create a reactive on the underlying object instead
|
|
2114
|
-
return reactive(targets.get(target), callback);
|
|
2115
|
-
}
|
|
2116
|
-
if (!reactiveCache.has(target)) {
|
|
2117
|
-
reactiveCache.set(target, new WeakMap());
|
|
2118
|
-
}
|
|
2119
|
-
const reactivesForTarget = reactiveCache.get(target);
|
|
2120
|
-
if (!reactivesForTarget.has(callback)) {
|
|
2121
|
-
const targetRawType = rawType(target);
|
|
2122
|
-
const handler = COLLECTION_RAW_TYPES.includes(targetRawType)
|
|
2123
|
-
? collectionsProxyHandler(target, callback, targetRawType)
|
|
2124
|
-
: basicProxyHandler(callback);
|
|
2125
|
-
const proxy = new Proxy(target, handler);
|
|
2126
|
-
reactivesForTarget.set(callback, proxy);
|
|
2127
|
-
targets.set(proxy, target);
|
|
2128
|
-
}
|
|
2129
|
-
return reactivesForTarget.get(callback);
|
|
2130
|
-
}
|
|
2131
|
-
/**
|
|
2132
|
-
* Creates a basic proxy handler for regular objects and arrays.
|
|
2133
|
-
*
|
|
2134
|
-
* @param callback @see reactive
|
|
2135
|
-
* @returns a proxy handler object
|
|
2136
|
-
*/
|
|
2137
|
-
function basicProxyHandler(callback) {
|
|
2138
|
-
return {
|
|
2139
|
-
get(target, key, receiver) {
|
|
2140
|
-
// non-writable non-configurable properties cannot be made reactive
|
|
2141
|
-
const desc = Object.getOwnPropertyDescriptor(target, key);
|
|
2142
|
-
if (desc && !desc.writable && !desc.configurable) {
|
|
2143
|
-
return Reflect.get(target, key, receiver);
|
|
2144
|
-
}
|
|
2145
|
-
observeTargetKey(target, key, callback);
|
|
2146
|
-
return possiblyReactive(Reflect.get(target, key, receiver), callback);
|
|
2147
|
-
},
|
|
2148
|
-
set(target, key, value, receiver) {
|
|
2149
|
-
const hadKey = objectHasOwnProperty.call(target, key);
|
|
2150
|
-
const originalValue = Reflect.get(target, key, receiver);
|
|
2151
|
-
const ret = Reflect.set(target, key, toRaw(value), receiver);
|
|
2152
|
-
if (!hadKey && objectHasOwnProperty.call(target, key)) {
|
|
2153
|
-
notifyReactives(target, KEYCHANGES);
|
|
2154
|
-
}
|
|
2155
|
-
// While Array length may trigger the set trap, it's not actually set by this
|
|
2156
|
-
// method but is updated behind the scenes, and the trap is not called with the
|
|
2157
|
-
// new value. We disable the "same-value-optimization" for it because of that.
|
|
2158
|
-
if (originalValue !== Reflect.get(target, key, receiver) ||
|
|
2159
|
-
(key === "length" && Array.isArray(target))) {
|
|
2160
|
-
notifyReactives(target, key);
|
|
2161
|
-
}
|
|
2162
|
-
return ret;
|
|
2163
|
-
},
|
|
2164
|
-
deleteProperty(target, key) {
|
|
2165
|
-
const ret = Reflect.deleteProperty(target, key);
|
|
2166
|
-
// TODO: only notify when something was actually deleted
|
|
2167
|
-
notifyReactives(target, KEYCHANGES);
|
|
2168
|
-
notifyReactives(target, key);
|
|
2169
|
-
return ret;
|
|
2170
|
-
},
|
|
2171
|
-
ownKeys(target) {
|
|
2172
|
-
observeTargetKey(target, KEYCHANGES, callback);
|
|
2173
|
-
return Reflect.ownKeys(target);
|
|
2174
|
-
},
|
|
2175
|
-
has(target, key) {
|
|
2176
|
-
// TODO: this observes all key changes instead of only the presence of the argument key
|
|
2177
|
-
// observing the key itself would observe value changes instead of presence changes
|
|
2178
|
-
// so we may need a finer grained system to distinguish observing value vs presence.
|
|
2179
|
-
observeTargetKey(target, KEYCHANGES, callback);
|
|
2180
|
-
return Reflect.has(target, key);
|
|
2181
|
-
},
|
|
2182
|
-
};
|
|
2183
|
-
}
|
|
2184
|
-
/**
|
|
2185
|
-
* Creates a function that will observe the key that is passed to it when called
|
|
2186
|
-
* and delegates to the underlying method.
|
|
2187
|
-
*
|
|
2188
|
-
* @param methodName name of the method to delegate to
|
|
2189
|
-
* @param target @see reactive
|
|
2190
|
-
* @param callback @see reactive
|
|
2191
|
-
*/
|
|
2192
|
-
function makeKeyObserver(methodName, target, callback) {
|
|
2193
|
-
return (key) => {
|
|
2194
|
-
key = toRaw(key);
|
|
2195
|
-
observeTargetKey(target, key, callback);
|
|
2196
|
-
return possiblyReactive(target[methodName](key), callback);
|
|
2197
|
-
};
|
|
2198
|
-
}
|
|
2199
|
-
/**
|
|
2200
|
-
* Creates an iterable that will delegate to the underlying iteration method and
|
|
2201
|
-
* observe keys as necessary.
|
|
2202
|
-
*
|
|
2203
|
-
* @param methodName name of the method to delegate to
|
|
2204
|
-
* @param target @see reactive
|
|
2205
|
-
* @param callback @see reactive
|
|
2206
|
-
*/
|
|
2207
|
-
function makeIteratorObserver(methodName, target, callback) {
|
|
2208
|
-
return function* () {
|
|
2209
|
-
observeTargetKey(target, KEYCHANGES, callback);
|
|
2210
|
-
const keys = target.keys();
|
|
2211
|
-
for (const item of target[methodName]()) {
|
|
2212
|
-
const key = keys.next().value;
|
|
2213
|
-
observeTargetKey(target, key, callback);
|
|
2214
|
-
yield possiblyReactive(item, callback);
|
|
2215
|
-
}
|
|
2216
|
-
};
|
|
2217
|
-
}
|
|
2218
|
-
/**
|
|
2219
|
-
* Creates a forEach function that will delegate to forEach on the underlying
|
|
2220
|
-
* collection while observing key changes, and keys as they're iterated over,
|
|
2221
|
-
* and making the passed keys/values reactive.
|
|
2222
|
-
*
|
|
2223
|
-
* @param target @see reactive
|
|
2224
|
-
* @param callback @see reactive
|
|
2225
|
-
*/
|
|
2226
|
-
function makeForEachObserver(target, callback) {
|
|
2227
|
-
return function forEach(forEachCb, thisArg) {
|
|
2228
|
-
observeTargetKey(target, KEYCHANGES, callback);
|
|
2229
|
-
target.forEach(function (val, key, targetObj) {
|
|
2230
|
-
observeTargetKey(target, key, callback);
|
|
2231
|
-
forEachCb.call(thisArg, possiblyReactive(val, callback), possiblyReactive(key, callback), possiblyReactive(targetObj, callback));
|
|
2232
|
-
}, thisArg);
|
|
2233
|
-
};
|
|
2234
|
-
}
|
|
2235
|
-
/**
|
|
2236
|
-
* Creates a function that will delegate to an underlying method, and check if
|
|
2237
|
-
* that method has modified the presence or value of a key, and notify the
|
|
2238
|
-
* reactives appropriately.
|
|
2239
|
-
*
|
|
2240
|
-
* @param setterName name of the method to delegate to
|
|
2241
|
-
* @param getterName name of the method which should be used to retrieve the
|
|
2242
|
-
* value before calling the delegate method for comparison purposes
|
|
2243
|
-
* @param target @see reactive
|
|
2244
|
-
*/
|
|
2245
|
-
function delegateAndNotify(setterName, getterName, target) {
|
|
2246
|
-
return (key, value) => {
|
|
2247
|
-
key = toRaw(key);
|
|
2248
|
-
const hadKey = target.has(key);
|
|
2249
|
-
const originalValue = target[getterName](key);
|
|
2250
|
-
const ret = target[setterName](key, value);
|
|
2251
|
-
const hasKey = target.has(key);
|
|
2252
|
-
if (hadKey !== hasKey) {
|
|
2253
|
-
notifyReactives(target, KEYCHANGES);
|
|
2254
|
-
}
|
|
2255
|
-
if (originalValue !== target[getterName](key)) {
|
|
2256
|
-
notifyReactives(target, key);
|
|
2257
|
-
}
|
|
2258
|
-
return ret;
|
|
2259
|
-
};
|
|
2260
|
-
}
|
|
2261
|
-
/**
|
|
2262
|
-
* Creates a function that will clear the underlying collection and notify that
|
|
2263
|
-
* the keys of the collection have changed.
|
|
2264
|
-
*
|
|
2265
|
-
* @param target @see reactive
|
|
2266
|
-
*/
|
|
2267
|
-
function makeClearNotifier(target) {
|
|
2268
|
-
return () => {
|
|
2269
|
-
const allKeys = [...target.keys()];
|
|
2270
|
-
target.clear();
|
|
2271
|
-
notifyReactives(target, KEYCHANGES);
|
|
2272
|
-
for (const key of allKeys) {
|
|
2273
|
-
notifyReactives(target, key);
|
|
2274
|
-
}
|
|
2275
|
-
};
|
|
2276
|
-
}
|
|
2277
|
-
/**
|
|
2278
|
-
* Maps raw type of an object to an object containing functions that can be used
|
|
2279
|
-
* to build an appropritate proxy handler for that raw type. Eg: when making a
|
|
2280
|
-
* reactive set, calling the has method should mark the key that is being
|
|
2281
|
-
* retrieved as observed, and calling the add or delete method should notify the
|
|
2282
|
-
* reactives that the key which is being added or deleted has been modified.
|
|
2283
|
-
*/
|
|
2284
|
-
const rawTypeToFuncHandlers = {
|
|
2285
|
-
Set: (target, callback) => ({
|
|
2286
|
-
has: makeKeyObserver("has", target, callback),
|
|
2287
|
-
add: delegateAndNotify("add", "has", target),
|
|
2288
|
-
delete: delegateAndNotify("delete", "has", target),
|
|
2289
|
-
keys: makeIteratorObserver("keys", target, callback),
|
|
2290
|
-
values: makeIteratorObserver("values", target, callback),
|
|
2291
|
-
entries: makeIteratorObserver("entries", target, callback),
|
|
2292
|
-
[Symbol.iterator]: makeIteratorObserver(Symbol.iterator, target, callback),
|
|
2293
|
-
forEach: makeForEachObserver(target, callback),
|
|
2294
|
-
clear: makeClearNotifier(target),
|
|
2295
|
-
get size() {
|
|
2296
|
-
observeTargetKey(target, KEYCHANGES, callback);
|
|
2297
|
-
return target.size;
|
|
2298
|
-
},
|
|
2299
|
-
}),
|
|
2300
|
-
Map: (target, callback) => ({
|
|
2301
|
-
has: makeKeyObserver("has", target, callback),
|
|
2302
|
-
get: makeKeyObserver("get", target, callback),
|
|
2303
|
-
set: delegateAndNotify("set", "get", target),
|
|
2304
|
-
delete: delegateAndNotify("delete", "has", target),
|
|
2305
|
-
keys: makeIteratorObserver("keys", target, callback),
|
|
2306
|
-
values: makeIteratorObserver("values", target, callback),
|
|
2307
|
-
entries: makeIteratorObserver("entries", target, callback),
|
|
2308
|
-
[Symbol.iterator]: makeIteratorObserver(Symbol.iterator, target, callback),
|
|
2309
|
-
forEach: makeForEachObserver(target, callback),
|
|
2310
|
-
clear: makeClearNotifier(target),
|
|
2311
|
-
get size() {
|
|
2312
|
-
observeTargetKey(target, KEYCHANGES, callback);
|
|
2313
|
-
return target.size;
|
|
2314
|
-
},
|
|
2315
|
-
}),
|
|
2316
|
-
WeakMap: (target, callback) => ({
|
|
2317
|
-
has: makeKeyObserver("has", target, callback),
|
|
2318
|
-
get: makeKeyObserver("get", target, callback),
|
|
2319
|
-
set: delegateAndNotify("set", "get", target),
|
|
2320
|
-
delete: delegateAndNotify("delete", "has", target),
|
|
2321
|
-
}),
|
|
2322
|
-
};
|
|
2323
|
-
/**
|
|
2324
|
-
* Creates a proxy handler for collections (Set/Map/WeakMap)
|
|
2325
|
-
*
|
|
2326
|
-
* @param callback @see reactive
|
|
2327
|
-
* @param target @see reactive
|
|
2328
|
-
* @returns a proxy handler object
|
|
2329
|
-
*/
|
|
2330
|
-
function collectionsProxyHandler(target, callback, targetRawType) {
|
|
2331
|
-
// TODO: if performance is an issue we can create the special handlers lazily when each
|
|
2332
|
-
// property is read.
|
|
2333
|
-
const specialHandlers = rawTypeToFuncHandlers[targetRawType](target, callback);
|
|
2334
|
-
return Object.assign(basicProxyHandler(callback), {
|
|
2335
|
-
// FIXME: probably broken when part of prototype chain since we ignore the receiver
|
|
2336
|
-
get(target, key) {
|
|
2337
|
-
if (objectHasOwnProperty.call(specialHandlers, key)) {
|
|
2338
|
-
return specialHandlers[key];
|
|
2339
|
-
}
|
|
2340
|
-
observeTargetKey(target, key, callback);
|
|
2341
|
-
return possiblyReactive(target[key], callback);
|
|
2342
|
-
},
|
|
2343
|
-
});
|
|
2344
|
-
}
|
|
2345
|
-
|
|
2346
|
-
let currentNode = null;
|
|
2347
|
-
function saveCurrent() {
|
|
2348
|
-
let n = currentNode;
|
|
2349
|
-
return () => {
|
|
2350
|
-
currentNode = n;
|
|
2351
|
-
};
|
|
2352
|
-
}
|
|
2353
|
-
function getCurrent() {
|
|
2354
|
-
if (!currentNode) {
|
|
2355
|
-
throw new OwlError("No active component (a hook function should only be called in 'setup')");
|
|
2356
|
-
}
|
|
2357
|
-
return currentNode;
|
|
2358
|
-
}
|
|
2359
|
-
function useComponent() {
|
|
2360
|
-
return currentNode.component;
|
|
2361
|
-
}
|
|
2362
|
-
/**
|
|
2363
|
-
* Apply default props (only top level).
|
|
2364
|
-
*/
|
|
2365
|
-
function applyDefaultProps(props, defaultProps) {
|
|
2366
|
-
for (let propName in defaultProps) {
|
|
2367
|
-
if (props[propName] === undefined) {
|
|
2368
|
-
props[propName] = defaultProps[propName];
|
|
2369
|
-
}
|
|
2370
|
-
}
|
|
2371
|
-
}
|
|
2372
|
-
// -----------------------------------------------------------------------------
|
|
2373
|
-
// Integration with reactivity system (useState)
|
|
2374
|
-
// -----------------------------------------------------------------------------
|
|
2375
|
-
const batchedRenderFunctions = new WeakMap();
|
|
2376
|
-
/**
|
|
2377
|
-
* Creates a reactive object that will be observed by the current component.
|
|
2378
|
-
* Reading data from the returned object (eg during rendering) will cause the
|
|
2379
|
-
* component to subscribe to that data and be rerendered when it changes.
|
|
2380
|
-
*
|
|
2381
|
-
* @param state the state to observe
|
|
2382
|
-
* @returns a reactive object that will cause the component to re-render on
|
|
2383
|
-
* relevant changes
|
|
2384
|
-
* @see reactive
|
|
2385
|
-
*/
|
|
2386
|
-
function useState(state) {
|
|
2387
|
-
const node = getCurrent();
|
|
2388
|
-
let render = batchedRenderFunctions.get(node);
|
|
2389
|
-
if (!render) {
|
|
2390
|
-
render = batched(node.render.bind(node, false));
|
|
2391
|
-
batchedRenderFunctions.set(node, render);
|
|
2392
|
-
// manual implementation of onWillDestroy to break cyclic dependency
|
|
2393
|
-
node.willDestroy.push(clearReactivesForCallback.bind(null, render));
|
|
2394
|
-
}
|
|
2395
|
-
return reactive(state, render);
|
|
2396
|
-
}
|
|
2397
|
-
class ComponentNode {
|
|
2398
|
-
constructor(C, props, app, parent, parentKey) {
|
|
2399
|
-
this.fiber = null;
|
|
2400
|
-
this.bdom = null;
|
|
2401
|
-
this.status = 0 /* NEW */;
|
|
2402
|
-
this.forceNextRender = false;
|
|
2403
|
-
this.nextProps = null;
|
|
2404
|
-
this.children = Object.create(null);
|
|
2405
|
-
this.refs = {};
|
|
2406
|
-
this.willStart = [];
|
|
2407
|
-
this.willUpdateProps = [];
|
|
2408
|
-
this.willUnmount = [];
|
|
2409
|
-
this.mounted = [];
|
|
2410
|
-
this.willPatch = [];
|
|
2411
|
-
this.patched = [];
|
|
2412
|
-
this.willDestroy = [];
|
|
2413
|
-
currentNode = this;
|
|
2414
|
-
this.app = app;
|
|
2415
|
-
this.parent = parent;
|
|
2416
|
-
this.props = props;
|
|
2417
|
-
this.parentKey = parentKey;
|
|
2418
|
-
const defaultProps = C.defaultProps;
|
|
2419
|
-
props = Object.assign({}, props);
|
|
2420
|
-
if (defaultProps) {
|
|
2421
|
-
applyDefaultProps(props, defaultProps);
|
|
2422
|
-
}
|
|
2423
|
-
const env = (parent && parent.childEnv) || app.env;
|
|
2424
|
-
this.childEnv = env;
|
|
2425
|
-
for (const key in props) {
|
|
2426
|
-
const prop = props[key];
|
|
2427
|
-
if (prop && typeof prop === "object" && targets.has(prop)) {
|
|
2428
|
-
props[key] = useState(prop);
|
|
2429
|
-
}
|
|
2430
|
-
}
|
|
2431
|
-
this.component = new C(props, env, this);
|
|
2432
|
-
const ctx = Object.assign(Object.create(this.component), { this: this.component });
|
|
2433
|
-
this.renderFn = app.getTemplate(C.template).bind(this.component, ctx, this);
|
|
2434
|
-
this.component.setup();
|
|
2435
|
-
currentNode = null;
|
|
2436
|
-
}
|
|
2437
|
-
mountComponent(target, options) {
|
|
2438
|
-
const fiber = new MountFiber(this, target, options);
|
|
2439
|
-
this.app.scheduler.addFiber(fiber);
|
|
2440
|
-
this.initiateRender(fiber);
|
|
2441
|
-
}
|
|
2442
|
-
async initiateRender(fiber) {
|
|
2443
|
-
this.fiber = fiber;
|
|
2444
|
-
if (this.mounted.length) {
|
|
2445
|
-
fiber.root.mounted.push(fiber);
|
|
2446
|
-
}
|
|
2447
|
-
const component = this.component;
|
|
2448
|
-
try {
|
|
2449
|
-
await Promise.all(this.willStart.map((f) => f.call(component)));
|
|
2450
|
-
}
|
|
2451
|
-
catch (e) {
|
|
2452
|
-
this.app.handleError({ node: this, error: e });
|
|
2453
|
-
return;
|
|
2454
|
-
}
|
|
2455
|
-
if (this.status === 0 /* NEW */ && this.fiber === fiber) {
|
|
2456
|
-
fiber.render();
|
|
2457
|
-
}
|
|
2458
|
-
}
|
|
2459
|
-
async render(deep) {
|
|
2460
|
-
if (this.status >= 2 /* CANCELLED */) {
|
|
2461
|
-
return;
|
|
2462
|
-
}
|
|
2463
|
-
let current = this.fiber;
|
|
2464
|
-
if (current && (current.root.locked || current.bdom === true)) {
|
|
2465
|
-
await Promise.resolve();
|
|
2466
|
-
// situation may have changed after the microtask tick
|
|
2467
|
-
current = this.fiber;
|
|
2468
|
-
}
|
|
2469
|
-
if (current) {
|
|
2470
|
-
if (!current.bdom && !fibersInError.has(current)) {
|
|
2471
|
-
if (deep) {
|
|
2472
|
-
// we want the render from this point on to be with deep=true
|
|
2473
|
-
current.deep = deep;
|
|
2474
|
-
}
|
|
2475
|
-
return;
|
|
2476
|
-
}
|
|
2477
|
-
// if current rendering was with deep=true, we want this one to be the same
|
|
2478
|
-
deep = deep || current.deep;
|
|
2146
|
+
// if current rendering was with deep=true, we want this one to be the same
|
|
2147
|
+
deep = deep || current.deep;
|
|
2479
2148
|
}
|
|
2480
2149
|
else if (!this.bdom) {
|
|
2481
2150
|
return;
|
|
@@ -2485,7 +2154,7 @@ class ComponentNode {
|
|
|
2485
2154
|
this.fiber = fiber;
|
|
2486
2155
|
this.app.scheduler.addFiber(fiber);
|
|
2487
2156
|
await Promise.resolve();
|
|
2488
|
-
if (this.status >= 2 /* CANCELLED */) {
|
|
2157
|
+
if (this.status >= 2 /* STATUS.CANCELLED */) {
|
|
2489
2158
|
return;
|
|
2490
2159
|
}
|
|
2491
2160
|
// We only want to actually render the component if the following two
|
|
@@ -2509,14 +2178,14 @@ class ComponentNode {
|
|
|
2509
2178
|
this.app.scheduler.scheduleDestroy(this);
|
|
2510
2179
|
}
|
|
2511
2180
|
_cancel() {
|
|
2512
|
-
this.status = 2 /* CANCELLED */;
|
|
2181
|
+
this.status = 2 /* STATUS.CANCELLED */;
|
|
2513
2182
|
const children = this.children;
|
|
2514
2183
|
for (let childKey in children) {
|
|
2515
2184
|
children[childKey]._cancel();
|
|
2516
2185
|
}
|
|
2517
2186
|
}
|
|
2518
2187
|
destroy() {
|
|
2519
|
-
let shouldRemove = this.status === 1 /* MOUNTED */;
|
|
2188
|
+
let shouldRemove = this.status === 1 /* STATUS.MOUNTED */;
|
|
2520
2189
|
this._destroy();
|
|
2521
2190
|
if (shouldRemove) {
|
|
2522
2191
|
this.bdom.remove();
|
|
@@ -2524,7 +2193,7 @@ class ComponentNode {
|
|
|
2524
2193
|
}
|
|
2525
2194
|
_destroy() {
|
|
2526
2195
|
const component = this.component;
|
|
2527
|
-
if (this.status === 1 /* MOUNTED */) {
|
|
2196
|
+
if (this.status === 1 /* STATUS.MOUNTED */) {
|
|
2528
2197
|
for (let cb of this.willUnmount) {
|
|
2529
2198
|
cb.call(component);
|
|
2530
2199
|
}
|
|
@@ -2542,33 +2211,23 @@ class ComponentNode {
|
|
|
2542
2211
|
this.app.handleError({ error: e, node: this });
|
|
2543
2212
|
}
|
|
2544
2213
|
}
|
|
2545
|
-
this.status = 3 /* DESTROYED */;
|
|
2214
|
+
this.status = 3 /* STATUS.DESTROYED */;
|
|
2546
2215
|
}
|
|
2547
2216
|
async updateAndRender(props, parentFiber) {
|
|
2548
|
-
this.nextProps = props;
|
|
2549
2217
|
props = Object.assign({}, props);
|
|
2550
2218
|
// update
|
|
2551
2219
|
const fiber = makeChildFiber(this, parentFiber);
|
|
2552
2220
|
this.fiber = fiber;
|
|
2553
2221
|
const component = this.component;
|
|
2554
|
-
|
|
2555
|
-
|
|
2556
|
-
|
|
2557
|
-
}
|
|
2558
|
-
currentNode = this;
|
|
2559
|
-
for (const key in props) {
|
|
2560
|
-
const prop = props[key];
|
|
2561
|
-
if (prop && typeof prop === "object" && targets.has(prop)) {
|
|
2562
|
-
props[key] = useState(prop);
|
|
2563
|
-
}
|
|
2564
|
-
}
|
|
2565
|
-
currentNode = null;
|
|
2566
|
-
const prom = Promise.all(this.willUpdateProps.map((f) => f.call(component, props)));
|
|
2222
|
+
let prom;
|
|
2223
|
+
untrack(() => {
|
|
2224
|
+
prom = Promise.all(this.willUpdateProps.map((f) => f.call(component, props)));
|
|
2225
|
+
});
|
|
2567
2226
|
await prom;
|
|
2568
2227
|
if (fiber !== this.fiber) {
|
|
2569
2228
|
return;
|
|
2570
2229
|
}
|
|
2571
|
-
|
|
2230
|
+
this.props = props;
|
|
2572
2231
|
fiber.render();
|
|
2573
2232
|
const parentRoot = parentFiber.root;
|
|
2574
2233
|
if (this.willPatch.length) {
|
|
@@ -2603,18 +2262,6 @@ class ComponentNode {
|
|
|
2603
2262
|
this.fiber = null;
|
|
2604
2263
|
}
|
|
2605
2264
|
}
|
|
2606
|
-
/**
|
|
2607
|
-
* Sets a ref to a given HTMLElement.
|
|
2608
|
-
*
|
|
2609
|
-
* @param name the name of the ref to set
|
|
2610
|
-
* @param el the HTMLElement to set the ref to. The ref is not set if the el
|
|
2611
|
-
* is null, but useRef will not return elements that are not in the DOM
|
|
2612
|
-
*/
|
|
2613
|
-
setRef(name, el) {
|
|
2614
|
-
if (el) {
|
|
2615
|
-
this.refs[name] = el;
|
|
2616
|
-
}
|
|
2617
|
-
}
|
|
2618
2265
|
// ---------------------------------------------------------------------------
|
|
2619
2266
|
// Block DOM methods
|
|
2620
2267
|
// ---------------------------------------------------------------------------
|
|
@@ -2626,7 +2273,7 @@ class ComponentNode {
|
|
|
2626
2273
|
const bdom = this.fiber.bdom;
|
|
2627
2274
|
this.bdom = bdom;
|
|
2628
2275
|
bdom.mount(parent, anchor);
|
|
2629
|
-
this.status = 1 /* MOUNTED */;
|
|
2276
|
+
this.status = 1 /* STATUS.MOUNTED */;
|
|
2630
2277
|
this.fiber.appliedToDom = true;
|
|
2631
2278
|
this.children = this.fiber.childrenMap;
|
|
2632
2279
|
this.fiber = null;
|
|
@@ -2643,7 +2290,6 @@ class ComponentNode {
|
|
|
2643
2290
|
// by the component will be patched independently in the appropriate
|
|
2644
2291
|
// fiber.complete
|
|
2645
2292
|
this._patch();
|
|
2646
|
-
this.props = this.nextProps;
|
|
2647
2293
|
}
|
|
2648
2294
|
}
|
|
2649
2295
|
_patch() {
|
|
@@ -2665,120 +2311,130 @@ class ComponentNode {
|
|
|
2665
2311
|
remove() {
|
|
2666
2312
|
this.bdom.remove();
|
|
2667
2313
|
}
|
|
2668
|
-
// ---------------------------------------------------------------------------
|
|
2669
|
-
// Some debug helpers
|
|
2670
|
-
// ---------------------------------------------------------------------------
|
|
2671
|
-
get name() {
|
|
2672
|
-
return this.component.constructor.name;
|
|
2673
|
-
}
|
|
2674
|
-
get subscriptions() {
|
|
2675
|
-
const render = batchedRenderFunctions.get(this);
|
|
2676
|
-
return render ? getSubscriptions(render) : [];
|
|
2677
|
-
}
|
|
2678
2314
|
}
|
|
2679
2315
|
|
|
2680
|
-
|
|
2681
|
-
const
|
|
2682
|
-
|
|
2683
|
-
|
|
2684
|
-
}
|
|
2685
|
-
|
|
2686
|
-
|
|
2687
|
-
|
|
2688
|
-
|
|
2689
|
-
|
|
2690
|
-
|
|
2691
|
-
|
|
2692
|
-
|
|
2693
|
-
|
|
2694
|
-
|
|
2695
|
-
|
|
2696
|
-
|
|
2697
|
-
|
|
2698
|
-
|
|
2699
|
-
try {
|
|
2700
|
-
result = fn(...args);
|
|
2316
|
+
let currentPluginManager = null;
|
|
2317
|
+
const _getCurrentPluginManager = () => currentPluginManager;
|
|
2318
|
+
class Plugin {
|
|
2319
|
+
setup() { }
|
|
2320
|
+
}
|
|
2321
|
+
Plugin.id = "";
|
|
2322
|
+
class PluginManager {
|
|
2323
|
+
constructor(parent) {
|
|
2324
|
+
var _a;
|
|
2325
|
+
this.children = [];
|
|
2326
|
+
this.onDestroyCb = [];
|
|
2327
|
+
this.status = 0 /* STATUS.NEW */;
|
|
2328
|
+
this.parent = parent;
|
|
2329
|
+
(_a = this.parent) === null || _a === void 0 ? void 0 : _a.children.push(this);
|
|
2330
|
+
this.plugins = this.parent ? Object.create(this.parent.plugins) : {};
|
|
2331
|
+
}
|
|
2332
|
+
destroy() {
|
|
2333
|
+
for (let children of this.children) {
|
|
2334
|
+
children.destroy();
|
|
2701
2335
|
}
|
|
2702
|
-
|
|
2703
|
-
|
|
2336
|
+
const cbs = this.onDestroyCb;
|
|
2337
|
+
while (cbs.length) {
|
|
2338
|
+
cbs.pop()();
|
|
2704
2339
|
}
|
|
2705
|
-
|
|
2706
|
-
|
|
2340
|
+
this.status = 3 /* STATUS.DESTROYED */;
|
|
2341
|
+
}
|
|
2342
|
+
getPluginById(id) {
|
|
2343
|
+
return this.plugins[id] || null;
|
|
2344
|
+
}
|
|
2345
|
+
getPlugin(pluginType) {
|
|
2346
|
+
return this.getPluginById(pluginType.id);
|
|
2347
|
+
}
|
|
2348
|
+
startPlugins(pluginTypes) {
|
|
2349
|
+
const previousManager = currentPluginManager;
|
|
2350
|
+
currentPluginManager = this;
|
|
2351
|
+
const plugins = [];
|
|
2352
|
+
// instantiate plugins
|
|
2353
|
+
for (const pluginType of pluginTypes) {
|
|
2354
|
+
if (!pluginType.id) {
|
|
2355
|
+
currentPluginManager = previousManager;
|
|
2356
|
+
throw new OwlError(`Plugin "${pluginType.name}" has no id`);
|
|
2357
|
+
}
|
|
2358
|
+
if (this.plugins.hasOwnProperty(pluginType.id)) {
|
|
2359
|
+
continue;
|
|
2360
|
+
}
|
|
2361
|
+
const plugin = new pluginType();
|
|
2362
|
+
this.plugins[pluginType.id] = plugin;
|
|
2363
|
+
plugins.push(plugin);
|
|
2707
2364
|
}
|
|
2708
|
-
|
|
2709
|
-
|
|
2710
|
-
|
|
2711
|
-
Promise.race([
|
|
2712
|
-
result.catch(() => { }),
|
|
2713
|
-
new Promise((resolve) => setTimeout(() => resolve(TIMEOUT), timeout)),
|
|
2714
|
-
]).then((res) => {
|
|
2715
|
-
if (res === TIMEOUT && node.fiber === fiber && node.status <= 2) {
|
|
2716
|
-
timeoutError.message = `${hookName}'s promise hasn't resolved after ${timeout / 1000} seconds`;
|
|
2717
|
-
console.log(timeoutError);
|
|
2718
|
-
}
|
|
2719
|
-
});
|
|
2365
|
+
// setup phase
|
|
2366
|
+
for (let p of plugins) {
|
|
2367
|
+
p.setup();
|
|
2720
2368
|
}
|
|
2721
|
-
|
|
2722
|
-
|
|
2369
|
+
currentPluginManager = previousManager;
|
|
2370
|
+
if (!currentPluginManager) {
|
|
2371
|
+
this.status = 1 /* STATUS.MOUNTED */;
|
|
2372
|
+
}
|
|
2373
|
+
return plugins;
|
|
2374
|
+
}
|
|
2723
2375
|
}
|
|
2376
|
+
function plugin(pluginType) {
|
|
2377
|
+
// getCurrent will throw if we're not in a component
|
|
2378
|
+
const manager = currentPluginManager || getCurrent().pluginManager;
|
|
2379
|
+
let plugin = manager.getPluginById(pluginType.id);
|
|
2380
|
+
if (!plugin) {
|
|
2381
|
+
if (manager === currentPluginManager) {
|
|
2382
|
+
manager.startPlugins([pluginType]);
|
|
2383
|
+
plugin = manager.getPluginById(pluginType.id);
|
|
2384
|
+
}
|
|
2385
|
+
else {
|
|
2386
|
+
throw new OwlError(`Unknown plugin "${pluginType.id}"`);
|
|
2387
|
+
}
|
|
2388
|
+
}
|
|
2389
|
+
return plugin;
|
|
2390
|
+
}
|
|
2391
|
+
|
|
2724
2392
|
// -----------------------------------------------------------------------------
|
|
2725
2393
|
// hooks
|
|
2726
2394
|
// -----------------------------------------------------------------------------
|
|
2395
|
+
function decorate(node, f, hookName) {
|
|
2396
|
+
const result = f.bind(node.component);
|
|
2397
|
+
if (node.app.dev) {
|
|
2398
|
+
const suffix = f.name ? ` <${f.name}>` : "";
|
|
2399
|
+
Reflect.defineProperty(result, "name", {
|
|
2400
|
+
value: hookName + suffix,
|
|
2401
|
+
});
|
|
2402
|
+
}
|
|
2403
|
+
return result;
|
|
2404
|
+
}
|
|
2727
2405
|
function onWillStart(fn) {
|
|
2728
2406
|
const node = getCurrent();
|
|
2729
|
-
|
|
2730
|
-
node.willStart.push(decorate(fn.bind(node.component), "onWillStart"));
|
|
2407
|
+
node.willStart.push(decorate(node, fn, "onWillStart"));
|
|
2731
2408
|
}
|
|
2732
2409
|
function onWillUpdateProps(fn) {
|
|
2733
2410
|
const node = getCurrent();
|
|
2734
|
-
|
|
2735
|
-
node.willUpdateProps.push(decorate(fn.bind(node.component), "onWillUpdateProps"));
|
|
2411
|
+
node.willUpdateProps.push(decorate(node, fn, "onWillUpdateProps"));
|
|
2736
2412
|
}
|
|
2737
2413
|
function onMounted(fn) {
|
|
2738
2414
|
const node = getCurrent();
|
|
2739
|
-
|
|
2740
|
-
node.mounted.push(decorate(fn.bind(node.component), "onMounted"));
|
|
2415
|
+
node.mounted.push(decorate(node, fn, "onMounted"));
|
|
2741
2416
|
}
|
|
2742
2417
|
function onWillPatch(fn) {
|
|
2743
2418
|
const node = getCurrent();
|
|
2744
|
-
|
|
2745
|
-
node.willPatch.unshift(decorate(fn.bind(node.component), "onWillPatch"));
|
|
2419
|
+
node.willPatch.unshift(decorate(node, fn, "onWillPatch"));
|
|
2746
2420
|
}
|
|
2747
2421
|
function onPatched(fn) {
|
|
2748
2422
|
const node = getCurrent();
|
|
2749
|
-
|
|
2750
|
-
node.patched.push(decorate(fn.bind(node.component), "onPatched"));
|
|
2423
|
+
node.patched.push(decorate(node, fn, "onPatched"));
|
|
2751
2424
|
}
|
|
2752
2425
|
function onWillUnmount(fn) {
|
|
2753
2426
|
const node = getCurrent();
|
|
2754
|
-
|
|
2755
|
-
node.willUnmount.unshift(decorate(fn.bind(node.component), "onWillUnmount"));
|
|
2427
|
+
node.willUnmount.unshift(decorate(node, fn, "onWillUnmount"));
|
|
2756
2428
|
}
|
|
2757
2429
|
function onWillDestroy(fn) {
|
|
2758
|
-
const
|
|
2759
|
-
|
|
2760
|
-
|
|
2761
|
-
}
|
|
2762
|
-
|
|
2763
|
-
|
|
2764
|
-
|
|
2765
|
-
|
|
2766
|
-
fn = decorate(fn.bind(node.component), "onWillRender");
|
|
2767
|
-
node.renderFn = () => {
|
|
2768
|
-
fn();
|
|
2769
|
-
return renderFn();
|
|
2770
|
-
};
|
|
2771
|
-
}
|
|
2772
|
-
function onRendered(fn) {
|
|
2773
|
-
const node = getCurrent();
|
|
2774
|
-
const renderFn = node.renderFn;
|
|
2775
|
-
const decorate = node.app.dev ? wrapError : (fn) => fn;
|
|
2776
|
-
fn = decorate(fn.bind(node.component), "onRendered");
|
|
2777
|
-
node.renderFn = () => {
|
|
2778
|
-
const result = renderFn();
|
|
2779
|
-
fn();
|
|
2780
|
-
return result;
|
|
2781
|
-
};
|
|
2430
|
+
const pm = _getCurrentPluginManager();
|
|
2431
|
+
if (pm) {
|
|
2432
|
+
pm.onDestroyCb.push(fn);
|
|
2433
|
+
}
|
|
2434
|
+
else {
|
|
2435
|
+
const node = getCurrent();
|
|
2436
|
+
node.willDestroy.unshift(decorate(node, fn, "onWillDestroy"));
|
|
2437
|
+
}
|
|
2782
2438
|
}
|
|
2783
2439
|
function onError(callback) {
|
|
2784
2440
|
const node = getCurrent();
|
|
@@ -2791,9 +2447,7 @@ function onError(callback) {
|
|
|
2791
2447
|
}
|
|
2792
2448
|
|
|
2793
2449
|
class Component {
|
|
2794
|
-
constructor(
|
|
2795
|
-
this.props = props;
|
|
2796
|
-
this.env = env;
|
|
2450
|
+
constructor(node) {
|
|
2797
2451
|
this.__owl__ = node;
|
|
2798
2452
|
}
|
|
2799
2453
|
setup() { }
|
|
@@ -2803,83 +2457,6 @@ class Component {
|
|
|
2803
2457
|
}
|
|
2804
2458
|
Component.template = "";
|
|
2805
2459
|
|
|
2806
|
-
const VText = text("").constructor;
|
|
2807
|
-
class VPortal extends VText {
|
|
2808
|
-
constructor(selector, content) {
|
|
2809
|
-
super("");
|
|
2810
|
-
this.target = null;
|
|
2811
|
-
this.selector = selector;
|
|
2812
|
-
this.content = content;
|
|
2813
|
-
}
|
|
2814
|
-
mount(parent, anchor) {
|
|
2815
|
-
super.mount(parent, anchor);
|
|
2816
|
-
this.target = document.querySelector(this.selector);
|
|
2817
|
-
if (this.target) {
|
|
2818
|
-
this.content.mount(this.target, null);
|
|
2819
|
-
}
|
|
2820
|
-
else {
|
|
2821
|
-
this.content.mount(parent, anchor);
|
|
2822
|
-
}
|
|
2823
|
-
}
|
|
2824
|
-
beforeRemove() {
|
|
2825
|
-
this.content.beforeRemove();
|
|
2826
|
-
}
|
|
2827
|
-
remove() {
|
|
2828
|
-
if (this.content) {
|
|
2829
|
-
super.remove();
|
|
2830
|
-
this.content.remove();
|
|
2831
|
-
this.content = null;
|
|
2832
|
-
}
|
|
2833
|
-
}
|
|
2834
|
-
patch(other) {
|
|
2835
|
-
super.patch(other);
|
|
2836
|
-
if (this.content) {
|
|
2837
|
-
this.content.patch(other.content, true);
|
|
2838
|
-
}
|
|
2839
|
-
else {
|
|
2840
|
-
this.content = other.content;
|
|
2841
|
-
this.content.mount(this.target, null);
|
|
2842
|
-
}
|
|
2843
|
-
}
|
|
2844
|
-
}
|
|
2845
|
-
/**
|
|
2846
|
-
* kind of similar to <t t-slot="default"/>, but it wraps it around a VPortal
|
|
2847
|
-
*/
|
|
2848
|
-
function portalTemplate(app, bdom, helpers) {
|
|
2849
|
-
let { callSlot } = helpers;
|
|
2850
|
-
return function template(ctx, node, key = "") {
|
|
2851
|
-
return new VPortal(ctx.props.target, callSlot(ctx, node, key, "default", false, null));
|
|
2852
|
-
};
|
|
2853
|
-
}
|
|
2854
|
-
class Portal extends Component {
|
|
2855
|
-
setup() {
|
|
2856
|
-
const node = this.__owl__;
|
|
2857
|
-
onMounted(() => {
|
|
2858
|
-
const portal = node.bdom;
|
|
2859
|
-
if (!portal.target) {
|
|
2860
|
-
const target = document.querySelector(this.props.target);
|
|
2861
|
-
if (target) {
|
|
2862
|
-
portal.content.moveBeforeDOMNode(target.firstChild, target);
|
|
2863
|
-
}
|
|
2864
|
-
else {
|
|
2865
|
-
throw new OwlError("invalid portal target");
|
|
2866
|
-
}
|
|
2867
|
-
}
|
|
2868
|
-
});
|
|
2869
|
-
onWillUnmount(() => {
|
|
2870
|
-
const portal = node.bdom;
|
|
2871
|
-
portal.remove();
|
|
2872
|
-
});
|
|
2873
|
-
}
|
|
2874
|
-
}
|
|
2875
|
-
Portal.template = "__portal__";
|
|
2876
|
-
Portal.props = {
|
|
2877
|
-
target: {
|
|
2878
|
-
type: String,
|
|
2879
|
-
},
|
|
2880
|
-
slots: true,
|
|
2881
|
-
};
|
|
2882
|
-
|
|
2883
2460
|
// -----------------------------------------------------------------------------
|
|
2884
2461
|
// helpers
|
|
2885
2462
|
// -----------------------------------------------------------------------------
|
|
@@ -2930,7 +2507,7 @@ function validateSchema(obj, schema) {
|
|
|
2930
2507
|
if (Array.isArray(schema)) {
|
|
2931
2508
|
schema = toSchema(schema);
|
|
2932
2509
|
}
|
|
2933
|
-
obj = toRaw(obj);
|
|
2510
|
+
// obj = toRaw(obj);
|
|
2934
2511
|
let errors = [];
|
|
2935
2512
|
// check if each value in obj has correct shape
|
|
2936
2513
|
for (let key in obj) {
|
|
@@ -3003,33 +2580,555 @@ function validateType(key, value, descr) {
|
|
|
3003
2580
|
if (typeof value !== "object" || Array.isArray(value)) {
|
|
3004
2581
|
result = `'${key}' is not an object`;
|
|
3005
2582
|
}
|
|
3006
|
-
else {
|
|
3007
|
-
const errors = validateSchema(value, descr.shape);
|
|
3008
|
-
if (errors.length) {
|
|
3009
|
-
result = `'${key}' doesn't have the correct shape (${errors.join(", ")})`;
|
|
3010
|
-
}
|
|
2583
|
+
else {
|
|
2584
|
+
const errors = validateSchema(value, descr.shape);
|
|
2585
|
+
if (errors.length) {
|
|
2586
|
+
result = `'${key}' doesn't have the correct shape (${errors.join(", ")})`;
|
|
2587
|
+
}
|
|
2588
|
+
}
|
|
2589
|
+
}
|
|
2590
|
+
else if ("values" in descr) {
|
|
2591
|
+
if (typeof value !== "object" || Array.isArray(value)) {
|
|
2592
|
+
result = `'${key}' is not an object`;
|
|
2593
|
+
}
|
|
2594
|
+
else {
|
|
2595
|
+
const errors = Object.entries(value)
|
|
2596
|
+
.map(([key, value]) => validateType(key, value, descr.values))
|
|
2597
|
+
.filter(Boolean);
|
|
2598
|
+
if (errors.length) {
|
|
2599
|
+
result = `some of the values in '${key}' are invalid (${errors.join(", ")})`;
|
|
2600
|
+
}
|
|
2601
|
+
}
|
|
2602
|
+
}
|
|
2603
|
+
if ("type" in descr && !result) {
|
|
2604
|
+
result = validateType(key, value, descr.type);
|
|
2605
|
+
}
|
|
2606
|
+
if ("validate" in descr && !result) {
|
|
2607
|
+
result = !descr.validate(value) ? `'${key}' is not valid` : null;
|
|
2608
|
+
}
|
|
2609
|
+
return result;
|
|
2610
|
+
}
|
|
2611
|
+
|
|
2612
|
+
function validateProps(componentName, props, validation, keys) {
|
|
2613
|
+
const propsToValidate = Object.create(null);
|
|
2614
|
+
const errors = [];
|
|
2615
|
+
for (const key of keys) {
|
|
2616
|
+
if (key in props) {
|
|
2617
|
+
propsToValidate[key] = props[key];
|
|
2618
|
+
}
|
|
2619
|
+
if (propsToValidate[key] === undefined &&
|
|
2620
|
+
!Array.isArray(validation) &&
|
|
2621
|
+
validation[key].defaultValue !== undefined) {
|
|
2622
|
+
if (!validation[key].optional) {
|
|
2623
|
+
errors.push(`A default value cannot be defined for the mandatory prop '${key}'`);
|
|
2624
|
+
continue;
|
|
2625
|
+
}
|
|
2626
|
+
propsToValidate[key] = validation[key].defaultValue;
|
|
2627
|
+
}
|
|
2628
|
+
}
|
|
2629
|
+
errors.push(...validateSchema(propsToValidate, validation));
|
|
2630
|
+
if (errors.length) {
|
|
2631
|
+
throw new OwlError(`Invalid props for component '${componentName}': ` + errors.join(", "));
|
|
2632
|
+
// node.app.handleError({
|
|
2633
|
+
// error: new OwlError(`Invalid props for component '${componentName}': ` + errors.join(", ")),
|
|
2634
|
+
// node,
|
|
2635
|
+
// });
|
|
2636
|
+
}
|
|
2637
|
+
}
|
|
2638
|
+
function props(validation) {
|
|
2639
|
+
const node = getCurrent();
|
|
2640
|
+
const isSchemaValidated = validation && !Array.isArray(validation);
|
|
2641
|
+
function getProp(key) {
|
|
2642
|
+
if (isSchemaValidated && node.props[key] === undefined) {
|
|
2643
|
+
return validation[key].defaultValue;
|
|
2644
|
+
}
|
|
2645
|
+
return node.props[key];
|
|
2646
|
+
}
|
|
2647
|
+
const result = Object.create(null);
|
|
2648
|
+
function applyPropGetters(keys) {
|
|
2649
|
+
for (const key of keys) {
|
|
2650
|
+
Reflect.defineProperty(result, key, {
|
|
2651
|
+
enumerable: true,
|
|
2652
|
+
get: getProp.bind(null, key),
|
|
2653
|
+
});
|
|
2654
|
+
}
|
|
2655
|
+
}
|
|
2656
|
+
if (validation) {
|
|
2657
|
+
const keys = (isSchemaValidated ? Object.keys(validation) : validation).map((key) => key.endsWith("?") ? key.slice(0, -1) : key);
|
|
2658
|
+
applyPropGetters(keys);
|
|
2659
|
+
if (node.app.dev) {
|
|
2660
|
+
validateProps(node.name, node.props, validation, keys);
|
|
2661
|
+
node.willUpdateProps.push((np) => {
|
|
2662
|
+
validateProps(node.name, np, validation, keys);
|
|
2663
|
+
});
|
|
2664
|
+
}
|
|
2665
|
+
}
|
|
2666
|
+
else {
|
|
2667
|
+
applyPropGetters(Object.keys(node.props));
|
|
2668
|
+
node.willUpdateProps.push((np) => {
|
|
2669
|
+
for (let key in result) {
|
|
2670
|
+
Reflect.deleteProperty(result, key);
|
|
2671
|
+
}
|
|
2672
|
+
applyPropGetters(Object.keys(np));
|
|
2673
|
+
});
|
|
2674
|
+
}
|
|
2675
|
+
return result;
|
|
2676
|
+
}
|
|
2677
|
+
|
|
2678
|
+
const VText = text("").constructor;
|
|
2679
|
+
class VPortal extends VText {
|
|
2680
|
+
constructor(selector, content) {
|
|
2681
|
+
super("");
|
|
2682
|
+
this.target = null;
|
|
2683
|
+
this.selector = selector;
|
|
2684
|
+
this.content = content;
|
|
2685
|
+
}
|
|
2686
|
+
mount(parent, anchor) {
|
|
2687
|
+
super.mount(parent, anchor);
|
|
2688
|
+
this.target = document.querySelector(this.selector);
|
|
2689
|
+
if (this.target) {
|
|
2690
|
+
this.content.mount(this.target, null);
|
|
2691
|
+
}
|
|
2692
|
+
else {
|
|
2693
|
+
this.content.mount(parent, anchor);
|
|
2694
|
+
}
|
|
2695
|
+
}
|
|
2696
|
+
beforeRemove() {
|
|
2697
|
+
this.content.beforeRemove();
|
|
2698
|
+
}
|
|
2699
|
+
remove() {
|
|
2700
|
+
if (this.content) {
|
|
2701
|
+
super.remove();
|
|
2702
|
+
this.content.remove();
|
|
2703
|
+
this.content = null;
|
|
2704
|
+
}
|
|
2705
|
+
}
|
|
2706
|
+
patch(other) {
|
|
2707
|
+
super.patch(other);
|
|
2708
|
+
if (this.content) {
|
|
2709
|
+
this.content.patch(other.content, true);
|
|
2710
|
+
}
|
|
2711
|
+
else {
|
|
2712
|
+
this.content = other.content;
|
|
2713
|
+
this.content.mount(this.target, null);
|
|
2714
|
+
}
|
|
2715
|
+
}
|
|
2716
|
+
}
|
|
2717
|
+
/**
|
|
2718
|
+
* kind of similar to <t t-slot="default"/>, but it wraps it around a VPortal
|
|
2719
|
+
*/
|
|
2720
|
+
function portalTemplate(app, bdom, helpers) {
|
|
2721
|
+
let { callSlot } = helpers;
|
|
2722
|
+
return function template(ctx, node, key = "") {
|
|
2723
|
+
return new VPortal(ctx.this.props.target, callSlot(ctx, node, key, "default", false, null));
|
|
2724
|
+
};
|
|
2725
|
+
}
|
|
2726
|
+
class Portal extends Component {
|
|
2727
|
+
constructor() {
|
|
2728
|
+
super(...arguments);
|
|
2729
|
+
this.props = props({
|
|
2730
|
+
target: String,
|
|
2731
|
+
slots: true,
|
|
2732
|
+
});
|
|
2733
|
+
}
|
|
2734
|
+
setup() {
|
|
2735
|
+
const node = this.__owl__;
|
|
2736
|
+
onMounted(() => {
|
|
2737
|
+
const portal = node.bdom;
|
|
2738
|
+
if (!portal.target) {
|
|
2739
|
+
const target = document.querySelector(node.props.target);
|
|
2740
|
+
if (target) {
|
|
2741
|
+
portal.content.moveBeforeDOMNode(target.firstChild, target);
|
|
2742
|
+
}
|
|
2743
|
+
else {
|
|
2744
|
+
throw new OwlError("invalid portal target");
|
|
2745
|
+
}
|
|
2746
|
+
}
|
|
2747
|
+
});
|
|
2748
|
+
onWillUnmount(() => {
|
|
2749
|
+
const portal = node.bdom;
|
|
2750
|
+
portal.remove();
|
|
2751
|
+
});
|
|
2752
|
+
}
|
|
2753
|
+
}
|
|
2754
|
+
Portal.template = "__portal__";
|
|
2755
|
+
|
|
2756
|
+
// Special key to subscribe to, to be notified of key creation/deletion
|
|
2757
|
+
const KEYCHANGES = Symbol("Key changes");
|
|
2758
|
+
const objectToString = Object.prototype.toString;
|
|
2759
|
+
const objectHasOwnProperty = Object.prototype.hasOwnProperty;
|
|
2760
|
+
// Use arrays because Array.includes is faster than Set.has for small arrays
|
|
2761
|
+
const SUPPORTED_RAW_TYPES = ["Object", "Array", "Set", "Map", "WeakMap"];
|
|
2762
|
+
const COLLECTION_RAW_TYPES = ["Set", "Map", "WeakMap"];
|
|
2763
|
+
/**
|
|
2764
|
+
* extract "RawType" from strings like "[object RawType]" => this lets us ignore
|
|
2765
|
+
* many native objects such as Promise (whose toString is [object Promise])
|
|
2766
|
+
* or Date ([object Date]), while also supporting collections without using
|
|
2767
|
+
* instanceof in a loop
|
|
2768
|
+
*
|
|
2769
|
+
* @param obj the object to check
|
|
2770
|
+
* @returns the raw type of the object
|
|
2771
|
+
*/
|
|
2772
|
+
function rawType(obj) {
|
|
2773
|
+
return objectToString.call(toRaw(obj)).slice(8, -1);
|
|
2774
|
+
}
|
|
2775
|
+
/**
|
|
2776
|
+
* Checks whether a given value can be made into a proxy object.
|
|
2777
|
+
*
|
|
2778
|
+
* @param value the value to check
|
|
2779
|
+
* @returns whether the value can be made proxy
|
|
2780
|
+
*/
|
|
2781
|
+
function canBeMadeReactive(value) {
|
|
2782
|
+
if (typeof value !== "object") {
|
|
2783
|
+
return false;
|
|
2784
|
+
}
|
|
2785
|
+
return SUPPORTED_RAW_TYPES.includes(rawType(value));
|
|
2786
|
+
}
|
|
2787
|
+
/**
|
|
2788
|
+
* Creates a proxy from the given object/callback if possible and returns it,
|
|
2789
|
+
* returns the original object otherwise.
|
|
2790
|
+
*
|
|
2791
|
+
* @param value the value make proxy
|
|
2792
|
+
* @returns a proxy for the given object when possible, the original otherwise
|
|
2793
|
+
*/
|
|
2794
|
+
function possiblyReactive(val) {
|
|
2795
|
+
return canBeMadeReactive(val) ? proxy(val) : val;
|
|
2796
|
+
}
|
|
2797
|
+
const skipped = new WeakSet();
|
|
2798
|
+
/**
|
|
2799
|
+
* Mark an object or array so that it is ignored by the reactivity system
|
|
2800
|
+
*
|
|
2801
|
+
* @param value the value to mark
|
|
2802
|
+
* @returns the object itself
|
|
2803
|
+
*/
|
|
2804
|
+
function markRaw(value) {
|
|
2805
|
+
skipped.add(value);
|
|
2806
|
+
return value;
|
|
2807
|
+
}
|
|
2808
|
+
/**
|
|
2809
|
+
* Given a proxy objet, return the raw (non proxy) underlying object
|
|
2810
|
+
*
|
|
2811
|
+
* @param value a proxy value
|
|
2812
|
+
* @returns the underlying value
|
|
2813
|
+
*/
|
|
2814
|
+
function toRaw(value) {
|
|
2815
|
+
return targets.has(value) ? targets.get(value) : value;
|
|
2816
|
+
}
|
|
2817
|
+
const targetToKeysToAtomItem = new WeakMap();
|
|
2818
|
+
function getTargetKeyAtom(target, key) {
|
|
2819
|
+
let keyToAtomItem = targetToKeysToAtomItem.get(target);
|
|
2820
|
+
if (!keyToAtomItem) {
|
|
2821
|
+
keyToAtomItem = new Map();
|
|
2822
|
+
targetToKeysToAtomItem.set(target, keyToAtomItem);
|
|
2823
|
+
}
|
|
2824
|
+
let atom = keyToAtomItem.get(key);
|
|
2825
|
+
if (!atom) {
|
|
2826
|
+
atom = {
|
|
2827
|
+
value: undefined,
|
|
2828
|
+
observers: new Set(),
|
|
2829
|
+
};
|
|
2830
|
+
keyToAtomItem.set(key, atom);
|
|
2831
|
+
}
|
|
2832
|
+
return atom;
|
|
2833
|
+
}
|
|
2834
|
+
/**
|
|
2835
|
+
* Observes a given key on a target with an callback. The callback will be
|
|
2836
|
+
* called when the given key changes on the target.
|
|
2837
|
+
*
|
|
2838
|
+
* @param target the target whose key should be observed
|
|
2839
|
+
* @param key the key to observe (or Symbol(KEYCHANGES) for key creation
|
|
2840
|
+
* or deletion)
|
|
2841
|
+
* @param callback the function to call when the key changes
|
|
2842
|
+
*/
|
|
2843
|
+
function onReadTargetKey(target, key) {
|
|
2844
|
+
onReadAtom(getTargetKeyAtom(target, key));
|
|
2845
|
+
}
|
|
2846
|
+
/**
|
|
2847
|
+
* Notify Reactives that are observing a given target that a key has changed on
|
|
2848
|
+
* the target.
|
|
2849
|
+
*
|
|
2850
|
+
* @param target target whose Reactives should be notified that the target was
|
|
2851
|
+
* changed.
|
|
2852
|
+
* @param key the key that changed (or Symbol `KEYCHANGES` if a key was created
|
|
2853
|
+
* or deleted)
|
|
2854
|
+
*/
|
|
2855
|
+
function onWriteTargetKey(target, key) {
|
|
2856
|
+
const keyToAtomItem = targetToKeysToAtomItem.get(target);
|
|
2857
|
+
if (!keyToAtomItem) {
|
|
2858
|
+
return;
|
|
2859
|
+
}
|
|
2860
|
+
const atom = keyToAtomItem.get(key);
|
|
2861
|
+
if (!atom) {
|
|
2862
|
+
return;
|
|
2863
|
+
}
|
|
2864
|
+
onWriteAtom(atom);
|
|
2865
|
+
}
|
|
2866
|
+
// Maps proxy objects to the underlying target
|
|
2867
|
+
const targets = new WeakMap();
|
|
2868
|
+
const proxyCache = new WeakMap();
|
|
2869
|
+
/**
|
|
2870
|
+
* Creates a reactive proxy for an object. Reading data on the proxy object
|
|
2871
|
+
* subscribes to changes to the data. Writing data on the object will cause the
|
|
2872
|
+
* notify callback to be called if there are suscriptions to that data. Nested
|
|
2873
|
+
* objects and arrays are automatically made reactive as well.
|
|
2874
|
+
*
|
|
2875
|
+
* Whenever you are notified of a change, all subscriptions are cleared, and if
|
|
2876
|
+
* you would like to be notified of any further changes, you should go read
|
|
2877
|
+
* the underlying data again. We assume that if you don't go read it again after
|
|
2878
|
+
* being notified, it means that you are no longer interested in that data.
|
|
2879
|
+
*
|
|
2880
|
+
* Subscriptions:
|
|
2881
|
+
* + Reading a property on an object will subscribe you to changes in the value
|
|
2882
|
+
* of that property.
|
|
2883
|
+
* + Accessing an object's keys (eg with Object.keys or with `for..in`) will
|
|
2884
|
+
* subscribe you to the creation/deletion of keys. Checking the presence of a
|
|
2885
|
+
* key on the object with 'in' has the same effect.
|
|
2886
|
+
* - getOwnPropertyDescriptor does not currently subscribe you to the property.
|
|
2887
|
+
* This is a choice that was made because changing a key's value will trigger
|
|
2888
|
+
* this trap and we do not want to subscribe by writes. This also means that
|
|
2889
|
+
* Object.hasOwnProperty doesn't subscribe as it goes through this trap.
|
|
2890
|
+
*
|
|
2891
|
+
* @param target the object for which to create a proxy proxy
|
|
2892
|
+
* @param callback the function to call when an observed property of the
|
|
2893
|
+
* proxy has changed
|
|
2894
|
+
* @returns a proxy that tracks changes to it
|
|
2895
|
+
*/
|
|
2896
|
+
function proxy(target) {
|
|
2897
|
+
if (!canBeMadeReactive(target)) {
|
|
2898
|
+
throw new OwlError(`Cannot make the given value reactive`);
|
|
2899
|
+
}
|
|
2900
|
+
if (skipped.has(target)) {
|
|
2901
|
+
return target;
|
|
2902
|
+
}
|
|
2903
|
+
if (targets.has(target)) {
|
|
2904
|
+
// target is reactive, create a reactive on the underlying object instead
|
|
2905
|
+
return target;
|
|
2906
|
+
}
|
|
2907
|
+
const reactive = proxyCache.get(target);
|
|
2908
|
+
if (reactive)
|
|
2909
|
+
return reactive;
|
|
2910
|
+
const targetRawType = rawType(target);
|
|
2911
|
+
const handler = COLLECTION_RAW_TYPES.includes(targetRawType)
|
|
2912
|
+
? collectionsProxyHandler(target, targetRawType)
|
|
2913
|
+
: basicProxyHandler();
|
|
2914
|
+
const proxy = new Proxy(target, handler);
|
|
2915
|
+
proxyCache.set(target, proxy);
|
|
2916
|
+
targets.set(proxy, target);
|
|
2917
|
+
return proxy;
|
|
2918
|
+
}
|
|
2919
|
+
/**
|
|
2920
|
+
* Creates a basic proxy handler for regular objects and arrays.
|
|
2921
|
+
*
|
|
2922
|
+
* @param callback @see proxy
|
|
2923
|
+
* @returns a proxy handler object
|
|
2924
|
+
*/
|
|
2925
|
+
function basicProxyHandler() {
|
|
2926
|
+
return {
|
|
2927
|
+
get(target, key, receiver) {
|
|
2928
|
+
// non-writable non-configurable properties cannot be made proxy
|
|
2929
|
+
const desc = Object.getOwnPropertyDescriptor(target, key);
|
|
2930
|
+
if (desc && !desc.writable && !desc.configurable) {
|
|
2931
|
+
return Reflect.get(target, key, receiver);
|
|
2932
|
+
}
|
|
2933
|
+
onReadTargetKey(target, key);
|
|
2934
|
+
return possiblyReactive(Reflect.get(target, key, receiver));
|
|
2935
|
+
},
|
|
2936
|
+
set(target, key, value, receiver) {
|
|
2937
|
+
const hadKey = objectHasOwnProperty.call(target, key);
|
|
2938
|
+
const originalValue = Reflect.get(target, key, receiver);
|
|
2939
|
+
const ret = Reflect.set(target, key, toRaw(value), receiver);
|
|
2940
|
+
if (!hadKey && objectHasOwnProperty.call(target, key)) {
|
|
2941
|
+
onWriteTargetKey(target, KEYCHANGES);
|
|
2942
|
+
}
|
|
2943
|
+
// While Array length may trigger the set trap, it's not actually set by this
|
|
2944
|
+
// method but is updated behind the scenes, and the trap is not called with the
|
|
2945
|
+
// new value. We disable the "same-value-optimization" for it because of that.
|
|
2946
|
+
if (originalValue !== Reflect.get(target, key, receiver) ||
|
|
2947
|
+
(key === "length" && Array.isArray(target))) {
|
|
2948
|
+
onWriteTargetKey(target, key);
|
|
2949
|
+
}
|
|
2950
|
+
return ret;
|
|
2951
|
+
},
|
|
2952
|
+
deleteProperty(target, key) {
|
|
2953
|
+
const ret = Reflect.deleteProperty(target, key);
|
|
2954
|
+
// TODO: only notify when something was actually deleted
|
|
2955
|
+
onWriteTargetKey(target, KEYCHANGES);
|
|
2956
|
+
onWriteTargetKey(target, key);
|
|
2957
|
+
return ret;
|
|
2958
|
+
},
|
|
2959
|
+
ownKeys(target) {
|
|
2960
|
+
onReadTargetKey(target, KEYCHANGES);
|
|
2961
|
+
return Reflect.ownKeys(target);
|
|
2962
|
+
},
|
|
2963
|
+
has(target, key) {
|
|
2964
|
+
// TODO: this observes all key changes instead of only the presence of the argument key
|
|
2965
|
+
// observing the key itself would observe value changes instead of presence changes
|
|
2966
|
+
// so we may need a finer grained system to distinguish observing value vs presence.
|
|
2967
|
+
onReadTargetKey(target, KEYCHANGES);
|
|
2968
|
+
return Reflect.has(target, key);
|
|
2969
|
+
},
|
|
2970
|
+
};
|
|
2971
|
+
}
|
|
2972
|
+
/**
|
|
2973
|
+
* Creates a function that will observe the key that is passed to it when called
|
|
2974
|
+
* and delegates to the underlying method.
|
|
2975
|
+
*
|
|
2976
|
+
* @param methodName name of the method to delegate to
|
|
2977
|
+
* @param target @see proxy
|
|
2978
|
+
* @param callback @see proxy
|
|
2979
|
+
*/
|
|
2980
|
+
function makeKeyObserver(methodName, target) {
|
|
2981
|
+
return (key) => {
|
|
2982
|
+
key = toRaw(key);
|
|
2983
|
+
onReadTargetKey(target, key);
|
|
2984
|
+
return possiblyReactive(target[methodName](key));
|
|
2985
|
+
};
|
|
2986
|
+
}
|
|
2987
|
+
/**
|
|
2988
|
+
* Creates an iterable that will delegate to the underlying iteration method and
|
|
2989
|
+
* observe keys as necessary.
|
|
2990
|
+
*
|
|
2991
|
+
* @param methodName name of the method to delegate to
|
|
2992
|
+
* @param target @see proxy
|
|
2993
|
+
* @param callback @see proxy
|
|
2994
|
+
*/
|
|
2995
|
+
function makeIteratorObserver(methodName, target) {
|
|
2996
|
+
return function* () {
|
|
2997
|
+
onReadTargetKey(target, KEYCHANGES);
|
|
2998
|
+
const keys = target.keys();
|
|
2999
|
+
for (const item of target[methodName]()) {
|
|
3000
|
+
const key = keys.next().value;
|
|
3001
|
+
onReadTargetKey(target, key);
|
|
3002
|
+
yield possiblyReactive(item);
|
|
3003
|
+
}
|
|
3004
|
+
};
|
|
3005
|
+
}
|
|
3006
|
+
/**
|
|
3007
|
+
* Creates a forEach function that will delegate to forEach on the underlying
|
|
3008
|
+
* collection while observing key changes, and keys as they're iterated over,
|
|
3009
|
+
* and making the passed keys/values proxy.
|
|
3010
|
+
*
|
|
3011
|
+
* @param target @see proxy
|
|
3012
|
+
* @param callback @see proxy
|
|
3013
|
+
*/
|
|
3014
|
+
function makeForEachObserver(target) {
|
|
3015
|
+
return function forEach(forEachCb, thisArg) {
|
|
3016
|
+
onReadTargetKey(target, KEYCHANGES);
|
|
3017
|
+
target.forEach(function (val, key, targetObj) {
|
|
3018
|
+
onReadTargetKey(target, key);
|
|
3019
|
+
forEachCb.call(thisArg, possiblyReactive(val), possiblyReactive(key), possiblyReactive(targetObj));
|
|
3020
|
+
}, thisArg);
|
|
3021
|
+
};
|
|
3022
|
+
}
|
|
3023
|
+
/**
|
|
3024
|
+
* Creates a function that will delegate to an underlying method, and check if
|
|
3025
|
+
* that method has modified the presence or value of a key, and notify the
|
|
3026
|
+
* proxys appropriately.
|
|
3027
|
+
*
|
|
3028
|
+
* @param setterName name of the method to delegate to
|
|
3029
|
+
* @param getterName name of the method which should be used to retrieve the
|
|
3030
|
+
* value before calling the delegate method for comparison purposes
|
|
3031
|
+
* @param target @see proxy
|
|
3032
|
+
*/
|
|
3033
|
+
function delegateAndNotify(setterName, getterName, target) {
|
|
3034
|
+
return (key, value) => {
|
|
3035
|
+
key = toRaw(key);
|
|
3036
|
+
const hadKey = target.has(key);
|
|
3037
|
+
const originalValue = target[getterName](key);
|
|
3038
|
+
const ret = target[setterName](key, value);
|
|
3039
|
+
const hasKey = target.has(key);
|
|
3040
|
+
if (hadKey !== hasKey) {
|
|
3041
|
+
onWriteTargetKey(target, KEYCHANGES);
|
|
3042
|
+
}
|
|
3043
|
+
if (originalValue !== target[getterName](key)) {
|
|
3044
|
+
onWriteTargetKey(target, key);
|
|
3011
3045
|
}
|
|
3012
|
-
|
|
3013
|
-
|
|
3014
|
-
|
|
3015
|
-
|
|
3046
|
+
return ret;
|
|
3047
|
+
};
|
|
3048
|
+
}
|
|
3049
|
+
/**
|
|
3050
|
+
* Creates a function that will clear the underlying collection and notify that
|
|
3051
|
+
* the keys of the collection have changed.
|
|
3052
|
+
*
|
|
3053
|
+
* @param target @see proxy
|
|
3054
|
+
*/
|
|
3055
|
+
function makeClearNotifier(target) {
|
|
3056
|
+
return () => {
|
|
3057
|
+
const allKeys = [...target.keys()];
|
|
3058
|
+
target.clear();
|
|
3059
|
+
onWriteTargetKey(target, KEYCHANGES);
|
|
3060
|
+
for (const key of allKeys) {
|
|
3061
|
+
onWriteTargetKey(target, key);
|
|
3016
3062
|
}
|
|
3017
|
-
|
|
3018
|
-
|
|
3019
|
-
|
|
3020
|
-
|
|
3021
|
-
|
|
3022
|
-
|
|
3063
|
+
};
|
|
3064
|
+
}
|
|
3065
|
+
/**
|
|
3066
|
+
* Maps raw type of an object to an object containing functions that can be used
|
|
3067
|
+
* to build an appropritate proxy handler for that raw type. Eg: when making a
|
|
3068
|
+
* proxy set, calling the has method should mark the key that is being
|
|
3069
|
+
* retrieved as observed, and calling the add or delete method should notify the
|
|
3070
|
+
* proxys that the key which is being added or deleted has been modified.
|
|
3071
|
+
*/
|
|
3072
|
+
const rawTypeToFuncHandlers = {
|
|
3073
|
+
Set: (target) => ({
|
|
3074
|
+
has: makeKeyObserver("has", target),
|
|
3075
|
+
add: delegateAndNotify("add", "has", target),
|
|
3076
|
+
delete: delegateAndNotify("delete", "has", target),
|
|
3077
|
+
keys: makeIteratorObserver("keys", target),
|
|
3078
|
+
values: makeIteratorObserver("values", target),
|
|
3079
|
+
entries: makeIteratorObserver("entries", target),
|
|
3080
|
+
[Symbol.iterator]: makeIteratorObserver(Symbol.iterator, target),
|
|
3081
|
+
forEach: makeForEachObserver(target),
|
|
3082
|
+
clear: makeClearNotifier(target),
|
|
3083
|
+
get size() {
|
|
3084
|
+
onReadTargetKey(target, KEYCHANGES);
|
|
3085
|
+
return target.size;
|
|
3086
|
+
},
|
|
3087
|
+
}),
|
|
3088
|
+
Map: (target) => ({
|
|
3089
|
+
has: makeKeyObserver("has", target),
|
|
3090
|
+
get: makeKeyObserver("get", target),
|
|
3091
|
+
set: delegateAndNotify("set", "get", target),
|
|
3092
|
+
delete: delegateAndNotify("delete", "has", target),
|
|
3093
|
+
keys: makeIteratorObserver("keys", target),
|
|
3094
|
+
values: makeIteratorObserver("values", target),
|
|
3095
|
+
entries: makeIteratorObserver("entries", target),
|
|
3096
|
+
[Symbol.iterator]: makeIteratorObserver(Symbol.iterator, target),
|
|
3097
|
+
forEach: makeForEachObserver(target),
|
|
3098
|
+
clear: makeClearNotifier(target),
|
|
3099
|
+
get size() {
|
|
3100
|
+
onReadTargetKey(target, KEYCHANGES);
|
|
3101
|
+
return target.size;
|
|
3102
|
+
},
|
|
3103
|
+
}),
|
|
3104
|
+
WeakMap: (target) => ({
|
|
3105
|
+
has: makeKeyObserver("has", target),
|
|
3106
|
+
get: makeKeyObserver("get", target),
|
|
3107
|
+
set: delegateAndNotify("set", "get", target),
|
|
3108
|
+
delete: delegateAndNotify("delete", "has", target),
|
|
3109
|
+
}),
|
|
3110
|
+
};
|
|
3111
|
+
/**
|
|
3112
|
+
* Creates a proxy handler for collections (Set/Map/WeakMap)
|
|
3113
|
+
*
|
|
3114
|
+
* @param callback @see proxy
|
|
3115
|
+
* @param target @see proxy
|
|
3116
|
+
* @returns a proxy handler object
|
|
3117
|
+
*/
|
|
3118
|
+
function collectionsProxyHandler(target, targetRawType) {
|
|
3119
|
+
// TODO: if performance is an issue we can create the special handlers lazily when each
|
|
3120
|
+
// property is read.
|
|
3121
|
+
const specialHandlers = rawTypeToFuncHandlers[targetRawType](target);
|
|
3122
|
+
return Object.assign(basicProxyHandler(), {
|
|
3123
|
+
// FIXME: probably broken when part of prototype chain since we ignore the receiver
|
|
3124
|
+
get(target, key) {
|
|
3125
|
+
if (objectHasOwnProperty.call(specialHandlers, key)) {
|
|
3126
|
+
return specialHandlers[key];
|
|
3023
3127
|
}
|
|
3024
|
-
|
|
3025
|
-
|
|
3026
|
-
|
|
3027
|
-
|
|
3028
|
-
}
|
|
3029
|
-
if ("validate" in descr && !result) {
|
|
3030
|
-
result = !descr.validate(value) ? `'${key}' is not valid` : null;
|
|
3031
|
-
}
|
|
3032
|
-
return result;
|
|
3128
|
+
onReadTargetKey(target, key);
|
|
3129
|
+
return possiblyReactive(target[key]);
|
|
3130
|
+
},
|
|
3131
|
+
});
|
|
3033
3132
|
}
|
|
3034
3133
|
|
|
3035
3134
|
const ObjectCreate = Object.create;
|
|
@@ -3042,7 +3141,7 @@ function withDefault(value, defaultValue) {
|
|
|
3042
3141
|
}
|
|
3043
3142
|
function callSlot(ctx, parent, key, name, dynamic, extra, defaultContent) {
|
|
3044
3143
|
key = key + "__slot_" + name;
|
|
3045
|
-
const slots = ctx.props.slots || {};
|
|
3144
|
+
const slots = ctx.__owl__.props.slots || {};
|
|
3046
3145
|
const { __render, __ctx, __scope } = slots[name] || {};
|
|
3047
3146
|
const slotScope = ObjectCreate(__ctx || {});
|
|
3048
3147
|
if (__scope) {
|
|
@@ -3147,84 +3246,52 @@ function safeOutput(value, defaultValue) {
|
|
|
3147
3246
|
}
|
|
3148
3247
|
let safeKey;
|
|
3149
3248
|
let block;
|
|
3150
|
-
|
|
3151
|
-
|
|
3152
|
-
|
|
3153
|
-
|
|
3154
|
-
|
|
3155
|
-
|
|
3156
|
-
|
|
3157
|
-
|
|
3158
|
-
|
|
3159
|
-
|
|
3160
|
-
|
|
3161
|
-
safeKey = "string_unsafe";
|
|
3162
|
-
block = text(value);
|
|
3163
|
-
}
|
|
3164
|
-
else {
|
|
3165
|
-
// Assuming it is a block
|
|
3166
|
-
safeKey = "block_safe";
|
|
3167
|
-
block = value;
|
|
3168
|
-
}
|
|
3169
|
-
break;
|
|
3170
|
-
case "string":
|
|
3171
|
-
safeKey = "string_unsafe";
|
|
3172
|
-
block = text(value);
|
|
3173
|
-
break;
|
|
3174
|
-
default:
|
|
3175
|
-
safeKey = "string_unsafe";
|
|
3176
|
-
block = text(String(value));
|
|
3249
|
+
if (value instanceof Markup) {
|
|
3250
|
+
safeKey = `string_safe`;
|
|
3251
|
+
block = html(value);
|
|
3252
|
+
}
|
|
3253
|
+
else if (value instanceof LazyValue) {
|
|
3254
|
+
safeKey = `lazy_value`;
|
|
3255
|
+
block = value.evaluate();
|
|
3256
|
+
}
|
|
3257
|
+
else {
|
|
3258
|
+
safeKey = "string_unsafe";
|
|
3259
|
+
block = text(value);
|
|
3177
3260
|
}
|
|
3178
3261
|
return toggler(safeKey, block);
|
|
3179
3262
|
}
|
|
3180
|
-
|
|
3181
|
-
|
|
3182
|
-
|
|
3183
|
-
* visit recursively the props and all the children to check if they are valid.
|
|
3184
|
-
* This is why it is only done in 'dev' mode.
|
|
3185
|
-
*/
|
|
3186
|
-
function validateProps(name, props, comp) {
|
|
3187
|
-
const ComponentClass = typeof name !== "string"
|
|
3188
|
-
? name
|
|
3189
|
-
: comp.constructor.components[name];
|
|
3190
|
-
if (!ComponentClass) {
|
|
3191
|
-
// this is an error, wrong component. We silently return here instead so the
|
|
3192
|
-
// error is triggered by the usual path ('component' function)
|
|
3193
|
-
return;
|
|
3263
|
+
function createRef(ref) {
|
|
3264
|
+
if (!ref) {
|
|
3265
|
+
throw new OwlError(`Ref is undefined or null`);
|
|
3194
3266
|
}
|
|
3195
|
-
|
|
3196
|
-
|
|
3197
|
-
|
|
3198
|
-
|
|
3199
|
-
|
|
3200
|
-
return;
|
|
3267
|
+
let add;
|
|
3268
|
+
let remove;
|
|
3269
|
+
if (ref.add && ref.remove) {
|
|
3270
|
+
add = ref.add.bind(ref);
|
|
3271
|
+
remove = ref.remove.bind(ref);
|
|
3201
3272
|
}
|
|
3202
|
-
|
|
3203
|
-
|
|
3204
|
-
|
|
3205
|
-
? schema.includes(name)
|
|
3206
|
-
: name in schema && !("*" in schema) && !isOptional(schema[name]);
|
|
3207
|
-
for (let p in defaultProps) {
|
|
3208
|
-
if (isMandatory(p)) {
|
|
3209
|
-
throw new OwlError(`A default value cannot be defined for a mandatory prop (name: '${p}', component: ${ComponentClass.name})`);
|
|
3210
|
-
}
|
|
3211
|
-
}
|
|
3273
|
+
else if (ref.set) {
|
|
3274
|
+
add = ref.set.bind(ref);
|
|
3275
|
+
remove = () => ref.set(null);
|
|
3212
3276
|
}
|
|
3213
|
-
|
|
3214
|
-
|
|
3215
|
-
throw new OwlError(`Invalid props for component '${ComponentClass.name}': ` + errors.join(", "));
|
|
3277
|
+
else {
|
|
3278
|
+
throw new OwlError(`Ref should implement either a 'set' function or 'add' and 'remove' functions`);
|
|
3216
3279
|
}
|
|
3217
|
-
|
|
3218
|
-
|
|
3219
|
-
|
|
3220
|
-
|
|
3221
|
-
if (
|
|
3222
|
-
|
|
3280
|
+
return (el, previousEl) => {
|
|
3281
|
+
if (previousEl) {
|
|
3282
|
+
remove(previousEl);
|
|
3283
|
+
}
|
|
3284
|
+
if (el) {
|
|
3285
|
+
add(el);
|
|
3223
3286
|
}
|
|
3224
|
-
refNames.add(name);
|
|
3225
|
-
return fn;
|
|
3226
3287
|
};
|
|
3227
3288
|
}
|
|
3289
|
+
function modelExpr(value) {
|
|
3290
|
+
if (!value.set || typeof value !== "function") {
|
|
3291
|
+
throw new OwlError(`Invalid t-model expression: expression should evaluate to a function with a 'set' method defined on it`);
|
|
3292
|
+
}
|
|
3293
|
+
return value;
|
|
3294
|
+
}
|
|
3228
3295
|
const helpers = {
|
|
3229
3296
|
withDefault,
|
|
3230
3297
|
zero: Symbol("zero"),
|
|
@@ -3236,13 +3303,13 @@ const helpers = {
|
|
|
3236
3303
|
setContextValue,
|
|
3237
3304
|
shallowEqual,
|
|
3238
3305
|
toNumber,
|
|
3239
|
-
validateProps,
|
|
3240
3306
|
LazyValue,
|
|
3241
3307
|
safeOutput,
|
|
3242
3308
|
createCatcher,
|
|
3243
3309
|
markRaw,
|
|
3244
3310
|
OwlError,
|
|
3245
|
-
|
|
3311
|
+
createRef,
|
|
3312
|
+
modelExpr,
|
|
3246
3313
|
};
|
|
3247
3314
|
|
|
3248
3315
|
/**
|
|
@@ -3283,6 +3350,9 @@ function parseXML(xml) {
|
|
|
3283
3350
|
|
|
3284
3351
|
const bdom = { text, createBlock, list, multi, html, toggler, comment };
|
|
3285
3352
|
class TemplateSet {
|
|
3353
|
+
static registerTemplate(name, fn) {
|
|
3354
|
+
globalTemplates[name] = fn;
|
|
3355
|
+
}
|
|
3286
3356
|
constructor(config = {}) {
|
|
3287
3357
|
this.rawTemplates = Object.create(globalTemplates);
|
|
3288
3358
|
this.templates = {};
|
|
@@ -3305,9 +3375,6 @@ class TemplateSet {
|
|
|
3305
3375
|
this.runtimeUtils = { ...helpers, __globals__: config.globalValues || {} };
|
|
3306
3376
|
this.hasGlobalValues = Boolean(config.globalValues && Object.keys(config.globalValues).length);
|
|
3307
3377
|
}
|
|
3308
|
-
static registerTemplate(name, fn) {
|
|
3309
|
-
globalTemplates[name] = fn;
|
|
3310
|
-
}
|
|
3311
3378
|
addTemplate(name, template) {
|
|
3312
3379
|
if (name in this.rawTemplates) {
|
|
3313
3380
|
// this check can be expensive, just silently ignore double definitions outside dev mode
|
|
@@ -3618,7 +3685,7 @@ function compileExprToArray(expr) {
|
|
|
3618
3685
|
stack.pop();
|
|
3619
3686
|
}
|
|
3620
3687
|
let isVar = token.type === "SYMBOL" && !RESERVED_WORDS.includes(token.value);
|
|
3621
|
-
if (
|
|
3688
|
+
if (isVar) {
|
|
3622
3689
|
if (prevToken) {
|
|
3623
3690
|
// normalize missing tokens: {a} should be equivalent to {a:a}
|
|
3624
3691
|
if (groupType === "LEFT_BRACE" &&
|
|
@@ -3810,7 +3877,6 @@ class CodeTarget {
|
|
|
3810
3877
|
this.hasRoot = false;
|
|
3811
3878
|
this.hasCache = false;
|
|
3812
3879
|
this.shouldProtectScope = false;
|
|
3813
|
-
this.hasRefWrapper = false;
|
|
3814
3880
|
this.name = name;
|
|
3815
3881
|
this.on = on || null;
|
|
3816
3882
|
}
|
|
@@ -3830,9 +3896,6 @@ class CodeTarget {
|
|
|
3830
3896
|
result.push(` ctx = Object.create(ctx);`);
|
|
3831
3897
|
result.push(` ctx[isBoundary] = 1`);
|
|
3832
3898
|
}
|
|
3833
|
-
if (this.hasRefWrapper) {
|
|
3834
|
-
result.push(` let refWrapper = makeRefWrapper(this.__owl__);`);
|
|
3835
|
-
}
|
|
3836
3899
|
if (this.hasCache) {
|
|
3837
3900
|
result.push(` let cache = ctx.cache || {};`);
|
|
3838
3901
|
result.push(` let nextCache = ctx.cache = {};`);
|
|
@@ -3899,7 +3962,7 @@ class CodeGenerator {
|
|
|
3899
3962
|
}
|
|
3900
3963
|
generateCode() {
|
|
3901
3964
|
const ast = this.ast;
|
|
3902
|
-
this.isDebug = ast.type ===
|
|
3965
|
+
this.isDebug = ast.type === 11 /* ASTType.TDebug */;
|
|
3903
3966
|
BlockDescription.nextBlockId = 1;
|
|
3904
3967
|
nextDataIds = {};
|
|
3905
3968
|
this.compileAST(ast, {
|
|
@@ -4055,43 +4118,41 @@ class CodeGenerator {
|
|
|
4055
4118
|
*/
|
|
4056
4119
|
compileAST(ast, ctx) {
|
|
4057
4120
|
switch (ast.type) {
|
|
4058
|
-
case 1 /* Comment */:
|
|
4121
|
+
case 1 /* ASTType.Comment */:
|
|
4059
4122
|
return this.compileComment(ast, ctx);
|
|
4060
|
-
case 0 /* Text */:
|
|
4123
|
+
case 0 /* ASTType.Text */:
|
|
4061
4124
|
return this.compileText(ast, ctx);
|
|
4062
|
-
case 2 /* DomNode */:
|
|
4125
|
+
case 2 /* ASTType.DomNode */:
|
|
4063
4126
|
return this.compileTDomNode(ast, ctx);
|
|
4064
|
-
case
|
|
4065
|
-
return this.compileTEsc(ast, ctx);
|
|
4066
|
-
case 8 /* TOut */:
|
|
4127
|
+
case 7 /* ASTType.TOut */:
|
|
4067
4128
|
return this.compileTOut(ast, ctx);
|
|
4068
|
-
case
|
|
4129
|
+
case 4 /* ASTType.TIf */:
|
|
4069
4130
|
return this.compileTIf(ast, ctx);
|
|
4070
|
-
case
|
|
4131
|
+
case 8 /* ASTType.TForEach */:
|
|
4071
4132
|
return this.compileTForeach(ast, ctx);
|
|
4072
|
-
case
|
|
4133
|
+
case 9 /* ASTType.TKey */:
|
|
4073
4134
|
return this.compileTKey(ast, ctx);
|
|
4074
|
-
case 3 /* Multi */:
|
|
4135
|
+
case 3 /* ASTType.Multi */:
|
|
4075
4136
|
return this.compileMulti(ast, ctx);
|
|
4076
|
-
case
|
|
4137
|
+
case 6 /* ASTType.TCall */:
|
|
4077
4138
|
return this.compileTCall(ast, ctx);
|
|
4078
|
-
case
|
|
4139
|
+
case 14 /* ASTType.TCallBlock */:
|
|
4079
4140
|
return this.compileTCallBlock(ast, ctx);
|
|
4080
|
-
case
|
|
4141
|
+
case 5 /* ASTType.TSet */:
|
|
4081
4142
|
return this.compileTSet(ast, ctx);
|
|
4082
|
-
case
|
|
4143
|
+
case 10 /* ASTType.TComponent */:
|
|
4083
4144
|
return this.compileComponent(ast, ctx);
|
|
4084
|
-
case
|
|
4145
|
+
case 11 /* ASTType.TDebug */:
|
|
4085
4146
|
return this.compileDebug(ast, ctx);
|
|
4086
|
-
case
|
|
4147
|
+
case 12 /* ASTType.TLog */:
|
|
4087
4148
|
return this.compileLog(ast, ctx);
|
|
4088
|
-
case
|
|
4089
|
-
return this.
|
|
4090
|
-
case
|
|
4149
|
+
case 13 /* ASTType.TCallSlot */:
|
|
4150
|
+
return this.compileTCallSlot(ast, ctx);
|
|
4151
|
+
case 15 /* ASTType.TTranslation */:
|
|
4091
4152
|
return this.compileTTranslation(ast, ctx);
|
|
4092
|
-
case
|
|
4153
|
+
case 16 /* ASTType.TTranslationContext */:
|
|
4093
4154
|
return this.compileTTranslationContext(ast, ctx);
|
|
4094
|
-
case
|
|
4155
|
+
case 17 /* ASTType.TPortal */:
|
|
4095
4156
|
return this.compileTPortal(ast, ctx);
|
|
4096
4157
|
}
|
|
4097
4158
|
}
|
|
@@ -4142,7 +4203,7 @@ class CodeGenerator {
|
|
|
4142
4203
|
});
|
|
4143
4204
|
}
|
|
4144
4205
|
else {
|
|
4145
|
-
const createFn = ast.type === 0 /* Text */ ? xmlDoc.createTextNode : xmlDoc.createComment;
|
|
4206
|
+
const createFn = ast.type === 0 /* ASTType.Text */ ? xmlDoc.createTextNode : xmlDoc.createComment;
|
|
4146
4207
|
block.insert(createFn.call(xmlDoc, value));
|
|
4147
4208
|
}
|
|
4148
4209
|
return block.varName;
|
|
@@ -4236,14 +4297,11 @@ class CodeGenerator {
|
|
|
4236
4297
|
// t-model
|
|
4237
4298
|
let tModelSelectedExpr;
|
|
4238
4299
|
if (ast.model) {
|
|
4239
|
-
const { hasDynamicChildren,
|
|
4240
|
-
const baseExpression = compileExpr(baseExpr);
|
|
4241
|
-
const bExprId = generateId("bExpr");
|
|
4242
|
-
this.define(bExprId, baseExpression);
|
|
4300
|
+
const { hasDynamicChildren, expr, eventType, shouldNumberize, shouldTrim, targetAttr, specialInitTargetAttr, } = ast.model;
|
|
4243
4301
|
const expression = compileExpr(expr);
|
|
4244
4302
|
const exprId = generateId("expr");
|
|
4245
|
-
this.
|
|
4246
|
-
|
|
4303
|
+
this.helpers.add("modelExpr");
|
|
4304
|
+
this.define(exprId, `modelExpr(${expression})`);
|
|
4247
4305
|
let idx;
|
|
4248
4306
|
if (specialInitTargetAttr) {
|
|
4249
4307
|
let targetExpr = targetAttr in attrs && `'${attrs[targetAttr]}'`;
|
|
@@ -4254,23 +4312,23 @@ class CodeGenerator {
|
|
|
4254
4312
|
targetExpr = compileExpr(dynamicTgExpr);
|
|
4255
4313
|
}
|
|
4256
4314
|
}
|
|
4257
|
-
idx = block.insertData(`${
|
|
4315
|
+
idx = block.insertData(`${exprId}() === ${targetExpr}`, "prop");
|
|
4258
4316
|
attrs[`block-property-${idx}`] = specialInitTargetAttr;
|
|
4259
4317
|
}
|
|
4260
4318
|
else if (hasDynamicChildren) {
|
|
4261
4319
|
const bValueId = generateId("bValue");
|
|
4262
4320
|
tModelSelectedExpr = `${bValueId}`;
|
|
4263
|
-
this.define(tModelSelectedExpr,
|
|
4321
|
+
this.define(tModelSelectedExpr, `${exprId}()`);
|
|
4264
4322
|
}
|
|
4265
4323
|
else {
|
|
4266
|
-
idx = block.insertData(`${
|
|
4324
|
+
idx = block.insertData(`${exprId}()`, "prop");
|
|
4267
4325
|
attrs[`block-property-${idx}`] = targetAttr;
|
|
4268
4326
|
}
|
|
4269
4327
|
this.helpers.add("toNumber");
|
|
4270
4328
|
let valueCode = `ev.target.${targetAttr}`;
|
|
4271
4329
|
valueCode = shouldTrim ? `${valueCode}.trim()` : valueCode;
|
|
4272
4330
|
valueCode = shouldNumberize ? `toNumber(${valueCode})` : valueCode;
|
|
4273
|
-
const handler = `[(ev) => { ${
|
|
4331
|
+
const handler = `[(ev) => { ${exprId}.set(${valueCode}); }]`;
|
|
4274
4332
|
idx = block.insertData(handler, "hdlr");
|
|
4275
4333
|
attrs[`block-handler-${idx}`] = eventType;
|
|
4276
4334
|
}
|
|
@@ -4282,19 +4340,9 @@ class CodeGenerator {
|
|
|
4282
4340
|
}
|
|
4283
4341
|
// t-ref
|
|
4284
4342
|
if (ast.ref) {
|
|
4285
|
-
|
|
4286
|
-
|
|
4287
|
-
|
|
4288
|
-
}
|
|
4289
|
-
const isDynamic = INTERP_REGEXP.test(ast.ref);
|
|
4290
|
-
let name = `\`${ast.ref}\``;
|
|
4291
|
-
if (isDynamic) {
|
|
4292
|
-
name = replaceDynamicParts(ast.ref, (expr) => this.captureExpression(expr, true));
|
|
4293
|
-
}
|
|
4294
|
-
let setRefStr = `(el) => this.__owl__.setRef((${name}), el)`;
|
|
4295
|
-
if (this.dev) {
|
|
4296
|
-
setRefStr = `refWrapper(${name}, ${setRefStr})`;
|
|
4297
|
-
}
|
|
4343
|
+
const refExpr = compileExpr(ast.ref);
|
|
4344
|
+
this.helpers.add("createRef");
|
|
4345
|
+
const setRefStr = `createRef(${refExpr})`;
|
|
4298
4346
|
const idx = block.insertData(setRefStr, "ref");
|
|
4299
4347
|
attrs["block-ref"] = String(idx);
|
|
4300
4348
|
}
|
|
@@ -4348,32 +4396,6 @@ class CodeGenerator {
|
|
|
4348
4396
|
}
|
|
4349
4397
|
return block.varName;
|
|
4350
4398
|
}
|
|
4351
|
-
compileTEsc(ast, ctx) {
|
|
4352
|
-
let { block, forceNewBlock } = ctx;
|
|
4353
|
-
let expr;
|
|
4354
|
-
if (ast.expr === "0") {
|
|
4355
|
-
this.helpers.add("zero");
|
|
4356
|
-
expr = `ctx[zero]`;
|
|
4357
|
-
}
|
|
4358
|
-
else {
|
|
4359
|
-
expr = compileExpr(ast.expr);
|
|
4360
|
-
if (ast.defaultValue) {
|
|
4361
|
-
this.helpers.add("withDefault");
|
|
4362
|
-
// FIXME: defaultValue is not translated
|
|
4363
|
-
expr = `withDefault(${expr}, ${toStringExpression(ast.defaultValue)})`;
|
|
4364
|
-
}
|
|
4365
|
-
}
|
|
4366
|
-
if (!block || forceNewBlock) {
|
|
4367
|
-
block = this.createBlock(block, "text", ctx);
|
|
4368
|
-
this.insertBlock(`text(${expr})`, block, { ...ctx, forceNewBlock: forceNewBlock && !block });
|
|
4369
|
-
}
|
|
4370
|
-
else {
|
|
4371
|
-
const idx = block.insertData(expr, "txt");
|
|
4372
|
-
const text = xmlDoc.createElement(`block-text-${idx}`);
|
|
4373
|
-
block.insert(text);
|
|
4374
|
-
}
|
|
4375
|
-
return block.varName;
|
|
4376
|
-
}
|
|
4377
4399
|
compileTOut(ast, ctx) {
|
|
4378
4400
|
let { block } = ctx;
|
|
4379
4401
|
if (block) {
|
|
@@ -4389,7 +4411,7 @@ class CodeGenerator {
|
|
|
4389
4411
|
let bodyValue = null;
|
|
4390
4412
|
bodyValue = BlockDescription.nextBlockId;
|
|
4391
4413
|
const subCtx = createContext(ctx);
|
|
4392
|
-
this.compileAST({ type: 3 /* Multi */, content: ast.body }, subCtx);
|
|
4414
|
+
this.compileAST({ type: 3 /* ASTType.Multi */, content: ast.body }, subCtx);
|
|
4393
4415
|
this.helpers.add("safeOutput");
|
|
4394
4416
|
blockStr = `safeOutput(${compileExpr(ast.expr)}, b${bodyValue})`;
|
|
4395
4417
|
}
|
|
@@ -4594,7 +4616,7 @@ class CodeGenerator {
|
|
|
4594
4616
|
let ctxVar = ctx.ctxVar || "ctx";
|
|
4595
4617
|
if (ast.context) {
|
|
4596
4618
|
ctxVar = generateId("ctx");
|
|
4597
|
-
this.addLine(`let ${ctxVar} = ${compileExpr(ast.context)};`);
|
|
4619
|
+
this.addLine(`let ${ctxVar} = {this: ${compileExpr(ast.context)}, __owl__: this.__owl__};`);
|
|
4598
4620
|
}
|
|
4599
4621
|
const isDynamic = INTERP_REGEXP.test(ast.name);
|
|
4600
4622
|
const subTemplate = isDynamic ? interpolate(ast.name) : "`" + ast.name + "`";
|
|
@@ -4607,7 +4629,7 @@ class CodeGenerator {
|
|
|
4607
4629
|
this.addLine(`${ctxVar}[isBoundary] = 1;`);
|
|
4608
4630
|
this.helpers.add("isBoundary");
|
|
4609
4631
|
const subCtx = createContext(ctx, { ctxVar });
|
|
4610
|
-
const bl = this.compileMulti({ type: 3 /* Multi */, content: ast.body }, subCtx);
|
|
4632
|
+
const bl = this.compileMulti({ type: 3 /* ASTType.Multi */, content: ast.body }, subCtx);
|
|
4611
4633
|
if (bl) {
|
|
4612
4634
|
this.helpers.add("zero");
|
|
4613
4635
|
this.addLine(`${ctxVar}[zero] = ${bl};`);
|
|
@@ -4655,7 +4677,7 @@ class CodeGenerator {
|
|
|
4655
4677
|
const expr = ast.value ? compileExpr(ast.value || "") : "null";
|
|
4656
4678
|
if (ast.body) {
|
|
4657
4679
|
this.helpers.add("LazyValue");
|
|
4658
|
-
const bodyAst = { type: 3 /* Multi */, content: ast.body };
|
|
4680
|
+
const bodyAst = { type: 3 /* ASTType.Multi */, content: ast.body };
|
|
4659
4681
|
const name = this.compileInNewTarget("value", bodyAst, ctx);
|
|
4660
4682
|
let key = this.target.currentKey(ctx);
|
|
4661
4683
|
let value = `new LazyValue(${name}, ctx, this, node, ${key})`;
|
|
@@ -4794,9 +4816,6 @@ class CodeGenerator {
|
|
|
4794
4816
|
else {
|
|
4795
4817
|
expr = `\`${ast.name}\``;
|
|
4796
4818
|
}
|
|
4797
|
-
if (this.dev) {
|
|
4798
|
-
this.addLine(`helpers.validateProps(${expr}, ${propVar}, this);`);
|
|
4799
|
-
}
|
|
4800
4819
|
if (block && (ctx.forceNewBlock === false || ctx.tKeyExpr)) {
|
|
4801
4820
|
// todo: check the forcenewblock condition
|
|
4802
4821
|
this.insertAnchor(block);
|
|
@@ -4851,7 +4870,7 @@ class CodeGenerator {
|
|
|
4851
4870
|
this.staticDefs.push({ id: name, expr: `createCatcher(${JSON.stringify(spec)})` });
|
|
4852
4871
|
return `${name}(${expr}, [${handlers.join(",")}])`;
|
|
4853
4872
|
}
|
|
4854
|
-
|
|
4873
|
+
compileTCallSlot(ast, ctx) {
|
|
4855
4874
|
this.helpers.add("callSlot");
|
|
4856
4875
|
let { block } = ctx;
|
|
4857
4876
|
let blockString;
|
|
@@ -4968,7 +4987,7 @@ function parse(xml, customDir) {
|
|
|
4968
4987
|
}
|
|
4969
4988
|
function _parse(xml, ctx) {
|
|
4970
4989
|
normalizeXML(xml);
|
|
4971
|
-
return parseNode(xml, ctx) || { type: 0 /* Text */, value: "" };
|
|
4990
|
+
return parseNode(xml, ctx) || { type: 0 /* ASTType.Text */, value: "" };
|
|
4972
4991
|
}
|
|
4973
4992
|
function parseNode(node, ctx) {
|
|
4974
4993
|
if (!(node instanceof Element)) {
|
|
@@ -4984,9 +5003,8 @@ function parseNode(node, ctx) {
|
|
|
4984
5003
|
parseTTranslation(node, ctx) ||
|
|
4985
5004
|
parseTTranslationContext(node, ctx) ||
|
|
4986
5005
|
parseTKey(node, ctx) ||
|
|
4987
|
-
parseTEscNode(node, ctx) ||
|
|
4988
5006
|
parseTOutNode(node, ctx) ||
|
|
4989
|
-
|
|
5007
|
+
parseTCallSlot(node, ctx) ||
|
|
4990
5008
|
parseComponent(node, ctx) ||
|
|
4991
5009
|
parseDOMNode(node, ctx) ||
|
|
4992
5010
|
parseTSetNode(node, ctx) ||
|
|
@@ -5011,10 +5029,10 @@ function parseTextCommentNode(node, ctx) {
|
|
|
5011
5029
|
if (!ctx.inPreTag && lineBreakRE.test(value) && !value.trim()) {
|
|
5012
5030
|
return null;
|
|
5013
5031
|
}
|
|
5014
|
-
return { type: 0 /* Text */, value };
|
|
5032
|
+
return { type: 0 /* ASTType.Text */, value };
|
|
5015
5033
|
}
|
|
5016
5034
|
else if (node.nodeType === Node.COMMENT_NODE) {
|
|
5017
|
-
return { type: 1 /* Comment */, value: node.textContent || "" };
|
|
5035
|
+
return { type: 1 /* ASTType.Comment */, value: node.textContent || "" };
|
|
5018
5036
|
}
|
|
5019
5037
|
return null;
|
|
5020
5038
|
}
|
|
@@ -5055,7 +5073,7 @@ function parseTDebugLog(node, ctx) {
|
|
|
5055
5073
|
node.removeAttribute("t-debug");
|
|
5056
5074
|
const content = parseNode(node, ctx);
|
|
5057
5075
|
const ast = {
|
|
5058
|
-
type:
|
|
5076
|
+
type: 11 /* ASTType.TDebug */,
|
|
5059
5077
|
content,
|
|
5060
5078
|
};
|
|
5061
5079
|
if (content === null || content === void 0 ? void 0 : content.hasNoRepresentation) {
|
|
@@ -5068,7 +5086,7 @@ function parseTDebugLog(node, ctx) {
|
|
|
5068
5086
|
node.removeAttribute("t-log");
|
|
5069
5087
|
const content = parseNode(node, ctx);
|
|
5070
5088
|
const ast = {
|
|
5071
|
-
type:
|
|
5089
|
+
type: 12 /* ASTType.TLog */,
|
|
5072
5090
|
expr,
|
|
5073
5091
|
content,
|
|
5074
5092
|
};
|
|
@@ -5082,8 +5100,6 @@ function parseTDebugLog(node, ctx) {
|
|
|
5082
5100
|
// -----------------------------------------------------------------------------
|
|
5083
5101
|
// Regular dom node
|
|
5084
5102
|
// -----------------------------------------------------------------------------
|
|
5085
|
-
const hasDotAtTheEnd = /\.[\w_]+\s*$/;
|
|
5086
|
-
const hasBracketsAtTheEnd = /\[[^\[]+\]\s*$/;
|
|
5087
5103
|
const ROOT_SVG_TAGS = new Set(["svg", "g", "path"]);
|
|
5088
5104
|
function parseDOMNode(node, ctx) {
|
|
5089
5105
|
const { tagName } = node;
|
|
@@ -5120,20 +5136,6 @@ function parseDOMNode(node, ctx) {
|
|
|
5120
5136
|
if (!["input", "select", "textarea"].includes(tagName)) {
|
|
5121
5137
|
throw new OwlError("The t-model directive only works with <input>, <textarea> and <select>");
|
|
5122
5138
|
}
|
|
5123
|
-
let baseExpr, expr;
|
|
5124
|
-
if (hasDotAtTheEnd.test(value)) {
|
|
5125
|
-
const index = value.lastIndexOf(".");
|
|
5126
|
-
baseExpr = value.slice(0, index);
|
|
5127
|
-
expr = `'${value.slice(index + 1)}'`;
|
|
5128
|
-
}
|
|
5129
|
-
else if (hasBracketsAtTheEnd.test(value)) {
|
|
5130
|
-
const index = value.lastIndexOf("[");
|
|
5131
|
-
baseExpr = value.slice(0, index);
|
|
5132
|
-
expr = value.slice(index + 1, -1);
|
|
5133
|
-
}
|
|
5134
|
-
else {
|
|
5135
|
-
throw new OwlError(`Invalid t-model expression: "${value}" (it should be assignable)`);
|
|
5136
|
-
}
|
|
5137
5139
|
const typeAttr = node.getAttribute("type");
|
|
5138
5140
|
const isInput = tagName === "input";
|
|
5139
5141
|
const isSelect = tagName === "select";
|
|
@@ -5144,8 +5146,7 @@ function parseDOMNode(node, ctx) {
|
|
|
5144
5146
|
const hasNumberMod = attr.includes(".number");
|
|
5145
5147
|
const eventType = isRadioInput ? "click" : isSelect || hasLazyMod ? "change" : "input";
|
|
5146
5148
|
model = {
|
|
5147
|
-
|
|
5148
|
-
expr,
|
|
5149
|
+
expr: value,
|
|
5149
5150
|
targetAttr: isCheckboxInput ? "checked" : "value",
|
|
5150
5151
|
specialInitTargetAttr: isRadioInput ? "checked" : null,
|
|
5151
5152
|
eventType,
|
|
@@ -5187,7 +5188,7 @@ function parseDOMNode(node, ctx) {
|
|
|
5187
5188
|
}
|
|
5188
5189
|
const children = parseChildren(node, ctx);
|
|
5189
5190
|
return {
|
|
5190
|
-
type: 2 /* DomNode */,
|
|
5191
|
+
type: 2 /* ASTType.DomNode */,
|
|
5191
5192
|
tag: tagName,
|
|
5192
5193
|
dynamicTag,
|
|
5193
5194
|
attrs,
|
|
@@ -5200,55 +5201,26 @@ function parseDOMNode(node, ctx) {
|
|
|
5200
5201
|
};
|
|
5201
5202
|
}
|
|
5202
5203
|
// -----------------------------------------------------------------------------
|
|
5203
|
-
// t-esc
|
|
5204
|
-
// -----------------------------------------------------------------------------
|
|
5205
|
-
function parseTEscNode(node, ctx) {
|
|
5206
|
-
if (!node.hasAttribute("t-esc")) {
|
|
5207
|
-
return null;
|
|
5208
|
-
}
|
|
5209
|
-
const escValue = node.getAttribute("t-esc");
|
|
5210
|
-
node.removeAttribute("t-esc");
|
|
5211
|
-
const tesc = {
|
|
5212
|
-
type: 4 /* TEsc */,
|
|
5213
|
-
expr: escValue,
|
|
5214
|
-
defaultValue: node.textContent || "",
|
|
5215
|
-
};
|
|
5216
|
-
let ref = node.getAttribute("t-ref");
|
|
5217
|
-
node.removeAttribute("t-ref");
|
|
5218
|
-
const ast = parseNode(node, ctx);
|
|
5219
|
-
if (!ast) {
|
|
5220
|
-
return tesc;
|
|
5221
|
-
}
|
|
5222
|
-
if (ast.type === 2 /* DomNode */) {
|
|
5223
|
-
return {
|
|
5224
|
-
...ast,
|
|
5225
|
-
ref,
|
|
5226
|
-
content: [tesc],
|
|
5227
|
-
};
|
|
5228
|
-
}
|
|
5229
|
-
return tesc;
|
|
5230
|
-
}
|
|
5231
|
-
// -----------------------------------------------------------------------------
|
|
5232
5204
|
// t-out
|
|
5233
5205
|
// -----------------------------------------------------------------------------
|
|
5234
5206
|
function parseTOutNode(node, ctx) {
|
|
5235
|
-
if (!node.hasAttribute("t-out") && !node.hasAttribute("t-
|
|
5207
|
+
if (!node.hasAttribute("t-out") && !node.hasAttribute("t-esc")) {
|
|
5236
5208
|
return null;
|
|
5237
5209
|
}
|
|
5238
|
-
if (node.hasAttribute("t-
|
|
5239
|
-
console.warn(`t-
|
|
5210
|
+
if (node.hasAttribute("t-esc")) {
|
|
5211
|
+
console.warn(`t-esc has been deprecated in favor of t-out. If the value to render is not wrapped by the "markup" function, it will be escaped`);
|
|
5240
5212
|
}
|
|
5241
|
-
const expr = (node.getAttribute("t-out") || node.getAttribute("t-
|
|
5213
|
+
const expr = (node.getAttribute("t-out") || node.getAttribute("t-esc"));
|
|
5242
5214
|
node.removeAttribute("t-out");
|
|
5243
|
-
node.removeAttribute("t-
|
|
5244
|
-
const tOut = { type:
|
|
5215
|
+
node.removeAttribute("t-esc");
|
|
5216
|
+
const tOut = { type: 7 /* ASTType.TOut */, expr, body: null };
|
|
5245
5217
|
const ref = node.getAttribute("t-ref");
|
|
5246
5218
|
node.removeAttribute("t-ref");
|
|
5247
5219
|
const ast = parseNode(node, ctx);
|
|
5248
5220
|
if (!ast) {
|
|
5249
5221
|
return tOut;
|
|
5250
5222
|
}
|
|
5251
|
-
if (ast.type === 2 /* DomNode */) {
|
|
5223
|
+
if (ast.type === 2 /* ASTType.DomNode */) {
|
|
5252
5224
|
tOut.body = ast.content.length ? ast.content : null;
|
|
5253
5225
|
return {
|
|
5254
5226
|
...ast,
|
|
@@ -5287,7 +5259,7 @@ function parseTForEach(node, ctx) {
|
|
|
5287
5259
|
const hasNoIndex = hasNoTCall && !html.includes(`${elem}_index`);
|
|
5288
5260
|
const hasNoValue = hasNoTCall && !html.includes(`${elem}_value`);
|
|
5289
5261
|
return {
|
|
5290
|
-
type:
|
|
5262
|
+
type: 8 /* ASTType.TForEach */,
|
|
5291
5263
|
collection,
|
|
5292
5264
|
elem,
|
|
5293
5265
|
body,
|
|
@@ -5310,7 +5282,7 @@ function parseTKey(node, ctx) {
|
|
|
5310
5282
|
return null;
|
|
5311
5283
|
}
|
|
5312
5284
|
const ast = {
|
|
5313
|
-
type:
|
|
5285
|
+
type: 9 /* ASTType.TKey */,
|
|
5314
5286
|
expr: key,
|
|
5315
5287
|
content,
|
|
5316
5288
|
};
|
|
@@ -5332,12 +5304,12 @@ function parseTCall(node, ctx) {
|
|
|
5332
5304
|
node.removeAttribute("t-call-context");
|
|
5333
5305
|
if (node.tagName !== "t") {
|
|
5334
5306
|
const ast = parseNode(node, ctx);
|
|
5335
|
-
const tcall = { type:
|
|
5336
|
-
if (ast && ast.type === 2 /* DomNode */) {
|
|
5307
|
+
const tcall = { type: 6 /* ASTType.TCall */, name: subTemplate, body: null, context };
|
|
5308
|
+
if (ast && ast.type === 2 /* ASTType.DomNode */) {
|
|
5337
5309
|
ast.content = [tcall];
|
|
5338
5310
|
return ast;
|
|
5339
5311
|
}
|
|
5340
|
-
if (ast && ast.type ===
|
|
5312
|
+
if (ast && ast.type === 10 /* ASTType.TComponent */) {
|
|
5341
5313
|
return {
|
|
5342
5314
|
...ast,
|
|
5343
5315
|
slots: {
|
|
@@ -5354,7 +5326,7 @@ function parseTCall(node, ctx) {
|
|
|
5354
5326
|
}
|
|
5355
5327
|
const body = parseChildren(node, ctx);
|
|
5356
5328
|
return {
|
|
5357
|
-
type:
|
|
5329
|
+
type: 6 /* ASTType.TCall */,
|
|
5358
5330
|
name: subTemplate,
|
|
5359
5331
|
body: body.length ? body : null,
|
|
5360
5332
|
context,
|
|
@@ -5369,7 +5341,7 @@ function parseTCallBlock(node, ctx) {
|
|
|
5369
5341
|
}
|
|
5370
5342
|
const name = node.getAttribute("t-call-block");
|
|
5371
5343
|
return {
|
|
5372
|
-
type:
|
|
5344
|
+
type: 14 /* ASTType.TCallBlock */,
|
|
5373
5345
|
name,
|
|
5374
5346
|
};
|
|
5375
5347
|
}
|
|
@@ -5382,7 +5354,7 @@ function parseTIf(node, ctx) {
|
|
|
5382
5354
|
}
|
|
5383
5355
|
const condition = node.getAttribute("t-if");
|
|
5384
5356
|
node.removeAttribute("t-if");
|
|
5385
|
-
const content = parseNode(node, ctx) || { type: 0 /* Text */, value: "" };
|
|
5357
|
+
const content = parseNode(node, ctx) || { type: 0 /* ASTType.Text */, value: "" };
|
|
5386
5358
|
let nextElement = node.nextElementSibling;
|
|
5387
5359
|
// t-elifs
|
|
5388
5360
|
const tElifs = [];
|
|
@@ -5405,7 +5377,7 @@ function parseTIf(node, ctx) {
|
|
|
5405
5377
|
nextElement.remove();
|
|
5406
5378
|
}
|
|
5407
5379
|
return {
|
|
5408
|
-
type:
|
|
5380
|
+
type: 4 /* ASTType.TIf */,
|
|
5409
5381
|
condition,
|
|
5410
5382
|
content,
|
|
5411
5383
|
tElif: tElifs.length ? tElifs : null,
|
|
@@ -5426,7 +5398,7 @@ function parseTSetNode(node, ctx) {
|
|
|
5426
5398
|
if (node.textContent !== node.innerHTML) {
|
|
5427
5399
|
body = parseChildren(node, ctx);
|
|
5428
5400
|
}
|
|
5429
|
-
return { type:
|
|
5401
|
+
return { type: 5 /* ASTType.TSet */, name, value, defaultValue, body, hasNoRepresentation: true };
|
|
5430
5402
|
}
|
|
5431
5403
|
// -----------------------------------------------------------------------------
|
|
5432
5404
|
// Components
|
|
@@ -5555,7 +5527,7 @@ function parseComponent(node, ctx) {
|
|
|
5555
5527
|
}
|
|
5556
5528
|
}
|
|
5557
5529
|
return {
|
|
5558
|
-
type:
|
|
5530
|
+
type: 10 /* ASTType.TComponent */,
|
|
5559
5531
|
name,
|
|
5560
5532
|
isDynamic,
|
|
5561
5533
|
dynamicProps,
|
|
@@ -5568,12 +5540,12 @@ function parseComponent(node, ctx) {
|
|
|
5568
5540
|
// -----------------------------------------------------------------------------
|
|
5569
5541
|
// Slots
|
|
5570
5542
|
// -----------------------------------------------------------------------------
|
|
5571
|
-
function
|
|
5572
|
-
if (!node.hasAttribute("t-slot")) {
|
|
5543
|
+
function parseTCallSlot(node, ctx) {
|
|
5544
|
+
if (!node.hasAttribute("t-call-slot")) {
|
|
5573
5545
|
return null;
|
|
5574
5546
|
}
|
|
5575
|
-
const name = node.getAttribute("t-slot");
|
|
5576
|
-
node.removeAttribute("t-slot");
|
|
5547
|
+
const name = node.getAttribute("t-call-slot");
|
|
5548
|
+
node.removeAttribute("t-call-slot");
|
|
5577
5549
|
let attrs = null;
|
|
5578
5550
|
let attrsTranslationCtx = null;
|
|
5579
5551
|
let on = null;
|
|
@@ -5594,7 +5566,7 @@ function parseTSlot(node, ctx) {
|
|
|
5594
5566
|
}
|
|
5595
5567
|
}
|
|
5596
5568
|
return {
|
|
5597
|
-
type:
|
|
5569
|
+
type: 13 /* ASTType.TCallSlot */,
|
|
5598
5570
|
name,
|
|
5599
5571
|
attrs,
|
|
5600
5572
|
attrsTranslationCtx,
|
|
@@ -5606,7 +5578,7 @@ function parseTSlot(node, ctx) {
|
|
|
5606
5578
|
// Translation
|
|
5607
5579
|
// -----------------------------------------------------------------------------
|
|
5608
5580
|
function wrapInTTranslationAST(r) {
|
|
5609
|
-
const ast = { type:
|
|
5581
|
+
const ast = { type: 15 /* ASTType.TTranslation */, content: r };
|
|
5610
5582
|
if (r === null || r === void 0 ? void 0 : r.hasNoRepresentation) {
|
|
5611
5583
|
ast.hasNoRepresentation = true;
|
|
5612
5584
|
}
|
|
@@ -5618,7 +5590,7 @@ function parseTTranslation(node, ctx) {
|
|
|
5618
5590
|
}
|
|
5619
5591
|
node.removeAttribute("t-translation");
|
|
5620
5592
|
const result = parseNode(node, ctx);
|
|
5621
|
-
if ((result === null || result === void 0 ? void 0 : result.type) === 3 /* Multi */) {
|
|
5593
|
+
if ((result === null || result === void 0 ? void 0 : result.type) === 3 /* ASTType.Multi */) {
|
|
5622
5594
|
const children = result.content.map(wrapInTTranslationAST);
|
|
5623
5595
|
return makeASTMulti(children);
|
|
5624
5596
|
}
|
|
@@ -5629,7 +5601,7 @@ function parseTTranslation(node, ctx) {
|
|
|
5629
5601
|
// -----------------------------------------------------------------------------
|
|
5630
5602
|
function wrapInTTranslationContextAST(r, translationCtx) {
|
|
5631
5603
|
const ast = {
|
|
5632
|
-
type:
|
|
5604
|
+
type: 16 /* ASTType.TTranslationContext */,
|
|
5633
5605
|
content: r,
|
|
5634
5606
|
translationCtx,
|
|
5635
5607
|
};
|
|
@@ -5645,7 +5617,7 @@ function parseTTranslationContext(node, ctx) {
|
|
|
5645
5617
|
}
|
|
5646
5618
|
node.removeAttribute("t-translation-context");
|
|
5647
5619
|
const result = parseNode(node, ctx);
|
|
5648
|
-
if ((result === null || result === void 0 ? void 0 : result.type) === 3 /* Multi */) {
|
|
5620
|
+
if ((result === null || result === void 0 ? void 0 : result.type) === 3 /* ASTType.Multi */) {
|
|
5649
5621
|
const children = result.content.map((c) => wrapInTTranslationContextAST(c, translationCtx));
|
|
5650
5622
|
return makeASTMulti(children);
|
|
5651
5623
|
}
|
|
@@ -5663,12 +5635,12 @@ function parseTPortal(node, ctx) {
|
|
|
5663
5635
|
const content = parseNode(node, ctx);
|
|
5664
5636
|
if (!content) {
|
|
5665
5637
|
return {
|
|
5666
|
-
type: 0 /* Text */,
|
|
5638
|
+
type: 0 /* ASTType.Text */,
|
|
5667
5639
|
value: "",
|
|
5668
5640
|
};
|
|
5669
5641
|
}
|
|
5670
5642
|
return {
|
|
5671
|
-
type:
|
|
5643
|
+
type: 17 /* ASTType.TPortal */,
|
|
5672
5644
|
target,
|
|
5673
5645
|
content,
|
|
5674
5646
|
};
|
|
@@ -5684,7 +5656,7 @@ function parseChildren(node, ctx) {
|
|
|
5684
5656
|
for (let child of node.childNodes) {
|
|
5685
5657
|
const childAst = parseNode(child, ctx);
|
|
5686
5658
|
if (childAst) {
|
|
5687
|
-
if (childAst.type === 3 /* Multi */) {
|
|
5659
|
+
if (childAst.type === 3 /* ASTType.Multi */) {
|
|
5688
5660
|
children.push(...childAst.content);
|
|
5689
5661
|
}
|
|
5690
5662
|
else {
|
|
@@ -5695,7 +5667,7 @@ function parseChildren(node, ctx) {
|
|
|
5695
5667
|
return children;
|
|
5696
5668
|
}
|
|
5697
5669
|
function makeASTMulti(children) {
|
|
5698
|
-
const ast = { type: 3 /* Multi */, content: children };
|
|
5670
|
+
const ast = { type: 3 /* ASTType.Multi */, content: children };
|
|
5699
5671
|
if (children.every((c) => c.hasNoRepresentation)) {
|
|
5700
5672
|
ast.hasNoRepresentation = true;
|
|
5701
5673
|
}
|
|
@@ -5756,28 +5728,26 @@ function normalizeTIf(el) {
|
|
|
5756
5728
|
}
|
|
5757
5729
|
}
|
|
5758
5730
|
/**
|
|
5759
|
-
* Normalizes the content of an Element so that t-
|
|
5760
|
-
* are removed and instead places a <t t-
|
|
5731
|
+
* Normalizes the content of an Element so that t-out directives on components
|
|
5732
|
+
* are removed and instead places a <t t-out=""> as the default slot of the
|
|
5761
5733
|
* component. Also throws if the component already has content. This function
|
|
5762
5734
|
* modifies the Element in place.
|
|
5763
5735
|
*
|
|
5764
5736
|
* @param el the element containing the tree that should be normalized
|
|
5765
5737
|
*/
|
|
5766
|
-
function
|
|
5767
|
-
|
|
5768
|
-
|
|
5769
|
-
|
|
5770
|
-
|
|
5771
|
-
|
|
5772
|
-
|
|
5773
|
-
|
|
5774
|
-
|
|
5775
|
-
|
|
5776
|
-
|
|
5777
|
-
t.setAttribute(d, value);
|
|
5778
|
-
}
|
|
5779
|
-
el.appendChild(t);
|
|
5738
|
+
function normalizeTOut(el) {
|
|
5739
|
+
const elements = [...el.querySelectorAll(`[t-out]`)].filter((el) => el.tagName[0] === el.tagName[0].toUpperCase() || el.hasAttribute("t-component"));
|
|
5740
|
+
for (const el of elements) {
|
|
5741
|
+
if (el.childNodes.length) {
|
|
5742
|
+
throw new OwlError(`Cannot have t-out on a component that already has content`);
|
|
5743
|
+
}
|
|
5744
|
+
const value = el.getAttribute("t-out");
|
|
5745
|
+
el.removeAttribute("t-out");
|
|
5746
|
+
const t = el.ownerDocument.createElement("t");
|
|
5747
|
+
if (value != null) {
|
|
5748
|
+
t.setAttribute("t-out", value);
|
|
5780
5749
|
}
|
|
5750
|
+
el.appendChild(t);
|
|
5781
5751
|
}
|
|
5782
5752
|
}
|
|
5783
5753
|
/**
|
|
@@ -5788,7 +5758,7 @@ function normalizeTEscTOut(el) {
|
|
|
5788
5758
|
*/
|
|
5789
5759
|
function normalizeXML(el) {
|
|
5790
5760
|
normalizeTIf(el);
|
|
5791
|
-
|
|
5761
|
+
normalizeTOut(el);
|
|
5792
5762
|
}
|
|
5793
5763
|
|
|
5794
5764
|
function compile(template, options = {
|
|
@@ -5799,7 +5769,7 @@ function compile(template, options = {
|
|
|
5799
5769
|
// some work
|
|
5800
5770
|
const hasSafeContext = template instanceof Node
|
|
5801
5771
|
? !(template instanceof Element) || template.querySelector("[t-set], [t-call]") === null
|
|
5802
|
-
: !template.includes("t-set") && !template.includes("t-call");
|
|
5772
|
+
: !template.includes("t-set=") && !template.includes("t-call=");
|
|
5803
5773
|
// code generation
|
|
5804
5774
|
const codeGenerator = new CodeGenerator(ast, { ...options, hasSafeContext });
|
|
5805
5775
|
const code = codeGenerator.generateCode();
|
|
@@ -5817,7 +5787,7 @@ function compile(template, options = {
|
|
|
5817
5787
|
}
|
|
5818
5788
|
|
|
5819
5789
|
// do not modify manually. This file is generated by the release script.
|
|
5820
|
-
const version = "
|
|
5790
|
+
const version = "3.0.0-alpha";
|
|
5821
5791
|
|
|
5822
5792
|
// -----------------------------------------------------------------------------
|
|
5823
5793
|
// Scheduler
|
|
@@ -5849,7 +5819,7 @@ class Scheduler {
|
|
|
5849
5819
|
let renders = this.delayedRenders;
|
|
5850
5820
|
this.delayedRenders = [];
|
|
5851
5821
|
for (let f of renders) {
|
|
5852
|
-
if (f.root && f.node.status !== 3 /* DESTROYED */ && f.node.fiber === f) {
|
|
5822
|
+
if (f.root && f.node.status !== 3 /* STATUS.DESTROYED */ && f.node.fiber === f) {
|
|
5853
5823
|
f.render();
|
|
5854
5824
|
}
|
|
5855
5825
|
}
|
|
@@ -5872,7 +5842,7 @@ class Scheduler {
|
|
|
5872
5842
|
this.processFiber(task);
|
|
5873
5843
|
}
|
|
5874
5844
|
for (let task of this.tasks) {
|
|
5875
|
-
if (task.node.status === 3 /* DESTROYED */) {
|
|
5845
|
+
if (task.node.status === 3 /* STATUS.DESTROYED */) {
|
|
5876
5846
|
this.tasks.delete(task);
|
|
5877
5847
|
}
|
|
5878
5848
|
}
|
|
@@ -5888,7 +5858,7 @@ class Scheduler {
|
|
|
5888
5858
|
this.tasks.delete(fiber);
|
|
5889
5859
|
return;
|
|
5890
5860
|
}
|
|
5891
|
-
if (fiber.node.status === 3 /* DESTROYED */) {
|
|
5861
|
+
if (fiber.node.status === 3 /* STATUS.DESTROYED */) {
|
|
5892
5862
|
this.tasks.delete(fiber);
|
|
5893
5863
|
return;
|
|
5894
5864
|
}
|
|
@@ -5913,105 +5883,94 @@ Scheduler.requestAnimationFrame = window.requestAnimationFrame.bind(window);
|
|
|
5913
5883
|
|
|
5914
5884
|
let hasBeenLogged = false;
|
|
5915
5885
|
const apps = new Set();
|
|
5916
|
-
window.__OWL_DEVTOOLS__ || (window.__OWL_DEVTOOLS__ = { apps, Fiber, RootFiber, toRaw,
|
|
5886
|
+
window.__OWL_DEVTOOLS__ || (window.__OWL_DEVTOOLS__ = { apps, Fiber, RootFiber, toRaw, proxy });
|
|
5917
5887
|
class App extends TemplateSet {
|
|
5918
|
-
constructor(
|
|
5888
|
+
constructor(config = {}) {
|
|
5919
5889
|
super(config);
|
|
5920
5890
|
this.scheduler = new Scheduler();
|
|
5921
|
-
this.
|
|
5922
|
-
this.root = null;
|
|
5891
|
+
this.roots = new Set();
|
|
5923
5892
|
this.name = config.name || "";
|
|
5924
|
-
this.Root = Root;
|
|
5925
5893
|
apps.add(this);
|
|
5894
|
+
this.pluginManager = config.pluginManager || new PluginManager(null);
|
|
5895
|
+
if (config.plugins) {
|
|
5896
|
+
this.pluginManager.startPlugins(config.plugins);
|
|
5897
|
+
}
|
|
5926
5898
|
if (config.test) {
|
|
5927
5899
|
this.dev = true;
|
|
5928
5900
|
}
|
|
5929
|
-
this.warnIfNoStaticProps = config.warnIfNoStaticProps || false;
|
|
5930
5901
|
if (this.dev && !config.test && !hasBeenLogged) {
|
|
5931
5902
|
console.info(`Owl is running in 'dev' mode.`);
|
|
5932
5903
|
hasBeenLogged = true;
|
|
5933
5904
|
}
|
|
5934
|
-
const env = config.env || {};
|
|
5935
|
-
const descrs = Object.getOwnPropertyDescriptors(env);
|
|
5936
|
-
this.env = Object.freeze(Object.create(Object.getPrototypeOf(env), descrs));
|
|
5937
|
-
this.props = config.props || {};
|
|
5938
|
-
}
|
|
5939
|
-
mount(target, options) {
|
|
5940
|
-
const root = this.createRoot(this.Root, { props: this.props });
|
|
5941
|
-
this.root = root.node;
|
|
5942
|
-
this.subRoots.delete(root.node);
|
|
5943
|
-
return root.mount(target, options);
|
|
5944
5905
|
}
|
|
5945
5906
|
createRoot(Root, config = {}) {
|
|
5946
5907
|
const props = config.props || {};
|
|
5947
|
-
|
|
5948
|
-
|
|
5949
|
-
|
|
5950
|
-
|
|
5951
|
-
|
|
5952
|
-
|
|
5953
|
-
this.env = config.env;
|
|
5954
|
-
}
|
|
5908
|
+
let resolve;
|
|
5909
|
+
let reject;
|
|
5910
|
+
const promise = new Promise((res, rej) => {
|
|
5911
|
+
resolve = res;
|
|
5912
|
+
reject = rej;
|
|
5913
|
+
});
|
|
5955
5914
|
const restore = saveCurrent();
|
|
5956
|
-
|
|
5957
|
-
|
|
5958
|
-
|
|
5959
|
-
this.
|
|
5915
|
+
let node;
|
|
5916
|
+
let error = null;
|
|
5917
|
+
try {
|
|
5918
|
+
node = this.makeNode(Root, props);
|
|
5960
5919
|
}
|
|
5961
|
-
|
|
5962
|
-
|
|
5963
|
-
|
|
5920
|
+
catch (e) {
|
|
5921
|
+
error = e;
|
|
5922
|
+
reject(e);
|
|
5923
|
+
}
|
|
5924
|
+
finally {
|
|
5925
|
+
restore();
|
|
5926
|
+
}
|
|
5927
|
+
const root = {
|
|
5928
|
+
node: node,
|
|
5929
|
+
promise,
|
|
5964
5930
|
mount: (target, options) => {
|
|
5965
|
-
|
|
5966
|
-
|
|
5967
|
-
validateProps(Root, props, { __owl__: { app: this } });
|
|
5931
|
+
if (error) {
|
|
5932
|
+
return promise;
|
|
5968
5933
|
}
|
|
5969
|
-
|
|
5970
|
-
|
|
5934
|
+
App.validateTarget(target);
|
|
5935
|
+
this.mountNode(node, target, resolve, reject, options);
|
|
5936
|
+
return promise;
|
|
5971
5937
|
},
|
|
5972
5938
|
destroy: () => {
|
|
5973
|
-
this.
|
|
5939
|
+
this.roots.delete(root);
|
|
5974
5940
|
node.destroy();
|
|
5975
5941
|
this.scheduler.processTasks();
|
|
5976
5942
|
},
|
|
5977
5943
|
};
|
|
5944
|
+
this.roots.add(root);
|
|
5945
|
+
return root;
|
|
5978
5946
|
}
|
|
5979
5947
|
makeNode(Component, props) {
|
|
5980
5948
|
return new ComponentNode(Component, props, this, null, null);
|
|
5981
5949
|
}
|
|
5982
|
-
mountNode(node, target, options) {
|
|
5983
|
-
|
|
5984
|
-
|
|
5985
|
-
|
|
5986
|
-
|
|
5987
|
-
|
|
5988
|
-
|
|
5989
|
-
|
|
5990
|
-
|
|
5991
|
-
|
|
5992
|
-
|
|
5993
|
-
|
|
5994
|
-
|
|
5995
|
-
|
|
5996
|
-
|
|
5997
|
-
handlers.
|
|
5998
|
-
if (!isResolved) {
|
|
5999
|
-
reject(e);
|
|
6000
|
-
}
|
|
6001
|
-
throw e;
|
|
6002
|
-
});
|
|
5950
|
+
mountNode(node, target, resolve, reject, options) {
|
|
5951
|
+
// Manually add the last resort error handler on the node
|
|
5952
|
+
let handlers = nodeErrorHandlers.get(node);
|
|
5953
|
+
if (!handlers) {
|
|
5954
|
+
handlers = [];
|
|
5955
|
+
nodeErrorHandlers.set(node, handlers);
|
|
5956
|
+
}
|
|
5957
|
+
handlers.unshift((e, finalize) => {
|
|
5958
|
+
const finalError = finalize();
|
|
5959
|
+
reject(finalError);
|
|
5960
|
+
});
|
|
5961
|
+
// manually set a onMounted callback.
|
|
5962
|
+
// that way, we are independant from the current node.
|
|
5963
|
+
node.mounted.push(() => {
|
|
5964
|
+
resolve(node.component);
|
|
5965
|
+
handlers.shift();
|
|
6003
5966
|
});
|
|
6004
5967
|
node.mountComponent(target, options);
|
|
6005
|
-
return promise;
|
|
6006
5968
|
}
|
|
6007
5969
|
destroy() {
|
|
6008
|
-
|
|
6009
|
-
|
|
6010
|
-
subroot.destroy();
|
|
6011
|
-
}
|
|
6012
|
-
this.root.destroy();
|
|
6013
|
-
this.scheduler.processTasks();
|
|
5970
|
+
for (let root of this.roots) {
|
|
5971
|
+
root.destroy();
|
|
6014
5972
|
}
|
|
5973
|
+
this.scheduler.processTasks();
|
|
6015
5974
|
apps.delete(this);
|
|
6016
5975
|
}
|
|
6017
5976
|
createComponent(name, isStatic, hasSlotsProp, hasDynamicPropList, propList) {
|
|
@@ -6090,7 +6049,9 @@ App.validateTarget = validateTarget;
|
|
|
6090
6049
|
App.apps = apps;
|
|
6091
6050
|
App.version = version;
|
|
6092
6051
|
async function mount(C, target, config = {}) {
|
|
6093
|
-
|
|
6052
|
+
const app = new App(config);
|
|
6053
|
+
const root = app.createRoot(C, config);
|
|
6054
|
+
return root.mount(target, config);
|
|
6094
6055
|
}
|
|
6095
6056
|
|
|
6096
6057
|
const mainEventHandler = (data, ev, currentTarget) => {
|
|
@@ -6131,72 +6092,227 @@ const mainEventHandler = (data, ev, currentTarget) => {
|
|
|
6131
6092
|
throw new OwlError(`Invalid handler (expected a function, received: '${handler}')`);
|
|
6132
6093
|
}
|
|
6133
6094
|
let node = data[1] ? data[1].__owl__ : null;
|
|
6134
|
-
if (node ? node.status === 1 /* MOUNTED */ : true) {
|
|
6095
|
+
if (node ? node.status === 1 /* STATUS.MOUNTED */ : true) {
|
|
6135
6096
|
handler.call(node ? node.component : null, ev);
|
|
6136
6097
|
}
|
|
6137
6098
|
}
|
|
6138
6099
|
return stopped;
|
|
6139
6100
|
};
|
|
6140
6101
|
|
|
6141
|
-
function
|
|
6142
|
-
|
|
6143
|
-
|
|
6144
|
-
|
|
6145
|
-
|
|
6146
|
-
|
|
6147
|
-
|
|
6148
|
-
|
|
6149
|
-
|
|
6150
|
-
|
|
6102
|
+
function computed(fn, opts) {
|
|
6103
|
+
// todo: handle cleanup
|
|
6104
|
+
let derivedComputation;
|
|
6105
|
+
return () => {
|
|
6106
|
+
derivedComputation !== null && derivedComputation !== void 0 ? derivedComputation : (derivedComputation = {
|
|
6107
|
+
state: ComputationState.STALE,
|
|
6108
|
+
sources: new Set(),
|
|
6109
|
+
compute: () => {
|
|
6110
|
+
onWriteAtom(derivedComputation);
|
|
6111
|
+
return fn();
|
|
6112
|
+
},
|
|
6113
|
+
isDerived: true,
|
|
6114
|
+
value: undefined,
|
|
6115
|
+
observers: new Set(),
|
|
6116
|
+
name: opts === null || opts === void 0 ? void 0 : opts.name,
|
|
6117
|
+
});
|
|
6118
|
+
updateComputation(derivedComputation);
|
|
6119
|
+
return derivedComputation.value;
|
|
6120
|
+
};
|
|
6121
|
+
}
|
|
6122
|
+
|
|
6123
|
+
function signal(value, opts) {
|
|
6124
|
+
const atom = {
|
|
6125
|
+
value,
|
|
6126
|
+
observers: new Set(),
|
|
6127
|
+
name: opts === null || opts === void 0 ? void 0 : opts.name,
|
|
6128
|
+
};
|
|
6129
|
+
const read = () => {
|
|
6130
|
+
onReadAtom(atom);
|
|
6131
|
+
return atom.value;
|
|
6132
|
+
};
|
|
6133
|
+
const write = (newValue) => {
|
|
6134
|
+
if (typeof newValue === "function") {
|
|
6135
|
+
newValue = newValue(atom.value);
|
|
6136
|
+
}
|
|
6137
|
+
if (Object.is(atom.value, newValue))
|
|
6138
|
+
return;
|
|
6139
|
+
atom.value = newValue;
|
|
6140
|
+
onWriteAtom(atom);
|
|
6141
|
+
};
|
|
6142
|
+
read.set = write;
|
|
6143
|
+
read.update = (updater) => {
|
|
6144
|
+
if (updater) {
|
|
6145
|
+
write(updater(atom.value));
|
|
6146
|
+
}
|
|
6147
|
+
else {
|
|
6148
|
+
onWriteAtom(atom);
|
|
6149
|
+
}
|
|
6150
|
+
};
|
|
6151
|
+
return read;
|
|
6152
|
+
}
|
|
6153
|
+
|
|
6154
|
+
class Resource {
|
|
6155
|
+
constructor(name, type) {
|
|
6156
|
+
this._items = signal([]);
|
|
6157
|
+
this.items = computed(() => {
|
|
6158
|
+
return this._items()
|
|
6159
|
+
.sort((el1, el2) => el1[0] - el2[0])
|
|
6160
|
+
.map((elem) => elem[1]);
|
|
6161
|
+
});
|
|
6162
|
+
this._name = name || "resource";
|
|
6163
|
+
this._type = type;
|
|
6164
|
+
}
|
|
6165
|
+
add(item, sequence = 50) {
|
|
6166
|
+
if (this._type) {
|
|
6167
|
+
const error = validateType("item", item, this._type);
|
|
6168
|
+
if (error) {
|
|
6169
|
+
throw new Error(`Invalid type: ${error} (resource '${this._name}')`);
|
|
6170
|
+
}
|
|
6171
|
+
}
|
|
6172
|
+
this._items().push([sequence, item]);
|
|
6173
|
+
this._items.update();
|
|
6174
|
+
return this;
|
|
6175
|
+
}
|
|
6176
|
+
remove(item) {
|
|
6177
|
+
const items = this._items().filter(([seq, val]) => val !== item);
|
|
6178
|
+
this._items.set(items);
|
|
6179
|
+
return this;
|
|
6180
|
+
}
|
|
6181
|
+
has(item) {
|
|
6182
|
+
return this._items().some(([s, value]) => value === item);
|
|
6183
|
+
}
|
|
6184
|
+
}
|
|
6185
|
+
function useResource(r, elements) {
|
|
6186
|
+
for (let elem of elements) {
|
|
6187
|
+
r.add(elem);
|
|
6151
6188
|
}
|
|
6189
|
+
onWillDestroy(() => {
|
|
6190
|
+
for (let elem of elements) {
|
|
6191
|
+
r.remove(elem);
|
|
6192
|
+
}
|
|
6193
|
+
});
|
|
6152
6194
|
}
|
|
6153
6195
|
|
|
6154
|
-
|
|
6155
|
-
|
|
6156
|
-
|
|
6157
|
-
|
|
6158
|
-
|
|
6159
|
-
|
|
6160
|
-
|
|
6161
|
-
|
|
6162
|
-
|
|
6163
|
-
|
|
6164
|
-
|
|
6165
|
-
|
|
6166
|
-
|
|
6167
|
-
|
|
6196
|
+
class Registry {
|
|
6197
|
+
constructor(name, type) {
|
|
6198
|
+
this._map = signal(Object.create(null));
|
|
6199
|
+
this.entries = computed(() => {
|
|
6200
|
+
const entries = Object.entries(this._map())
|
|
6201
|
+
.sort((el1, el2) => el1[1][0] - el2[1][0])
|
|
6202
|
+
.map(([str, elem]) => [str, elem[1]]);
|
|
6203
|
+
return entries;
|
|
6204
|
+
});
|
|
6205
|
+
this.items = computed(() => this.entries().map((e) => e[1]));
|
|
6206
|
+
this._name = name || "registry";
|
|
6207
|
+
this._type = type;
|
|
6208
|
+
}
|
|
6209
|
+
addById(item, sequence = 50) {
|
|
6210
|
+
if (!item.id) {
|
|
6211
|
+
throw new Error(`Item should have an id key`);
|
|
6212
|
+
}
|
|
6213
|
+
return this.add(item.id, item, sequence);
|
|
6214
|
+
}
|
|
6215
|
+
add(key, value, sequence = 50) {
|
|
6216
|
+
if (this._type) {
|
|
6217
|
+
const error = validateType(key, value, this._type);
|
|
6218
|
+
// todo: move error handling in validation.js
|
|
6219
|
+
if (error) {
|
|
6220
|
+
throw new Error("Invalid type: " + error);
|
|
6221
|
+
}
|
|
6222
|
+
}
|
|
6223
|
+
this._map()[key] = [sequence, value];
|
|
6224
|
+
this._map.update();
|
|
6225
|
+
return this;
|
|
6226
|
+
}
|
|
6227
|
+
get(key, defaultValue) {
|
|
6228
|
+
const hasKey = key in this._map();
|
|
6229
|
+
if (!hasKey && arguments.length < 2) {
|
|
6230
|
+
throw new Error(`KeyNotFoundError: Cannot find key "${key}" in this registry`);
|
|
6231
|
+
}
|
|
6232
|
+
return hasKey ? this._map()[key][1] : defaultValue;
|
|
6233
|
+
}
|
|
6234
|
+
remove(key) {
|
|
6235
|
+
delete this._map()[key];
|
|
6236
|
+
this._map.update();
|
|
6237
|
+
}
|
|
6238
|
+
has(key) {
|
|
6239
|
+
return key in this._map();
|
|
6240
|
+
}
|
|
6241
|
+
}
|
|
6242
|
+
|
|
6243
|
+
function status() {
|
|
6244
|
+
const pm = _getCurrentPluginManager();
|
|
6245
|
+
const node = pm || getCurrent();
|
|
6246
|
+
return () => {
|
|
6247
|
+
switch (node.status) {
|
|
6248
|
+
case 0 /* STATUS.NEW */:
|
|
6249
|
+
return "new";
|
|
6250
|
+
case 2 /* STATUS.CANCELLED */:
|
|
6251
|
+
return "cancelled";
|
|
6252
|
+
case 1 /* STATUS.MOUNTED */:
|
|
6253
|
+
return pm ? "started" : "mounted";
|
|
6254
|
+
case 3 /* STATUS.DESTROYED */:
|
|
6255
|
+
return "destroyed";
|
|
6256
|
+
}
|
|
6257
|
+
};
|
|
6258
|
+
}
|
|
6259
|
+
|
|
6260
|
+
function effect(fn, opts) {
|
|
6261
|
+
var _a, _b, _c;
|
|
6262
|
+
const effectComputation = {
|
|
6263
|
+
state: ComputationState.STALE,
|
|
6264
|
+
value: undefined,
|
|
6265
|
+
compute() {
|
|
6266
|
+
// In case the cleanup read an atom.
|
|
6267
|
+
// todo: test it
|
|
6268
|
+
setComputation(undefined);
|
|
6269
|
+
// CurrentComputation = undefined!;
|
|
6270
|
+
// `removeSources` is made by `runComputation`.
|
|
6271
|
+
unsubscribeEffect(effectComputation);
|
|
6272
|
+
setComputation(effectComputation);
|
|
6273
|
+
// CurrentComputation = effectComputation;
|
|
6274
|
+
return fn();
|
|
6168
6275
|
},
|
|
6276
|
+
sources: new Set(),
|
|
6277
|
+
childrenEffect: [],
|
|
6278
|
+
name: opts === null || opts === void 0 ? void 0 : opts.name,
|
|
6279
|
+
};
|
|
6280
|
+
(_c = (_b = (_a = getCurrentComputation()) === null || _a === void 0 ? void 0 : _a.childrenEffect) === null || _b === void 0 ? void 0 : _b.push) === null || _c === void 0 ? void 0 : _c.call(_b, effectComputation);
|
|
6281
|
+
updateComputation(effectComputation);
|
|
6282
|
+
// Remove sources and unsubscribe
|
|
6283
|
+
return () => {
|
|
6284
|
+
// In case the cleanup read an atom.
|
|
6285
|
+
// todo: test it
|
|
6286
|
+
const previousComputation = getCurrentComputation();
|
|
6287
|
+
setComputation(undefined);
|
|
6288
|
+
unsubscribeEffect(effectComputation);
|
|
6289
|
+
setComputation(previousComputation);
|
|
6169
6290
|
};
|
|
6170
6291
|
}
|
|
6292
|
+
function unsubscribeEffect(effectComputation) {
|
|
6293
|
+
removeSources(effectComputation);
|
|
6294
|
+
cleanupEffect(effectComputation);
|
|
6295
|
+
for (const children of effectComputation.childrenEffect) {
|
|
6296
|
+
// Consider it executed to avoid it's re-execution
|
|
6297
|
+
// todo: make a test for it
|
|
6298
|
+
children.state = ComputationState.EXECUTED;
|
|
6299
|
+
removeSources(children);
|
|
6300
|
+
unsubscribeEffect(children);
|
|
6301
|
+
}
|
|
6302
|
+
effectComputation.childrenEffect.length = 0;
|
|
6303
|
+
}
|
|
6304
|
+
function cleanupEffect(computation) {
|
|
6305
|
+
// the computation.value of an effect is a cleanup function
|
|
6306
|
+
const cleanupFn = computation.value;
|
|
6307
|
+
if (cleanupFn && typeof cleanupFn === "function") {
|
|
6308
|
+
cleanupFn();
|
|
6309
|
+
computation.value = undefined;
|
|
6310
|
+
}
|
|
6311
|
+
}
|
|
6312
|
+
|
|
6171
6313
|
// -----------------------------------------------------------------------------
|
|
6172
|
-
//
|
|
6314
|
+
// useEffect
|
|
6173
6315
|
// -----------------------------------------------------------------------------
|
|
6174
|
-
/**
|
|
6175
|
-
* This hook is useful as a building block for some customized hooks, that may
|
|
6176
|
-
* need a reference to the env of the component calling them.
|
|
6177
|
-
*/
|
|
6178
|
-
function useEnv() {
|
|
6179
|
-
return getCurrent().component.env;
|
|
6180
|
-
}
|
|
6181
|
-
function extendEnv(currentEnv, extension) {
|
|
6182
|
-
const env = Object.create(currentEnv);
|
|
6183
|
-
const descrs = Object.getOwnPropertyDescriptors(extension);
|
|
6184
|
-
return Object.freeze(Object.defineProperties(env, descrs));
|
|
6185
|
-
}
|
|
6186
|
-
/**
|
|
6187
|
-
* This hook is a simple way to let components use a sub environment. Note that
|
|
6188
|
-
* like for all hooks, it is important that this is only called in the
|
|
6189
|
-
* constructor method.
|
|
6190
|
-
*/
|
|
6191
|
-
function useSubEnv(envExtension) {
|
|
6192
|
-
const node = getCurrent();
|
|
6193
|
-
node.component.env = extendEnv(node.component.env, envExtension);
|
|
6194
|
-
useChildSubEnv(envExtension);
|
|
6195
|
-
}
|
|
6196
|
-
function useChildSubEnv(envExtension) {
|
|
6197
|
-
const node = getCurrent();
|
|
6198
|
-
node.childEnv = extendEnv(node.childEnv, envExtension);
|
|
6199
|
-
}
|
|
6200
6316
|
/**
|
|
6201
6317
|
* This hook will run a callback when a component is mounted and patched, and
|
|
6202
6318
|
* will run a cleanup function before patching and before unmounting the
|
|
@@ -6210,32 +6326,15 @@ function useChildSubEnv(envExtension) {
|
|
|
6210
6326
|
* again. The default value returns an array containing only NaN because
|
|
6211
6327
|
* NaN !== NaN, which will cause the effect to rerun on every patch.
|
|
6212
6328
|
*/
|
|
6213
|
-
function useEffect(
|
|
6214
|
-
|
|
6215
|
-
let dependencies;
|
|
6216
|
-
onMounted(() => {
|
|
6217
|
-
dependencies = computeDependencies();
|
|
6218
|
-
cleanup = effect(...dependencies);
|
|
6219
|
-
});
|
|
6220
|
-
onPatched(() => {
|
|
6221
|
-
const newDeps = computeDependencies();
|
|
6222
|
-
const shouldReapply = newDeps.some((val, i) => val !== dependencies[i]);
|
|
6223
|
-
if (shouldReapply) {
|
|
6224
|
-
dependencies = newDeps;
|
|
6225
|
-
if (cleanup) {
|
|
6226
|
-
cleanup();
|
|
6227
|
-
}
|
|
6228
|
-
cleanup = effect(...dependencies);
|
|
6229
|
-
}
|
|
6230
|
-
});
|
|
6231
|
-
onWillUnmount(() => cleanup && cleanup());
|
|
6329
|
+
function useEffect(fn) {
|
|
6330
|
+
onWillDestroy(effect(fn));
|
|
6232
6331
|
}
|
|
6233
6332
|
// -----------------------------------------------------------------------------
|
|
6234
|
-
//
|
|
6333
|
+
// useListener
|
|
6235
6334
|
// -----------------------------------------------------------------------------
|
|
6236
6335
|
/**
|
|
6237
6336
|
* When a component needs to listen to DOM Events on element(s) that are not
|
|
6238
|
-
* part of his hierarchy, we can use the `
|
|
6337
|
+
* part of his hierarchy, we can use the `useListener` hook.
|
|
6239
6338
|
* It will correctly add and remove the event listener, whenever the
|
|
6240
6339
|
* component is mounted and unmounted.
|
|
6241
6340
|
*
|
|
@@ -6244,13 +6343,20 @@ function useEffect(effect, computeDependencies = () => [NaN]) {
|
|
|
6244
6343
|
*
|
|
6245
6344
|
* Usage:
|
|
6246
6345
|
* in the constructor of the OWL component that needs to be notified,
|
|
6247
|
-
* `
|
|
6346
|
+
* `useListener(window, 'click', this._doSomething);`
|
|
6248
6347
|
* */
|
|
6249
|
-
function
|
|
6348
|
+
function useListener(target, eventName, handler, eventParams) {
|
|
6250
6349
|
const node = getCurrent();
|
|
6251
6350
|
const boundHandler = handler.bind(node.component);
|
|
6252
6351
|
onMounted(() => target.addEventListener(eventName, boundHandler, eventParams));
|
|
6253
6352
|
onWillUnmount(() => target.removeEventListener(eventName, boundHandler, eventParams));
|
|
6353
|
+
}
|
|
6354
|
+
function usePlugins(Plugins) {
|
|
6355
|
+
const node = getCurrent();
|
|
6356
|
+
const manager = new PluginManager(node.pluginManager);
|
|
6357
|
+
node.pluginManager = manager;
|
|
6358
|
+
onWillDestroy(() => manager.destroy());
|
|
6359
|
+
return manager.startPlugins(Plugins);
|
|
6254
6360
|
}
|
|
6255
6361
|
|
|
6256
6362
|
config.shouldNormalizeDom = false;
|
|
@@ -6289,41 +6395,45 @@ exports.App = App;
|
|
|
6289
6395
|
exports.Component = Component;
|
|
6290
6396
|
exports.EventBus = EventBus;
|
|
6291
6397
|
exports.OwlError = OwlError;
|
|
6398
|
+
exports.Plugin = Plugin;
|
|
6399
|
+
exports.PluginManager = PluginManager;
|
|
6400
|
+
exports.Registry = Registry;
|
|
6401
|
+
exports.Resource = Resource;
|
|
6292
6402
|
exports.__info__ = __info__;
|
|
6293
6403
|
exports.batched = batched;
|
|
6294
6404
|
exports.blockDom = blockDom;
|
|
6405
|
+
exports.computed = computed;
|
|
6406
|
+
exports.effect = effect;
|
|
6295
6407
|
exports.htmlEscape = htmlEscape;
|
|
6296
|
-
exports.loadFile = loadFile;
|
|
6297
6408
|
exports.markRaw = markRaw;
|
|
6298
6409
|
exports.markup = markup;
|
|
6299
6410
|
exports.mount = mount;
|
|
6300
6411
|
exports.onError = onError;
|
|
6301
6412
|
exports.onMounted = onMounted;
|
|
6302
6413
|
exports.onPatched = onPatched;
|
|
6303
|
-
exports.onRendered = onRendered;
|
|
6304
6414
|
exports.onWillDestroy = onWillDestroy;
|
|
6305
6415
|
exports.onWillPatch = onWillPatch;
|
|
6306
|
-
exports.onWillRender = onWillRender;
|
|
6307
6416
|
exports.onWillStart = onWillStart;
|
|
6308
6417
|
exports.onWillUnmount = onWillUnmount;
|
|
6309
6418
|
exports.onWillUpdateProps = onWillUpdateProps;
|
|
6310
|
-
exports.
|
|
6419
|
+
exports.plugin = plugin;
|
|
6420
|
+
exports.props = props;
|
|
6421
|
+
exports.proxy = proxy;
|
|
6422
|
+
exports.signal = signal;
|
|
6311
6423
|
exports.status = status;
|
|
6312
6424
|
exports.toRaw = toRaw;
|
|
6313
|
-
exports.
|
|
6425
|
+
exports.untrack = untrack;
|
|
6314
6426
|
exports.useComponent = useComponent;
|
|
6315
6427
|
exports.useEffect = useEffect;
|
|
6316
|
-
exports.
|
|
6317
|
-
exports.
|
|
6318
|
-
exports.
|
|
6319
|
-
exports.useState = useState;
|
|
6320
|
-
exports.useSubEnv = useSubEnv;
|
|
6428
|
+
exports.useListener = useListener;
|
|
6429
|
+
exports.usePlugins = usePlugins;
|
|
6430
|
+
exports.useResource = useResource;
|
|
6321
6431
|
exports.validate = validate;
|
|
6322
6432
|
exports.validateType = validateType;
|
|
6323
6433
|
exports.whenReady = whenReady;
|
|
6324
6434
|
exports.xml = xml;
|
|
6325
6435
|
|
|
6326
6436
|
|
|
6327
|
-
__info__.date = '2025-
|
|
6328
|
-
__info__.hash = '
|
|
6437
|
+
__info__.date = '2025-12-22T15:42:39.839Z';
|
|
6438
|
+
__info__.hash = '169a8cc';
|
|
6329
6439
|
__info__.url = 'https://github.com/odoo/owl';
|