@odoo/owl 2.0.0-beta-9 → 2.0.0-beta-12
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/owl.cjs.js +863 -827
- package/dist/owl.es.js +863 -827
- package/dist/owl.iife.js +863 -827
- package/dist/owl.iife.min.js +1 -1
- package/dist/types/compiler/code_generator.d.ts +3 -1
- package/dist/types/compiler/parser.d.ts +1 -0
- package/dist/types/owl.d.ts +6 -6
- package/dist/types/runtime/app.d.ts +2 -1
- package/dist/types/runtime/component.d.ts +1 -1
- package/dist/types/runtime/component_node.d.ts +2 -6
- package/dist/types/runtime/template_helpers.d.ts +3 -2
- package/dist/types/runtime/validation.d.ts +4 -2
- package/package.json +1 -1
package/dist/owl.es.js
CHANGED
|
@@ -167,6 +167,10 @@ function toClassObj(expr) {
|
|
|
167
167
|
for (let key in expr) {
|
|
168
168
|
const value = expr[key];
|
|
169
169
|
if (value) {
|
|
170
|
+
key = trim.call(key);
|
|
171
|
+
if (!key) {
|
|
172
|
+
continue;
|
|
173
|
+
}
|
|
170
174
|
const words = split.call(key, wordRegexp);
|
|
171
175
|
for (let word of words) {
|
|
172
176
|
result[word] = value;
|
|
@@ -1378,798 +1382,786 @@ function remove(vnode, withBeforeRemove = false) {
|
|
|
1378
1382
|
vnode.remove();
|
|
1379
1383
|
}
|
|
1380
1384
|
|
|
1381
|
-
//
|
|
1382
|
-
const
|
|
1383
|
-
|
|
1384
|
-
|
|
1385
|
-
|
|
1386
|
-
const KEYCHANGES = Symbol("Key changes");
|
|
1387
|
-
const objectToString = Object.prototype.toString;
|
|
1388
|
-
const objectHasOwnProperty = Object.prototype.hasOwnProperty;
|
|
1389
|
-
const SUPPORTED_RAW_TYPES = new Set(["Object", "Array", "Set", "Map", "WeakMap"]);
|
|
1390
|
-
const COLLECTION_RAWTYPES = new Set(["Set", "Map", "WeakMap"]);
|
|
1391
|
-
/**
|
|
1392
|
-
* extract "RawType" from strings like "[object RawType]" => this lets us ignore
|
|
1393
|
-
* many native objects such as Promise (whose toString is [object Promise])
|
|
1394
|
-
* or Date ([object Date]), while also supporting collections without using
|
|
1395
|
-
* instanceof in a loop
|
|
1396
|
-
*
|
|
1397
|
-
* @param obj the object to check
|
|
1398
|
-
* @returns the raw type of the object
|
|
1399
|
-
*/
|
|
1400
|
-
function rawType(obj) {
|
|
1401
|
-
return objectToString.call(obj).slice(8, -1);
|
|
1402
|
-
}
|
|
1403
|
-
/**
|
|
1404
|
-
* Checks whether a given value can be made into a reactive object.
|
|
1405
|
-
*
|
|
1406
|
-
* @param value the value to check
|
|
1407
|
-
* @returns whether the value can be made reactive
|
|
1408
|
-
*/
|
|
1409
|
-
function canBeMadeReactive(value) {
|
|
1410
|
-
if (typeof value !== "object") {
|
|
1385
|
+
// Maps fibers to thrown errors
|
|
1386
|
+
const fibersInError = new WeakMap();
|
|
1387
|
+
const nodeErrorHandlers = new WeakMap();
|
|
1388
|
+
function _handleError(node, error) {
|
|
1389
|
+
if (!node) {
|
|
1411
1390
|
return false;
|
|
1412
1391
|
}
|
|
1413
|
-
|
|
1414
|
-
|
|
1415
|
-
|
|
1416
|
-
* Creates a reactive from the given object/callback if possible and returns it,
|
|
1417
|
-
* returns the original object otherwise.
|
|
1418
|
-
*
|
|
1419
|
-
* @param value the value make reactive
|
|
1420
|
-
* @returns a reactive for the given object when possible, the original otherwise
|
|
1421
|
-
*/
|
|
1422
|
-
function possiblyReactive(val, cb) {
|
|
1423
|
-
return canBeMadeReactive(val) ? reactive(val, cb) : val;
|
|
1424
|
-
}
|
|
1425
|
-
/**
|
|
1426
|
-
* Mark an object or array so that it is ignored by the reactivity system
|
|
1427
|
-
*
|
|
1428
|
-
* @param value the value to mark
|
|
1429
|
-
* @returns the object itself
|
|
1430
|
-
*/
|
|
1431
|
-
function markRaw(value) {
|
|
1432
|
-
value[SKIP] = true;
|
|
1433
|
-
return value;
|
|
1434
|
-
}
|
|
1435
|
-
/**
|
|
1436
|
-
* Given a reactive objet, return the raw (non reactive) underlying object
|
|
1437
|
-
*
|
|
1438
|
-
* @param value a reactive value
|
|
1439
|
-
* @returns the underlying value
|
|
1440
|
-
*/
|
|
1441
|
-
function toRaw(value) {
|
|
1442
|
-
return value[TARGET] || value;
|
|
1443
|
-
}
|
|
1444
|
-
const targetToKeysToCallbacks = new WeakMap();
|
|
1445
|
-
/**
|
|
1446
|
-
* Observes a given key on a target with an callback. The callback will be
|
|
1447
|
-
* called when the given key changes on the target.
|
|
1448
|
-
*
|
|
1449
|
-
* @param target the target whose key should be observed
|
|
1450
|
-
* @param key the key to observe (or Symbol(KEYCHANGES) for key creation
|
|
1451
|
-
* or deletion)
|
|
1452
|
-
* @param callback the function to call when the key changes
|
|
1453
|
-
*/
|
|
1454
|
-
function observeTargetKey(target, key, callback) {
|
|
1455
|
-
if (!targetToKeysToCallbacks.get(target)) {
|
|
1456
|
-
targetToKeysToCallbacks.set(target, new Map());
|
|
1392
|
+
const fiber = node.fiber;
|
|
1393
|
+
if (fiber) {
|
|
1394
|
+
fibersInError.set(fiber, error);
|
|
1457
1395
|
}
|
|
1458
|
-
const
|
|
1459
|
-
if (
|
|
1460
|
-
|
|
1396
|
+
const errorHandlers = nodeErrorHandlers.get(node);
|
|
1397
|
+
if (errorHandlers) {
|
|
1398
|
+
let handled = false;
|
|
1399
|
+
// execute in the opposite order
|
|
1400
|
+
for (let i = errorHandlers.length - 1; i >= 0; i--) {
|
|
1401
|
+
try {
|
|
1402
|
+
errorHandlers[i](error);
|
|
1403
|
+
handled = true;
|
|
1404
|
+
break;
|
|
1405
|
+
}
|
|
1406
|
+
catch (e) {
|
|
1407
|
+
error = e;
|
|
1408
|
+
}
|
|
1409
|
+
}
|
|
1410
|
+
if (handled) {
|
|
1411
|
+
return true;
|
|
1412
|
+
}
|
|
1461
1413
|
}
|
|
1462
|
-
|
|
1463
|
-
|
|
1464
|
-
|
|
1414
|
+
return _handleError(node.parent, error);
|
|
1415
|
+
}
|
|
1416
|
+
function handleError(params) {
|
|
1417
|
+
const error = params.error;
|
|
1418
|
+
const node = "node" in params ? params.node : params.fiber.node;
|
|
1419
|
+
const fiber = "fiber" in params ? params.fiber : node.fiber;
|
|
1420
|
+
// resets the fibers on components if possible. This is important so that
|
|
1421
|
+
// new renderings can be properly included in the initial one, if any.
|
|
1422
|
+
let current = fiber;
|
|
1423
|
+
do {
|
|
1424
|
+
current.node.fiber = current;
|
|
1425
|
+
current = current.parent;
|
|
1426
|
+
} while (current);
|
|
1427
|
+
fibersInError.set(fiber.root, error);
|
|
1428
|
+
const handled = _handleError(node, error);
|
|
1429
|
+
if (!handled) {
|
|
1430
|
+
console.warn(`[Owl] Unhandled error. Destroying the root component`);
|
|
1431
|
+
try {
|
|
1432
|
+
node.app.destroy();
|
|
1433
|
+
}
|
|
1434
|
+
catch (e) {
|
|
1435
|
+
console.error(e);
|
|
1436
|
+
}
|
|
1465
1437
|
}
|
|
1466
|
-
|
|
1438
|
+
}
|
|
1439
|
+
|
|
1440
|
+
function makeChildFiber(node, parent) {
|
|
1441
|
+
let current = node.fiber;
|
|
1442
|
+
if (current) {
|
|
1443
|
+
cancelFibers(current.children);
|
|
1444
|
+
current.root = null;
|
|
1445
|
+
}
|
|
1446
|
+
return new Fiber(node, parent);
|
|
1467
1447
|
}
|
|
1468
|
-
|
|
1469
|
-
|
|
1470
|
-
|
|
1471
|
-
|
|
1472
|
-
|
|
1473
|
-
|
|
1474
|
-
|
|
1475
|
-
|
|
1476
|
-
|
|
1477
|
-
|
|
1478
|
-
|
|
1479
|
-
|
|
1480
|
-
|
|
1448
|
+
function makeRootFiber(node) {
|
|
1449
|
+
let current = node.fiber;
|
|
1450
|
+
if (current) {
|
|
1451
|
+
let root = current.root;
|
|
1452
|
+
// lock root fiber because canceling children fibers may destroy components,
|
|
1453
|
+
// which means any arbitrary code can be run in onWillDestroy, which may
|
|
1454
|
+
// trigger new renderings
|
|
1455
|
+
root.locked = true;
|
|
1456
|
+
root.setCounter(root.counter + 1 - cancelFibers(current.children));
|
|
1457
|
+
root.locked = false;
|
|
1458
|
+
current.children = [];
|
|
1459
|
+
current.childrenMap = {};
|
|
1460
|
+
current.bdom = null;
|
|
1461
|
+
if (fibersInError.has(current)) {
|
|
1462
|
+
fibersInError.delete(current);
|
|
1463
|
+
fibersInError.delete(root);
|
|
1464
|
+
current.appliedToDom = false;
|
|
1465
|
+
}
|
|
1466
|
+
return current;
|
|
1481
1467
|
}
|
|
1482
|
-
const
|
|
1483
|
-
if (
|
|
1484
|
-
|
|
1468
|
+
const fiber = new RootFiber(node, null);
|
|
1469
|
+
if (node.willPatch.length) {
|
|
1470
|
+
fiber.willPatch.push(fiber);
|
|
1485
1471
|
}
|
|
1486
|
-
|
|
1487
|
-
|
|
1488
|
-
clearReactivesForCallback(callback);
|
|
1489
|
-
callback();
|
|
1472
|
+
if (node.patched.length) {
|
|
1473
|
+
fiber.patched.push(fiber);
|
|
1490
1474
|
}
|
|
1475
|
+
return fiber;
|
|
1476
|
+
}
|
|
1477
|
+
function throwOnRender() {
|
|
1478
|
+
throw new Error("Attempted to render cancelled fiber");
|
|
1491
1479
|
}
|
|
1492
|
-
const callbacksToTargets = new WeakMap();
|
|
1493
1480
|
/**
|
|
1494
|
-
*
|
|
1495
|
-
*
|
|
1496
|
-
* @param callback the callback for which the reactives need to be cleared
|
|
1481
|
+
* @returns number of not-yet rendered fibers cancelled
|
|
1497
1482
|
*/
|
|
1498
|
-
function
|
|
1499
|
-
|
|
1500
|
-
|
|
1501
|
-
|
|
1502
|
-
|
|
1503
|
-
|
|
1504
|
-
|
|
1505
|
-
|
|
1506
|
-
continue;
|
|
1483
|
+
function cancelFibers(fibers) {
|
|
1484
|
+
let result = 0;
|
|
1485
|
+
for (let fiber of fibers) {
|
|
1486
|
+
let node = fiber.node;
|
|
1487
|
+
fiber.render = throwOnRender;
|
|
1488
|
+
if (node.status === 0 /* NEW */) {
|
|
1489
|
+
node.destroy();
|
|
1490
|
+
delete node.parent.children[node.parentKey];
|
|
1507
1491
|
}
|
|
1508
|
-
|
|
1509
|
-
|
|
1492
|
+
node.fiber = null;
|
|
1493
|
+
if (fiber.bdom) {
|
|
1494
|
+
// if fiber has been rendered, this means that the component props have
|
|
1495
|
+
// been updated. however, this fiber will not be patched to the dom, so
|
|
1496
|
+
// it could happen that the next render compare the current props with
|
|
1497
|
+
// the same props, and skip the render completely. With the next line,
|
|
1498
|
+
// we kindly request the component code to force a render, so it works as
|
|
1499
|
+
// expected.
|
|
1500
|
+
node.forceNextRender = true;
|
|
1510
1501
|
}
|
|
1502
|
+
else {
|
|
1503
|
+
result++;
|
|
1504
|
+
}
|
|
1505
|
+
result += cancelFibers(fiber.children);
|
|
1511
1506
|
}
|
|
1512
|
-
|
|
1513
|
-
}
|
|
1514
|
-
function getSubscriptions(callback) {
|
|
1515
|
-
const targets = callbacksToTargets.get(callback) || [];
|
|
1516
|
-
return [...targets].map((target) => {
|
|
1517
|
-
const keysToCallbacks = targetToKeysToCallbacks.get(target);
|
|
1518
|
-
return {
|
|
1519
|
-
target,
|
|
1520
|
-
keys: keysToCallbacks ? [...keysToCallbacks.keys()] : [],
|
|
1521
|
-
};
|
|
1522
|
-
});
|
|
1507
|
+
return result;
|
|
1523
1508
|
}
|
|
1524
|
-
|
|
1525
|
-
|
|
1526
|
-
|
|
1527
|
-
|
|
1528
|
-
|
|
1529
|
-
|
|
1530
|
-
|
|
1531
|
-
|
|
1532
|
-
|
|
1533
|
-
|
|
1534
|
-
|
|
1535
|
-
|
|
1536
|
-
|
|
1537
|
-
|
|
1538
|
-
|
|
1539
|
-
|
|
1540
|
-
|
|
1541
|
-
|
|
1542
|
-
|
|
1543
|
-
* This is a choice that was made because changing a key's value will trigger
|
|
1544
|
-
* this trap and we do not want to subscribe by writes. This also means that
|
|
1545
|
-
* Object.hasOwnProperty doesn't subscribe as it goes through this trap.
|
|
1546
|
-
*
|
|
1547
|
-
* @param target the object for which to create a reactive proxy
|
|
1548
|
-
* @param callback the function to call when an observed property of the
|
|
1549
|
-
* reactive has changed
|
|
1550
|
-
* @returns a proxy that tracks changes to it
|
|
1551
|
-
*/
|
|
1552
|
-
function reactive(target, callback = () => { }) {
|
|
1553
|
-
if (!canBeMadeReactive(target)) {
|
|
1554
|
-
throw new Error(`Cannot make the given value reactive`);
|
|
1509
|
+
class Fiber {
|
|
1510
|
+
constructor(node, parent) {
|
|
1511
|
+
this.bdom = null;
|
|
1512
|
+
this.children = [];
|
|
1513
|
+
this.appliedToDom = false;
|
|
1514
|
+
this.deep = false;
|
|
1515
|
+
this.childrenMap = {};
|
|
1516
|
+
this.node = node;
|
|
1517
|
+
this.parent = parent;
|
|
1518
|
+
if (parent) {
|
|
1519
|
+
this.deep = parent.deep;
|
|
1520
|
+
const root = parent.root;
|
|
1521
|
+
root.setCounter(root.counter + 1);
|
|
1522
|
+
this.root = root;
|
|
1523
|
+
parent.children.push(this);
|
|
1524
|
+
}
|
|
1525
|
+
else {
|
|
1526
|
+
this.root = this;
|
|
1527
|
+
}
|
|
1555
1528
|
}
|
|
1556
|
-
|
|
1557
|
-
|
|
1529
|
+
render() {
|
|
1530
|
+
// if some parent has a fiber => register in followup
|
|
1531
|
+
let prev = this.root.node;
|
|
1532
|
+
let scheduler = prev.app.scheduler;
|
|
1533
|
+
let current = prev.parent;
|
|
1534
|
+
while (current) {
|
|
1535
|
+
if (current.fiber) {
|
|
1536
|
+
let root = current.fiber.root;
|
|
1537
|
+
if (root.counter === 0 && prev.parentKey in current.fiber.childrenMap) {
|
|
1538
|
+
current = root.node;
|
|
1539
|
+
}
|
|
1540
|
+
else {
|
|
1541
|
+
scheduler.delayedRenders.push(this);
|
|
1542
|
+
return;
|
|
1543
|
+
}
|
|
1544
|
+
}
|
|
1545
|
+
prev = current;
|
|
1546
|
+
current = current.parent;
|
|
1547
|
+
}
|
|
1548
|
+
// there are no current rendering from above => we can render
|
|
1549
|
+
this._render();
|
|
1558
1550
|
}
|
|
1559
|
-
|
|
1560
|
-
|
|
1561
|
-
|
|
1551
|
+
_render() {
|
|
1552
|
+
const node = this.node;
|
|
1553
|
+
const root = this.root;
|
|
1554
|
+
if (root) {
|
|
1555
|
+
try {
|
|
1556
|
+
this.bdom = true;
|
|
1557
|
+
this.bdom = node.renderFn();
|
|
1558
|
+
}
|
|
1559
|
+
catch (e) {
|
|
1560
|
+
handleError({ node, error: e });
|
|
1561
|
+
}
|
|
1562
|
+
root.setCounter(root.counter - 1);
|
|
1563
|
+
}
|
|
1562
1564
|
}
|
|
1563
|
-
|
|
1564
|
-
|
|
1565
|
+
}
|
|
1566
|
+
class RootFiber extends Fiber {
|
|
1567
|
+
constructor() {
|
|
1568
|
+
super(...arguments);
|
|
1569
|
+
this.counter = 1;
|
|
1570
|
+
// only add stuff in this if they have registered some hooks
|
|
1571
|
+
this.willPatch = [];
|
|
1572
|
+
this.patched = [];
|
|
1573
|
+
this.mounted = [];
|
|
1574
|
+
// A fiber is typically locked when it is completing and the patch has not, or is being applied.
|
|
1575
|
+
// i.e.: render triggered in onWillUnmount or in willPatch will be delayed
|
|
1576
|
+
this.locked = false;
|
|
1565
1577
|
}
|
|
1566
|
-
|
|
1567
|
-
|
|
1568
|
-
|
|
1569
|
-
|
|
1570
|
-
|
|
1571
|
-
:
|
|
1572
|
-
|
|
1573
|
-
|
|
1578
|
+
complete() {
|
|
1579
|
+
const node = this.node;
|
|
1580
|
+
this.locked = true;
|
|
1581
|
+
let current = undefined;
|
|
1582
|
+
try {
|
|
1583
|
+
// Step 1: calling all willPatch lifecycle hooks
|
|
1584
|
+
for (current of this.willPatch) {
|
|
1585
|
+
// because of the asynchronous nature of the rendering, some parts of the
|
|
1586
|
+
// UI may have been rendered, then deleted in a followup rendering, and we
|
|
1587
|
+
// do not want to call onWillPatch in that case.
|
|
1588
|
+
let node = current.node;
|
|
1589
|
+
if (node.fiber === current) {
|
|
1590
|
+
const component = node.component;
|
|
1591
|
+
for (let cb of node.willPatch) {
|
|
1592
|
+
cb.call(component);
|
|
1593
|
+
}
|
|
1594
|
+
}
|
|
1595
|
+
}
|
|
1596
|
+
current = undefined;
|
|
1597
|
+
// Step 2: patching the dom
|
|
1598
|
+
node._patch();
|
|
1599
|
+
this.locked = false;
|
|
1600
|
+
// Step 4: calling all mounted lifecycle hooks
|
|
1601
|
+
let mountedFibers = this.mounted;
|
|
1602
|
+
while ((current = mountedFibers.pop())) {
|
|
1603
|
+
current = current;
|
|
1604
|
+
if (current.appliedToDom) {
|
|
1605
|
+
for (let cb of current.node.mounted) {
|
|
1606
|
+
cb();
|
|
1607
|
+
}
|
|
1608
|
+
}
|
|
1609
|
+
}
|
|
1610
|
+
// Step 5: calling all patched hooks
|
|
1611
|
+
let patchedFibers = this.patched;
|
|
1612
|
+
while ((current = patchedFibers.pop())) {
|
|
1613
|
+
current = current;
|
|
1614
|
+
if (current.appliedToDom) {
|
|
1615
|
+
for (let cb of current.node.patched) {
|
|
1616
|
+
cb();
|
|
1617
|
+
}
|
|
1618
|
+
}
|
|
1619
|
+
}
|
|
1620
|
+
}
|
|
1621
|
+
catch (e) {
|
|
1622
|
+
this.locked = false;
|
|
1623
|
+
handleError({ fiber: current || this, error: e });
|
|
1624
|
+
}
|
|
1625
|
+
}
|
|
1626
|
+
setCounter(newValue) {
|
|
1627
|
+
this.counter = newValue;
|
|
1628
|
+
if (newValue === 0) {
|
|
1629
|
+
this.node.app.scheduler.flush();
|
|
1630
|
+
}
|
|
1574
1631
|
}
|
|
1575
|
-
return reactivesForTarget.get(callback);
|
|
1576
1632
|
}
|
|
1577
|
-
|
|
1578
|
-
|
|
1579
|
-
|
|
1580
|
-
|
|
1581
|
-
|
|
1582
|
-
|
|
1583
|
-
|
|
1584
|
-
|
|
1585
|
-
|
|
1586
|
-
|
|
1587
|
-
|
|
1588
|
-
|
|
1589
|
-
|
|
1590
|
-
|
|
1591
|
-
|
|
1592
|
-
|
|
1633
|
+
class MountFiber extends RootFiber {
|
|
1634
|
+
constructor(node, target, options = {}) {
|
|
1635
|
+
super(node, null);
|
|
1636
|
+
this.target = target;
|
|
1637
|
+
this.position = options.position || "last-child";
|
|
1638
|
+
}
|
|
1639
|
+
complete() {
|
|
1640
|
+
let current = this;
|
|
1641
|
+
try {
|
|
1642
|
+
const node = this.node;
|
|
1643
|
+
node.children = this.childrenMap;
|
|
1644
|
+
node.app.constructor.validateTarget(this.target);
|
|
1645
|
+
if (node.bdom) {
|
|
1646
|
+
// this is a complicated situation: if we mount a fiber with an existing
|
|
1647
|
+
// bdom, this means that this same fiber was already completed, mounted,
|
|
1648
|
+
// but a crash occurred in some mounted hook. Then, it was handled and
|
|
1649
|
+
// the new rendering is being applied.
|
|
1650
|
+
node.updateDom();
|
|
1593
1651
|
}
|
|
1594
|
-
|
|
1595
|
-
|
|
1596
|
-
|
|
1597
|
-
|
|
1598
|
-
|
|
1599
|
-
|
|
1600
|
-
|
|
1601
|
-
|
|
1602
|
-
|
|
1652
|
+
else {
|
|
1653
|
+
node.bdom = this.bdom;
|
|
1654
|
+
if (this.position === "last-child" || this.target.childNodes.length === 0) {
|
|
1655
|
+
mount$1(node.bdom, this.target);
|
|
1656
|
+
}
|
|
1657
|
+
else {
|
|
1658
|
+
const firstChild = this.target.childNodes[0];
|
|
1659
|
+
mount$1(node.bdom, this.target, firstChild);
|
|
1660
|
+
}
|
|
1603
1661
|
}
|
|
1604
|
-
//
|
|
1605
|
-
//
|
|
1606
|
-
|
|
1607
|
-
|
|
1608
|
-
|
|
1662
|
+
// unregistering the fiber before mounted since it can do another render
|
|
1663
|
+
// and that the current rendering is obviously completed
|
|
1664
|
+
node.fiber = null;
|
|
1665
|
+
node.status = 1 /* MOUNTED */;
|
|
1666
|
+
this.appliedToDom = true;
|
|
1667
|
+
let mountedFibers = this.mounted;
|
|
1668
|
+
while ((current = mountedFibers.pop())) {
|
|
1669
|
+
if (current.appliedToDom) {
|
|
1670
|
+
for (let cb of current.node.mounted) {
|
|
1671
|
+
cb();
|
|
1672
|
+
}
|
|
1673
|
+
}
|
|
1609
1674
|
}
|
|
1610
|
-
|
|
1611
|
-
|
|
1612
|
-
|
|
1613
|
-
|
|
1614
|
-
|
|
1615
|
-
|
|
1616
|
-
|
|
1617
|
-
|
|
1618
|
-
|
|
1619
|
-
|
|
1620
|
-
|
|
1621
|
-
|
|
1622
|
-
|
|
1623
|
-
|
|
1624
|
-
|
|
1625
|
-
|
|
1626
|
-
|
|
1627
|
-
observeTargetKey(target, KEYCHANGES, callback);
|
|
1628
|
-
return Reflect.has(target, key);
|
|
1629
|
-
},
|
|
1630
|
-
};
|
|
1631
|
-
}
|
|
1675
|
+
}
|
|
1676
|
+
catch (e) {
|
|
1677
|
+
handleError({ fiber: current, error: e });
|
|
1678
|
+
}
|
|
1679
|
+
}
|
|
1680
|
+
}
|
|
1681
|
+
|
|
1682
|
+
// Allows to get the target of a Reactive (used for making a new Reactive from the underlying object)
|
|
1683
|
+
const TARGET = Symbol("Target");
|
|
1684
|
+
// Escape hatch to prevent reactivity system to turn something into a reactive
|
|
1685
|
+
const SKIP = Symbol("Skip");
|
|
1686
|
+
// Special key to subscribe to, to be notified of key creation/deletion
|
|
1687
|
+
const KEYCHANGES = Symbol("Key changes");
|
|
1688
|
+
const objectToString = Object.prototype.toString;
|
|
1689
|
+
const objectHasOwnProperty = Object.prototype.hasOwnProperty;
|
|
1690
|
+
const SUPPORTED_RAW_TYPES = new Set(["Object", "Array", "Set", "Map", "WeakMap"]);
|
|
1691
|
+
const COLLECTION_RAWTYPES = new Set(["Set", "Map", "WeakMap"]);
|
|
1632
1692
|
/**
|
|
1633
|
-
*
|
|
1634
|
-
*
|
|
1693
|
+
* extract "RawType" from strings like "[object RawType]" => this lets us ignore
|
|
1694
|
+
* many native objects such as Promise (whose toString is [object Promise])
|
|
1695
|
+
* or Date ([object Date]), while also supporting collections without using
|
|
1696
|
+
* instanceof in a loop
|
|
1635
1697
|
*
|
|
1636
|
-
* @param
|
|
1637
|
-
* @
|
|
1638
|
-
* @param callback @see reactive
|
|
1698
|
+
* @param obj the object to check
|
|
1699
|
+
* @returns the raw type of the object
|
|
1639
1700
|
*/
|
|
1640
|
-
function
|
|
1641
|
-
return (
|
|
1642
|
-
key = toRaw(key);
|
|
1643
|
-
observeTargetKey(target, key, callback);
|
|
1644
|
-
return possiblyReactive(target[methodName](key), callback);
|
|
1645
|
-
};
|
|
1701
|
+
function rawType(obj) {
|
|
1702
|
+
return objectToString.call(obj).slice(8, -1);
|
|
1646
1703
|
}
|
|
1647
1704
|
/**
|
|
1648
|
-
*
|
|
1649
|
-
* observe keys as necessary.
|
|
1705
|
+
* Checks whether a given value can be made into a reactive object.
|
|
1650
1706
|
*
|
|
1651
|
-
* @param
|
|
1652
|
-
* @
|
|
1653
|
-
* @param callback @see reactive
|
|
1707
|
+
* @param value the value to check
|
|
1708
|
+
* @returns whether the value can be made reactive
|
|
1654
1709
|
*/
|
|
1655
|
-
function
|
|
1656
|
-
|
|
1657
|
-
|
|
1658
|
-
|
|
1659
|
-
|
|
1660
|
-
const key = keys.next().value;
|
|
1661
|
-
observeTargetKey(target, key, callback);
|
|
1662
|
-
yield possiblyReactive(item, callback);
|
|
1663
|
-
}
|
|
1664
|
-
};
|
|
1710
|
+
function canBeMadeReactive(value) {
|
|
1711
|
+
if (typeof value !== "object") {
|
|
1712
|
+
return false;
|
|
1713
|
+
}
|
|
1714
|
+
return SUPPORTED_RAW_TYPES.has(rawType(value));
|
|
1665
1715
|
}
|
|
1666
1716
|
/**
|
|
1667
|
-
* Creates a
|
|
1668
|
-
*
|
|
1669
|
-
* and making the passed keys/values reactive.
|
|
1717
|
+
* Creates a reactive from the given object/callback if possible and returns it,
|
|
1718
|
+
* returns the original object otherwise.
|
|
1670
1719
|
*
|
|
1671
|
-
* @param
|
|
1672
|
-
* @
|
|
1720
|
+
* @param value the value make reactive
|
|
1721
|
+
* @returns a reactive for the given object when possible, the original otherwise
|
|
1673
1722
|
*/
|
|
1674
|
-
function
|
|
1675
|
-
return
|
|
1676
|
-
observeTargetKey(target, KEYCHANGES, callback);
|
|
1677
|
-
target.forEach(function (val, key, targetObj) {
|
|
1678
|
-
observeTargetKey(target, key, callback);
|
|
1679
|
-
forEachCb.call(thisArg, possiblyReactive(val, callback), possiblyReactive(key, callback), possiblyReactive(targetObj, callback));
|
|
1680
|
-
}, thisArg);
|
|
1681
|
-
};
|
|
1723
|
+
function possiblyReactive(val, cb) {
|
|
1724
|
+
return canBeMadeReactive(val) ? reactive(val, cb) : val;
|
|
1682
1725
|
}
|
|
1683
1726
|
/**
|
|
1684
|
-
*
|
|
1685
|
-
* that method has modified the presence or value of a key, and notify the
|
|
1686
|
-
* reactives appropriately.
|
|
1727
|
+
* Mark an object or array so that it is ignored by the reactivity system
|
|
1687
1728
|
*
|
|
1688
|
-
* @param
|
|
1689
|
-
* @
|
|
1690
|
-
* value before calling the delegate method for comparison purposes
|
|
1691
|
-
* @param target @see reactive
|
|
1729
|
+
* @param value the value to mark
|
|
1730
|
+
* @returns the object itself
|
|
1692
1731
|
*/
|
|
1693
|
-
function
|
|
1694
|
-
|
|
1695
|
-
|
|
1696
|
-
const hadKey = target.has(key);
|
|
1697
|
-
const originalValue = target[getterName](key);
|
|
1698
|
-
const ret = target[setterName](key, value);
|
|
1699
|
-
const hasKey = target.has(key);
|
|
1700
|
-
if (hadKey !== hasKey) {
|
|
1701
|
-
notifyReactives(target, KEYCHANGES);
|
|
1702
|
-
}
|
|
1703
|
-
if (originalValue !== value) {
|
|
1704
|
-
notifyReactives(target, key);
|
|
1705
|
-
}
|
|
1706
|
-
return ret;
|
|
1707
|
-
};
|
|
1732
|
+
function markRaw(value) {
|
|
1733
|
+
value[SKIP] = true;
|
|
1734
|
+
return value;
|
|
1708
1735
|
}
|
|
1709
1736
|
/**
|
|
1710
|
-
*
|
|
1711
|
-
* the keys of the collection have changed.
|
|
1737
|
+
* Given a reactive objet, return the raw (non reactive) underlying object
|
|
1712
1738
|
*
|
|
1713
|
-
* @param
|
|
1739
|
+
* @param value a reactive value
|
|
1740
|
+
* @returns the underlying value
|
|
1714
1741
|
*/
|
|
1715
|
-
function
|
|
1716
|
-
return
|
|
1717
|
-
const allKeys = [...target.keys()];
|
|
1718
|
-
target.clear();
|
|
1719
|
-
notifyReactives(target, KEYCHANGES);
|
|
1720
|
-
for (const key of allKeys) {
|
|
1721
|
-
notifyReactives(target, key);
|
|
1722
|
-
}
|
|
1723
|
-
};
|
|
1742
|
+
function toRaw(value) {
|
|
1743
|
+
return value[TARGET] || value;
|
|
1724
1744
|
}
|
|
1745
|
+
const targetToKeysToCallbacks = new WeakMap();
|
|
1725
1746
|
/**
|
|
1726
|
-
*
|
|
1727
|
-
*
|
|
1728
|
-
* reactive set, calling the has method should mark the key that is being
|
|
1729
|
-
* retrieved as observed, and calling the add or delete method should notify the
|
|
1730
|
-
* reactives that the key which is being added or deleted has been modified.
|
|
1731
|
-
*/
|
|
1732
|
-
const rawTypeToFuncHandlers = {
|
|
1733
|
-
Set: (target, callback) => ({
|
|
1734
|
-
has: makeKeyObserver("has", target, callback),
|
|
1735
|
-
add: delegateAndNotify("add", "has", target),
|
|
1736
|
-
delete: delegateAndNotify("delete", "has", target),
|
|
1737
|
-
keys: makeIteratorObserver("keys", target, callback),
|
|
1738
|
-
values: makeIteratorObserver("values", target, callback),
|
|
1739
|
-
entries: makeIteratorObserver("entries", target, callback),
|
|
1740
|
-
[Symbol.iterator]: makeIteratorObserver(Symbol.iterator, target, callback),
|
|
1741
|
-
forEach: makeForEachObserver(target, callback),
|
|
1742
|
-
clear: makeClearNotifier(target),
|
|
1743
|
-
get size() {
|
|
1744
|
-
observeTargetKey(target, KEYCHANGES, callback);
|
|
1745
|
-
return target.size;
|
|
1746
|
-
},
|
|
1747
|
-
}),
|
|
1748
|
-
Map: (target, callback) => ({
|
|
1749
|
-
has: makeKeyObserver("has", target, callback),
|
|
1750
|
-
get: makeKeyObserver("get", target, callback),
|
|
1751
|
-
set: delegateAndNotify("set", "get", target),
|
|
1752
|
-
delete: delegateAndNotify("delete", "has", target),
|
|
1753
|
-
keys: makeIteratorObserver("keys", target, callback),
|
|
1754
|
-
values: makeIteratorObserver("values", target, callback),
|
|
1755
|
-
entries: makeIteratorObserver("entries", target, callback),
|
|
1756
|
-
[Symbol.iterator]: makeIteratorObserver(Symbol.iterator, target, callback),
|
|
1757
|
-
forEach: makeForEachObserver(target, callback),
|
|
1758
|
-
clear: makeClearNotifier(target),
|
|
1759
|
-
get size() {
|
|
1760
|
-
observeTargetKey(target, KEYCHANGES, callback);
|
|
1761
|
-
return target.size;
|
|
1762
|
-
},
|
|
1763
|
-
}),
|
|
1764
|
-
WeakMap: (target, callback) => ({
|
|
1765
|
-
has: makeKeyObserver("has", target, callback),
|
|
1766
|
-
get: makeKeyObserver("get", target, callback),
|
|
1767
|
-
set: delegateAndNotify("set", "get", target),
|
|
1768
|
-
delete: delegateAndNotify("delete", "has", target),
|
|
1769
|
-
}),
|
|
1770
|
-
};
|
|
1771
|
-
/**
|
|
1772
|
-
* Creates a proxy handler for collections (Set/Map/WeakMap)
|
|
1747
|
+
* Observes a given key on a target with an callback. The callback will be
|
|
1748
|
+
* called when the given key changes on the target.
|
|
1773
1749
|
*
|
|
1774
|
-
* @param
|
|
1775
|
-
* @param
|
|
1776
|
-
*
|
|
1750
|
+
* @param target the target whose key should be observed
|
|
1751
|
+
* @param key the key to observe (or Symbol(KEYCHANGES) for key creation
|
|
1752
|
+
* or deletion)
|
|
1753
|
+
* @param callback the function to call when the key changes
|
|
1777
1754
|
*/
|
|
1778
|
-
function
|
|
1779
|
-
|
|
1780
|
-
|
|
1781
|
-
|
|
1782
|
-
|
|
1783
|
-
|
|
1784
|
-
|
|
1785
|
-
|
|
1786
|
-
|
|
1787
|
-
|
|
1788
|
-
|
|
1789
|
-
|
|
1790
|
-
|
|
1791
|
-
|
|
1792
|
-
},
|
|
1793
|
-
});
|
|
1794
|
-
}
|
|
1795
|
-
|
|
1755
|
+
function observeTargetKey(target, key, callback) {
|
|
1756
|
+
if (!targetToKeysToCallbacks.get(target)) {
|
|
1757
|
+
targetToKeysToCallbacks.set(target, new Map());
|
|
1758
|
+
}
|
|
1759
|
+
const keyToCallbacks = targetToKeysToCallbacks.get(target);
|
|
1760
|
+
if (!keyToCallbacks.get(key)) {
|
|
1761
|
+
keyToCallbacks.set(key, new Set());
|
|
1762
|
+
}
|
|
1763
|
+
keyToCallbacks.get(key).add(callback);
|
|
1764
|
+
if (!callbacksToTargets.has(callback)) {
|
|
1765
|
+
callbacksToTargets.set(callback, new Set());
|
|
1766
|
+
}
|
|
1767
|
+
callbacksToTargets.get(callback).add(target);
|
|
1768
|
+
}
|
|
1796
1769
|
/**
|
|
1797
|
-
*
|
|
1798
|
-
*
|
|
1770
|
+
* Notify Reactives that are observing a given target that a key has changed on
|
|
1771
|
+
* the target.
|
|
1799
1772
|
*
|
|
1800
|
-
* @param
|
|
1801
|
-
*
|
|
1773
|
+
* @param target target whose Reactives should be notified that the target was
|
|
1774
|
+
* changed.
|
|
1775
|
+
* @param key the key that changed (or Symbol `KEYCHANGES` if a key was created
|
|
1776
|
+
* or deleted)
|
|
1802
1777
|
*/
|
|
1803
|
-
function
|
|
1804
|
-
|
|
1805
|
-
|
|
1806
|
-
|
|
1807
|
-
// in the next microtick. This line decides the granularity of the batch.
|
|
1808
|
-
await Promise.resolve();
|
|
1809
|
-
if (!called) {
|
|
1810
|
-
called = true;
|
|
1811
|
-
// wait for all calls in this microtick to fall through before resetting "called"
|
|
1812
|
-
// so that only the first call to the batched function calls the original callback.
|
|
1813
|
-
// Schedule this before calling the callback so that calls to the batched function
|
|
1814
|
-
// within the callback will proceed only after resetting called to false, and have
|
|
1815
|
-
// a chance to execute the callback again
|
|
1816
|
-
Promise.resolve().then(() => (called = false));
|
|
1817
|
-
callback();
|
|
1818
|
-
}
|
|
1819
|
-
};
|
|
1820
|
-
}
|
|
1821
|
-
function validateTarget(target) {
|
|
1822
|
-
if (!(target instanceof HTMLElement)) {
|
|
1823
|
-
throw new Error("Cannot mount component: the target is not a valid DOM element");
|
|
1778
|
+
function notifyReactives(target, key) {
|
|
1779
|
+
const keyToCallbacks = targetToKeysToCallbacks.get(target);
|
|
1780
|
+
if (!keyToCallbacks) {
|
|
1781
|
+
return;
|
|
1824
1782
|
}
|
|
1825
|
-
|
|
1826
|
-
|
|
1783
|
+
const callbacks = keyToCallbacks.get(key);
|
|
1784
|
+
if (!callbacks) {
|
|
1785
|
+
return;
|
|
1827
1786
|
}
|
|
1828
|
-
|
|
1829
|
-
|
|
1830
|
-
|
|
1831
|
-
|
|
1787
|
+
// Loop on copy because clearReactivesForCallback will modify the set in place
|
|
1788
|
+
for (const callback of [...callbacks]) {
|
|
1789
|
+
clearReactivesForCallback(callback);
|
|
1790
|
+
callback();
|
|
1832
1791
|
}
|
|
1833
1792
|
}
|
|
1834
|
-
|
|
1835
|
-
|
|
1836
|
-
|
|
1837
|
-
|
|
1793
|
+
const callbacksToTargets = new WeakMap();
|
|
1794
|
+
/**
|
|
1795
|
+
* Clears all subscriptions of the Reactives associated with a given callback.
|
|
1796
|
+
*
|
|
1797
|
+
* @param callback the callback for which the reactives need to be cleared
|
|
1798
|
+
*/
|
|
1799
|
+
function clearReactivesForCallback(callback) {
|
|
1800
|
+
const targetsToClear = callbacksToTargets.get(callback);
|
|
1801
|
+
if (!targetsToClear) {
|
|
1802
|
+
return;
|
|
1803
|
+
}
|
|
1804
|
+
for (const target of targetsToClear) {
|
|
1805
|
+
const observedKeys = targetToKeysToCallbacks.get(target);
|
|
1806
|
+
if (!observedKeys) {
|
|
1807
|
+
continue;
|
|
1838
1808
|
}
|
|
1839
|
-
|
|
1840
|
-
|
|
1809
|
+
for (const callbacks of observedKeys.values()) {
|
|
1810
|
+
callbacks.delete(callback);
|
|
1841
1811
|
}
|
|
1842
|
-
}).then(fn || function () { });
|
|
1843
|
-
}
|
|
1844
|
-
async function loadFile(url) {
|
|
1845
|
-
const result = await fetch(url);
|
|
1846
|
-
if (!result.ok) {
|
|
1847
|
-
throw new Error("Error while fetching xml templates");
|
|
1848
1812
|
}
|
|
1849
|
-
|
|
1813
|
+
targetsToClear.clear();
|
|
1850
1814
|
}
|
|
1851
|
-
|
|
1852
|
-
|
|
1853
|
-
|
|
1854
|
-
|
|
1855
|
-
|
|
1856
|
-
|
|
1815
|
+
function getSubscriptions(callback) {
|
|
1816
|
+
const targets = callbacksToTargets.get(callback) || [];
|
|
1817
|
+
return [...targets].map((target) => {
|
|
1818
|
+
const keysToCallbacks = targetToKeysToCallbacks.get(target);
|
|
1819
|
+
return {
|
|
1820
|
+
target,
|
|
1821
|
+
keys: keysToCallbacks ? [...keysToCallbacks.keys()] : [],
|
|
1822
|
+
};
|
|
1823
|
+
});
|
|
1857
1824
|
}
|
|
1858
|
-
|
|
1859
|
-
|
|
1860
|
-
*
|
|
1825
|
+
const reactiveCache = new WeakMap();
|
|
1826
|
+
/**
|
|
1827
|
+
* Creates a reactive proxy for an object. Reading data on the reactive object
|
|
1828
|
+
* subscribes to changes to the data. Writing data on the object will cause the
|
|
1829
|
+
* notify callback to be called if there are suscriptions to that data. Nested
|
|
1830
|
+
* objects and arrays are automatically made reactive as well.
|
|
1831
|
+
*
|
|
1832
|
+
* Whenever you are notified of a change, all subscriptions are cleared, and if
|
|
1833
|
+
* you would like to be notified of any further changes, you should go read
|
|
1834
|
+
* the underlying data again. We assume that if you don't go read it again after
|
|
1835
|
+
* being notified, it means that you are no longer interested in that data.
|
|
1836
|
+
*
|
|
1837
|
+
* Subscriptions:
|
|
1838
|
+
* + Reading a property on an object will subscribe you to changes in the value
|
|
1839
|
+
* of that property.
|
|
1840
|
+
* + Accessing an object keys (eg with Object.keys or with `for..in`) will
|
|
1841
|
+
* subscribe you to the creation/deletion of keys. Checking the presence of a
|
|
1842
|
+
* key on the object with 'in' has the same effect.
|
|
1843
|
+
* - getOwnPropertyDescriptor does not currently subscribe you to the property.
|
|
1844
|
+
* This is a choice that was made because changing a key's value will trigger
|
|
1845
|
+
* this trap and we do not want to subscribe by writes. This also means that
|
|
1846
|
+
* Object.hasOwnProperty doesn't subscribe as it goes through this trap.
|
|
1847
|
+
*
|
|
1848
|
+
* @param target the object for which to create a reactive proxy
|
|
1849
|
+
* @param callback the function to call when an observed property of the
|
|
1850
|
+
* reactive has changed
|
|
1851
|
+
* @returns a proxy that tracks changes to it
|
|
1861
1852
|
*/
|
|
1862
|
-
function
|
|
1863
|
-
|
|
1864
|
-
|
|
1865
|
-
|
|
1866
|
-
class Component {
|
|
1867
|
-
constructor(props, env, node) {
|
|
1868
|
-
this.props = props;
|
|
1869
|
-
this.env = env;
|
|
1870
|
-
this.__owl__ = node;
|
|
1853
|
+
function reactive(target, callback = () => { }) {
|
|
1854
|
+
if (!canBeMadeReactive(target)) {
|
|
1855
|
+
throw new Error(`Cannot make the given value reactive`);
|
|
1871
1856
|
}
|
|
1872
|
-
|
|
1873
|
-
|
|
1874
|
-
this.__owl__.render(deep === true);
|
|
1857
|
+
if (SKIP in target) {
|
|
1858
|
+
return target;
|
|
1875
1859
|
}
|
|
1876
|
-
|
|
1877
|
-
|
|
1878
|
-
|
|
1879
|
-
// Maps fibers to thrown errors
|
|
1880
|
-
const fibersInError = new WeakMap();
|
|
1881
|
-
const nodeErrorHandlers = new WeakMap();
|
|
1882
|
-
function _handleError(node, error) {
|
|
1883
|
-
if (!node) {
|
|
1884
|
-
return false;
|
|
1860
|
+
const originalTarget = target[TARGET];
|
|
1861
|
+
if (originalTarget) {
|
|
1862
|
+
return reactive(originalTarget, callback);
|
|
1885
1863
|
}
|
|
1886
|
-
|
|
1887
|
-
|
|
1888
|
-
fibersInError.set(fiber, error);
|
|
1864
|
+
if (!reactiveCache.has(target)) {
|
|
1865
|
+
reactiveCache.set(target, new WeakMap());
|
|
1889
1866
|
}
|
|
1890
|
-
const
|
|
1891
|
-
if (
|
|
1892
|
-
|
|
1893
|
-
|
|
1894
|
-
|
|
1895
|
-
|
|
1896
|
-
|
|
1897
|
-
|
|
1898
|
-
break;
|
|
1899
|
-
}
|
|
1900
|
-
catch (e) {
|
|
1901
|
-
error = e;
|
|
1902
|
-
}
|
|
1903
|
-
}
|
|
1904
|
-
if (handled) {
|
|
1905
|
-
return true;
|
|
1906
|
-
}
|
|
1867
|
+
const reactivesForTarget = reactiveCache.get(target);
|
|
1868
|
+
if (!reactivesForTarget.has(callback)) {
|
|
1869
|
+
const targetRawType = rawType(target);
|
|
1870
|
+
const handler = COLLECTION_RAWTYPES.has(targetRawType)
|
|
1871
|
+
? collectionsProxyHandler(target, callback, targetRawType)
|
|
1872
|
+
: basicProxyHandler(callback);
|
|
1873
|
+
const proxy = new Proxy(target, handler);
|
|
1874
|
+
reactivesForTarget.set(callback, proxy);
|
|
1907
1875
|
}
|
|
1908
|
-
return
|
|
1876
|
+
return reactivesForTarget.get(callback);
|
|
1909
1877
|
}
|
|
1910
|
-
|
|
1911
|
-
|
|
1912
|
-
|
|
1913
|
-
|
|
1914
|
-
|
|
1915
|
-
|
|
1916
|
-
|
|
1917
|
-
|
|
1918
|
-
|
|
1919
|
-
|
|
1920
|
-
|
|
1921
|
-
|
|
1922
|
-
|
|
1923
|
-
|
|
1924
|
-
|
|
1925
|
-
|
|
1926
|
-
|
|
1927
|
-
|
|
1928
|
-
|
|
1929
|
-
|
|
1930
|
-
|
|
1931
|
-
|
|
1932
|
-
|
|
1933
|
-
|
|
1934
|
-
|
|
1935
|
-
|
|
1936
|
-
|
|
1937
|
-
|
|
1938
|
-
|
|
1939
|
-
|
|
1940
|
-
|
|
1878
|
+
/**
|
|
1879
|
+
* Creates a basic proxy handler for regular objects and arrays.
|
|
1880
|
+
*
|
|
1881
|
+
* @param callback @see reactive
|
|
1882
|
+
* @returns a proxy handler object
|
|
1883
|
+
*/
|
|
1884
|
+
function basicProxyHandler(callback) {
|
|
1885
|
+
return {
|
|
1886
|
+
get(target, key, proxy) {
|
|
1887
|
+
if (key === TARGET) {
|
|
1888
|
+
return target;
|
|
1889
|
+
}
|
|
1890
|
+
// non-writable non-configurable properties cannot be made reactive
|
|
1891
|
+
const desc = Object.getOwnPropertyDescriptor(target, key);
|
|
1892
|
+
if (desc && !desc.writable && !desc.configurable) {
|
|
1893
|
+
return Reflect.get(target, key, proxy);
|
|
1894
|
+
}
|
|
1895
|
+
observeTargetKey(target, key, callback);
|
|
1896
|
+
return possiblyReactive(Reflect.get(target, key, proxy), callback);
|
|
1897
|
+
},
|
|
1898
|
+
set(target, key, value, proxy) {
|
|
1899
|
+
const isNewKey = !objectHasOwnProperty.call(target, key);
|
|
1900
|
+
const originalValue = Reflect.get(target, key, proxy);
|
|
1901
|
+
const ret = Reflect.set(target, key, value, proxy);
|
|
1902
|
+
if (isNewKey) {
|
|
1903
|
+
notifyReactives(target, KEYCHANGES);
|
|
1904
|
+
}
|
|
1905
|
+
// While Array length may trigger the set trap, it's not actually set by this
|
|
1906
|
+
// method but is updated behind the scenes, and the trap is not called with the
|
|
1907
|
+
// new value. We disable the "same-value-optimization" for it because of that.
|
|
1908
|
+
if (originalValue !== value || (Array.isArray(target) && key === "length")) {
|
|
1909
|
+
notifyReactives(target, key);
|
|
1910
|
+
}
|
|
1911
|
+
return ret;
|
|
1912
|
+
},
|
|
1913
|
+
deleteProperty(target, key) {
|
|
1914
|
+
const ret = Reflect.deleteProperty(target, key);
|
|
1915
|
+
// TODO: only notify when something was actually deleted
|
|
1916
|
+
notifyReactives(target, KEYCHANGES);
|
|
1917
|
+
notifyReactives(target, key);
|
|
1918
|
+
return ret;
|
|
1919
|
+
},
|
|
1920
|
+
ownKeys(target) {
|
|
1921
|
+
observeTargetKey(target, KEYCHANGES, callback);
|
|
1922
|
+
return Reflect.ownKeys(target);
|
|
1923
|
+
},
|
|
1924
|
+
has(target, key) {
|
|
1925
|
+
// TODO: this observes all key changes instead of only the presence of the argument key
|
|
1926
|
+
// observing the key itself would observe value changes instead of presence changes
|
|
1927
|
+
// so we may need a finer grained system to distinguish observing value vs presence.
|
|
1928
|
+
observeTargetKey(target, KEYCHANGES, callback);
|
|
1929
|
+
return Reflect.has(target, key);
|
|
1930
|
+
},
|
|
1931
|
+
};
|
|
1941
1932
|
}
|
|
1942
|
-
|
|
1943
|
-
|
|
1944
|
-
|
|
1945
|
-
|
|
1946
|
-
|
|
1947
|
-
|
|
1948
|
-
|
|
1949
|
-
|
|
1950
|
-
|
|
1951
|
-
|
|
1952
|
-
|
|
1953
|
-
|
|
1954
|
-
|
|
1955
|
-
|
|
1956
|
-
|
|
1957
|
-
|
|
1958
|
-
|
|
1933
|
+
/**
|
|
1934
|
+
* Creates a function that will observe the key that is passed to it when called
|
|
1935
|
+
* and delegates to the underlying method.
|
|
1936
|
+
*
|
|
1937
|
+
* @param methodName name of the method to delegate to
|
|
1938
|
+
* @param target @see reactive
|
|
1939
|
+
* @param callback @see reactive
|
|
1940
|
+
*/
|
|
1941
|
+
function makeKeyObserver(methodName, target, callback) {
|
|
1942
|
+
return (key) => {
|
|
1943
|
+
key = toRaw(key);
|
|
1944
|
+
observeTargetKey(target, key, callback);
|
|
1945
|
+
return possiblyReactive(target[methodName](key), callback);
|
|
1946
|
+
};
|
|
1947
|
+
}
|
|
1948
|
+
/**
|
|
1949
|
+
* Creates an iterable that will delegate to the underlying iteration method and
|
|
1950
|
+
* observe keys as necessary.
|
|
1951
|
+
*
|
|
1952
|
+
* @param methodName name of the method to delegate to
|
|
1953
|
+
* @param target @see reactive
|
|
1954
|
+
* @param callback @see reactive
|
|
1955
|
+
*/
|
|
1956
|
+
function makeIteratorObserver(methodName, target, callback) {
|
|
1957
|
+
return function* () {
|
|
1958
|
+
observeTargetKey(target, KEYCHANGES, callback);
|
|
1959
|
+
const keys = target.keys();
|
|
1960
|
+
for (const item of target[methodName]()) {
|
|
1961
|
+
const key = keys.next().value;
|
|
1962
|
+
observeTargetKey(target, key, callback);
|
|
1963
|
+
yield possiblyReactive(item, callback);
|
|
1959
1964
|
}
|
|
1960
|
-
|
|
1961
|
-
}
|
|
1962
|
-
const fiber = new RootFiber(node, null);
|
|
1963
|
-
if (node.willPatch.length) {
|
|
1964
|
-
fiber.willPatch.push(fiber);
|
|
1965
|
-
}
|
|
1966
|
-
if (node.patched.length) {
|
|
1967
|
-
fiber.patched.push(fiber);
|
|
1968
|
-
}
|
|
1969
|
-
return fiber;
|
|
1965
|
+
};
|
|
1970
1966
|
}
|
|
1971
|
-
|
|
1972
|
-
|
|
1967
|
+
/**
|
|
1968
|
+
* Creates a forEach function that will delegate to forEach on the underlying
|
|
1969
|
+
* collection while observing key changes, and keys as they're iterated over,
|
|
1970
|
+
* and making the passed keys/values reactive.
|
|
1971
|
+
*
|
|
1972
|
+
* @param target @see reactive
|
|
1973
|
+
* @param callback @see reactive
|
|
1974
|
+
*/
|
|
1975
|
+
function makeForEachObserver(target, callback) {
|
|
1976
|
+
return function forEach(forEachCb, thisArg) {
|
|
1977
|
+
observeTargetKey(target, KEYCHANGES, callback);
|
|
1978
|
+
target.forEach(function (val, key, targetObj) {
|
|
1979
|
+
observeTargetKey(target, key, callback);
|
|
1980
|
+
forEachCb.call(thisArg, possiblyReactive(val, callback), possiblyReactive(key, callback), possiblyReactive(targetObj, callback));
|
|
1981
|
+
}, thisArg);
|
|
1982
|
+
};
|
|
1973
1983
|
}
|
|
1974
1984
|
/**
|
|
1975
|
-
*
|
|
1985
|
+
* Creates a function that will delegate to an underlying method, and check if
|
|
1986
|
+
* that method has modified the presence or value of a key, and notify the
|
|
1987
|
+
* reactives appropriately.
|
|
1988
|
+
*
|
|
1989
|
+
* @param setterName name of the method to delegate to
|
|
1990
|
+
* @param getterName name of the method which should be used to retrieve the
|
|
1991
|
+
* value before calling the delegate method for comparison purposes
|
|
1992
|
+
* @param target @see reactive
|
|
1976
1993
|
*/
|
|
1977
|
-
function
|
|
1978
|
-
|
|
1979
|
-
|
|
1980
|
-
|
|
1981
|
-
|
|
1982
|
-
|
|
1983
|
-
|
|
1984
|
-
|
|
1985
|
-
|
|
1986
|
-
if (fiber.bdom) {
|
|
1987
|
-
// if fiber has been rendered, this means that the component props have
|
|
1988
|
-
// been updated. however, this fiber will not be patched to the dom, so
|
|
1989
|
-
// it could happen that the next render compare the current props with
|
|
1990
|
-
// the same props, and skip the render completely. With the next line,
|
|
1991
|
-
// we kindly request the component code to force a render, so it works as
|
|
1992
|
-
// expected.
|
|
1993
|
-
node.forceNextRender = true;
|
|
1994
|
+
function delegateAndNotify(setterName, getterName, target) {
|
|
1995
|
+
return (key, value) => {
|
|
1996
|
+
key = toRaw(key);
|
|
1997
|
+
const hadKey = target.has(key);
|
|
1998
|
+
const originalValue = target[getterName](key);
|
|
1999
|
+
const ret = target[setterName](key, value);
|
|
2000
|
+
const hasKey = target.has(key);
|
|
2001
|
+
if (hadKey !== hasKey) {
|
|
2002
|
+
notifyReactives(target, KEYCHANGES);
|
|
1994
2003
|
}
|
|
1995
|
-
|
|
1996
|
-
|
|
2004
|
+
if (originalValue !== value) {
|
|
2005
|
+
notifyReactives(target, key);
|
|
1997
2006
|
}
|
|
1998
|
-
|
|
1999
|
-
}
|
|
2000
|
-
return result;
|
|
2007
|
+
return ret;
|
|
2008
|
+
};
|
|
2001
2009
|
}
|
|
2002
|
-
|
|
2003
|
-
|
|
2004
|
-
|
|
2005
|
-
|
|
2006
|
-
|
|
2007
|
-
|
|
2008
|
-
|
|
2009
|
-
|
|
2010
|
-
|
|
2011
|
-
|
|
2012
|
-
|
|
2013
|
-
|
|
2014
|
-
|
|
2015
|
-
this.root = root;
|
|
2016
|
-
parent.children.push(this);
|
|
2017
|
-
}
|
|
2018
|
-
else {
|
|
2019
|
-
this.root = this;
|
|
2020
|
-
}
|
|
2021
|
-
}
|
|
2022
|
-
render() {
|
|
2023
|
-
// if some parent has a fiber => register in followup
|
|
2024
|
-
let prev = this.root.node;
|
|
2025
|
-
let scheduler = prev.app.scheduler;
|
|
2026
|
-
let current = prev.parent;
|
|
2027
|
-
while (current) {
|
|
2028
|
-
if (current.fiber) {
|
|
2029
|
-
let root = current.fiber.root;
|
|
2030
|
-
if (root.counter === 0 && prev.parentKey in current.fiber.childrenMap) {
|
|
2031
|
-
current = root.node;
|
|
2032
|
-
}
|
|
2033
|
-
else {
|
|
2034
|
-
scheduler.delayedRenders.push(this);
|
|
2035
|
-
return;
|
|
2036
|
-
}
|
|
2037
|
-
}
|
|
2038
|
-
prev = current;
|
|
2039
|
-
current = current.parent;
|
|
2010
|
+
/**
|
|
2011
|
+
* Creates a function that will clear the underlying collection and notify that
|
|
2012
|
+
* the keys of the collection have changed.
|
|
2013
|
+
*
|
|
2014
|
+
* @param target @see reactive
|
|
2015
|
+
*/
|
|
2016
|
+
function makeClearNotifier(target) {
|
|
2017
|
+
return () => {
|
|
2018
|
+
const allKeys = [...target.keys()];
|
|
2019
|
+
target.clear();
|
|
2020
|
+
notifyReactives(target, KEYCHANGES);
|
|
2021
|
+
for (const key of allKeys) {
|
|
2022
|
+
notifyReactives(target, key);
|
|
2040
2023
|
}
|
|
2041
|
-
|
|
2042
|
-
|
|
2043
|
-
|
|
2044
|
-
|
|
2045
|
-
|
|
2046
|
-
|
|
2047
|
-
|
|
2048
|
-
|
|
2049
|
-
|
|
2050
|
-
|
|
2024
|
+
};
|
|
2025
|
+
}
|
|
2026
|
+
/**
|
|
2027
|
+
* Maps raw type of an object to an object containing functions that can be used
|
|
2028
|
+
* to build an appropritate proxy handler for that raw type. Eg: when making a
|
|
2029
|
+
* reactive set, calling the has method should mark the key that is being
|
|
2030
|
+
* retrieved as observed, and calling the add or delete method should notify the
|
|
2031
|
+
* reactives that the key which is being added or deleted has been modified.
|
|
2032
|
+
*/
|
|
2033
|
+
const rawTypeToFuncHandlers = {
|
|
2034
|
+
Set: (target, callback) => ({
|
|
2035
|
+
has: makeKeyObserver("has", target, callback),
|
|
2036
|
+
add: delegateAndNotify("add", "has", target),
|
|
2037
|
+
delete: delegateAndNotify("delete", "has", target),
|
|
2038
|
+
keys: makeIteratorObserver("keys", target, callback),
|
|
2039
|
+
values: makeIteratorObserver("values", target, callback),
|
|
2040
|
+
entries: makeIteratorObserver("entries", target, callback),
|
|
2041
|
+
[Symbol.iterator]: makeIteratorObserver(Symbol.iterator, target, callback),
|
|
2042
|
+
forEach: makeForEachObserver(target, callback),
|
|
2043
|
+
clear: makeClearNotifier(target),
|
|
2044
|
+
get size() {
|
|
2045
|
+
observeTargetKey(target, KEYCHANGES, callback);
|
|
2046
|
+
return target.size;
|
|
2047
|
+
},
|
|
2048
|
+
}),
|
|
2049
|
+
Map: (target, callback) => ({
|
|
2050
|
+
has: makeKeyObserver("has", target, callback),
|
|
2051
|
+
get: makeKeyObserver("get", target, callback),
|
|
2052
|
+
set: delegateAndNotify("set", "get", target),
|
|
2053
|
+
delete: delegateAndNotify("delete", "has", target),
|
|
2054
|
+
keys: makeIteratorObserver("keys", target, callback),
|
|
2055
|
+
values: makeIteratorObserver("values", target, callback),
|
|
2056
|
+
entries: makeIteratorObserver("entries", target, callback),
|
|
2057
|
+
[Symbol.iterator]: makeIteratorObserver(Symbol.iterator, target, callback),
|
|
2058
|
+
forEach: makeForEachObserver(target, callback),
|
|
2059
|
+
clear: makeClearNotifier(target),
|
|
2060
|
+
get size() {
|
|
2061
|
+
observeTargetKey(target, KEYCHANGES, callback);
|
|
2062
|
+
return target.size;
|
|
2063
|
+
},
|
|
2064
|
+
}),
|
|
2065
|
+
WeakMap: (target, callback) => ({
|
|
2066
|
+
has: makeKeyObserver("has", target, callback),
|
|
2067
|
+
get: makeKeyObserver("get", target, callback),
|
|
2068
|
+
set: delegateAndNotify("set", "get", target),
|
|
2069
|
+
delete: delegateAndNotify("delete", "has", target),
|
|
2070
|
+
}),
|
|
2071
|
+
};
|
|
2072
|
+
/**
|
|
2073
|
+
* Creates a proxy handler for collections (Set/Map/WeakMap)
|
|
2074
|
+
*
|
|
2075
|
+
* @param callback @see reactive
|
|
2076
|
+
* @param target @see reactive
|
|
2077
|
+
* @returns a proxy handler object
|
|
2078
|
+
*/
|
|
2079
|
+
function collectionsProxyHandler(target, callback, targetRawType) {
|
|
2080
|
+
// TODO: if performance is an issue we can create the special handlers lazily when each
|
|
2081
|
+
// property is read.
|
|
2082
|
+
const specialHandlers = rawTypeToFuncHandlers[targetRawType](target, callback);
|
|
2083
|
+
return Object.assign(basicProxyHandler(callback), {
|
|
2084
|
+
get(target, key) {
|
|
2085
|
+
if (key === TARGET) {
|
|
2086
|
+
return target;
|
|
2051
2087
|
}
|
|
2052
|
-
|
|
2053
|
-
|
|
2088
|
+
if (objectHasOwnProperty.call(specialHandlers, key)) {
|
|
2089
|
+
return specialHandlers[key];
|
|
2054
2090
|
}
|
|
2055
|
-
|
|
2091
|
+
observeTargetKey(target, key, callback);
|
|
2092
|
+
return possiblyReactive(target[key], callback);
|
|
2093
|
+
},
|
|
2094
|
+
});
|
|
2095
|
+
}
|
|
2096
|
+
|
|
2097
|
+
/**
|
|
2098
|
+
* Creates a batched version of a callback so that all calls to it in the same
|
|
2099
|
+
* microtick will only call the original callback once.
|
|
2100
|
+
*
|
|
2101
|
+
* @param callback the callback to batch
|
|
2102
|
+
* @returns a batched version of the original callback
|
|
2103
|
+
*/
|
|
2104
|
+
function batched(callback) {
|
|
2105
|
+
let called = false;
|
|
2106
|
+
return async () => {
|
|
2107
|
+
// This await blocks all calls to the callback here, then releases them sequentially
|
|
2108
|
+
// in the next microtick. This line decides the granularity of the batch.
|
|
2109
|
+
await Promise.resolve();
|
|
2110
|
+
if (!called) {
|
|
2111
|
+
called = true;
|
|
2112
|
+
// wait for all calls in this microtick to fall through before resetting "called"
|
|
2113
|
+
// so that only the first call to the batched function calls the original callback.
|
|
2114
|
+
// Schedule this before calling the callback so that calls to the batched function
|
|
2115
|
+
// within the callback will proceed only after resetting called to false, and have
|
|
2116
|
+
// a chance to execute the callback again
|
|
2117
|
+
Promise.resolve().then(() => (called = false));
|
|
2118
|
+
callback();
|
|
2056
2119
|
}
|
|
2057
|
-
}
|
|
2120
|
+
};
|
|
2058
2121
|
}
|
|
2059
|
-
|
|
2060
|
-
|
|
2061
|
-
|
|
2062
|
-
this.counter = 1;
|
|
2063
|
-
// only add stuff in this if they have registered some hooks
|
|
2064
|
-
this.willPatch = [];
|
|
2065
|
-
this.patched = [];
|
|
2066
|
-
this.mounted = [];
|
|
2067
|
-
// A fiber is typically locked when it is completing and the patch has not, or is being applied.
|
|
2068
|
-
// i.e.: render triggered in onWillUnmount or in willPatch will be delayed
|
|
2069
|
-
this.locked = false;
|
|
2070
|
-
}
|
|
2071
|
-
complete() {
|
|
2072
|
-
const node = this.node;
|
|
2073
|
-
this.locked = true;
|
|
2074
|
-
let current = undefined;
|
|
2075
|
-
try {
|
|
2076
|
-
// Step 1: calling all willPatch lifecycle hooks
|
|
2077
|
-
for (current of this.willPatch) {
|
|
2078
|
-
// because of the asynchronous nature of the rendering, some parts of the
|
|
2079
|
-
// UI may have been rendered, then deleted in a followup rendering, and we
|
|
2080
|
-
// do not want to call onWillPatch in that case.
|
|
2081
|
-
let node = current.node;
|
|
2082
|
-
if (node.fiber === current) {
|
|
2083
|
-
const component = node.component;
|
|
2084
|
-
for (let cb of node.willPatch) {
|
|
2085
|
-
cb.call(component);
|
|
2086
|
-
}
|
|
2087
|
-
}
|
|
2088
|
-
}
|
|
2089
|
-
current = undefined;
|
|
2090
|
-
// Step 2: patching the dom
|
|
2091
|
-
node._patch();
|
|
2092
|
-
this.locked = false;
|
|
2093
|
-
// Step 4: calling all mounted lifecycle hooks
|
|
2094
|
-
let mountedFibers = this.mounted;
|
|
2095
|
-
while ((current = mountedFibers.pop())) {
|
|
2096
|
-
current = current;
|
|
2097
|
-
if (current.appliedToDom) {
|
|
2098
|
-
for (let cb of current.node.mounted) {
|
|
2099
|
-
cb();
|
|
2100
|
-
}
|
|
2101
|
-
}
|
|
2102
|
-
}
|
|
2103
|
-
// Step 5: calling all patched hooks
|
|
2104
|
-
let patchedFibers = this.patched;
|
|
2105
|
-
while ((current = patchedFibers.pop())) {
|
|
2106
|
-
current = current;
|
|
2107
|
-
if (current.appliedToDom) {
|
|
2108
|
-
for (let cb of current.node.patched) {
|
|
2109
|
-
cb();
|
|
2110
|
-
}
|
|
2111
|
-
}
|
|
2112
|
-
}
|
|
2113
|
-
}
|
|
2114
|
-
catch (e) {
|
|
2115
|
-
this.locked = false;
|
|
2116
|
-
handleError({ fiber: current || this, error: e });
|
|
2117
|
-
}
|
|
2122
|
+
function validateTarget(target) {
|
|
2123
|
+
if (!(target instanceof HTMLElement)) {
|
|
2124
|
+
throw new Error("Cannot mount component: the target is not a valid DOM element");
|
|
2118
2125
|
}
|
|
2119
|
-
|
|
2120
|
-
|
|
2121
|
-
if (newValue === 0) {
|
|
2122
|
-
this.node.app.scheduler.flush();
|
|
2123
|
-
}
|
|
2126
|
+
if (!document.body.contains(target)) {
|
|
2127
|
+
throw new Error("Cannot mount a component on a detached dom node");
|
|
2124
2128
|
}
|
|
2125
2129
|
}
|
|
2126
|
-
class
|
|
2127
|
-
|
|
2128
|
-
|
|
2129
|
-
this.target = target;
|
|
2130
|
-
this.position = options.position || "last-child";
|
|
2130
|
+
class EventBus extends EventTarget {
|
|
2131
|
+
trigger(name, payload) {
|
|
2132
|
+
this.dispatchEvent(new CustomEvent(name, { detail: payload }));
|
|
2131
2133
|
}
|
|
2132
|
-
|
|
2133
|
-
|
|
2134
|
-
|
|
2135
|
-
|
|
2136
|
-
|
|
2137
|
-
node.app.constructor.validateTarget(this.target);
|
|
2138
|
-
if (node.bdom) {
|
|
2139
|
-
// this is a complicated situation: if we mount a fiber with an existing
|
|
2140
|
-
// bdom, this means that this same fiber was already completed, mounted,
|
|
2141
|
-
// but a crash occurred in some mounted hook. Then, it was handled and
|
|
2142
|
-
// the new rendering is being applied.
|
|
2143
|
-
node.updateDom();
|
|
2144
|
-
}
|
|
2145
|
-
else {
|
|
2146
|
-
node.bdom = this.bdom;
|
|
2147
|
-
if (this.position === "last-child" || this.target.childNodes.length === 0) {
|
|
2148
|
-
mount$1(node.bdom, this.target);
|
|
2149
|
-
}
|
|
2150
|
-
else {
|
|
2151
|
-
const firstChild = this.target.childNodes[0];
|
|
2152
|
-
mount$1(node.bdom, this.target, firstChild);
|
|
2153
|
-
}
|
|
2154
|
-
}
|
|
2155
|
-
// unregistering the fiber before mounted since it can do another render
|
|
2156
|
-
// and that the current rendering is obviously completed
|
|
2157
|
-
node.fiber = null;
|
|
2158
|
-
node.status = 1 /* MOUNTED */;
|
|
2159
|
-
this.appliedToDom = true;
|
|
2160
|
-
let mountedFibers = this.mounted;
|
|
2161
|
-
while ((current = mountedFibers.pop())) {
|
|
2162
|
-
if (current.appliedToDom) {
|
|
2163
|
-
for (let cb of current.node.mounted) {
|
|
2164
|
-
cb();
|
|
2165
|
-
}
|
|
2166
|
-
}
|
|
2167
|
-
}
|
|
2134
|
+
}
|
|
2135
|
+
function whenReady(fn) {
|
|
2136
|
+
return new Promise(function (resolve) {
|
|
2137
|
+
if (document.readyState !== "loading") {
|
|
2138
|
+
resolve(true);
|
|
2168
2139
|
}
|
|
2169
|
-
|
|
2170
|
-
|
|
2140
|
+
else {
|
|
2141
|
+
document.addEventListener("DOMContentLoaded", resolve, false);
|
|
2171
2142
|
}
|
|
2143
|
+
}).then(fn || function () { });
|
|
2144
|
+
}
|
|
2145
|
+
async function loadFile(url) {
|
|
2146
|
+
const result = await fetch(url);
|
|
2147
|
+
if (!result.ok) {
|
|
2148
|
+
throw new Error("Error while fetching xml templates");
|
|
2172
2149
|
}
|
|
2150
|
+
return await result.text();
|
|
2151
|
+
}
|
|
2152
|
+
/*
|
|
2153
|
+
* This class just transports the fact that a string is safe
|
|
2154
|
+
* to be injected as HTML. Overriding a JS primitive is quite painful though
|
|
2155
|
+
* so we need to redfine toString and valueOf.
|
|
2156
|
+
*/
|
|
2157
|
+
class Markup extends String {
|
|
2158
|
+
}
|
|
2159
|
+
/*
|
|
2160
|
+
* Marks a value as safe, that is, a value that can be injected as HTML directly.
|
|
2161
|
+
* It should be used to wrap the value passed to a t-out directive to allow a raw rendering.
|
|
2162
|
+
*/
|
|
2163
|
+
function markup(value) {
|
|
2164
|
+
return new Markup(value);
|
|
2173
2165
|
}
|
|
2174
2166
|
|
|
2175
2167
|
let currentNode = null;
|
|
@@ -2186,13 +2178,11 @@ function useComponent() {
|
|
|
2186
2178
|
* Apply default props (only top level).
|
|
2187
2179
|
*/
|
|
2188
2180
|
function applyDefaultProps(props, defaultProps) {
|
|
2189
|
-
const result = Object.assign({}, props);
|
|
2190
2181
|
for (let propName in defaultProps) {
|
|
2191
2182
|
if (props[propName] === undefined) {
|
|
2192
|
-
|
|
2183
|
+
props[propName] = defaultProps[propName];
|
|
2193
2184
|
}
|
|
2194
2185
|
}
|
|
2195
|
-
return result;
|
|
2196
2186
|
}
|
|
2197
2187
|
// -----------------------------------------------------------------------------
|
|
2198
2188
|
// Integration with reactivity system (useState)
|
|
@@ -2219,61 +2209,6 @@ function useState(state) {
|
|
|
2219
2209
|
}
|
|
2220
2210
|
return reactive(state, render);
|
|
2221
2211
|
}
|
|
2222
|
-
function arePropsDifferent(props1, props2) {
|
|
2223
|
-
for (let k in props1) {
|
|
2224
|
-
const prop1 = props1[k] && typeof props1[k] === "object" ? toRaw(props1[k]) : props1[k];
|
|
2225
|
-
const prop2 = props2[k] && typeof props2[k] === "object" ? toRaw(props2[k]) : props2[k];
|
|
2226
|
-
if (prop1 !== prop2) {
|
|
2227
|
-
return true;
|
|
2228
|
-
}
|
|
2229
|
-
}
|
|
2230
|
-
return Object.keys(props1).length !== Object.keys(props2).length;
|
|
2231
|
-
}
|
|
2232
|
-
function component(name, props, key, ctx, parent) {
|
|
2233
|
-
let node = ctx.children[key];
|
|
2234
|
-
let isDynamic = typeof name !== "string";
|
|
2235
|
-
if (node && node.status === 2 /* DESTROYED */) {
|
|
2236
|
-
node = undefined;
|
|
2237
|
-
}
|
|
2238
|
-
if (isDynamic && node && node.component.constructor !== name) {
|
|
2239
|
-
node = undefined;
|
|
2240
|
-
}
|
|
2241
|
-
const parentFiber = ctx.fiber;
|
|
2242
|
-
if (node) {
|
|
2243
|
-
let shouldRender = node.forceNextRender;
|
|
2244
|
-
if (shouldRender) {
|
|
2245
|
-
node.forceNextRender = false;
|
|
2246
|
-
}
|
|
2247
|
-
else {
|
|
2248
|
-
const currentProps = node.props;
|
|
2249
|
-
shouldRender = parentFiber.deep || arePropsDifferent(currentProps, props);
|
|
2250
|
-
}
|
|
2251
|
-
if (shouldRender) {
|
|
2252
|
-
node.updateAndRender(props, parentFiber);
|
|
2253
|
-
}
|
|
2254
|
-
}
|
|
2255
|
-
else {
|
|
2256
|
-
// new component
|
|
2257
|
-
let C;
|
|
2258
|
-
if (isDynamic) {
|
|
2259
|
-
C = name;
|
|
2260
|
-
}
|
|
2261
|
-
else {
|
|
2262
|
-
C = parent.constructor.components[name];
|
|
2263
|
-
if (!C) {
|
|
2264
|
-
throw new Error(`Cannot find the definition of component "${name}"`);
|
|
2265
|
-
}
|
|
2266
|
-
else if (!(C.prototype instanceof Component)) {
|
|
2267
|
-
throw new Error(`"${name}" is not a Component. It must inherit from the Component class`);
|
|
2268
|
-
}
|
|
2269
|
-
}
|
|
2270
|
-
node = new ComponentNode(C, props, ctx.app, ctx, key);
|
|
2271
|
-
ctx.children[key] = node;
|
|
2272
|
-
node.initiateRender(new Fiber(node, parentFiber));
|
|
2273
|
-
}
|
|
2274
|
-
parentFiber.childrenMap[key] = node;
|
|
2275
|
-
return node;
|
|
2276
|
-
}
|
|
2277
2212
|
class ComponentNode {
|
|
2278
2213
|
constructor(C, props, app, parent, parentKey) {
|
|
2279
2214
|
this.fiber = null;
|
|
@@ -2296,8 +2231,9 @@ class ComponentNode {
|
|
|
2296
2231
|
this.parentKey = parentKey;
|
|
2297
2232
|
this.level = parent ? parent.level + 1 : 0;
|
|
2298
2233
|
const defaultProps = C.defaultProps;
|
|
2234
|
+
props = Object.assign({}, props);
|
|
2299
2235
|
if (defaultProps) {
|
|
2300
|
-
|
|
2236
|
+
applyDefaultProps(props, defaultProps);
|
|
2301
2237
|
}
|
|
2302
2238
|
const env = (parent && parent.childEnv) || app.env;
|
|
2303
2239
|
this.childEnv = env;
|
|
@@ -2409,13 +2345,14 @@ class ComponentNode {
|
|
|
2409
2345
|
}
|
|
2410
2346
|
async updateAndRender(props, parentFiber) {
|
|
2411
2347
|
const rawProps = props;
|
|
2348
|
+
props = Object.assign({}, props);
|
|
2412
2349
|
// update
|
|
2413
2350
|
const fiber = makeChildFiber(this, parentFiber);
|
|
2414
2351
|
this.fiber = fiber;
|
|
2415
2352
|
const component = this.component;
|
|
2416
2353
|
const defaultProps = component.constructor.defaultProps;
|
|
2417
2354
|
if (defaultProps) {
|
|
2418
|
-
|
|
2355
|
+
applyDefaultProps(props, defaultProps);
|
|
2419
2356
|
}
|
|
2420
2357
|
currentNode = this;
|
|
2421
2358
|
for (const key in props) {
|
|
@@ -2494,10 +2431,15 @@ class ComponentNode {
|
|
|
2494
2431
|
}
|
|
2495
2432
|
}
|
|
2496
2433
|
_patch() {
|
|
2497
|
-
|
|
2498
|
-
this.children
|
|
2499
|
-
|
|
2500
|
-
|
|
2434
|
+
let hasChildren = false;
|
|
2435
|
+
for (let _k in this.children) {
|
|
2436
|
+
hasChildren = true;
|
|
2437
|
+
break;
|
|
2438
|
+
}
|
|
2439
|
+
const fiber = this.fiber;
|
|
2440
|
+
this.children = fiber.childrenMap;
|
|
2441
|
+
this.bdom.patch(fiber.bdom, hasChildren);
|
|
2442
|
+
fiber.appliedToDom = true;
|
|
2501
2443
|
this.fiber = null;
|
|
2502
2444
|
}
|
|
2503
2445
|
beforeRemove() {
|
|
@@ -2625,6 +2567,19 @@ function onError(callback) {
|
|
|
2625
2567
|
handlers.push(callback.bind(node.component));
|
|
2626
2568
|
}
|
|
2627
2569
|
|
|
2570
|
+
class Component {
|
|
2571
|
+
constructor(props, env, node) {
|
|
2572
|
+
this.props = props;
|
|
2573
|
+
this.env = env;
|
|
2574
|
+
this.__owl__ = node;
|
|
2575
|
+
}
|
|
2576
|
+
setup() { }
|
|
2577
|
+
render(deep = false) {
|
|
2578
|
+
this.__owl__.render(deep === true);
|
|
2579
|
+
}
|
|
2580
|
+
}
|
|
2581
|
+
Component.template = "";
|
|
2582
|
+
|
|
2628
2583
|
const VText = text("").constructor;
|
|
2629
2584
|
class VPortal extends VText {
|
|
2630
2585
|
constructor(selector, realBDom) {
|
|
@@ -2703,6 +2658,7 @@ Portal.props = {
|
|
|
2703
2658
|
// -----------------------------------------------------------------------------
|
|
2704
2659
|
const isUnionType = (t) => Array.isArray(t);
|
|
2705
2660
|
const isBaseType = (t) => typeof t !== "object";
|
|
2661
|
+
const isValueType = (t) => typeof t === "object" && t && "value" in t;
|
|
2706
2662
|
function isOptional(t) {
|
|
2707
2663
|
return typeof t === "object" && "optional" in t ? t.optional || false : false;
|
|
2708
2664
|
}
|
|
@@ -2716,6 +2672,9 @@ function describe(info) {
|
|
|
2716
2672
|
else if (isUnionType(info)) {
|
|
2717
2673
|
return info.map(describe).join(" or ");
|
|
2718
2674
|
}
|
|
2675
|
+
else if (isValueType(info)) {
|
|
2676
|
+
return String(info.value);
|
|
2677
|
+
}
|
|
2719
2678
|
if ("element" in info) {
|
|
2720
2679
|
return `list of ${describe({ type: info.element, optional: false })}s`;
|
|
2721
2680
|
}
|
|
@@ -2801,6 +2760,9 @@ function validateType(key, value, descr) {
|
|
|
2801
2760
|
else if (isBaseType(descr)) {
|
|
2802
2761
|
return validateBaseType(key, value, descr);
|
|
2803
2762
|
}
|
|
2763
|
+
else if (isValueType(descr)) {
|
|
2764
|
+
return value === descr.value ? null : `'${key}' is not equal to '${descr.value}'`;
|
|
2765
|
+
}
|
|
2804
2766
|
else if (isUnionType(descr)) {
|
|
2805
2767
|
let validDescr = descr.find((p) => !validateType(key, value, p));
|
|
2806
2768
|
return validDescr ? null : `'${key}' is not a ${describe(descr)}`;
|
|
@@ -2829,6 +2791,7 @@ function validateType(key, value, descr) {
|
|
|
2829
2791
|
return result;
|
|
2830
2792
|
}
|
|
2831
2793
|
|
|
2794
|
+
const ObjectCreate = Object.create;
|
|
2832
2795
|
/**
|
|
2833
2796
|
* This file contains utility functions that will be injected in each template,
|
|
2834
2797
|
* to perform various useful tasks in the compiled code.
|
|
@@ -2840,7 +2803,7 @@ function callSlot(ctx, parent, key, name, dynamic, extra, defaultContent) {
|
|
|
2840
2803
|
key = key + "__slot_" + name;
|
|
2841
2804
|
const slots = ctx.props.slots || {};
|
|
2842
2805
|
const { __render, __ctx, __scope } = slots[name] || {};
|
|
2843
|
-
const slotScope =
|
|
2806
|
+
const slotScope = ObjectCreate(__ctx || {});
|
|
2844
2807
|
if (__scope) {
|
|
2845
2808
|
slotScope[__scope] = extra;
|
|
2846
2809
|
}
|
|
@@ -2860,7 +2823,7 @@ function callSlot(ctx, parent, key, name, dynamic, extra, defaultContent) {
|
|
|
2860
2823
|
}
|
|
2861
2824
|
function capture(ctx) {
|
|
2862
2825
|
const component = ctx.__owl__.component;
|
|
2863
|
-
const result =
|
|
2826
|
+
const result = ObjectCreate(component);
|
|
2864
2827
|
for (let k in ctx) {
|
|
2865
2828
|
result[k] = ctx[k];
|
|
2866
2829
|
}
|
|
@@ -2913,13 +2876,14 @@ function shallowEqual(l1, l2) {
|
|
|
2913
2876
|
return true;
|
|
2914
2877
|
}
|
|
2915
2878
|
class LazyValue {
|
|
2916
|
-
constructor(fn, ctx, node) {
|
|
2879
|
+
constructor(fn, ctx, component, node) {
|
|
2917
2880
|
this.fn = fn;
|
|
2918
2881
|
this.ctx = capture(ctx);
|
|
2882
|
+
this.component = component;
|
|
2919
2883
|
this.node = node;
|
|
2920
2884
|
}
|
|
2921
2885
|
evaluate() {
|
|
2922
|
-
return this.fn(this.ctx, this.node);
|
|
2886
|
+
return this.fn.call(this.component, this.ctx, this.node);
|
|
2923
2887
|
}
|
|
2924
2888
|
toString() {
|
|
2925
2889
|
return this.evaluate().toString();
|
|
@@ -2928,9 +2892,9 @@ class LazyValue {
|
|
|
2928
2892
|
/*
|
|
2929
2893
|
* Safely outputs `value` as a block depending on the nature of `value`
|
|
2930
2894
|
*/
|
|
2931
|
-
function safeOutput(value) {
|
|
2932
|
-
if (
|
|
2933
|
-
return
|
|
2895
|
+
function safeOutput(value, defaultValue) {
|
|
2896
|
+
if (value === undefined) {
|
|
2897
|
+
return defaultValue ? toggler("default", defaultValue) : toggler("undefined", text(""));
|
|
2934
2898
|
}
|
|
2935
2899
|
let safeKey;
|
|
2936
2900
|
let block;
|
|
@@ -2965,17 +2929,19 @@ function safeOutput(value) {
|
|
|
2965
2929
|
return toggler(safeKey, block);
|
|
2966
2930
|
}
|
|
2967
2931
|
let boundFunctions = new WeakMap();
|
|
2932
|
+
const WeakMapGet = WeakMap.prototype.get;
|
|
2933
|
+
const WeakMapSet = WeakMap.prototype.set;
|
|
2968
2934
|
function bind(ctx, fn) {
|
|
2969
2935
|
let component = ctx.__owl__.component;
|
|
2970
|
-
let boundFnMap =
|
|
2936
|
+
let boundFnMap = WeakMapGet.call(boundFunctions, component);
|
|
2971
2937
|
if (!boundFnMap) {
|
|
2972
2938
|
boundFnMap = new WeakMap();
|
|
2973
|
-
|
|
2939
|
+
WeakMapSet.call(boundFunctions, component, boundFnMap);
|
|
2974
2940
|
}
|
|
2975
|
-
let boundFn =
|
|
2941
|
+
let boundFn = WeakMapGet.call(boundFnMap, fn);
|
|
2976
2942
|
if (!boundFn) {
|
|
2977
2943
|
boundFn = fn.bind(component);
|
|
2978
|
-
|
|
2944
|
+
WeakMapSet.call(boundFnMap, fn, boundFn);
|
|
2979
2945
|
}
|
|
2980
2946
|
return boundFn;
|
|
2981
2947
|
}
|
|
@@ -3051,7 +3017,7 @@ const helpers = {
|
|
|
3051
3017
|
markRaw,
|
|
3052
3018
|
};
|
|
3053
3019
|
|
|
3054
|
-
const bdom = { text, createBlock, list, multi, html, toggler,
|
|
3020
|
+
const bdom = { text, createBlock, list, multi, html, toggler, comment };
|
|
3055
3021
|
function parseXML$1(xml) {
|
|
3056
3022
|
const parser = new DOMParser();
|
|
3057
3023
|
const doc = parser.parseFromString(xml, "text/xml");
|
|
@@ -3214,7 +3180,7 @@ const STATIC_TOKEN_MAP = Object.assign(Object.create(null), {
|
|
|
3214
3180
|
});
|
|
3215
3181
|
// note that the space after typeof is relevant. It makes sure that the formatted
|
|
3216
3182
|
// expression has a space after typeof. Currently we don't support delete and void
|
|
3217
|
-
const OPERATORS = "...,.,===,==,+,!==,!=,!,||,&&,>=,>,<=,<,?,-,*,/,%,typeof ,=>,=,;,in ,new ".split(",");
|
|
3183
|
+
const OPERATORS = "...,.,===,==,+,!==,!=,!,||,&&,>=,>,<=,<,?,-,*,/,%,typeof ,=>,=,;,in ,new ,|,&,^,~".split(",");
|
|
3218
3184
|
let tokenizeString = function (expr) {
|
|
3219
3185
|
let s = expr[0];
|
|
3220
3186
|
let start = s;
|
|
@@ -3647,9 +3613,7 @@ class CodeGenerator {
|
|
|
3647
3613
|
tKeyExpr: null,
|
|
3648
3614
|
});
|
|
3649
3615
|
// define blocks and utility functions
|
|
3650
|
-
let mainCode = [
|
|
3651
|
-
` let { text, createBlock, list, multi, html, toggler, component, comment } = bdom;`,
|
|
3652
|
-
];
|
|
3616
|
+
let mainCode = [` let { text, createBlock, list, multi, html, toggler, comment } = bdom;`];
|
|
3653
3617
|
if (this.helpers.size) {
|
|
3654
3618
|
mainCode.push(`let { ${[...this.helpers].join(", ")} } = helpers;`);
|
|
3655
3619
|
}
|
|
@@ -3665,6 +3629,7 @@ class CodeGenerator {
|
|
|
3665
3629
|
for (let block of this.blocks) {
|
|
3666
3630
|
if (block.dom) {
|
|
3667
3631
|
let xmlString = block.asXmlString();
|
|
3632
|
+
xmlString = xmlString.replace(/`/g, "\\`");
|
|
3668
3633
|
if (block.dynamicTagName) {
|
|
3669
3634
|
xmlString = xmlString.replace(/^<\w+/, `<\${tag || '${block.dom.nodeName}'}`);
|
|
3670
3635
|
xmlString = xmlString.replace(/\w+>$/, `\${tag || '${block.dom.nodeName}'}>`);
|
|
@@ -3709,8 +3674,8 @@ class CodeGenerator {
|
|
|
3709
3674
|
define(varName, expr) {
|
|
3710
3675
|
this.addLine(`const ${varName} = ${expr};`);
|
|
3711
3676
|
}
|
|
3712
|
-
insertAnchor(block) {
|
|
3713
|
-
const tag = `block-child-${
|
|
3677
|
+
insertAnchor(block, index = block.children.length) {
|
|
3678
|
+
const tag = `block-child-${index}`;
|
|
3714
3679
|
const anchor = xmlDoc.createElement(tag);
|
|
3715
3680
|
block.insert(anchor);
|
|
3716
3681
|
}
|
|
@@ -4102,20 +4067,37 @@ class CodeGenerator {
|
|
|
4102
4067
|
this.insertAnchor(block);
|
|
4103
4068
|
}
|
|
4104
4069
|
block = this.createBlock(block, "html", ctx);
|
|
4105
|
-
|
|
4106
|
-
|
|
4107
|
-
|
|
4108
|
-
|
|
4070
|
+
let blockStr;
|
|
4071
|
+
if (ast.expr === "0") {
|
|
4072
|
+
this.helpers.add("zero");
|
|
4073
|
+
blockStr = `ctx[zero]`;
|
|
4074
|
+
}
|
|
4075
|
+
else if (ast.body) {
|
|
4076
|
+
let bodyValue = null;
|
|
4077
|
+
bodyValue = BlockDescription.nextBlockId;
|
|
4109
4078
|
const subCtx = createContext(ctx);
|
|
4110
4079
|
this.compileAST({ type: 3 /* Multi */, content: ast.body }, subCtx);
|
|
4111
|
-
this.helpers.add("
|
|
4112
|
-
|
|
4080
|
+
this.helpers.add("safeOutput");
|
|
4081
|
+
blockStr = `safeOutput(${compileExpr(ast.expr)}, b${bodyValue})`;
|
|
4082
|
+
}
|
|
4083
|
+
else {
|
|
4084
|
+
this.helpers.add("safeOutput");
|
|
4085
|
+
blockStr = `safeOutput(${compileExpr(ast.expr)})`;
|
|
4086
|
+
}
|
|
4087
|
+
this.insertBlock(blockStr, block, ctx);
|
|
4088
|
+
}
|
|
4089
|
+
compileTIfBranch(content, block, ctx) {
|
|
4090
|
+
this.target.indentLevel++;
|
|
4091
|
+
let childN = block.children.length;
|
|
4092
|
+
this.compileAST(content, createContext(ctx, { block, index: ctx.index }));
|
|
4093
|
+
if (block.children.length > childN) {
|
|
4094
|
+
// we have some content => need to insert an anchor at correct index
|
|
4095
|
+
this.insertAnchor(block, childN);
|
|
4113
4096
|
}
|
|
4114
|
-
this.
|
|
4097
|
+
this.target.indentLevel--;
|
|
4115
4098
|
}
|
|
4116
4099
|
compileTIf(ast, ctx, nextNode) {
|
|
4117
|
-
let { block, forceNewBlock
|
|
4118
|
-
let currentIndex = index;
|
|
4100
|
+
let { block, forceNewBlock } = ctx;
|
|
4119
4101
|
const codeIdx = this.target.code.length;
|
|
4120
4102
|
const isNewBlock = !block || (block.type !== "multi" && forceNewBlock);
|
|
4121
4103
|
if (block) {
|
|
@@ -4125,28 +4107,16 @@ class CodeGenerator {
|
|
|
4125
4107
|
block = this.createBlock(block, "multi", ctx);
|
|
4126
4108
|
}
|
|
4127
4109
|
this.addLine(`if (${compileExpr(ast.condition)}) {`);
|
|
4128
|
-
this.
|
|
4129
|
-
this.insertAnchor(block);
|
|
4130
|
-
const subCtx = createContext(ctx, { block, index: currentIndex });
|
|
4131
|
-
this.compileAST(ast.content, subCtx);
|
|
4132
|
-
this.target.indentLevel--;
|
|
4110
|
+
this.compileTIfBranch(ast.content, block, ctx);
|
|
4133
4111
|
if (ast.tElif) {
|
|
4134
4112
|
for (let clause of ast.tElif) {
|
|
4135
4113
|
this.addLine(`} else if (${compileExpr(clause.condition)}) {`);
|
|
4136
|
-
this.
|
|
4137
|
-
this.insertAnchor(block);
|
|
4138
|
-
const subCtx = createContext(ctx, { block, index: currentIndex });
|
|
4139
|
-
this.compileAST(clause.content, subCtx);
|
|
4140
|
-
this.target.indentLevel--;
|
|
4114
|
+
this.compileTIfBranch(clause.content, block, ctx);
|
|
4141
4115
|
}
|
|
4142
4116
|
}
|
|
4143
4117
|
if (ast.tElse) {
|
|
4144
4118
|
this.addLine(`} else {`);
|
|
4145
|
-
this.
|
|
4146
|
-
this.insertAnchor(block);
|
|
4147
|
-
const subCtx = createContext(ctx, { block, index: currentIndex });
|
|
4148
|
-
this.compileAST(ast.tElse, subCtx);
|
|
4149
|
-
this.target.indentLevel--;
|
|
4119
|
+
this.compileTIfBranch(ast.tElse, block, ctx);
|
|
4150
4120
|
}
|
|
4151
4121
|
this.addLine("}");
|
|
4152
4122
|
if (isNewBlock) {
|
|
@@ -4304,16 +4274,21 @@ class CodeGenerator {
|
|
|
4304
4274
|
}
|
|
4305
4275
|
compileTCall(ast, ctx) {
|
|
4306
4276
|
let { block, forceNewBlock } = ctx;
|
|
4277
|
+
let ctxVar = ctx.ctxVar || "ctx";
|
|
4278
|
+
if (ast.context) {
|
|
4279
|
+
ctxVar = generateId("ctx");
|
|
4280
|
+
this.addLine(`let ${ctxVar} = ${compileExpr(ast.context)};`);
|
|
4281
|
+
}
|
|
4307
4282
|
if (ast.body) {
|
|
4308
|
-
this.addLine(
|
|
4309
|
-
this.addLine(
|
|
4283
|
+
this.addLine(`${ctxVar} = Object.create(${ctxVar});`);
|
|
4284
|
+
this.addLine(`${ctxVar}[isBoundary] = 1;`);
|
|
4310
4285
|
this.helpers.add("isBoundary");
|
|
4311
4286
|
const nextId = BlockDescription.nextBlockId;
|
|
4312
|
-
const subCtx = createContext(ctx, { preventRoot: true });
|
|
4287
|
+
const subCtx = createContext(ctx, { preventRoot: true, ctxVar });
|
|
4313
4288
|
this.compileAST({ type: 3 /* Multi */, content: ast.body }, subCtx);
|
|
4314
4289
|
if (nextId !== BlockDescription.nextBlockId) {
|
|
4315
4290
|
this.helpers.add("zero");
|
|
4316
|
-
this.addLine(
|
|
4291
|
+
this.addLine(`${ctxVar}[zero] = b${nextId};`);
|
|
4317
4292
|
}
|
|
4318
4293
|
}
|
|
4319
4294
|
const isDynamic = INTERP_REGEXP.test(ast.name);
|
|
@@ -4331,7 +4306,7 @@ class CodeGenerator {
|
|
|
4331
4306
|
}
|
|
4332
4307
|
this.define(templateVar, subTemplate);
|
|
4333
4308
|
block = this.createBlock(block, "multi", ctx);
|
|
4334
|
-
this.insertBlock(`call(this, ${templateVar},
|
|
4309
|
+
this.insertBlock(`call(this, ${templateVar}, ${ctxVar}, node, ${key})`, block, {
|
|
4335
4310
|
...ctx,
|
|
4336
4311
|
forceNewBlock: !block,
|
|
4337
4312
|
});
|
|
@@ -4340,13 +4315,13 @@ class CodeGenerator {
|
|
|
4340
4315
|
const id = generateId(`callTemplate_`);
|
|
4341
4316
|
this.staticDefs.push({ id, expr: `app.getTemplate(${subTemplate})` });
|
|
4342
4317
|
block = this.createBlock(block, "multi", ctx);
|
|
4343
|
-
this.insertBlock(`${id}.call(this,
|
|
4318
|
+
this.insertBlock(`${id}.call(this, ${ctxVar}, node, ${key})`, block, {
|
|
4344
4319
|
...ctx,
|
|
4345
4320
|
forceNewBlock: !block,
|
|
4346
4321
|
});
|
|
4347
4322
|
}
|
|
4348
4323
|
if (ast.body && !ctx.isLast) {
|
|
4349
|
-
this.addLine(
|
|
4324
|
+
this.addLine(`${ctxVar} = ${ctxVar}.__proto__;`);
|
|
4350
4325
|
}
|
|
4351
4326
|
}
|
|
4352
4327
|
compileTCallBlock(ast, ctx) {
|
|
@@ -4367,7 +4342,7 @@ class CodeGenerator {
|
|
|
4367
4342
|
this.helpers.add("LazyValue");
|
|
4368
4343
|
const bodyAst = { type: 3 /* Multi */, content: ast.body };
|
|
4369
4344
|
const name = this.compileInNewTarget("value", bodyAst, ctx);
|
|
4370
|
-
let value = `new LazyValue(${name}, ctx, node)`;
|
|
4345
|
+
let value = `new LazyValue(${name}, ctx, this, node)`;
|
|
4371
4346
|
value = ast.value ? (value ? `withDefault(${expr}, ${value})` : expr) : value;
|
|
4372
4347
|
this.addLine(`ctx[\`${ast.name}\`] = ${value};`);
|
|
4373
4348
|
}
|
|
@@ -4385,7 +4360,7 @@ class CodeGenerator {
|
|
|
4385
4360
|
value = expr;
|
|
4386
4361
|
}
|
|
4387
4362
|
this.helpers.add("setContextValue");
|
|
4388
|
-
this.addLine(`setContextValue(ctx, "${ast.name}", ${value});`);
|
|
4363
|
+
this.addLine(`setContextValue(${ctx.ctxVar || "ctx"}, "${ast.name}", ${value});`);
|
|
4389
4364
|
}
|
|
4390
4365
|
}
|
|
4391
4366
|
generateComponentKey() {
|
|
@@ -4502,8 +4477,12 @@ class CodeGenerator {
|
|
|
4502
4477
|
if (ctx.tKeyExpr) {
|
|
4503
4478
|
keyArg = `${ctx.tKeyExpr} + ${keyArg}`;
|
|
4504
4479
|
}
|
|
4505
|
-
|
|
4506
|
-
|
|
4480
|
+
let id = generateId("comp");
|
|
4481
|
+
this.staticDefs.push({
|
|
4482
|
+
id,
|
|
4483
|
+
expr: `app.createComponent(${ast.isDynamic ? null : expr}, ${!ast.isDynamic}, ${!!ast.slots}, ${!!ast.dynamicProps}, ${!ast.props && !ast.dynamicProps})`,
|
|
4484
|
+
});
|
|
4485
|
+
let blockExpr = `${id}(${propString}, ${keyArg}, node, this, ${ast.isDynamic ? expr : null})`;
|
|
4507
4486
|
if (ast.isDynamic) {
|
|
4508
4487
|
blockExpr = `toggler(${expr}, ${blockExpr})`;
|
|
4509
4488
|
}
|
|
@@ -4588,10 +4567,15 @@ class CodeGenerator {
|
|
|
4588
4567
|
if (this.target.loopLevel || !this.hasSafeContext) {
|
|
4589
4568
|
ctxStr = generateId("ctx");
|
|
4590
4569
|
this.helpers.add("capture");
|
|
4591
|
-
this.define(ctxStr, `capture(ctx)
|
|
4570
|
+
this.define(ctxStr, `capture(ctx)`);
|
|
4592
4571
|
}
|
|
4572
|
+
let id = generateId("comp");
|
|
4573
|
+
this.staticDefs.push({
|
|
4574
|
+
id,
|
|
4575
|
+
expr: `app.createComponent(null, false, true, false, false)`,
|
|
4576
|
+
});
|
|
4593
4577
|
const target = compileExpr(ast.target);
|
|
4594
|
-
const blockString =
|
|
4578
|
+
const blockString = `${id}({target: ${target},slots: {'default': {__render: ${name}, __ctx: ${ctxStr}}}}, key + \`${key}\`, node, ctx, Portal)`;
|
|
4595
4579
|
if (block) {
|
|
4596
4580
|
this.insertAnchor(block);
|
|
4597
4581
|
}
|
|
@@ -4931,10 +4915,12 @@ function parseTCall(node, ctx) {
|
|
|
4931
4915
|
return null;
|
|
4932
4916
|
}
|
|
4933
4917
|
const subTemplate = node.getAttribute("t-call");
|
|
4918
|
+
const context = node.getAttribute("t-call-context");
|
|
4934
4919
|
node.removeAttribute("t-call");
|
|
4920
|
+
node.removeAttribute("t-call-context");
|
|
4935
4921
|
if (node.tagName !== "t") {
|
|
4936
4922
|
const ast = parseNode(node, ctx);
|
|
4937
|
-
const tcall = { type: 7 /* TCall */, name: subTemplate, body: null };
|
|
4923
|
+
const tcall = { type: 7 /* TCall */, name: subTemplate, body: null, context };
|
|
4938
4924
|
if (ast && ast.type === 2 /* DomNode */) {
|
|
4939
4925
|
ast.content = [tcall];
|
|
4940
4926
|
return ast;
|
|
@@ -4951,6 +4937,7 @@ function parseTCall(node, ctx) {
|
|
|
4951
4937
|
type: 7 /* TCall */,
|
|
4952
4938
|
name: subTemplate,
|
|
4953
4939
|
body: body.length ? body : null,
|
|
4940
|
+
context,
|
|
4954
4941
|
};
|
|
4955
4942
|
}
|
|
4956
4943
|
// -----------------------------------------------------------------------------
|
|
@@ -5534,6 +5521,55 @@ class App extends TemplateSet {
|
|
|
5534
5521
|
this.root.destroy();
|
|
5535
5522
|
}
|
|
5536
5523
|
}
|
|
5524
|
+
createComponent(name, isStatic, hasSlotsProp, hasDynamicPropList, hasNoProp) {
|
|
5525
|
+
const isDynamic = !isStatic;
|
|
5526
|
+
function _arePropsDifferent(props1, props2) {
|
|
5527
|
+
for (let k in props1) {
|
|
5528
|
+
if (props1[k] !== props2[k]) {
|
|
5529
|
+
return true;
|
|
5530
|
+
}
|
|
5531
|
+
}
|
|
5532
|
+
return hasDynamicPropList && Object.keys(props1).length !== Object.keys(props2).length;
|
|
5533
|
+
}
|
|
5534
|
+
const arePropsDifferent = hasSlotsProp
|
|
5535
|
+
? (_1, _2) => true
|
|
5536
|
+
: hasNoProp
|
|
5537
|
+
? (_1, _2) => false
|
|
5538
|
+
: _arePropsDifferent;
|
|
5539
|
+
const updateAndRender = ComponentNode.prototype.updateAndRender;
|
|
5540
|
+
const initiateRender = ComponentNode.prototype.initiateRender;
|
|
5541
|
+
return (props, key, ctx, parent, C) => {
|
|
5542
|
+
let children = ctx.children;
|
|
5543
|
+
let node = children[key];
|
|
5544
|
+
if (isDynamic && node && node.component.constructor !== C) {
|
|
5545
|
+
node = undefined;
|
|
5546
|
+
}
|
|
5547
|
+
const parentFiber = ctx.fiber;
|
|
5548
|
+
if (node) {
|
|
5549
|
+
if (arePropsDifferent(node.props, props) || parentFiber.deep || node.forceNextRender) {
|
|
5550
|
+
node.forceNextRender = false;
|
|
5551
|
+
updateAndRender.call(node, props, parentFiber);
|
|
5552
|
+
}
|
|
5553
|
+
}
|
|
5554
|
+
else {
|
|
5555
|
+
// new component
|
|
5556
|
+
if (isStatic) {
|
|
5557
|
+
C = parent.constructor.components[name];
|
|
5558
|
+
if (!C) {
|
|
5559
|
+
throw new Error(`Cannot find the definition of component "${name}"`);
|
|
5560
|
+
}
|
|
5561
|
+
else if (!(C.prototype instanceof Component)) {
|
|
5562
|
+
throw new Error(`"${name}" is not a Component. It must inherit from the Component class`);
|
|
5563
|
+
}
|
|
5564
|
+
}
|
|
5565
|
+
node = new ComponentNode(C, props, this, ctx, key);
|
|
5566
|
+
children[key] = node;
|
|
5567
|
+
initiateRender.call(node, new Fiber(node, parentFiber));
|
|
5568
|
+
}
|
|
5569
|
+
parentFiber.childrenMap[key] = node;
|
|
5570
|
+
return node;
|
|
5571
|
+
};
|
|
5572
|
+
}
|
|
5537
5573
|
}
|
|
5538
5574
|
App.validateTarget = validateTarget;
|
|
5539
5575
|
async function mount(C, target, config = {}) {
|
|
@@ -5682,7 +5718,7 @@ TemplateSet.prototype._compileTemplate = function _compileTemplate(name, templat
|
|
|
5682
5718
|
export { App, Component, EventBus, __info__, blockDom, loadFile, markRaw, markup, mount, onError, onMounted, onPatched, onRendered, onWillDestroy, onWillPatch, onWillRender, onWillStart, onWillUnmount, onWillUpdateProps, reactive, status, toRaw, useChildSubEnv, useComponent, useEffect, useEnv, useExternalListener, useRef, useState, useSubEnv, validate, whenReady, xml };
|
|
5683
5719
|
|
|
5684
5720
|
|
|
5685
|
-
__info__.version = '2.0.0-beta-
|
|
5686
|
-
__info__.date = '2022-06-
|
|
5687
|
-
__info__.hash = '
|
|
5721
|
+
__info__.version = '2.0.0-beta-12';
|
|
5722
|
+
__info__.date = '2022-06-29T09:13:12.680Z';
|
|
5723
|
+
__info__.hash = '6c72e0a';
|
|
5688
5724
|
__info__.url = 'https://github.com/odoo/owl';
|