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