@odoo/o-spreadsheet 18.4.7 → 18.4.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.
@@ -2,9 +2,9 @@
2
2
  /**
3
3
  * This file is generated by o-spreadsheet build tools. Do not edit it.
4
4
  * @see https://github.com/odoo/o-spreadsheet
5
- * @version 18.4.7
6
- * @date 2025-08-21T06:42:12.438Z
7
- * @hash d5eb3a6
5
+ * @version 18.4.9
6
+ * @date 2025-09-05T07:38:32.126Z
7
+ * @hash a261873
8
8
  */
9
9
 
10
10
  'use strict';
@@ -2645,6 +2645,7 @@ const invalidateBordersCommands = new Set([
2645
2645
  "AUTOFILL_CELL",
2646
2646
  "SET_BORDER",
2647
2647
  "SET_ZONE_BORDERS",
2648
+ "SET_BORDERS_ON_TARGET",
2648
2649
  ]);
2649
2650
  const readonlyAllowedCommands = new Set([
2650
2651
  "START",
@@ -3991,6 +3992,10 @@ function isDataNonEmpty(data) {
3991
3992
  }
3992
3993
  return true;
3993
3994
  }
3995
+ const noValidInputErrorMessage = _t("[[FUNCTION_NAME]] has no valid input data.");
3996
+ function emptyDataErrorMessage(argName) {
3997
+ return _t("[[FUNCTION_NAME]] expects the provided values of %(argName)s to be a non-empty matrix.", { argName });
3998
+ }
3994
3999
 
3995
4000
  function tokenizeFormat(str) {
3996
4001
  const chars = new TokenizingChars(str);
@@ -6592,40 +6597,44 @@ function orderRange(range) {
6592
6597
  };
6593
6598
  }
6594
6599
  function getRangeAdapter(cmd) {
6595
- switch (cmd.type) {
6596
- case "REMOVE_COLUMNS_ROWS":
6597
- return {
6598
- applyChange: getApplyRangeChangeRemoveColRow(cmd),
6599
- sheetId: cmd.sheetId,
6600
- sheetName: cmd.sheetName,
6601
- };
6602
- case "ADD_COLUMNS_ROWS":
6603
- return {
6604
- applyChange: getApplyRangeChangeAddColRow(cmd),
6605
- sheetId: cmd.sheetId,
6606
- sheetName: cmd.sheetName,
6607
- };
6608
- case "DELETE_SHEET":
6609
- return {
6610
- applyChange: getApplyRangeChangeDeleteSheet(cmd),
6611
- sheetId: cmd.sheetId,
6612
- sheetName: cmd.sheetName,
6613
- };
6614
- case "RENAME_SHEET":
6615
- return {
6616
- applyChange: getApplyRangeChangeRenameSheet(cmd),
6617
- sheetId: cmd.sheetId,
6618
- sheetName: cmd.oldName,
6619
- };
6620
- case "MOVE_RANGES":
6621
- return {
6622
- applyChange: getApplyRangeChangeMoveRange(cmd),
6623
- sheetId: cmd.sheetId,
6624
- sheetName: cmd.sheetName,
6625
- };
6600
+ return rangeAdapterRegistry.get(cmd.type)?.(cmd);
6601
+ }
6602
+ class RangeAdapterRegistry extends Registry {
6603
+ add(cmdType, fn) {
6604
+ super.add(cmdType, fn);
6605
+ return this;
6606
+ }
6607
+ get(cmdType) {
6608
+ return this.content[cmdType];
6626
6609
  }
6627
- return undefined;
6628
6610
  }
6611
+ const rangeAdapterRegistry = new RangeAdapterRegistry();
6612
+ rangeAdapterRegistry
6613
+ .add("REMOVE_COLUMNS_ROWS", (cmd) => ({
6614
+ applyChange: getApplyRangeChangeRemoveColRow(cmd),
6615
+ sheetId: cmd.sheetId,
6616
+ sheetName: { old: cmd.sheetName, current: cmd.sheetName },
6617
+ }))
6618
+ .add("ADD_COLUMNS_ROWS", (cmd) => ({
6619
+ applyChange: getApplyRangeChangeAddColRow(cmd),
6620
+ sheetId: cmd.sheetId,
6621
+ sheetName: { old: cmd.sheetName, current: cmd.sheetName },
6622
+ }))
6623
+ .add("DELETE_SHEET", (cmd) => ({
6624
+ applyChange: getApplyRangeChangeDeleteSheet(cmd),
6625
+ sheetId: cmd.sheetId,
6626
+ sheetName: { old: cmd.sheetName, current: cmd.sheetName },
6627
+ }))
6628
+ .add("RENAME_SHEET", (cmd) => ({
6629
+ applyChange: getApplyRangeChangeRenameSheet(cmd),
6630
+ sheetId: cmd.sheetId,
6631
+ sheetName: { old: cmd.oldName, current: cmd.newName },
6632
+ }))
6633
+ .add("MOVE_RANGES", (cmd) => ({
6634
+ applyChange: getApplyRangeChangeMoveRange(cmd),
6635
+ sheetId: cmd.sheetId,
6636
+ sheetName: { old: cmd.sheetName, current: cmd.sheetName },
6637
+ }));
6629
6638
  function getApplyRangeChangeRemoveColRow(cmd) {
6630
6639
  const start = cmd.dimension === "COL" ? "left" : "top";
6631
6640
  const end = cmd.dimension === "COL" ? "right" : "bottom";
@@ -7248,14 +7257,11 @@ function getPasteZones(target, content) {
7248
7257
  const width = content[0].length, height = content.length;
7249
7258
  return target.map((t) => splitZoneForPaste(t, width, height)).flat();
7250
7259
  }
7251
- function parseOSClipboardContent(content, clipboardId) {
7260
+ function parseOSClipboardContent(content) {
7252
7261
  let spreadsheetContent = undefined;
7253
7262
  if (content[ClipboardMIMEType.Html]) {
7254
7263
  const htmlDocument = new DOMParser().parseFromString(content[ClipboardMIMEType.Html], "text/html");
7255
- const oSheetClipboardData = htmlDocument
7256
- .querySelector("div")
7257
- ?.getAttribute("data-osheet-clipboard");
7258
- spreadsheetContent = oSheetClipboardData && JSON.parse(oSheetClipboardData);
7264
+ spreadsheetContent = getOSheetDataFromHTML(htmlDocument);
7259
7265
  }
7260
7266
  const textContent = content[ClipboardMIMEType.PlainText] || "";
7261
7267
  let imageBlob = undefined;
@@ -7274,6 +7280,17 @@ function parseOSClipboardContent(content, clipboardId) {
7274
7280
  };
7275
7281
  return osClipboardContent;
7276
7282
  }
7283
+ function getOSheetDataFromHTML(htmlDocument) {
7284
+ const attributes = [...htmlDocument.documentElement.attributes];
7285
+ // Check if it's a Microsoft Office clipboard data (it will have some namespaces defined in the root element)
7286
+ if (attributes.some((attr) => attr.value.includes("microsoft"))) {
7287
+ return undefined;
7288
+ }
7289
+ const oSheetClipboardData = htmlDocument
7290
+ .querySelector("div")
7291
+ ?.getAttribute("data-osheet-clipboard");
7292
+ return oSheetClipboardData && JSON.parse(oSheetClipboardData);
7293
+ }
7277
7294
  /**
7278
7295
  * Applies each clipboard handler to paste its corresponding data into the target.
7279
7296
  */
@@ -7994,8 +8011,11 @@ function invertMatrix(M) {
7994
8011
  // (a) Swap 2 rows. This multiply the determinant by -1.
7995
8012
  // (b) Multiply a row by a scalar. This multiply the determinant by that scalar.
7996
8013
  // (c) Add to a row a multiple of another row. This does not change the determinant.
8014
+ if (M.length < 1 || M[0].length < 1) {
8015
+ throw new Error("invertMatrix: an empty matrix cannot be inverted.");
8016
+ }
7997
8017
  if (M.length !== M[0].length) {
7998
- throw new EvaluationError(_t("Function [[FUNCTION_NAME]] invert matrix error, only square matrices are invertible"));
8018
+ throw new Error("invertMatrix: only square matrices are invertible");
7999
8019
  }
8000
8020
  let determinant = 1;
8001
8021
  const dim = M.length;
@@ -8064,8 +8084,11 @@ function swapMatrixRows(matrix, row1, row2) {
8064
8084
  * Note: we use indexing [col][row] instead of the standard mathematical notation [row][col]
8065
8085
  */
8066
8086
  function multiplyMatrices(matrix1, matrix2) {
8087
+ if (matrix1.length < 1 || matrix2.length < 1) {
8088
+ throw new Error("multiplyMatrices: empty matrices cannot be multiplied.");
8089
+ }
8067
8090
  if (matrix1.length !== matrix2[0].length) {
8068
- throw new EvaluationError(_t("Cannot multiply matrices : incompatible matrices size."));
8091
+ throw new Error("multiplyMatrices: incompatible matrices size.");
8069
8092
  }
8070
8093
  const rowsM1 = matrix1[0].length;
8071
8094
  const colsM2 = matrix2.length;
@@ -8091,7 +8114,7 @@ function toScalar(arg) {
8091
8114
  return arg;
8092
8115
  }
8093
8116
  if (!isSingleElementMatrix(arg)) {
8094
- throw new EvaluationError(_t("The value should be a scalar or a 1x1 matrix"));
8117
+ throw new Error("The value should be a scalar or a 1x1 matrix");
8095
8118
  }
8096
8119
  return arg[0][0];
8097
8120
  }
@@ -11230,6 +11253,9 @@ const MMULT = {
11230
11253
  compute: function (matrix1, matrix2) {
11231
11254
  const _matrix1 = toNumberMatrix(matrix1, "matrix1");
11232
11255
  const _matrix2 = toNumberMatrix(matrix2, "matrix2");
11256
+ if (_matrix1.length === 0 || _matrix2.length === 0) {
11257
+ return new EvaluationError(_t("The first and second arguments of [[FUNCTION_NAME]] must be non-empty matrices."));
11258
+ }
11233
11259
  if (_matrix1.length !== _matrix2[0].length) {
11234
11260
  return new EvaluationError(_t("In [[FUNCTION_NAME]], the number of columns of the first matrix (%s) must be equal to the \
11235
11261
  number of rows of the second matrix (%s).", _matrix1.length.toString(), _matrix2[0].length.toString()));
@@ -12842,7 +12868,7 @@ function centile(data, percent, isInclusive, locale) {
12842
12868
  count++;
12843
12869
  }
12844
12870
  });
12845
- assert(count !== 0, _t("[[FUNCTION_NAME]] has no valid input data."));
12871
+ assert(count !== 0, noValidInputErrorMessage);
12846
12872
  if (!isInclusive) {
12847
12873
  // 2nd argument must be between 1/(n+1) and n/(n+1) with n the number of data
12848
12874
  assert(1 / (count + 1) <= _percent && _percent <= count / (count + 1), _t("Function [[FUNCTION_NAME]] parameter 2 value is out of range."));
@@ -13117,6 +13143,9 @@ const FORECAST = {
13117
13143
  ],
13118
13144
  compute: function (x, dataY, dataX) {
13119
13145
  const { flatDataX, flatDataY } = filterAndFlatData(dataY, dataX);
13146
+ if (flatDataX.length === 0 || flatDataY.length === 0) {
13147
+ return new NotAvailableError(noValidInputErrorMessage);
13148
+ }
13120
13149
  return predictLinearValues([flatDataY], [flatDataX], matrixMap(toMatrix(x), (value) => toNumber(value, this.locale)), true);
13121
13150
  },
13122
13151
  isExported: true,
@@ -13133,6 +13162,9 @@ const GROWTH = {
13133
13162
  arg("b (boolean, default=TRUE)", _t("Given a general exponential form of y = b*m^x for a curve fit, calculates b if TRUE or forces b to be 1 and only calculates the m values if FALSE.")),
13134
13163
  ],
13135
13164
  compute: function (knownDataY, knownDataX = [[]], newDataX = [[]], b = { value: true }) {
13165
+ if (knownDataY.length === 0 || knownDataY[0].length === 0) {
13166
+ return new EvaluationError(emptyDataErrorMessage("known_data_y"));
13167
+ }
13136
13168
  return expM(predictLinearValues(logM(toNumberMatrix(knownDataY, "the first argument (known_data_y)")), toNumberMatrix(knownDataX, "the second argument (known_data_x)"), toNumberMatrix(newDataX, "the third argument (new_data_y)"), toBoolean(b)));
13137
13169
  },
13138
13170
  };
@@ -13147,6 +13179,9 @@ const INTERCEPT = {
13147
13179
  ],
13148
13180
  compute: function (dataY, dataX) {
13149
13181
  const { flatDataX, flatDataY } = filterAndFlatData(dataY, dataX);
13182
+ if (flatDataX.length === 0 || flatDataY.length === 0) {
13183
+ return new NotAvailableError(noValidInputErrorMessage);
13184
+ }
13150
13185
  const [[], [intercept]] = fullLinearRegression([flatDataX], [flatDataY]);
13151
13186
  return intercept;
13152
13187
  },
@@ -13179,7 +13214,7 @@ const LARGE = {
13179
13214
  });
13180
13215
  const result = largests.shift();
13181
13216
  if (result === undefined) {
13182
- return new EvaluationError(_t("[[FUNCTION_NAME]] has no valid input data."));
13217
+ return new EvaluationError(noValidInputErrorMessage);
13183
13218
  }
13184
13219
  if (count < _n) {
13185
13220
  return new EvaluationError(_t("Function [[FUNCTION_NAME]] parameter 2 value (%s) is out of range.", _n));
@@ -13200,6 +13235,9 @@ const LINEST = {
13200
13235
  arg("verbose (boolean, default=FALSE)", _t("A flag specifying whether to return additional regression statistics or only the linear coefficients and the y-intercept")),
13201
13236
  ],
13202
13237
  compute: function (dataY, dataX = [[]], calculateB = { value: true }, verbose = { value: false }) {
13238
+ if (dataY.length === 0 || dataY[0].length === 0) {
13239
+ return new EvaluationError(emptyDataErrorMessage("data_y"));
13240
+ }
13203
13241
  return fullLinearRegression(toNumberMatrix(dataX, "the first argument (data_y)"), toNumberMatrix(dataY, "the second argument (data_x)"), toBoolean(calculateB), toBoolean(verbose));
13204
13242
  },
13205
13243
  isExported: true,
@@ -13216,6 +13254,9 @@ const LOGEST = {
13216
13254
  arg("verbose (boolean, default=FALSE)", _t("A flag specifying whether to return additional regression statistics or only the linear coefficients and the y-intercept")),
13217
13255
  ],
13218
13256
  compute: function (dataY, dataX = [[]], calculateB = { value: true }, verbose = { value: false }) {
13257
+ if (dataY.length === 0 || dataY[0].length === 0) {
13258
+ return new EvaluationError(emptyDataErrorMessage("data_y"));
13259
+ }
13219
13260
  const coeffs = fullLinearRegression(toNumberMatrix(dataX, "the second argument (data_x)"), logM(toNumberMatrix(dataY, "the first argument (data_y)")), toBoolean(calculateB), toBoolean(verbose));
13220
13261
  for (let i = 0; i < coeffs.length; i++) {
13221
13262
  coeffs[i][0] = Math.exp(coeffs[i][0]);
@@ -13237,8 +13278,8 @@ const MATTHEWS = {
13237
13278
  const flatX = dataX.flat();
13238
13279
  const flatY = dataY.flat();
13239
13280
  assertSameNumberOfElements(flatX, flatY);
13240
- if (flatX.length === 0) {
13241
- return new EvaluationError(_t("[[FUNCTION_NAME]] expects non-empty ranges for both parameters."));
13281
+ if (flatX.length === 0 || flatY.length === 0) {
13282
+ return new NotAvailableError(noValidInputErrorMessage);
13242
13283
  }
13243
13284
  const n = flatX.length;
13244
13285
  let trueN = 0, trueP = 0, falseP = 0, falseN = 0;
@@ -13403,11 +13444,8 @@ const MINIFS = {
13403
13444
  // -----------------------------------------------------------------------------
13404
13445
  function pearson(dataY, dataX) {
13405
13446
  const { flatDataX, flatDataY } = filterAndFlatData(dataY, dataX);
13406
- if (flatDataX.length === 0) {
13407
- throw new EvaluationError(_t("[[FUNCTION_NAME]] expects non-empty ranges for both parameters."));
13408
- }
13409
- if (flatDataX.length < 2) {
13410
- throw new EvaluationError(_t("[[FUNCTION_NAME]] needs at least two values for both parameters."));
13447
+ if (flatDataX.length === 0 || flatDataY.length === 0) {
13448
+ return new NotAvailableError(noValidInputErrorMessage);
13411
13449
  }
13412
13450
  const n = flatDataX.length;
13413
13451
  let sumX = 0, sumY = 0, sumXY = 0, sumXX = 0, sumYY = 0;
@@ -13497,6 +13535,9 @@ const POLYFIT_COEFFS = {
13497
13535
  ],
13498
13536
  compute: function (dataY, dataX, order, intercept = { value: true }) {
13499
13537
  const { flatDataX, flatDataY } = filterAndFlatData(dataY, dataX);
13538
+ if (flatDataX.length === 0 || flatDataY.length === 0) {
13539
+ return new NotAvailableError(noValidInputErrorMessage);
13540
+ }
13500
13541
  return polynomialRegression(flatDataY, flatDataX, toNumber(order, this.locale), toBoolean(intercept));
13501
13542
  },
13502
13543
  isExported: false,
@@ -13516,6 +13557,9 @@ const POLYFIT_FORECAST = {
13516
13557
  compute: function (x, dataY, dataX, order, intercept = { value: true }) {
13517
13558
  const _order = toNumber(order, this.locale);
13518
13559
  const { flatDataX, flatDataY } = filterAndFlatData(dataY, dataX);
13560
+ if (flatDataX.length === 0 || flatDataY.length === 0) {
13561
+ return new NotAvailableError(noValidInputErrorMessage);
13562
+ }
13519
13563
  const coeffs = polynomialRegression(flatDataY, flatDataX, _order, toBoolean(intercept)).flat();
13520
13564
  return matrixMap(toMatrix(x), (xij) => evaluatePolynomial(coeffs, toNumber(xij, this.locale), _order));
13521
13565
  },
@@ -13617,7 +13661,11 @@ const RSQ = {
13617
13661
  arg("data_x (range<number>)", _t("The range representing the array or matrix of independent data.")),
13618
13662
  ],
13619
13663
  compute: function (dataY, dataX) {
13620
- return Math.pow(pearson(dataX, dataY), 2.0);
13664
+ const value = pearson(dataY, dataX);
13665
+ if (value instanceof Error) {
13666
+ throw value;
13667
+ }
13668
+ return Math.pow(value, 2.0);
13621
13669
  },
13622
13670
  isExported: true,
13623
13671
  };
@@ -13632,6 +13680,9 @@ const SLOPE = {
13632
13680
  ],
13633
13681
  compute: function (dataY, dataX) {
13634
13682
  const { flatDataX, flatDataY } = filterAndFlatData(dataY, dataX);
13683
+ if (flatDataX.length === 0 || flatDataY.length === 0) {
13684
+ return new NotAvailableError(noValidInputErrorMessage);
13685
+ }
13635
13686
  const [[slope]] = fullLinearRegression([flatDataX], [flatDataY]);
13636
13687
  return slope;
13637
13688
  },
@@ -13664,7 +13715,7 @@ const SMALL = {
13664
13715
  });
13665
13716
  const result = largests.pop();
13666
13717
  if (result === undefined) {
13667
- return new EvaluationError(_t("[[FUNCTION_NAME]] has no valid input data."));
13718
+ return new EvaluationError(noValidInputErrorMessage);
13668
13719
  }
13669
13720
  if (count < _n) {
13670
13721
  return new EvaluationError(_t("Function [[FUNCTION_NAME]] parameter 2 value (%s) is out of range.", _n));
@@ -13684,6 +13735,9 @@ const SPEARMAN = {
13684
13735
  ],
13685
13736
  compute: function (dataX, dataY) {
13686
13737
  const { flatDataX, flatDataY } = filterAndFlatData(dataY, dataX);
13738
+ if (flatDataX.length === 0 || flatDataY.length === 0) {
13739
+ return new NotAvailableError(noValidInputErrorMessage);
13740
+ }
13687
13741
  const n = flatDataX.length;
13688
13742
  const order = flatDataX.map((e, i) => [e, flatDataY[i]]);
13689
13743
  order.sort((a, b) => a[0] - b[0]);
@@ -13794,6 +13848,9 @@ const STEYX = {
13794
13848
  ],
13795
13849
  compute: function (dataY, dataX) {
13796
13850
  const { flatDataX, flatDataY } = filterAndFlatData(dataY, dataX);
13851
+ if (flatDataX.length === 0 || flatDataY.length === 0) {
13852
+ return new NotAvailableError(noValidInputErrorMessage);
13853
+ }
13797
13854
  const data = fullLinearRegression([flatDataX], [flatDataY], true, true);
13798
13855
  return data[1][2];
13799
13856
  },
@@ -13811,6 +13868,9 @@ const TREND = {
13811
13868
  arg("b (boolean, optional, default=TRUE)", _t("Given a general linear form of y = m*x+b for a curve fit, calculates b if TRUE or forces b to be 0 and only calculates the m values if FALSE, i.e. forces the curve fit to pass through the origin.")),
13812
13869
  ],
13813
13870
  compute: function (knownDataY, knownDataX = [[]], newDataX = [[]], b = { value: true }) {
13871
+ if (knownDataY.length === 0 || knownDataY[0].length === 0) {
13872
+ return new EvaluationError(emptyDataErrorMessage("known_data_y"));
13873
+ }
13814
13874
  return predictLinearValues(toNumberMatrix(knownDataY, "the first argument (known_data_y)"), toNumberMatrix(knownDataX, "the second argument (known_data_x)"), toNumberMatrix(newDataX, "the third argument (new_data_y)"), toBoolean(b));
13815
13875
  },
13816
13876
  };
@@ -18726,7 +18786,7 @@ const AND = {
18726
18786
  compute: function (...logicalExpressions) {
18727
18787
  const { result, foundBoolean } = boolAnd(logicalExpressions);
18728
18788
  if (!foundBoolean) {
18729
- return new EvaluationError(_t("[[FUNCTION_NAME]] has no valid input data."));
18789
+ return new EvaluationError(noValidInputErrorMessage);
18730
18790
  }
18731
18791
  return result;
18732
18792
  },
@@ -18852,7 +18912,7 @@ const OR = {
18852
18912
  compute: function (...logicalExpressions) {
18853
18913
  const { result, foundBoolean } = boolOr(logicalExpressions);
18854
18914
  if (!foundBoolean) {
18855
- return new EvaluationError(_t("[[FUNCTION_NAME]] has no valid input data."));
18915
+ return new EvaluationError(noValidInputErrorMessage);
18856
18916
  }
18857
18917
  return result;
18858
18918
  },
@@ -18915,7 +18975,7 @@ const XOR = {
18915
18975
  return true; // no stop condition
18916
18976
  });
18917
18977
  if (!foundBoolean) {
18918
- return new EvaluationError(_t("[[FUNCTION_NAME]] has no valid input data."));
18978
+ return new EvaluationError(noValidInputErrorMessage);
18919
18979
  }
18920
18980
  return acc;
18921
18981
  },
@@ -21379,7 +21439,7 @@ function adaptFormulaStringRanges(defaultSheetId, formula, applyChange) {
21379
21439
  function adaptStringRange(defaultSheetId, sheetXC, applyChange) {
21380
21440
  const sheetName = splitReference(sheetXC).sheetName;
21381
21441
  if (sheetName
21382
- ? !isSheetNameEqual(sheetName, applyChange.sheetName)
21442
+ ? !isSheetNameEqual(sheetName, applyChange.sheetName.old)
21383
21443
  : defaultSheetId !== applyChange.sheetId) {
21384
21444
  return sheetXC;
21385
21445
  }
@@ -21396,7 +21456,7 @@ function adaptStringRange(defaultSheetId, sheetXC, applyChange) {
21396
21456
  }
21397
21457
  function getSheetNameGetter(applyChange) {
21398
21458
  return (sheetId) => {
21399
- return sheetId === applyChange.sheetId ? applyChange.sheetName : "";
21459
+ return sheetId === applyChange.sheetId ? applyChange.sheetName.current : "";
21400
21460
  };
21401
21461
  }
21402
21462
  function defaultGetSheetSize(sheetId) {
@@ -27014,10 +27074,6 @@ class ComboChart extends AbstractChart {
27014
27074
  };
27015
27075
  }
27016
27076
  getDefinitionForExcel() {
27017
- // Excel does not support aggregating labels
27018
- if (this.aggregated) {
27019
- return undefined;
27020
- }
27021
27077
  const dataSets = this.dataSets
27022
27078
  .map((ds) => toExcelDataset(this.getters, ds))
27023
27079
  .filter((ds) => ds.range !== "" && ds.range !== CellErrorType.InvalidReference);
@@ -27391,9 +27447,13 @@ class GaugeChart extends AbstractChart {
27391
27447
  : undefined,
27392
27448
  };
27393
27449
  }
27394
- updateRanges(applyChange) {
27450
+ updateRanges(applyChange, sheetId, adaptSheetName) {
27395
27451
  const dataRange = adaptChartRange(this.dataRange, applyChange);
27396
- const adaptFormula = (formula) => this.getters.adaptFormulaStringDependencies(this.sheetId, formula, applyChange);
27452
+ const adaptFormula = (formula) => adaptFormulaStringRanges(this.sheetId, formula, {
27453
+ applyChange,
27454
+ sheetId,
27455
+ sheetName: adaptSheetName,
27456
+ });
27397
27457
  const sectionRule = adaptSectionRuleFormulas(this.sectionRule, adaptFormula);
27398
27458
  const definition = this.getDefinitionWithSpecificRanges(dataRange, sectionRule);
27399
27459
  return new GaugeChart(definition, this.sheetId, this.getters);
@@ -28332,10 +28392,6 @@ class ScatterChart extends AbstractChart {
28332
28392
  return new ScatterChart(definition, this.sheetId, this.getters);
28333
28393
  }
28334
28394
  getDefinitionForExcel() {
28335
- // Excel does not support aggregating labels
28336
- if (this.aggregated) {
28337
- return undefined;
28338
- }
28339
28395
  const dataSets = this.dataSets
28340
28396
  .map((ds) => toExcelDataset(this.getters, ds))
28341
28397
  .filter((ds) => ds.range !== "");
@@ -33720,6 +33776,9 @@ class Composer extends owl.Component {
33720
33776
  this.contentHelper.removeSelection();
33721
33777
  }
33722
33778
  onMouseup() {
33779
+ if (this.env.model.getters.isReadonly()) {
33780
+ return;
33781
+ }
33723
33782
  const selection = this.contentHelper.getCurrentSelection();
33724
33783
  if (selection.start !== selection.end) {
33725
33784
  this.props.composerStore.hoverToken(undefined);
@@ -38183,9 +38242,9 @@ function convertConditionalFormats(xlsxCfs, dxfs, warningManager) {
38183
38242
  if (!rule.operator || !rule.formula || rule.formula.length === 0)
38184
38243
  continue;
38185
38244
  operator = CF_OPERATOR_TYPE_CONVERSION_MAP[rule.operator];
38186
- values.push(rule.formula[0]);
38245
+ values.push(prefixFormula(rule.formula[0]));
38187
38246
  if (rule.formula.length === 2) {
38188
- values.push(rule.formula[1]);
38247
+ values.push(prefixFormula(rule.formula[1]));
38189
38248
  }
38190
38249
  break;
38191
38250
  }
@@ -38343,6 +38402,11 @@ function convertIcons(xlsxIconSet, index) {
38343
38402
  ? ICON_SETS[iconSet].neutral
38344
38403
  : ICON_SETS[iconSet].good;
38345
38404
  }
38405
+ /** Prefix the string by "=" if the string looks like a formula */
38406
+ function prefixFormula(formula) {
38407
+ const tokens = tokenize(formula);
38408
+ return tokens.length === 1 && tokens[0].type !== "REFERENCE" ? formula : "=" + formula;
38409
+ }
38346
38410
  // ---------------------------------------------------------------------------
38347
38411
  // Warnings
38348
38412
  // ---------------------------------------------------------------------------
@@ -38625,7 +38689,7 @@ function getColPosition(colIndex, sheetData) {
38625
38689
  function getRowPosition(rowIndex, sheetData) {
38626
38690
  let position = 0;
38627
38691
  for (let i = 0; i < rowIndex; i++) {
38628
- const rowAtIndex = sheetData.rows[i];
38692
+ const rowAtIndex = sheetData.rows.find((row) => row.index - 1 === i);
38629
38693
  if (rowAtIndex?.height) {
38630
38694
  position += rowAtIndex.height;
38631
38695
  }
@@ -38780,8 +38844,8 @@ function convertExcelTrendline(trend) {
38780
38844
  }
38781
38845
  function convertAnchor(XLSXanchor) {
38782
38846
  const offset = {
38783
- x: convertEMUToDotValue(XLSXanchor.colOffset),
38784
- y: convertEMUToDotValue(XLSXanchor.rowOffset),
38847
+ x: convertEMUToDotValue(XLSXanchor.colOffset) - FIGURE_BORDER_WIDTH,
38848
+ y: convertEMUToDotValue(XLSXanchor.rowOffset) - FIGURE_BORDER_WIDTH,
38785
38849
  };
38786
38850
  return { col: XLSXanchor.col, row: XLSXanchor.row, offset };
38787
38851
  }
@@ -39164,8 +39228,12 @@ function getSheetDims(sheet) {
39164
39228
  dims[0] = Math.max(dims[0], largeMax(row.cells.map((cell) => toCartesian(cell.xc).col)));
39165
39229
  dims[1] = Math.max(dims[1], row.index);
39166
39230
  }
39167
- dims[0] = Math.max(dims[0], EXCEL_IMPORT_DEFAULT_NUMBER_OF_COLS);
39168
- dims[1] = Math.max(dims[1], EXCEL_IMPORT_DEFAULT_NUMBER_OF_ROWS);
39231
+ for (const fig of sheet.figures) {
39232
+ dims[0] = Math.max(dims[0], fig.anchors[fig.anchors.length - 1]?.col ?? 0);
39233
+ dims[1] = Math.max(dims[1], fig.anchors[fig.anchors.length - 1]?.row ?? 0);
39234
+ }
39235
+ dims[0] = Math.max(dims[0] + 5, EXCEL_IMPORT_DEFAULT_NUMBER_OF_COLS);
39236
+ dims[1] = Math.max(dims[1] + 5, EXCEL_IMPORT_DEFAULT_NUMBER_OF_ROWS);
39169
39237
  return dims;
39170
39238
  }
39171
39239
  /**
@@ -40868,7 +40936,7 @@ function getRelationFile(file, xmls) {
40868
40936
  return relsFile;
40869
40937
  }
40870
40938
 
40871
- const EXCEL_IMPORT_VERSION = "18.4.1";
40939
+ const EXCEL_IMPORT_VERSION = "18.4.2";
40872
40940
  class XlsxReader {
40873
40941
  warningManager;
40874
40942
  xmls;
@@ -44413,7 +44481,7 @@ const splitToColumns = {
44413
44481
  const reinsertDynamicPivotMenu = {
44414
44482
  id: "reinsert_dynamic_pivot",
44415
44483
  name: _t("Re-insert dynamic pivot"),
44416
- sequence: 1020,
44484
+ sequence: 60,
44417
44485
  icon: "o-spreadsheet-Icon.INSERT_PIVOT",
44418
44486
  children: [REINSERT_DYNAMIC_PIVOT_CHILDREN],
44419
44487
  isVisible: (env) => env.model.getters.getPivotIds().some((id) => env.model.getters.getPivot(id).isValid()),
@@ -44421,7 +44489,7 @@ const reinsertDynamicPivotMenu = {
44421
44489
  const reinsertStaticPivotMenu = {
44422
44490
  id: "reinsert_static_pivot",
44423
44491
  name: _t("Re-insert static pivot"),
44424
- sequence: 1020,
44492
+ sequence: 70,
44425
44493
  icon: "o-spreadsheet-Icon.INSERT_PIVOT",
44426
44494
  children: [REINSERT_STATIC_PIVOT_CHILDREN],
44427
44495
  isVisible: (env) => env.model.getters.getPivotIds().some((id) => env.model.getters.getPivot(id).isValid()),
@@ -58692,7 +58760,8 @@ class CorePlugin extends BasePlugin {
58692
58760
  * the type of change that occurred.
58693
58761
  *
58694
58762
  * @param applyChange a function that, when called, will adapt the range according to the change on the grid
58695
- * @param sheetId an optional sheetId to adapt either range of that sheet specifically, or ranges pointing to that sheet
58763
+ * @param sheetId an sheetId to adapt either range of that sheet specifically, or ranges pointing to that sheet
58764
+ * @param sheetName couple of old and new sheet names to adapt ranges pointing to that sheet
58696
58765
  */
58697
58766
  adaptRanges(applyChange, sheetId, sheetName) { }
58698
58767
  /**
@@ -59295,9 +59364,7 @@ class CellPlugin extends CorePlugin {
59295
59364
  for (const cell of Object.values(this.cells[sheet] || {})) {
59296
59365
  if (cell.isFormula) {
59297
59366
  for (const range of cell.compiledFormula.dependencies) {
59298
- if (!sheetId ||
59299
- range.sheetId === sheetId ||
59300
- (sheetName && range.invalidSheetName === sheetName)) {
59367
+ if (range.sheetId === sheetId || range.invalidSheetName === sheetName.old) {
59301
59368
  const change = applyChange(range);
59302
59369
  if (change.changeType !== "NONE") {
59303
59370
  this.history.update("cells", sheet, cell.id, "compiledFormula", "dependencies", cell.compiledFormula.dependencies.indexOf(range), change.range);
@@ -59913,9 +59980,9 @@ class ChartPlugin extends CorePlugin {
59913
59980
  charts = {};
59914
59981
  createChart = chartFactory(this.getters);
59915
59982
  validateChartDefinition = (cmd) => validateChartDefinition(this, cmd.definition);
59916
- adaptRanges(applyChange) {
59983
+ adaptRanges(applyChange, sheetId, adaptSheetName) {
59917
59984
  for (const [chartId, chart] of Object.entries(this.charts)) {
59918
- this.history.update("charts", chartId, chart?.updateRanges(applyChange));
59985
+ this.history.update("charts", chartId, chart?.updateRanges(applyChange, sheetId, adaptSheetName));
59919
59986
  }
59920
59987
  }
59921
59988
  // ---------------------------------------------------------------------------
@@ -60178,7 +60245,7 @@ class ConditionalFormatPlugin extends CorePlugin {
60178
60245
  }
60179
60246
  }
60180
60247
  }
60181
- adaptRanges(applyChange, sheetId, sheetName) {
60248
+ adaptRanges(applyChange, sheetId) {
60182
60249
  const sheetIds = sheetId ? [sheetId] : Object.keys(this.cfRules);
60183
60250
  for (const sheetId of sheetIds) {
60184
60251
  this.adaptCFRanges(sheetId, applyChange);
@@ -60560,11 +60627,8 @@ class DataValidationPlugin extends CorePlugin {
60560
60627
  "getValidationRuleForCell",
60561
60628
  ];
60562
60629
  rules = {};
60563
- adaptRanges(applyChange, sheetId, sheetName) {
60564
- const sheetIds = sheetId ? [sheetId] : Object.keys(this.rules);
60565
- for (const sheetId of sheetIds) {
60566
- this.adaptDVRanges(sheetId, applyChange);
60567
- }
60630
+ adaptRanges(applyChange, sheetId) {
60631
+ this.adaptDVRanges(sheetId, applyChange);
60568
60632
  this.adaptDVFormulas(applyChange);
60569
60633
  }
60570
60634
  adaptDVFormulas(applyChange) {
@@ -60854,9 +60918,6 @@ class FigurePlugin extends CorePlugin {
60854
60918
  // Command Handling
60855
60919
  // ---------------------------------------------------------------------------
60856
60920
  adaptRanges(applyChange, sheetId) {
60857
- if (!sheetId) {
60858
- return;
60859
- }
60860
60921
  for (const figure of this.getFigures(sheetId)) {
60861
60922
  const change = applyChange(this.getters.getRangeFromZone(sheetId, {
60862
60923
  left: figure.col,
@@ -61665,11 +61726,8 @@ class MergePlugin extends CorePlugin {
61665
61726
  break;
61666
61727
  }
61667
61728
  }
61668
- adaptRanges(applyChange, sheetId, sheetName) {
61669
- const sheetIds = sheetId ? [sheetId] : Object.keys(this.merges);
61670
- for (const sheetId of sheetIds) {
61671
- this.applyRangeChangeOnSheet(sheetId, applyChange);
61672
- }
61729
+ adaptRanges(applyChange, sheetId) {
61730
+ this.applyRangeChangeOnSheet(sheetId, applyChange);
61673
61731
  }
61674
61732
  // ---------------------------------------------------------------------------
61675
61733
  // Getters
@@ -63174,12 +63232,9 @@ class TablePlugin extends CorePlugin {
63174
63232
  static getters = ["getCoreTable", "getCoreTables", "getCoreTableMatchingTopLeft"];
63175
63233
  tables = {};
63176
63234
  nextTableId = 1;
63177
- adaptRanges(applyChange, sheetId, sheetName) {
63178
- const sheetIds = sheetId ? [sheetId] : this.getters.getSheetIds();
63179
- for (const sheetId of sheetIds) {
63180
- for (const table of this.getCoreTables(sheetId)) {
63181
- this.applyRangeChangeOnTable(sheetId, table, applyChange);
63182
- }
63235
+ adaptRanges(applyChange, sheetId) {
63236
+ for (const table of this.getCoreTables(sheetId)) {
63237
+ this.applyRangeChangeOnTable(sheetId, table, applyChange);
63183
63238
  }
63184
63239
  }
63185
63240
  allowDispatch(cmd) {
@@ -64142,7 +64197,7 @@ class PivotCorePlugin extends CorePlugin {
64142
64197
  }
64143
64198
  }
64144
64199
  }
64145
- adaptRanges(applyChange, sheetId, sheetName) {
64200
+ adaptRanges(applyChange) {
64146
64201
  for (const sheetId in this.compiledMeasureFormulas) {
64147
64202
  for (const formulaString in this.compiledMeasureFormulas[sheetId]) {
64148
64203
  const compiledFormula = this.compiledMeasureFormulas[sheetId][formulaString];
@@ -68543,7 +68598,7 @@ class PivotUIPlugin extends CoreViewPlugin {
68543
68598
  if (!result) {
68544
68599
  return EMPTY_PIVOT_CELL;
68545
68600
  }
68546
- const { functionName, args } = result;
68601
+ let { functionName, args } = result;
68547
68602
  const formulaId = args[0];
68548
68603
  if (!formulaId) {
68549
68604
  return EMPTY_PIVOT_CELL;
@@ -68578,6 +68633,9 @@ class PivotUIPlugin extends CoreViewPlugin {
68578
68633
  return pivotCells[pivotCol][pivotRow];
68579
68634
  }
68580
68635
  try {
68636
+ const offsetRow = position.row - mainPosition.row;
68637
+ const offsetCol = position.col - mainPosition.col;
68638
+ args = args.map((arg) => (isMatrix(arg) ? arg[offsetCol][offsetRow] : arg));
68581
68639
  if (functionName === "PIVOT.HEADER" && args.at(-2) === "measure") {
68582
68640
  const domain = pivot.parseArgsToPivotDomain(args.slice(1, -2).map((value) => ({ value })));
68583
68641
  return {
@@ -70186,7 +70244,8 @@ function transformAll(toTransform, executed) {
70186
70244
  // If the executed command is not in the registry, we skip it
70187
70245
  // because we know there won't be any transformation impacting the
70188
70246
  // commands to transform.
70189
- if (possibleTransformations.has(executedCommand.type)) {
70247
+ if (possibleTransformations.has(executedCommand.type) ||
70248
+ rangeAdapterRegistry.contains(executedCommand.type)) {
70190
70249
  transformedCommands = transformedCommands.reduce((acc, cmd) => {
70191
70250
  const transformed = transform(cmd, executedCommand);
70192
70251
  if (transformed) {
@@ -76178,7 +76237,8 @@ const inverseCommandRegistry = new Registry()
76178
76237
  .add("HIDE_COLUMNS_ROWS", inverseHideColumnsRows)
76179
76238
  .add("UNHIDE_COLUMNS_ROWS", inverseUnhideColumnsRows)
76180
76239
  .add("CREATE_TABLE_STYLE", inverseCreateTableStyle)
76181
- .add("ADD_PIVOT", inverseAddPivot);
76240
+ .add("ADD_PIVOT", inverseAddPivot)
76241
+ .add("RENAME_SHEET", inverseRenameSheet);
76182
76242
  for (const cmd of coreTypes.values()) {
76183
76243
  if (!inverseCommandRegistry.contains(cmd)) {
76184
76244
  inverseCommandRegistry.add(cmd, identity);
@@ -76276,6 +76336,16 @@ function inverseUnhideColumnsRows(cmd) {
76276
76336
  function inverseCreateTableStyle(cmd) {
76277
76337
  return [{ type: "REMOVE_TABLE_STYLE", tableStyleId: cmd.tableStyleId }];
76278
76338
  }
76339
+ function inverseRenameSheet(cmd) {
76340
+ return [
76341
+ {
76342
+ type: "RENAME_SHEET",
76343
+ sheetId: cmd.sheetId,
76344
+ oldName: cmd.newName,
76345
+ newName: cmd.oldName,
76346
+ },
76347
+ ];
76348
+ }
76279
76349
 
76280
76350
  const numberFormatMenuRegistry = new Registry();
76281
76351
  numberFormatMenuRegistry
@@ -76874,8 +76944,9 @@ topbarMenuRegistry
76874
76944
  sequence: 40,
76875
76945
  separator: true,
76876
76946
  })
76877
- .addChild("data_sources_data", ["data"], (env) => {
76947
+ .addChild("pivot_data_sources", ["data"], (env) => {
76878
76948
  const sequence = 50;
76949
+ const numberOfPivots = env.model.getters.getPivotIds().length;
76879
76950
  return env.model.getters.getPivotIds().map((pivotId, index) => {
76880
76951
  const highlightProvider = {
76881
76952
  get highlights() {
@@ -76885,7 +76956,7 @@ topbarMenuRegistry
76885
76956
  return {
76886
76957
  id: `item_pivot_${env.model.getters.getPivotFormulaId(pivotId)}`,
76887
76958
  name: env.model.getters.getPivotDisplayName(pivotId),
76888
- sequence: sequence + index,
76959
+ sequence: sequence + index / numberOfPivots,
76889
76960
  isReadonlyAllowed: true,
76890
76961
  execute: (env) => env.openSidePanel("PivotSidePanel", { pivotId }),
76891
76962
  isEnabled: (env) => !env.isSmall,
@@ -83005,7 +83076,7 @@ function figureCoordinates(headers, anchor, offset) {
83005
83076
  for (const [headerIndex, header] of headers.slice(anchor).entries()) {
83006
83077
  if (currentPosition <= offset && offset < currentPosition + header.size) {
83007
83078
  return {
83008
- index: headerIndex,
83079
+ index: anchor + headerIndex,
83009
83080
  offset: convertDotValueToEMU(offset - currentPosition + FIGURE_BORDER_WIDTH),
83010
83081
  };
83011
83082
  }
@@ -84001,7 +84072,7 @@ class Model extends EventBus {
84001
84072
  handlers = [];
84002
84073
  uiHandlers = [];
84003
84074
  coreHandlers = [];
84004
- constructor(data = {}, config = {}, stateUpdateMessages = [], uuidGenerator = new UuidGenerator(), verboseImport = false) {
84075
+ constructor(data = {}, config = {}, stateUpdateMessages = [], uuidGenerator = new UuidGenerator(), verboseImport = true) {
84005
84076
  const start = performance.now();
84006
84077
  console.debug("##### Model creation #####");
84007
84078
  super();
@@ -84751,6 +84822,6 @@ exports.tokenColors = tokenColors;
84751
84822
  exports.tokenize = tokenize;
84752
84823
 
84753
84824
 
84754
- __info__.version = "18.4.7";
84755
- __info__.date = "2025-08-21T06:42:12.438Z";
84756
- __info__.hash = "d5eb3a6";
84825
+ __info__.version = "18.4.9";
84826
+ __info__.date = "2025-09-05T07:38:32.126Z";
84827
+ __info__.hash = "a261873";