@odoo/o-spreadsheet 16.0.7 → 16.0.9

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.
@@ -1027,6 +1027,10 @@ function deepEquals(o1, o2) {
1027
1027
  }
1028
1028
  return true;
1029
1029
  }
1030
+ /** Check if the given array contains all the values of the other array. */
1031
+ function includesAll(arr, values) {
1032
+ return values.every((value) => arr.includes(value));
1033
+ }
1030
1034
  /**
1031
1035
  * Return an object with all the keys in the object that have a falsy value removed.
1032
1036
  */
@@ -1572,12 +1576,77 @@ const numberRepresentation = [];
1572
1576
  * - all digit stored in the decimal part of the number
1573
1577
  *
1574
1578
  * The 'maxDecimal' parameter allows to indicate the number of digits to not
1575
- * exceed in the decimal part, in which case digits are rounded
1579
+ * exceed in the decimal part, in which case digits are rounded.
1576
1580
  *
1577
- * Intl.Numberformat is used to properly handle all the roundings.
1578
- * e.g. 1234.7 with format ### (<> maxDecimals=0) should become 1235, not 1234
1579
1581
  **/
1580
1582
  function splitNumber(value, maxDecimals = MAX_DECIMAL_PLACES) {
1583
+ const asString = value.toString();
1584
+ if (asString.includes("e"))
1585
+ return splitNumberIntl(value, maxDecimals);
1586
+ if (Number.isInteger(value)) {
1587
+ return { integerDigits: asString, decimalDigits: undefined };
1588
+ }
1589
+ const indexOfDot = asString.indexOf(".");
1590
+ let integerDigits = asString.substring(0, indexOfDot);
1591
+ let decimalDigits = asString.substring(indexOfDot + 1);
1592
+ if (maxDecimals === 0) {
1593
+ if (Number(decimalDigits[0]) >= 5) {
1594
+ integerDigits = (Number(integerDigits) + 1).toString();
1595
+ }
1596
+ return { integerDigits, decimalDigits: undefined };
1597
+ }
1598
+ if (decimalDigits.length > maxDecimals) {
1599
+ const { integerDigits: roundedIntegerDigits, decimalDigits: roundedDecimalDigits } = limitDecimalDigits(decimalDigits, maxDecimals);
1600
+ decimalDigits = roundedDecimalDigits;
1601
+ if (roundedIntegerDigits !== "0") {
1602
+ integerDigits = (Number(integerDigits) + Number(roundedIntegerDigits)).toString();
1603
+ }
1604
+ }
1605
+ return { integerDigits, decimalDigits: removeTrailingZeroes(decimalDigits || "") };
1606
+ }
1607
+ /**
1608
+ * Return the given string minus the trailing "0" characters.
1609
+ *
1610
+ * @param numberString : a string of integers
1611
+ * @returns the numberString, minus the eventual zeroes at the end
1612
+ */
1613
+ function removeTrailingZeroes(numberString) {
1614
+ let i = numberString.length - 1;
1615
+ while (i >= 0 && numberString[i] === "0") {
1616
+ i--;
1617
+ }
1618
+ return numberString.slice(0, i + 1) || undefined;
1619
+ }
1620
+ /**
1621
+ * Limit the size of the decimal part of a number to the given number of digits.
1622
+ */
1623
+ function limitDecimalDigits(decimalDigits, maxDecimals) {
1624
+ let integerDigits = "0";
1625
+ let resultDecimalDigits = decimalDigits;
1626
+ // Note : we'd want to simply use number.toFixed() to handle the max digits & rounding,
1627
+ // but it has very strange behaviour. Ex: 12.345.toFixed(2) => "12.35", but 1.345.toFixed(2) => "1.34"
1628
+ let slicedDecimalDigits = decimalDigits.slice(0, maxDecimals);
1629
+ const i = maxDecimals;
1630
+ if (Number(Number(decimalDigits[i]) < 5)) {
1631
+ return { integerDigits, decimalDigits: slicedDecimalDigits };
1632
+ }
1633
+ // round up
1634
+ const slicedRoundedUp = (Number(slicedDecimalDigits) + 1).toString();
1635
+ if (slicedRoundedUp.length > slicedDecimalDigits.length) {
1636
+ integerDigits = (Number(integerDigits) + 1).toString();
1637
+ resultDecimalDigits = undefined;
1638
+ }
1639
+ else {
1640
+ resultDecimalDigits = slicedRoundedUp;
1641
+ }
1642
+ return { integerDigits, decimalDigits: resultDecimalDigits };
1643
+ }
1644
+ /**
1645
+ * Split numbers into decimal/integer digits using Intl.NumberFormat.
1646
+ * Supports numbers with a lot of digits that are transformed to scientific notation by
1647
+ * number.toString(), but is slow.
1648
+ */
1649
+ function splitNumberIntl(value, maxDecimals = MAX_DECIMAL_PLACES) {
1581
1650
  let formatter = numberRepresentation[maxDecimals];
1582
1651
  if (!formatter) {
1583
1652
  formatter = new Intl.NumberFormat("en-US", {
@@ -3204,6 +3273,8 @@ exports.CommandResult = void 0;
3204
3273
  CommandResult[CommandResult["InvalidSelectionStep"] = 82] = "InvalidSelectionStep";
3205
3274
  CommandResult[CommandResult["DuplicatedChartId"] = 83] = "DuplicatedChartId";
3206
3275
  CommandResult[CommandResult["ChartDoesNotExist"] = 84] = "ChartDoesNotExist";
3276
+ CommandResult[CommandResult["InvalidHeaderIndex"] = 85] = "InvalidHeaderIndex";
3277
+ CommandResult[CommandResult["InvalidQuantity"] = 86] = "InvalidQuantity";
3207
3278
  })(exports.CommandResult || (exports.CommandResult = {}));
3208
3279
 
3209
3280
  var DIRECTION;
@@ -3974,13 +4045,16 @@ class Menu extends owl.Component {
3974
4045
  }
3975
4046
  });
3976
4047
  }
4048
+ get visibleMenuItems() {
4049
+ return this.props.menuItems.filter((x) => x.isVisible(this.env));
4050
+ }
3977
4051
  get subMenuPosition() {
3978
4052
  const position = Object.assign({}, this.subMenu.position);
3979
4053
  position.y -= this.subMenu.scrollOffset || 0;
3980
4054
  return position;
3981
4055
  }
3982
4056
  get menuHeight() {
3983
- return this.menuComponentHeight(this.props.menuItems);
4057
+ return this.menuComponentHeight(this.visibleMenuItems);
3984
4058
  }
3985
4059
  get subMenuHeight() {
3986
4060
  return this.menuComponentHeight(this.subMenu.menuItems);
@@ -4016,7 +4090,7 @@ class Menu extends owl.Component {
4016
4090
  * and the menu item at a given index.
4017
4091
  */
4018
4092
  subMenuVerticalPosition(position) {
4019
- const menusAbove = this.props.menuItems.slice(0, position);
4093
+ const menusAbove = this.visibleMenuItems.slice(0, position);
4020
4094
  return this.menuComponentHeight(menusAbove) + this.position.y;
4021
4095
  }
4022
4096
  onExternalClick(ev) {
@@ -5549,6 +5623,11 @@ colMenuRegistry
5549
5623
  name: REMOVE_COLUMNS_NAME,
5550
5624
  sequence: 90,
5551
5625
  action: REMOVE_COLUMNS_ACTION,
5626
+ isVisible: (env) => {
5627
+ const sheetId = env.model.getters.getActiveSheetId();
5628
+ const selectedCols = env.model.getters.getElementsFromSelection("COL");
5629
+ return env.model.getters.canRemoveHeaders(sheetId, "COL", selectedCols);
5630
+ },
5552
5631
  })
5553
5632
  .add("clear_column", {
5554
5633
  name: DELETE_CONTENT_COLUMNS_NAME,
@@ -5561,9 +5640,8 @@ colMenuRegistry
5561
5640
  action: HIDE_COLUMNS_ACTION,
5562
5641
  isVisible: (env) => {
5563
5642
  const sheetId = env.model.getters.getActiveSheetId();
5564
- const hiddenCols = env.model.getters.getHiddenColsGroups(sheetId).flat();
5565
- return (env.model.getters.getNumberCols(sheetId) >
5566
- hiddenCols.length + env.model.getters.getElementsFromSelection("COL").length);
5643
+ const selectedCols = env.model.getters.getElementsFromSelection("COL");
5644
+ return env.model.getters.canRemoveHeaders(sheetId, "COL", selectedCols);
5567
5645
  },
5568
5646
  separator: true,
5569
5647
  })
@@ -5637,6 +5715,11 @@ rowMenuRegistry
5637
5715
  name: REMOVE_ROWS_NAME,
5638
5716
  sequence: 70,
5639
5717
  action: REMOVE_ROWS_ACTION,
5718
+ isVisible: (env) => {
5719
+ const sheetId = env.model.getters.getActiveSheetId();
5720
+ const selectedRows = env.model.getters.getElementsFromSelection("ROW");
5721
+ return env.model.getters.canRemoveHeaders(sheetId, "ROW", selectedRows);
5722
+ },
5640
5723
  })
5641
5724
  .add("clear_row", {
5642
5725
  name: DELETE_CONTENT_ROWS_NAME,
@@ -5649,9 +5732,8 @@ rowMenuRegistry
5649
5732
  action: HIDE_ROWS_ACTION,
5650
5733
  isVisible: (env) => {
5651
5734
  const sheetId = env.model.getters.getActiveSheetId();
5652
- const hiddenRows = env.model.getters.getHiddenRowsGroups(sheetId).flat();
5653
- return (env.model.getters.getNumberRows(sheetId) >
5654
- hiddenRows.length + env.model.getters.getElementsFromSelection("ROW").length);
5735
+ const selectedRows = env.model.getters.getElementsFromSelection("ROW");
5736
+ return env.model.getters.canRemoveHeaders(sheetId, "ROW", selectedRows);
5655
5737
  },
5656
5738
  separator: true,
5657
5739
  })
@@ -10313,7 +10395,7 @@ class ChartFigure extends owl.Component {
10313
10395
  openContextMenu(position) {
10314
10396
  const registry = this.getMenuItemRegistry();
10315
10397
  this.menuState.isOpen = true;
10316
- this.menuState.menuItems = registry.getAll().filter((x) => x.isVisible(this.env));
10398
+ this.menuState.menuItems = registry.getAll();
10317
10399
  this.menuState.position = position;
10318
10400
  }
10319
10401
  get chartComponent() {
@@ -17331,7 +17413,7 @@ const POW = {
17331
17413
  base (number) ${_lt("The number to raise to the exponent power.")}
17332
17414
  exponent (number) ${_lt("The exponent to raise base to.")}
17333
17415
  `),
17334
- returns: ["BOOLEAN"],
17416
+ returns: ["NUMBER"],
17335
17417
  compute: function (base, exponent) {
17336
17418
  return POWER.compute(base, exponent);
17337
17419
  },
@@ -19833,14 +19915,31 @@ class ContentEditableHelper {
19833
19915
  * */
19834
19916
  getCurrentSelection() {
19835
19917
  let { startElement, endElement, startSelectionOffset, endSelectionOffset } = this.getStartAndEndSelection();
19836
- let startSizeBefore = this.findSizeBeforeElement(startElement);
19837
- let endSizeBefore = this.findSizeBeforeElement(endElement);
19918
+ let startSizeBefore = this.findSelectionIndex(startElement, startSelectionOffset);
19919
+ let endSizeBefore = this.findSelectionIndex(endElement, endSelectionOffset);
19838
19920
  return {
19839
- start: startSizeBefore + startSelectionOffset,
19840
- end: endSizeBefore + endSelectionOffset,
19921
+ start: startSizeBefore,
19922
+ end: endSizeBefore,
19841
19923
  };
19842
19924
  }
19843
- findSizeBeforeElement(nodeToFind) {
19925
+ /**
19926
+ * Computes the text 'index' inside this.el based on the currently selected node and its offset.
19927
+ * The selected node is either a Text node or an Element node.
19928
+ *
19929
+ * case 1 -Text node:
19930
+ * the offset is the number of characters from the start of the node. We have to add this offset to the
19931
+ * content length of all previous nodes.
19932
+ *
19933
+ * case 2 - Element node:
19934
+ * the offset is the number of child nodes before the selected node. We have to add the content length of
19935
+ * all the bnodes prior to the selected node as well as the content of the child node before the offset.
19936
+ *
19937
+ * See the MDN documentation for more details.
19938
+ * https://developer.mozilla.org/en-US/docs/Web/API/Range/startOffset
19939
+ * https://developer.mozilla.org/en-US/docs/Web/API/Range/endOffset
19940
+ *
19941
+ */
19942
+ findSelectionIndex(nodeToFind, nodeOffset) {
19844
19943
  let it = this.iterateChildren(this.el);
19845
19944
  let usedCharacters = 0;
19846
19945
  let current = it.next();
@@ -19852,6 +19951,19 @@ class ContentEditableHelper {
19852
19951
  }
19853
19952
  current = it.next();
19854
19953
  }
19954
+ if (current.value === nodeToFind) {
19955
+ if (!current.value.hasChildNodes()) {
19956
+ usedCharacters += nodeOffset;
19957
+ }
19958
+ else {
19959
+ const children = [...current.value.childNodes].slice(0, nodeOffset);
19960
+ for (const child of children) {
19961
+ if (!child.textContent)
19962
+ continue;
19963
+ usedCharacters += child.textContent.length;
19964
+ }
19965
+ }
19966
+ }
19855
19967
  return usedCharacters;
19856
19968
  }
19857
19969
  getStartAndEndSelection() {
@@ -22337,8 +22449,8 @@ class Grid extends owl.Component {
22337
22449
  },
22338
22450
  "CTRL+END": () => {
22339
22451
  const sheetId = this.env.model.getters.getActiveSheetId();
22340
- const col = this.env.model.getters.findVisibleHeader(sheetId, "COL", range(0, this.env.model.getters.getNumberCols(sheetId)).reverse());
22341
- const row = this.env.model.getters.findVisibleHeader(sheetId, "ROW", range(0, this.env.model.getters.getNumberRows(sheetId)).reverse());
22452
+ const col = this.env.model.getters.findVisibleHeader(sheetId, "COL", this.env.model.getters.getNumberCols(sheetId) - 1, 0);
22453
+ const row = this.env.model.getters.findVisibleHeader(sheetId, "ROW", this.env.model.getters.getNumberRows(sheetId) - 1, 0);
22342
22454
  this.env.model.selection.selectCell(col, row);
22343
22455
  },
22344
22456
  "SHIFT+ ": () => {
@@ -22414,9 +22526,10 @@ class Grid extends owl.Component {
22414
22526
  this.focus();
22415
22527
  }
22416
22528
  focus() {
22529
+ var _a;
22417
22530
  if (!this.env.model.getters.getSelectedFigureId() &&
22418
22531
  this.env.model.getters.getEditionMode() === "inactive") {
22419
- this.hiddenInput.el.focus();
22532
+ (_a = this.hiddenInput.el) === null || _a === void 0 ? void 0 : _a.focus();
22420
22533
  }
22421
22534
  }
22422
22535
  get gridEl() {
@@ -22620,9 +22733,7 @@ class Grid extends owl.Component {
22620
22733
  this.closeOpenedPopover();
22621
22734
  this.menuState.isOpen = true;
22622
22735
  this.menuState.position = { x, y };
22623
- this.menuState.menuItems = registries$1[type]
22624
- .getAll()
22625
- .filter((item) => !item.isVisible || item.isVisible(this.env));
22736
+ this.menuState.menuItems = registries$1[type].getAll();
22626
22737
  }
22627
22738
  copy(cut, ev) {
22628
22739
  if (!this.gridEl.contains(document.activeElement)) {
@@ -24246,7 +24357,7 @@ function extractStyle(cell, data) {
24246
24357
  if (cell.style) {
24247
24358
  style = data.styles[cell.style];
24248
24359
  }
24249
- const format = cell.format ? data.formats[cell.format] : undefined;
24360
+ const format = extractFormat(cell, data);
24250
24361
  const exportedBorder = {};
24251
24362
  if (cell.border) {
24252
24363
  const border = data.borders[cell.border];
@@ -24280,6 +24391,22 @@ function extractStyle(cell, data) {
24280
24391
  styles.font["italic"] = !!(style === null || style === void 0 ? void 0 : style.italic) || undefined;
24281
24392
  return styles;
24282
24393
  }
24394
+ function extractFormat(cell, data) {
24395
+ if (cell.format) {
24396
+ return data.formats[cell.format];
24397
+ }
24398
+ if (cell.isFormula) {
24399
+ const tokens = tokenize(cell.content || "");
24400
+ const functions = functionRegistry.content;
24401
+ const isExported = tokens
24402
+ .filter((tk) => tk.type === "FUNCTION")
24403
+ .every((tk) => functions[tk.value.toUpperCase()].isExported);
24404
+ if (!isExported) {
24405
+ return cell.computedFormat;
24406
+ }
24407
+ }
24408
+ return undefined;
24409
+ }
24283
24410
  function normalizeStyle(construct, styles) {
24284
24411
  const { id: fontId } = pushElement(styles["font"], construct.fonts);
24285
24412
  const { id: fillId } = pushElement(styles["fill"], construct.fills);
@@ -26360,7 +26487,7 @@ function load(data, verboseImport) {
26360
26487
  }
26361
26488
  }
26362
26489
  }
26363
- data = JSON.parse(JSON.stringify(data));
26490
+ data = deepCopy(data);
26364
26491
  // apply migrations, if needed
26365
26492
  if ("version" in data) {
26366
26493
  if (data.version < CURRENT_VERSION) {
@@ -27502,6 +27629,9 @@ class CellPlugin extends CorePlugin {
27502
27629
  const exportedCellData = sheet.cells[xc];
27503
27630
  exportedCellData.value = cell.evaluated.value;
27504
27631
  exportedCellData.isFormula = cell.isFormula();
27632
+ if (cell.format !== cell.evaluated.format) {
27633
+ exportedCellData.computedFormat = cell.evaluated.format;
27634
+ }
27505
27635
  }
27506
27636
  }
27507
27637
  }
@@ -28945,7 +29075,7 @@ class HeaderVisibilityPlugin extends CorePlugin {
28945
29075
  allowDispatch(cmd) {
28946
29076
  switch (cmd.type) {
28947
29077
  case "HIDE_COLUMNS_ROWS": {
28948
- if (!this.hiddenHeaders[cmd.sheetId]) {
29078
+ if (!this.getters.tryGetSheet(cmd.sheetId)) {
28949
29079
  return 27 /* CommandResult.InvalidSheetId */;
28950
29080
  }
28951
29081
  const hiddenGroup = cmd.dimension === "COL"
@@ -28954,10 +29084,25 @@ class HeaderVisibilityPlugin extends CorePlugin {
28954
29084
  const elements = cmd.dimension === "COL"
28955
29085
  ? this.getters.getNumberCols(cmd.sheetId)
28956
29086
  : this.getters.getNumberRows(cmd.sheetId);
28957
- return (hiddenGroup || []).flat().concat(cmd.elements).length < elements
28958
- ? 0 /* CommandResult.Success */
28959
- : 65 /* CommandResult.TooManyHiddenElements */;
29087
+ const hiddenElements = new Set((hiddenGroup || []).flat().concat(cmd.elements));
29088
+ if (hiddenElements.size >= elements) {
29089
+ return 65 /* CommandResult.TooManyHiddenElements */;
29090
+ }
29091
+ else if (Math.min(...cmd.elements) < 0 || Math.max(...cmd.elements) > elements) {
29092
+ return 85 /* CommandResult.InvalidHeaderIndex */;
29093
+ }
29094
+ else {
29095
+ return 0 /* CommandResult.Success */;
29096
+ }
28960
29097
  }
29098
+ case "REMOVE_COLUMNS_ROWS":
29099
+ if (!this.getters.tryGetSheet(cmd.sheetId)) {
29100
+ return 27 /* CommandResult.InvalidSheetId */;
29101
+ }
29102
+ if (!this.canRemoveHeaders(cmd.sheetId, cmd.dimension, cmd.elements)) {
29103
+ return 8 /* CommandResult.NotEnoughElements */;
29104
+ }
29105
+ return 0 /* CommandResult.Success */;
28961
29106
  }
28962
29107
  return 0 /* CommandResult.Success */;
28963
29108
  }
@@ -29004,6 +29149,10 @@ class HeaderVisibilityPlugin extends CorePlugin {
29004
29149
  }
29005
29150
  return;
29006
29151
  }
29152
+ canRemoveHeaders(sheetId, dimension, elements) {
29153
+ const visibleHeaders = this.getAllVisibleHeaders(sheetId, dimension);
29154
+ return !includesAll(elements, visibleHeaders);
29155
+ }
29007
29156
  isRowHiddenByUser(sheetId, index) {
29008
29157
  return this.hiddenHeaders[sheetId].ROW[index];
29009
29158
  }
@@ -29048,6 +29197,9 @@ class HeaderVisibilityPlugin extends CorePlugin {
29048
29197
  }
29049
29198
  return consecutiveIndexes;
29050
29199
  }
29200
+ getAllVisibleHeaders(sheetId, dimension) {
29201
+ return range(0, this.hiddenHeaders[sheetId][dimension].length).filter((i) => !this.hiddenHeaders[sheetId][dimension][i]);
29202
+ }
29051
29203
  import(data) {
29052
29204
  var _a, _b;
29053
29205
  for (let sheet of data.sheets) {
@@ -29095,6 +29247,7 @@ class HeaderVisibilityPlugin extends CorePlugin {
29095
29247
  }
29096
29248
  }
29097
29249
  HeaderVisibilityPlugin.getters = [
29250
+ "canRemoveHeaders",
29098
29251
  "getHiddenColsGroups",
29099
29252
  "getHiddenRowsGroups",
29100
29253
  "isRowHiddenByUser",
@@ -29595,13 +29748,27 @@ class SheetPlugin extends CorePlugin {
29595
29748
  return this.orderedSheetIds.length > 1
29596
29749
  ? 0 /* CommandResult.Success */
29597
29750
  : 9 /* CommandResult.NotEnoughSheets */;
29751
+ case "ADD_COLUMNS_ROWS":
29752
+ const elements = cmd.dimension === "COL"
29753
+ ? this.getNumberCols(cmd.sheetId)
29754
+ : this.getNumberRows(cmd.sheetId);
29755
+ if (cmd.base < 0 || cmd.base > elements) {
29756
+ return 85 /* CommandResult.InvalidHeaderIndex */;
29757
+ }
29758
+ else if (cmd.quantity <= 0) {
29759
+ return 86 /* CommandResult.InvalidQuantity */;
29760
+ }
29761
+ return 0 /* CommandResult.Success */;
29598
29762
  case "REMOVE_COLUMNS_ROWS": {
29599
- const length = cmd.dimension === "COL"
29763
+ const elements = cmd.dimension === "COL"
29600
29764
  ? this.getNumberCols(cmd.sheetId)
29601
29765
  : this.getNumberRows(cmd.sheetId);
29602
- return length > cmd.elements.length
29603
- ? 0 /* CommandResult.Success */
29604
- : 8 /* CommandResult.NotEnoughElements */;
29766
+ if (Math.min(...cmd.elements) < 0 || Math.max(...cmd.elements) > elements) {
29767
+ return 85 /* CommandResult.InvalidHeaderIndex */;
29768
+ }
29769
+ else {
29770
+ return 0 /* CommandResult.Success */;
29771
+ }
29605
29772
  }
29606
29773
  case "FREEZE_ROWS": {
29607
29774
  return this.checkValidations(cmd, this.checkRowFreezeQuantity, this.checkRowFreezeOverlapMerge);
@@ -30086,7 +30253,7 @@ class SheetPlugin extends CorePlugin {
30086
30253
  duplicateSheet(fromId, toId) {
30087
30254
  const sheet = this.getSheet(fromId);
30088
30255
  const toName = this.getDuplicateSheetName(sheet.name);
30089
- const newSheet = JSON.parse(JSON.stringify(sheet));
30256
+ const newSheet = deepCopy(sheet);
30090
30257
  newSheet.id = toId;
30091
30258
  newSheet.name = toName;
30092
30259
  for (let col = 0; col <= newSheet.numberOfCols; col++) {
@@ -30213,10 +30380,10 @@ class SheetPlugin extends CorePlugin {
30213
30380
  }
30214
30381
  }
30215
30382
  moveCellOnColumnsDeletion(sheet, deletedColumn) {
30216
- for (let [index, row] of Object.entries(sheet.rows)) {
30217
- const rowIndex = parseInt(index, 10);
30383
+ for (let rowIndex = 0; rowIndex < sheet.rows.length; rowIndex++) {
30384
+ const row = sheet.rows[rowIndex];
30218
30385
  for (let i in row.cells) {
30219
- const colIndex = parseInt(i, 10);
30386
+ const colIndex = Number(i);
30220
30387
  const cellId = row.cells[i];
30221
30388
  if (cellId) {
30222
30389
  if (colIndex === deletedColumn) {
@@ -30243,11 +30410,11 @@ class SheetPlugin extends CorePlugin {
30243
30410
  */
30244
30411
  moveCellsOnAddition(sheet, addedElement, quantity, dimension) {
30245
30412
  const commands = [];
30246
- for (const [index, row] of Object.entries(sheet.rows)) {
30247
- const rowIndex = parseInt(index, 10);
30413
+ for (let rowIndex = 0; rowIndex < sheet.rows.length; rowIndex++) {
30414
+ const row = sheet.rows[rowIndex];
30248
30415
  if (dimension !== "rows" || rowIndex >= addedElement) {
30249
30416
  for (let i in row.cells) {
30250
- const colIndex = parseInt(i, 10);
30417
+ const colIndex = Number(i);
30251
30418
  const cellId = row.cells[i];
30252
30419
  if (cellId) {
30253
30420
  if (dimension === "rows" || colIndex >= addedElement) {
@@ -30277,11 +30444,11 @@ class SheetPlugin extends CorePlugin {
30277
30444
  */
30278
30445
  moveCellOnRowsDeletion(sheet, deleteFromRow, deleteToRow) {
30279
30446
  const numberRows = deleteToRow - deleteFromRow + 1;
30280
- for (let [index, row] of Object.entries(sheet.rows)) {
30281
- const rowIndex = parseInt(index, 10);
30447
+ for (let rowIndex = 0; rowIndex < sheet.rows.length; rowIndex++) {
30448
+ const row = sheet.rows[rowIndex];
30282
30449
  if (rowIndex >= deleteFromRow && rowIndex <= deleteToRow) {
30283
30450
  for (let i in row.cells) {
30284
- const colIndex = parseInt(i, 10);
30451
+ const colIndex = Number(i);
30285
30452
  const cellId = row.cells[i];
30286
30453
  if (cellId) {
30287
30454
  this.dispatch("CLEAR_CELL", {
@@ -30294,7 +30461,7 @@ class SheetPlugin extends CorePlugin {
30294
30461
  }
30295
30462
  if (rowIndex > deleteToRow) {
30296
30463
  for (let i in row.cells) {
30297
- const colIndex = parseInt(i, 10);
30464
+ const colIndex = Number(i);
30298
30465
  const cellId = row.cells[i];
30299
30466
  if (cellId) {
30300
30467
  this.dispatch("UPDATE_CELL_POSITION", {
@@ -30312,7 +30479,7 @@ class SheetPlugin extends CorePlugin {
30312
30479
  const rows = [];
30313
30480
  const cellsQueue = sheet.rows.map((row) => row.cells);
30314
30481
  for (let i in sheet.rows) {
30315
- if (parseInt(i, 10) === index) {
30482
+ if (Number(i) === index) {
30316
30483
  continue;
30317
30484
  }
30318
30485
  rows.push({
@@ -32948,13 +33115,11 @@ class FilterEvaluationPlugin extends UIPlugin {
32948
33115
  return Boolean(id && ((_b = (_a = this.filterValues[sheetId]) === null || _a === void 0 ? void 0 : _a[id]) === null || _b === void 0 ? void 0 : _b.length));
32949
33116
  }
32950
33117
  intersectZoneWithViewport(sheetId, zone) {
32951
- const colsRange = range(zone.left, zone.right + 1);
32952
- const rowsRange = range(zone.top, zone.bottom + 1);
32953
33118
  return {
32954
- left: this.getters.findVisibleHeader(sheetId, "COL", colsRange),
32955
- right: this.getters.findVisibleHeader(sheetId, "COL", colsRange.reverse()),
32956
- top: this.getters.findVisibleHeader(sheetId, "ROW", rowsRange),
32957
- bottom: this.getters.findVisibleHeader(sheetId, "ROW", rowsRange.reverse()),
33119
+ left: this.getters.findVisibleHeader(sheetId, "COL", zone.left, zone.right),
33120
+ right: this.getters.findVisibleHeader(sheetId, "COL", zone.right, zone.left),
33121
+ top: this.getters.findVisibleHeader(sheetId, "ROW", zone.top, zone.bottom),
33122
+ bottom: this.getters.findVisibleHeader(sheetId, "ROW", zone.bottom, zone.top),
32958
33123
  };
32959
33124
  }
32960
33125
  updateFilter({ col, row, values, sheetId }) {
@@ -33396,13 +33561,33 @@ class HeaderVisibilityUIPlugin extends UIPlugin {
33396
33561
  }
33397
33562
  getNextVisibleCellPosition(sheetId, col, row) {
33398
33563
  return {
33399
- col: this.findVisibleHeader(sheetId, "COL", range(col, this.getters.getNumberCols(sheetId))),
33400
- row: this.findVisibleHeader(sheetId, "ROW", range(row, this.getters.getNumberRows(sheetId))),
33564
+ col: this.findVisibleHeader(sheetId, "COL", col, this.getters.getNumberCols(sheetId) - 1),
33565
+ row: this.findVisibleHeader(sheetId, "ROW", row, this.getters.getNumberRows(sheetId) - 1),
33401
33566
  };
33402
33567
  }
33403
- findVisibleHeader(sheetId, dimension, indexes) {
33404
- return indexes.find((index) => this.getters.doesHeaderExist(sheetId, dimension, index) &&
33405
- !this.isHeaderHidden(sheetId, dimension, index));
33568
+ /**
33569
+ * Find the first visible header in the range [`from` => `to`].
33570
+ *
33571
+ * Both `from` and `to` are inclusive.
33572
+ */
33573
+ findVisibleHeader(sheetId, dimension, from, to) {
33574
+ if (from <= to) {
33575
+ for (let i = from; i <= to; i++) {
33576
+ if (this.getters.doesHeaderExist(sheetId, dimension, i) &&
33577
+ !this.isHeaderHidden(sheetId, dimension, i)) {
33578
+ return i;
33579
+ }
33580
+ }
33581
+ }
33582
+ if (from > to) {
33583
+ for (let i = from; i >= to; i--) {
33584
+ if (this.getters.doesHeaderExist(sheetId, dimension, i) &&
33585
+ !this.isHeaderHidden(sheetId, dimension, i)) {
33586
+ return i;
33587
+ }
33588
+ }
33589
+ }
33590
+ return undefined;
33406
33591
  }
33407
33592
  findLastVisibleColRowIndex(sheetId, dimension, indexes) {
33408
33593
  let lastIndex;
@@ -33415,7 +33600,7 @@ class HeaderVisibilityUIPlugin extends UIPlugin {
33415
33600
  }
33416
33601
  findFirstVisibleColRowIndex(sheetId, dimension) {
33417
33602
  const numberOfHeaders = this.getters.getNumberHeaders(sheetId, dimension);
33418
- for (let i = 0; i < numberOfHeaders - 1; i++) {
33603
+ for (let i = 0; i < numberOfHeaders; i++) {
33419
33604
  if (dimension === "COL" && !this.isColHidden(sheetId, i)) {
33420
33605
  return i;
33421
33606
  }
@@ -37542,7 +37727,7 @@ class BottomBar extends owl.Component {
37542
37727
  }
37543
37728
  openContextMenu(x, y, registry, menuId) {
37544
37729
  this.menuState.isOpen = true;
37545
- this.menuState.menuItems = registry.getAll().filter((x) => x.isVisible(this.env));
37730
+ this.menuState.menuItems = registry.getAll();
37546
37731
  this.menuState.position = { x, y };
37547
37732
  this.menuState.menuId = menuId;
37548
37733
  }
@@ -38303,7 +38488,7 @@ class TopBar extends owl.Component {
38303
38488
  const { left, top, height } = ev.target.getBoundingClientRect();
38304
38489
  this.state.menuState.isOpen = true;
38305
38490
  this.state.menuState.position = { x: left, y: top + height };
38306
- this.state.menuState.menuItems = getMenuChildren(menu, this.env).filter((item) => !item.isVisible || item.isVisible(this.env));
38491
+ this.state.menuState.menuItems = getMenuChildren(menu, this.env);
38307
38492
  this.state.menuState.parentMenu = menu;
38308
38493
  this.isSelectingMenu = true;
38309
38494
  this.openedEl = ev.target;
@@ -38352,9 +38537,7 @@ class TopBar extends owl.Component {
38352
38537
  this.style.align = this.style.align || (cell === null || cell === void 0 ? void 0 : cell.defaultAlign);
38353
38538
  this.fillColor = this.style.fillColor || "#ffffff";
38354
38539
  this.textColor = this.style.textColor || "#000000";
38355
- this.menus = topbarMenuRegistry
38356
- .getAll()
38357
- .filter((item) => !item.isVisible || item.isVisible(this.env));
38540
+ this.menus = topbarMenuRegistry.getAll();
38358
38541
  }
38359
38542
  getMenuName(menu) {
38360
38543
  return getMenuName(menu, this.env);
@@ -40420,8 +40603,8 @@ class SelectionStreamProcessor {
40420
40603
  }
40421
40604
  const { left, right, top, bottom } = zone;
40422
40605
  const sheetId = this.getters.getActiveSheetId();
40423
- const refCol = this.getters.findVisibleHeader(sheetId, "COL", range(left, right + 1));
40424
- const refRow = this.getters.findVisibleHeader(sheetId, "ROW", range(top, bottom + 1));
40606
+ const refCol = this.getters.findVisibleHeader(sheetId, "COL", left, right);
40607
+ const refRow = this.getters.findVisibleHeader(sheetId, "ROW", top, bottom);
40425
40608
  if (refRow === undefined || refCol === undefined) {
40426
40609
  return 17 /* CommandResult.SelectionOutOfBound */;
40427
40610
  }
@@ -40496,10 +40679,10 @@ class SelectionStreamProcessor {
40496
40679
  const { col: anchorCol, row: anchorRow } = anchor.cell;
40497
40680
  return {
40498
40681
  col: this.getters.isColHidden(sheetId, anchorCol)
40499
- ? this.getters.findVisibleHeader(sheetId, "COL", range(left, right + 1)) || anchorCol
40682
+ ? this.getters.findVisibleHeader(sheetId, "COL", left, right) || anchorCol
40500
40683
  : anchorCol,
40501
40684
  row: this.getters.isRowHidden(sheetId, anchorRow)
40502
- ? this.getters.findVisibleHeader(sheetId, "ROW", range(top, bottom + 1)) || anchorRow
40685
+ ? this.getters.findVisibleHeader(sheetId, "ROW", top, bottom) || anchorRow
40503
40686
  : anchorRow,
40504
40687
  };
40505
40688
  }
@@ -41034,12 +41217,9 @@ function addFormula(cell) {
41034
41217
  else {
41035
41218
  // Shouldn't we always output the value then ?
41036
41219
  const value = cell.value;
41037
- // what if value = 0? Is this condition correct?
41038
- if (value) {
41039
- const type = getCellType(value);
41040
- attrs.push(["t", type]);
41041
- node = escapeXml /*xml*/ `<v>${value}</v>`;
41042
- }
41220
+ const type = getCellType(value);
41221
+ attrs.push(["t", type]);
41222
+ node = escapeXml /*xml*/ `<v>${value}</v>`;
41043
41223
  return { attrs, node };
41044
41224
  }
41045
41225
  }
@@ -42139,6 +42319,8 @@ class Model extends EventBus {
42139
42319
  * Internal status of the model. Important for command handling coordination
42140
42320
  */
42141
42321
  this.status = 0 /* Status.Ready */;
42322
+ this.handlers = [];
42323
+ this.coreHandlers = [];
42142
42324
  /**
42143
42325
  * The dispatch method is the only entry point to manipulate data in the model.
42144
42326
  * This is through this method that commands are dispatched most of the time
@@ -42211,7 +42393,7 @@ class Model extends EventBus {
42211
42393
  const command = { type, ...payload };
42212
42394
  const previousStatus = this.status;
42213
42395
  this.status = 2 /* Status.RunningCore */;
42214
- const handlers = this.isReplayingCommand ? [this.range, ...this.corePlugins] : this.handlers;
42396
+ const handlers = this.isReplayingCommand ? this.coreHandlers : this.handlers;
42215
42397
  this.dispatchToHandlers(handlers, command);
42216
42398
  this.status = previousStatus;
42217
42399
  return DispatchResult.Success;
@@ -42245,6 +42427,8 @@ class Model extends EventBus {
42245
42427
  this.uuidGenerator.setIsFastStrategy(true);
42246
42428
  // Initiate stream processor
42247
42429
  this.selection = new SelectionStreamProcessor(this.getters);
42430
+ this.coreHandlers.push(this.range);
42431
+ this.handlers.push(this.range);
42248
42432
  // registering plugins
42249
42433
  for (let Plugin of corePluginRegistry.getAll()) {
42250
42434
  this.setupCorePlugin(Plugin, workbookData);
@@ -42254,6 +42438,7 @@ class Model extends EventBus {
42254
42438
  this.setupUiPlugin(Plugin);
42255
42439
  }
42256
42440
  this.uuidGenerator.setIsFastStrategy(false);
42441
+ this.handlers.push(this.history);
42257
42442
  // starting plugins
42258
42443
  this.dispatch("START");
42259
42444
  // Model should be the last permanent subscriber in the list since he should render
@@ -42274,9 +42459,6 @@ class Model extends EventBus {
42274
42459
  // by owl, since we do not rely on reactivity
42275
42460
  owl.markRaw(this);
42276
42461
  }
42277
- get handlers() {
42278
- return [this.range, ...this.corePlugins, ...this.uiPlugins, this.history];
42279
- }
42280
42462
  joinSession() {
42281
42463
  this.session.join(this.config.client);
42282
42464
  }
@@ -42295,6 +42477,7 @@ class Model extends EventBus {
42295
42477
  this.getters[name] = plugin[name].bind(plugin);
42296
42478
  }
42297
42479
  this.uiPlugins.push(plugin);
42480
+ this.handlers.push(plugin);
42298
42481
  const layers = Plugin.layers.map((l) => [plugin, l]);
42299
42482
  this.renderers.push(...layers);
42300
42483
  this.renderers.sort((p1, p2) => p1[1] - p2[1]);
@@ -42318,6 +42501,8 @@ class Model extends EventBus {
42318
42501
  }
42319
42502
  plugin.import(data);
42320
42503
  this.corePlugins.push(plugin);
42504
+ this.coreHandlers.push(plugin);
42505
+ this.handlers.push(plugin);
42321
42506
  }
42322
42507
  onRemoteRevisionReceived({ commands }) {
42323
42508
  for (let command of commands) {
@@ -42335,7 +42520,7 @@ class Model extends EventBus {
42335
42520
  return;
42336
42521
  }
42337
42522
  this.isReplayingCommand = true;
42338
- this.dispatchToHandlers([this.range, ...this.corePlugins], command);
42523
+ this.dispatchToHandlers(this.coreHandlers, command);
42339
42524
  this.isReplayingCommand = false;
42340
42525
  }), this.config.transportService, revisionId);
42341
42526
  return session;
@@ -42435,7 +42620,7 @@ class Model extends EventBus {
42435
42620
  }
42436
42621
  }
42437
42622
  data.revisionId = this.session.getRevisionId() || DEFAULT_REVISION_ID;
42438
- data = JSON.parse(JSON.stringify(data));
42623
+ data = deepCopy(data);
42439
42624
  return data;
42440
42625
  }
42441
42626
  updateMode(mode) {
@@ -42462,7 +42647,7 @@ class Model extends EventBus {
42462
42647
  handler.exportForExcel(data);
42463
42648
  }
42464
42649
  }
42465
- data = JSON.parse(JSON.stringify(data));
42650
+ data = deepCopy(data);
42466
42651
  return getXLSX(data);
42467
42652
  }
42468
42653
  }
@@ -42594,6 +42779,6 @@ exports.setTranslationMethod = setTranslationMethod;
42594
42779
  exports.tokenize = tokenize;
42595
42780
 
42596
42781
 
42597
- __info__.version = '16.0.7';
42598
- __info__.date = '2023-04-14T12:46:24.076Z';
42599
- __info__.hash = '2b4831d';
42782
+ __info__.version = '16.0.9';
42783
+ __info__.date = '2023-04-28T12:25:41.572Z';
42784
+ __info__.hash = 'e06ca83';