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