@odoo/owl 2.1.4 → 2.2.1

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.iife.js CHANGED
@@ -125,14 +125,16 @@
125
125
  }
126
126
  const node = "node" in params ? params.node : params.fiber.node;
127
127
  const fiber = "fiber" in params ? params.fiber : node.fiber;
128
- // resets the fibers on components if possible. This is important so that
129
- // new renderings can be properly included in the initial one, if any.
130
- let current = fiber;
131
- do {
132
- current.node.fiber = current;
133
- current = current.parent;
134
- } while (current);
135
- fibersInError.set(fiber.root, error);
128
+ if (fiber) {
129
+ // resets the fibers on components if possible. This is important so that
130
+ // new renderings can be properly included in the initial one, if any.
131
+ let current = fiber;
132
+ do {
133
+ current.node.fiber = current;
134
+ current = current.parent;
135
+ } while (current);
136
+ fibersInError.set(fiber.root, error);
137
+ }
136
138
  const handled = _handleError(node, error);
137
139
  if (!handled) {
138
140
  console.warn(`[Owl] Unhandled error. Destroying the root component`);
@@ -314,20 +316,13 @@
314
316
  * @returns a batched version of the original callback
315
317
  */
316
318
  function batched(callback) {
317
- let called = false;
318
- return async () => {
319
- // This await blocks all calls to the callback here, then releases them sequentially
320
- // in the next microtick. This line decides the granularity of the batch.
321
- await Promise.resolve();
322
- if (!called) {
323
- called = true;
324
- // wait for all calls in this microtick to fall through before resetting "called"
325
- // so that only the first call to the batched function calls the original callback.
326
- // Schedule this before calling the callback so that calls to the batched function
327
- // within the callback will proceed only after resetting called to false, and have
328
- // a chance to execute the callback again
329
- Promise.resolve().then(() => (called = false));
330
- callback();
319
+ let scheduled = false;
320
+ return async (...args) => {
321
+ if (!scheduled) {
322
+ scheduled = true;
323
+ await Promise.resolve();
324
+ scheduled = false;
325
+ callback(...args);
331
326
  }
332
327
  };
333
328
  }
@@ -1660,8 +1655,7 @@
1660
1655
  let node = fiber.node;
1661
1656
  fiber.render = throwOnRender;
1662
1657
  if (node.status === 0 /* NEW */) {
1663
- node.destroy();
1664
- delete node.parent.children[node.parentKey];
1658
+ node.cancel();
1665
1659
  }
1666
1660
  node.fiber = null;
1667
1661
  if (fiber.bdom) {
@@ -2388,6 +2382,9 @@
2388
2382
  }
2389
2383
  }
2390
2384
  async render(deep) {
2385
+ if (this.status >= 2 /* CANCELLED */) {
2386
+ return;
2387
+ }
2391
2388
  let current = this.fiber;
2392
2389
  if (current && (current.root.locked || current.bdom === true)) {
2393
2390
  await Promise.resolve();
@@ -2413,7 +2410,7 @@
2413
2410
  this.fiber = fiber;
2414
2411
  this.app.scheduler.addFiber(fiber);
2415
2412
  await Promise.resolve();
2416
- if (this.status === 2 /* DESTROYED */) {
2413
+ if (this.status >= 2 /* CANCELLED */) {
2417
2414
  return;
2418
2415
  }
2419
2416
  // We only want to actually render the component if the following two
@@ -2431,6 +2428,18 @@
2431
2428
  fiber.render();
2432
2429
  }
2433
2430
  }
2431
+ cancel() {
2432
+ this._cancel();
2433
+ delete this.parent.children[this.parentKey];
2434
+ this.app.scheduler.scheduleDestroy(this);
2435
+ }
2436
+ _cancel() {
2437
+ this.status = 2 /* CANCELLED */;
2438
+ const children = this.children;
2439
+ for (let childKey in children) {
2440
+ children[childKey]._cancel();
2441
+ }
2442
+ }
2434
2443
  destroy() {
2435
2444
  let shouldRemove = this.status === 1 /* MOUNTED */;
2436
2445
  this._destroy();
@@ -2458,7 +2467,7 @@
2458
2467
  this.app.handleError({ error: e, node: this });
2459
2468
  }
2460
2469
  }
2461
- this.status = 2 /* DESTROYED */;
2470
+ this.status = 3 /* DESTROYED */;
2462
2471
  }
2463
2472
  async updateAndRender(props, parentFiber) {
2464
2473
  this.nextProps = props;
@@ -2991,12 +3000,22 @@
2991
3000
  keys = collection;
2992
3001
  values = collection;
2993
3002
  }
2994
- else if (collection) {
2995
- values = Object.keys(collection);
2996
- keys = Object.values(collection);
3003
+ else if (collection instanceof Map) {
3004
+ keys = [...collection.keys()];
3005
+ values = [...collection.values()];
3006
+ }
3007
+ else if (collection && typeof collection === "object") {
3008
+ if (Symbol.iterator in collection) {
3009
+ keys = [...collection];
3010
+ values = keys;
3011
+ }
3012
+ else {
3013
+ values = Object.keys(collection);
3014
+ keys = Object.values(collection);
3015
+ }
2997
3016
  }
2998
3017
  else {
2999
- throw new OwlError("Invalid loop expression");
3018
+ throw new OwlError(`Invalid loop expression: "${collection}" is not iterable`);
3000
3019
  }
3001
3020
  const n = values.length;
3002
3021
  return [keys, values, n, new Array(n)];
@@ -4823,10 +4842,10 @@
4823
4842
  parseTCall(node, ctx) ||
4824
4843
  parseTCallBlock(node) ||
4825
4844
  parseTEscNode(node, ctx) ||
4845
+ parseTOutNode(node, ctx) ||
4826
4846
  parseTKey(node, ctx) ||
4827
4847
  parseTTranslation(node, ctx) ||
4828
4848
  parseTSlot(node, ctx) ||
4829
- parseTOutNode(node, ctx) ||
4830
4849
  parseComponent(node, ctx) ||
4831
4850
  parseDOMNode(node, ctx) ||
4832
4851
  parseTSetNode(node, ctx) ||
@@ -4939,10 +4958,8 @@
4939
4958
  const typeAttr = node.getAttribute("type");
4940
4959
  const isInput = tagName === "input";
4941
4960
  const isSelect = tagName === "select";
4942
- const isTextarea = tagName === "textarea";
4943
4961
  const isCheckboxInput = isInput && typeAttr === "checkbox";
4944
4962
  const isRadioInput = isInput && typeAttr === "radio";
4945
- const isOtherInput = isInput && !isCheckboxInput && !isRadioInput;
4946
4963
  const hasLazyMod = attr.includes(".lazy");
4947
4964
  const hasNumberMod = attr.includes(".number");
4948
4965
  const hasTrimMod = attr.includes(".trim");
@@ -4954,8 +4971,8 @@
4954
4971
  specialInitTargetAttr: isRadioInput ? "checked" : null,
4955
4972
  eventType,
4956
4973
  hasDynamicChildren: false,
4957
- shouldTrim: hasTrimMod && (isOtherInput || isTextarea),
4958
- shouldNumberize: hasNumberMod && (isOtherInput || isTextarea),
4974
+ shouldTrim: hasTrimMod,
4975
+ shouldNumberize: hasNumberMod,
4959
4976
  };
4960
4977
  if (isSelect) {
4961
4978
  // don't pollute the original ctx
@@ -5018,9 +5035,6 @@
5018
5035
  content: [tesc],
5019
5036
  };
5020
5037
  }
5021
- if (ast.type === 11 /* TComponent */) {
5022
- throw new OwlError("t-esc is not supported on Component nodes");
5023
- }
5024
5038
  return tesc;
5025
5039
  }
5026
5040
  // -----------------------------------------------------------------------------
@@ -5462,19 +5476,21 @@
5462
5476
  *
5463
5477
  * @param el the element containing the tree that should be normalized
5464
5478
  */
5465
- function normalizeTEsc(el) {
5466
- const elements = [...el.querySelectorAll("[t-esc]")].filter((el) => el.tagName[0] === el.tagName[0].toUpperCase() || el.hasAttribute("t-component"));
5467
- for (const el of elements) {
5468
- if (el.childNodes.length) {
5469
- throw new OwlError("Cannot have t-esc on a component that already has content");
5470
- }
5471
- const value = el.getAttribute("t-esc");
5472
- el.removeAttribute("t-esc");
5473
- const t = el.ownerDocument.createElement("t");
5474
- if (value != null) {
5475
- t.setAttribute("t-esc", value);
5479
+ function normalizeTEscTOut(el) {
5480
+ for (const d of ["t-esc", "t-out"]) {
5481
+ const elements = [...el.querySelectorAll(`[${d}]`)].filter((el) => el.tagName[0] === el.tagName[0].toUpperCase() || el.hasAttribute("t-component"));
5482
+ for (const el of elements) {
5483
+ if (el.childNodes.length) {
5484
+ throw new OwlError(`Cannot have ${d} on a component that already has content`);
5485
+ }
5486
+ const value = el.getAttribute(d);
5487
+ el.removeAttribute(d);
5488
+ const t = el.ownerDocument.createElement("t");
5489
+ if (value != null) {
5490
+ t.setAttribute(d, value);
5491
+ }
5492
+ el.appendChild(t);
5476
5493
  }
5477
- el.appendChild(t);
5478
5494
  }
5479
5495
  }
5480
5496
  /**
@@ -5485,7 +5501,7 @@
5485
5501
  */
5486
5502
  function normalizeXML(el) {
5487
5503
  normalizeTIf(el);
5488
- normalizeTEsc(el);
5504
+ normalizeTEscTOut(el);
5489
5505
  }
5490
5506
  /**
5491
5507
  * Parses an XML string into an XML document, throwing errors on parser errors
@@ -5538,7 +5554,7 @@
5538
5554
  }
5539
5555
 
5540
5556
  // do not modify manually. This file is generated by the release script.
5541
- const version = "2.1.3";
5557
+ const version = "2.2";
5542
5558
 
5543
5559
  // -----------------------------------------------------------------------------
5544
5560
  // Scheduler
@@ -5548,11 +5564,18 @@
5548
5564
  this.tasks = new Set();
5549
5565
  this.frame = 0;
5550
5566
  this.delayedRenders = [];
5567
+ this.cancelledNodes = new Set();
5551
5568
  this.requestAnimationFrame = Scheduler.requestAnimationFrame;
5552
5569
  }
5553
5570
  addFiber(fiber) {
5554
5571
  this.tasks.add(fiber.root);
5555
5572
  }
5573
+ scheduleDestroy(node) {
5574
+ this.cancelledNodes.add(node);
5575
+ if (this.frame === 0) {
5576
+ this.frame = this.requestAnimationFrame(() => this.processTasks());
5577
+ }
5578
+ }
5556
5579
  /**
5557
5580
  * Process all current tasks. This only applies to the fibers that are ready.
5558
5581
  * Other tasks are left unchanged.
@@ -5562,21 +5585,28 @@
5562
5585
  let renders = this.delayedRenders;
5563
5586
  this.delayedRenders = [];
5564
5587
  for (let f of renders) {
5565
- if (f.root && f.node.status !== 2 /* DESTROYED */ && f.node.fiber === f) {
5588
+ if (f.root && f.node.status !== 3 /* DESTROYED */ && f.node.fiber === f) {
5566
5589
  f.render();
5567
5590
  }
5568
5591
  }
5569
5592
  }
5570
5593
  if (this.frame === 0) {
5571
- this.frame = this.requestAnimationFrame(() => {
5572
- this.frame = 0;
5573
- this.tasks.forEach((fiber) => this.processFiber(fiber));
5574
- for (let task of this.tasks) {
5575
- if (task.node.status === 2 /* DESTROYED */) {
5576
- this.tasks.delete(task);
5577
- }
5578
- }
5579
- });
5594
+ this.frame = this.requestAnimationFrame(() => this.processTasks());
5595
+ }
5596
+ }
5597
+ processTasks() {
5598
+ this.frame = 0;
5599
+ for (let node of this.cancelledNodes) {
5600
+ node._destroy();
5601
+ }
5602
+ this.cancelledNodes.clear();
5603
+ for (let task of this.tasks) {
5604
+ this.processFiber(task);
5605
+ }
5606
+ for (let task of this.tasks) {
5607
+ if (task.node.status === 3 /* DESTROYED */) {
5608
+ this.tasks.delete(task);
5609
+ }
5580
5610
  }
5581
5611
  }
5582
5612
  processFiber(fiber) {
@@ -5589,7 +5619,7 @@
5589
5619
  this.tasks.delete(fiber);
5590
5620
  return;
5591
5621
  }
5592
- if (fiber.node.status === 2 /* DESTROYED */) {
5622
+ if (fiber.node.status === 3 /* DESTROYED */) {
5593
5623
  this.tasks.delete(fiber);
5594
5624
  return;
5595
5625
  }
@@ -5813,9 +5843,11 @@ See https://github.com/odoo/owl/blob/${hash}/doc/reference/app.md#configuration
5813
5843
  switch (component.__owl__.status) {
5814
5844
  case 0 /* NEW */:
5815
5845
  return "new";
5846
+ case 2 /* CANCELLED */:
5847
+ return "cancelled";
5816
5848
  case 1 /* MOUNTED */:
5817
5849
  return "mounted";
5818
- case 2 /* DESTROYED */:
5850
+ case 3 /* DESTROYED */:
5819
5851
  return "destroyed";
5820
5852
  }
5821
5853
  }
@@ -5991,8 +6023,8 @@ See https://github.com/odoo/owl/blob/${hash}/doc/reference/app.md#configuration
5991
6023
  Object.defineProperty(exports, '__esModule', { value: true });
5992
6024
 
5993
6025
 
5994
- __info__.date = '2023-06-28T09:17:13.630Z';
5995
- __info__.hash = '432ff44';
6026
+ __info__.date = '2023-07-19T13:22:24.480Z';
6027
+ __info__.hash = '2e07799';
5996
6028
  __info__.url = 'https://github.com/odoo/owl';
5997
6029
 
5998
6030