@odoo/o-spreadsheet 18.2.27 → 18.2.28

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.2.27
6
- * @date 2025-08-26T10:07:14.516Z
7
- * @hash fbf9445
5
+ * @version 18.2.28
6
+ * @date 2025-09-05T07:38:26.582Z
7
+ * @hash 84335fb
8
8
  */
9
9
 
10
10
  import { useEnv, useSubEnv, onWillUnmount, useComponent, status, Component, useRef, onMounted, useEffect, App, blockDom, useState, onPatched, onWillPatch, onWillUpdateProps, useExternalListener, onWillStart, xml, useChildSubEnv, markRaw, toRaw } from '@odoo/owl';
@@ -3476,6 +3476,7 @@ const invalidateBordersCommands = new Set([
3476
3476
  "AUTOFILL_CELL",
3477
3477
  "SET_BORDER",
3478
3478
  "SET_ZONE_BORDERS",
3479
+ "SET_BORDERS_ON_TARGET",
3479
3480
  ]);
3480
3481
  const readonlyAllowedCommands = new Set([
3481
3482
  "START",
@@ -7526,8 +7527,11 @@ function invertMatrix(M) {
7526
7527
  // (a) Swap 2 rows. This multiply the determinant by -1.
7527
7528
  // (b) Multiply a row by a scalar. This multiply the determinant by that scalar.
7528
7529
  // (c) Add to a row a multiple of another row. This does not change the determinant.
7530
+ if (M.length < 1 || M[0].length < 1) {
7531
+ throw new Error("invertMatrix: an empty matrix cannot be inverted.");
7532
+ }
7529
7533
  if (M.length !== M[0].length) {
7530
- throw new EvaluationError(_t("Function [[FUNCTION_NAME]] invert matrix error, only square matrices are invertible"));
7534
+ throw new Error("invertMatrix: only square matrices are invertible");
7531
7535
  }
7532
7536
  let determinant = 1;
7533
7537
  const dim = M.length;
@@ -7596,8 +7600,11 @@ function swapMatrixRows(matrix, row1, row2) {
7596
7600
  * Note: we use indexing [col][row] instead of the standard mathematical notation [row][col]
7597
7601
  */
7598
7602
  function multiplyMatrices(matrix1, matrix2) {
7603
+ if (matrix1.length < 1 || matrix2.length < 1) {
7604
+ throw new Error("multiplyMatrices: empty matrices cannot be multiplied.");
7605
+ }
7599
7606
  if (matrix1.length !== matrix2[0].length) {
7600
- throw new EvaluationError(_t("Cannot multiply matrices : incompatible matrices size."));
7607
+ throw new Error("multiplyMatrices: incompatible matrices size.");
7601
7608
  }
7602
7609
  const rowsM1 = matrix1[0].length;
7603
7610
  const colsM2 = matrix2.length;
@@ -7623,7 +7630,7 @@ function toScalar(arg) {
7623
7630
  return arg;
7624
7631
  }
7625
7632
  if (!isSingleElementMatrix(arg)) {
7626
- throw new EvaluationError(_t("The value should be a scalar or a 1x1 matrix"));
7633
+ throw new Error("The value should be a scalar or a 1x1 matrix");
7627
7634
  }
7628
7635
  return arg[0][0];
7629
7636
  }
@@ -7860,6 +7867,16 @@ function getMovingAverageValues(dataset, labels, windowSize = DEFAULT_WINDOW_SIZ
7860
7867
  }
7861
7868
  return values;
7862
7869
  }
7870
+ function assertNonEmptyMatrix(matrix, argName) {
7871
+ assert(() => matrix.length > 0 && matrix[0].length > 0, _t("[[FUNCTION_NAME]] expects the provided values of %(argName)s to be a non-empty matrix.", {
7872
+ argName,
7873
+ }));
7874
+ }
7875
+ function assertNonEmpty(...data) {
7876
+ if (data.length === 0 || data.some((arg) => arg.length === 0)) {
7877
+ throw new NotAvailableError(_t("[[FUNCTION_NAME]] has no valid input data."));
7878
+ }
7879
+ }
7863
7880
 
7864
7881
  const PREVIOUS_VALUE = "(previous)";
7865
7882
  const NEXT_VALUE = "(next)";
@@ -11797,6 +11814,7 @@ const MMULT = {
11797
11814
  compute: function (matrix1, matrix2) {
11798
11815
  const _matrix1 = toNumberMatrix(matrix1, "matrix1");
11799
11816
  const _matrix2 = toNumberMatrix(matrix2, "matrix2");
11817
+ assert(() => _matrix1.length > 0 && _matrix2.length > 0, _t("The first and second arguments of [[FUNCTION_NAME]] must be non-empty matrices."));
11800
11818
  assert(() => _matrix1.length === _matrix2[0].length, _t("In [[FUNCTION_NAME]], the number of columns of the first matrix (%s) must be equal to the \
11801
11819
  number of rows of the second matrix (%s).", _matrix1.length.toString(), _matrix2[0].length.toString()));
11802
11820
  return multiplyMatrices(_matrix1, _matrix2);
@@ -13592,6 +13610,7 @@ const FORECAST = {
13592
13610
  ],
13593
13611
  compute: function (x, dataY, dataX) {
13594
13612
  const { flatDataX, flatDataY } = filterAndFlatData(dataY, dataX);
13613
+ assertNonEmpty(flatDataX, flatDataY);
13595
13614
  return predictLinearValues([flatDataY], [flatDataX], matrixMap(toMatrix(x), (value) => toNumber(value, this.locale)), true);
13596
13615
  },
13597
13616
  isExported: true,
@@ -13608,6 +13627,7 @@ const GROWTH = {
13608
13627
  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.")),
13609
13628
  ],
13610
13629
  compute: function (knownDataY, knownDataX = [[]], newDataX = [[]], b = { value: true }) {
13630
+ assertNonEmptyMatrix(knownDataY, "known_data_y");
13611
13631
  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)));
13612
13632
  },
13613
13633
  };
@@ -13622,6 +13642,7 @@ const INTERCEPT = {
13622
13642
  ],
13623
13643
  compute: function (dataY, dataX) {
13624
13644
  const { flatDataX, flatDataY } = filterAndFlatData(dataY, dataX);
13645
+ assertNonEmpty(flatDataX, flatDataY);
13625
13646
  const [[], [intercept]] = fullLinearRegression([flatDataX], [flatDataY]);
13626
13647
  return intercept;
13627
13648
  },
@@ -13671,6 +13692,7 @@ const LINEST = {
13671
13692
  arg("verbose (boolean, default=FALSE)", _t("A flag specifying whether to return additional regression statistics or only the linear coefficients and the y-intercept")),
13672
13693
  ],
13673
13694
  compute: function (dataY, dataX = [[]], calculateB = { value: true }, verbose = { value: false }) {
13695
+ assertNonEmptyMatrix(dataY, "data_y");
13674
13696
  return fullLinearRegression(toNumberMatrix(dataX, "the first argument (data_y)"), toNumberMatrix(dataY, "the second argument (data_x)"), toBoolean(calculateB), toBoolean(verbose));
13675
13697
  },
13676
13698
  isExported: true,
@@ -13687,6 +13709,7 @@ const LOGEST = {
13687
13709
  arg("verbose (boolean, default=FALSE)", _t("A flag specifying whether to return additional regression statistics or only the linear coefficients and the y-intercept")),
13688
13710
  ],
13689
13711
  compute: function (dataY, dataX = [[]], calculateB = { value: true }, verbose = { value: false }) {
13712
+ assertNonEmptyMatrix(dataY, "data_y");
13690
13713
  const coeffs = fullLinearRegression(toNumberMatrix(dataX, "the second argument (data_x)"), logM(toNumberMatrix(dataY, "the first argument (data_y)")), toBoolean(calculateB), toBoolean(verbose));
13691
13714
  for (let i = 0; i < coeffs.length; i++) {
13692
13715
  coeffs[i][0] = Math.exp(coeffs[i][0]);
@@ -13708,9 +13731,7 @@ const MATTHEWS = {
13708
13731
  const flatX = dataX.flat();
13709
13732
  const flatY = dataY.flat();
13710
13733
  assertSameNumberOfElements(flatX, flatY);
13711
- if (flatX.length === 0) {
13712
- return new EvaluationError(_t("[[FUNCTION_NAME]] expects non-empty ranges for both parameters."));
13713
- }
13734
+ assertNonEmpty(flatX, flatY);
13714
13735
  const n = flatX.length;
13715
13736
  let trueN = 0, trueP = 0, falseP = 0, falseN = 0;
13716
13737
  for (let i = 0; i < n; ++i) {
@@ -13874,12 +13895,7 @@ const MINIFS = {
13874
13895
  // -----------------------------------------------------------------------------
13875
13896
  function pearson(dataY, dataX) {
13876
13897
  const { flatDataX, flatDataY } = filterAndFlatData(dataY, dataX);
13877
- if (flatDataX.length === 0) {
13878
- throw new EvaluationError(_t("[[FUNCTION_NAME]] expects non-empty ranges for both parameters."));
13879
- }
13880
- if (flatDataX.length < 2) {
13881
- throw new EvaluationError(_t("[[FUNCTION_NAME]] needs at least two values for both parameters."));
13882
- }
13898
+ assertNonEmpty(flatDataX, flatDataY);
13883
13899
  const n = flatDataX.length;
13884
13900
  let sumX = 0, sumY = 0, sumXY = 0, sumXX = 0, sumYY = 0;
13885
13901
  for (let i = 0; i < n; i++) {
@@ -13968,6 +13984,7 @@ const POLYFIT_COEFFS = {
13968
13984
  ],
13969
13985
  compute: function (dataY, dataX, order, intercept = { value: true }) {
13970
13986
  const { flatDataX, flatDataY } = filterAndFlatData(dataY, dataX);
13987
+ assertNonEmpty(flatDataX, flatDataY);
13971
13988
  return polynomialRegression(flatDataY, flatDataX, toNumber(order, this.locale), toBoolean(intercept));
13972
13989
  },
13973
13990
  isExported: false,
@@ -13987,6 +14004,7 @@ const POLYFIT_FORECAST = {
13987
14004
  compute: function (x, dataY, dataX, order, intercept = { value: true }) {
13988
14005
  const _order = toNumber(order, this.locale);
13989
14006
  const { flatDataX, flatDataY } = filterAndFlatData(dataY, dataX);
14007
+ assertNonEmpty(flatDataX, flatDataY);
13990
14008
  const coeffs = polynomialRegression(flatDataY, flatDataX, _order, toBoolean(intercept)).flat();
13991
14009
  return matrixMap(toMatrix(x), (xij) => evaluatePolynomial(coeffs, toNumber(xij, this.locale), _order));
13992
14010
  },
@@ -14103,6 +14121,7 @@ const SLOPE = {
14103
14121
  ],
14104
14122
  compute: function (dataY, dataX) {
14105
14123
  const { flatDataX, flatDataY } = filterAndFlatData(dataY, dataX);
14124
+ assertNonEmpty(flatDataX, flatDataY);
14106
14125
  const [[slope]] = fullLinearRegression([flatDataX], [flatDataY]);
14107
14126
  return slope;
14108
14127
  },
@@ -14151,6 +14170,7 @@ const SPEARMAN = {
14151
14170
  ],
14152
14171
  compute: function (dataX, dataY) {
14153
14172
  const { flatDataX, flatDataY } = filterAndFlatData(dataY, dataX);
14173
+ assertNonEmpty(flatDataX, flatDataY);
14154
14174
  const n = flatDataX.length;
14155
14175
  const order = flatDataX.map((e, i) => [e, flatDataY[i]]);
14156
14176
  order.sort((a, b) => a[0] - b[0]);
@@ -14261,6 +14281,7 @@ const STEYX = {
14261
14281
  ],
14262
14282
  compute: function (dataY, dataX) {
14263
14283
  const { flatDataX, flatDataY } = filterAndFlatData(dataY, dataX);
14284
+ assertNonEmpty(flatDataX, flatDataY);
14264
14285
  const data = fullLinearRegression([flatDataX], [flatDataY], true, true);
14265
14286
  return data[1][2];
14266
14287
  },
@@ -14278,6 +14299,7 @@ const TREND = {
14278
14299
  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.")),
14279
14300
  ],
14280
14301
  compute: function (knownDataY, knownDataX = [[]], newDataX = [[]], b = { value: true }) {
14302
+ assertNonEmptyMatrix(knownDataY, "known_data_y");
14281
14303
  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));
14282
14304
  },
14283
14305
  };
@@ -24484,9 +24506,9 @@ function convertConditionalFormats(xlsxCfs, dxfs, warningManager) {
24484
24506
  if (!rule.operator || !rule.formula || rule.formula.length === 0)
24485
24507
  continue;
24486
24508
  operator = convertCFCellIsOperator(rule.operator);
24487
- values.push(rule.formula[0]);
24509
+ values.push(prefixFormula(rule.formula[0]));
24488
24510
  if (rule.formula.length === 2) {
24489
- values.push(rule.formula[1]);
24511
+ values.push(prefixFormula(rule.formula[1]));
24490
24512
  }
24491
24513
  break;
24492
24514
  }
@@ -24644,6 +24666,11 @@ function convertIcons(xlsxIconSet, index) {
24644
24666
  ? ICON_SETS[iconSet].neutral
24645
24667
  : ICON_SETS[iconSet].good;
24646
24668
  }
24669
+ /** Prefix the string by "=" if the string looks like a formula */
24670
+ function prefixFormula(formula) {
24671
+ const tokens = tokenize(formula);
24672
+ return tokens.length === 1 && tokens[0].type !== "REFERENCE" ? formula : "=" + formula;
24673
+ }
24647
24674
  // ---------------------------------------------------------------------------
24648
24675
  // Warnings
24649
24676
  // ---------------------------------------------------------------------------
@@ -36168,7 +36195,7 @@ const splitToColumns = {
36168
36195
  const reinsertDynamicPivotMenu = {
36169
36196
  id: "reinsert_dynamic_pivot",
36170
36197
  name: _t("Re-insert dynamic pivot"),
36171
- sequence: 1020,
36198
+ sequence: 60,
36172
36199
  icon: "o-spreadsheet-Icon.INSERT_PIVOT",
36173
36200
  children: [REINSERT_DYNAMIC_PIVOT_CHILDREN],
36174
36201
  isVisible: (env) => env.model.getters.getPivotIds().some((id) => env.model.getters.getPivot(id).isValid()),
@@ -36176,7 +36203,7 @@ const reinsertDynamicPivotMenu = {
36176
36203
  const reinsertStaticPivotMenu = {
36177
36204
  id: "reinsert_static_pivot",
36178
36205
  name: _t("Re-insert static pivot"),
36179
- sequence: 1020,
36206
+ sequence: 70,
36180
36207
  icon: "o-spreadsheet-Icon.INSERT_PIVOT",
36181
36208
  children: [REINSERT_STATIC_PIVOT_CHILDREN],
36182
36209
  isVisible: (env) => env.model.getters.getPivotIds().some((id) => env.model.getters.getPivot(id).isValid()),
@@ -37914,8 +37941,9 @@ topbarMenuRegistry
37914
37941
  sequence: 40,
37915
37942
  separator: true,
37916
37943
  })
37917
- .addChild("data_sources_data", ["data"], (env) => {
37944
+ .addChild("pivot_data_sources", ["data"], (env) => {
37918
37945
  const sequence = 50;
37946
+ const numberOfPivots = env.model.getters.getPivotIds().length;
37919
37947
  return env.model.getters.getPivotIds().map((pivotId, index) => {
37920
37948
  const highlightProvider = {
37921
37949
  get highlights() {
@@ -37925,7 +37953,7 @@ topbarMenuRegistry
37925
37953
  return {
37926
37954
  id: `item_pivot_${env.model.getters.getPivotFormulaId(pivotId)}`,
37927
37955
  name: env.model.getters.getPivotDisplayName(pivotId),
37928
- sequence: sequence + index,
37956
+ sequence: sequence + index / numberOfPivots,
37929
37957
  isReadonlyAllowed: true,
37930
37958
  execute: (env) => env.openSidePanel("PivotSidePanel", { pivotId }),
37931
37959
  onStartHover: (env) => env.getStore(HighlightStore).register(highlightProvider),
@@ -63897,7 +63925,7 @@ class PivotUIPlugin extends CoreViewPlugin {
63897
63925
  if (!result) {
63898
63926
  return EMPTY_PIVOT_CELL;
63899
63927
  }
63900
- const { functionName, args } = result;
63928
+ let { functionName, args } = result;
63901
63929
  const formulaId = args[0];
63902
63930
  if (!formulaId) {
63903
63931
  return EMPTY_PIVOT_CELL;
@@ -63927,6 +63955,9 @@ class PivotUIPlugin extends CoreViewPlugin {
63927
63955
  return pivotCells[pivotCol][pivotRow];
63928
63956
  }
63929
63957
  try {
63958
+ const offsetRow = position.row - mainPosition.row;
63959
+ const offsetCol = position.col - mainPosition.col;
63960
+ args = args.map((arg) => (isMatrix(arg) ? arg[offsetCol][offsetRow] : arg));
63930
63961
  if (functionName === "PIVOT.HEADER" && args.at(-2) === "measure") {
63931
63962
  const domain = pivot.parseArgsToPivotDomain(args.slice(1, -2).map((value) => ({ value })));
63932
63963
  return {
@@ -76547,7 +76578,7 @@ class Model extends EventBus {
76547
76578
  handlers = [];
76548
76579
  uiHandlers = [];
76549
76580
  coreHandlers = [];
76550
- constructor(data = {}, config = {}, stateUpdateMessages = [], uuidGenerator = new UuidGenerator(), verboseImport = false) {
76581
+ constructor(data = {}, config = {}, stateUpdateMessages = [], uuidGenerator = new UuidGenerator(), verboseImport = true) {
76551
76582
  const start = performance.now();
76552
76583
  console.debug("##### Model creation #####");
76553
76584
  super();
@@ -77238,6 +77269,6 @@ const chartHelpers = { ...CHART_HELPERS, ...CHART_RUNTIME_HELPERS };
77238
77269
  export { AbstractCellClipboardHandler, AbstractChart, AbstractFigureClipboardHandler, CellErrorType, CommandResult, CorePlugin, CoreViewPlugin, DispatchResult, EvaluationError, Model, PivotRuntimeDefinition, Registry, Revision, SPREADSHEET_DIMENSIONS, Spreadsheet, SpreadsheetPivotTable, UIPlugin, __info__, addFunction, addRenderingLayer, astToFormula, chartHelpers, compile, compileTokens, components, constants, convertAstNodes, coreTypes, findCellInNewZone, functionCache, helpers, hooks, invalidateCFEvaluationCommands, invalidateDependenciesCommands, invalidateEvaluationCommands, iterateAstNodes, links, load, parse, parseTokens, readonlyAllowedCommands, registries, setDefaultSheetViewSize, setTranslationMethod, stores, tokenColors, tokenize };
77239
77270
 
77240
77271
 
77241
- __info__.version = "18.2.27";
77242
- __info__.date = "2025-08-26T10:07:14.516Z";
77243
- __info__.hash = "fbf9445";
77272
+ __info__.version = "18.2.28";
77273
+ __info__.date = "2025-09-05T07:38:26.582Z";
77274
+ __info__.hash = "84335fb";
@@ -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.2.27
6
- * @date 2025-08-26T10:07:14.516Z
7
- * @hash fbf9445
5
+ * @version 18.2.28
6
+ * @date 2025-09-05T07:38:26.582Z
7
+ * @hash 84335fb
8
8
  */
9
9
 
10
10
  (function (exports, owl) {
@@ -3477,6 +3477,7 @@
3477
3477
  "AUTOFILL_CELL",
3478
3478
  "SET_BORDER",
3479
3479
  "SET_ZONE_BORDERS",
3480
+ "SET_BORDERS_ON_TARGET",
3480
3481
  ]);
3481
3482
  const readonlyAllowedCommands = new Set([
3482
3483
  "START",
@@ -7527,8 +7528,11 @@
7527
7528
  // (a) Swap 2 rows. This multiply the determinant by -1.
7528
7529
  // (b) Multiply a row by a scalar. This multiply the determinant by that scalar.
7529
7530
  // (c) Add to a row a multiple of another row. This does not change the determinant.
7531
+ if (M.length < 1 || M[0].length < 1) {
7532
+ throw new Error("invertMatrix: an empty matrix cannot be inverted.");
7533
+ }
7530
7534
  if (M.length !== M[0].length) {
7531
- throw new EvaluationError(_t("Function [[FUNCTION_NAME]] invert matrix error, only square matrices are invertible"));
7535
+ throw new Error("invertMatrix: only square matrices are invertible");
7532
7536
  }
7533
7537
  let determinant = 1;
7534
7538
  const dim = M.length;
@@ -7597,8 +7601,11 @@
7597
7601
  * Note: we use indexing [col][row] instead of the standard mathematical notation [row][col]
7598
7602
  */
7599
7603
  function multiplyMatrices(matrix1, matrix2) {
7604
+ if (matrix1.length < 1 || matrix2.length < 1) {
7605
+ throw new Error("multiplyMatrices: empty matrices cannot be multiplied.");
7606
+ }
7600
7607
  if (matrix1.length !== matrix2[0].length) {
7601
- throw new EvaluationError(_t("Cannot multiply matrices : incompatible matrices size."));
7608
+ throw new Error("multiplyMatrices: incompatible matrices size.");
7602
7609
  }
7603
7610
  const rowsM1 = matrix1[0].length;
7604
7611
  const colsM2 = matrix2.length;
@@ -7624,7 +7631,7 @@
7624
7631
  return arg;
7625
7632
  }
7626
7633
  if (!isSingleElementMatrix(arg)) {
7627
- throw new EvaluationError(_t("The value should be a scalar or a 1x1 matrix"));
7634
+ throw new Error("The value should be a scalar or a 1x1 matrix");
7628
7635
  }
7629
7636
  return arg[0][0];
7630
7637
  }
@@ -7861,6 +7868,16 @@
7861
7868
  }
7862
7869
  return values;
7863
7870
  }
7871
+ function assertNonEmptyMatrix(matrix, argName) {
7872
+ assert(() => matrix.length > 0 && matrix[0].length > 0, _t("[[FUNCTION_NAME]] expects the provided values of %(argName)s to be a non-empty matrix.", {
7873
+ argName,
7874
+ }));
7875
+ }
7876
+ function assertNonEmpty(...data) {
7877
+ if (data.length === 0 || data.some((arg) => arg.length === 0)) {
7878
+ throw new NotAvailableError(_t("[[FUNCTION_NAME]] has no valid input data."));
7879
+ }
7880
+ }
7864
7881
 
7865
7882
  const PREVIOUS_VALUE = "(previous)";
7866
7883
  const NEXT_VALUE = "(next)";
@@ -11798,6 +11815,7 @@ stores.inject(MyMetaStore, storeInstance);
11798
11815
  compute: function (matrix1, matrix2) {
11799
11816
  const _matrix1 = toNumberMatrix(matrix1, "matrix1");
11800
11817
  const _matrix2 = toNumberMatrix(matrix2, "matrix2");
11818
+ assert(() => _matrix1.length > 0 && _matrix2.length > 0, _t("The first and second arguments of [[FUNCTION_NAME]] must be non-empty matrices."));
11801
11819
  assert(() => _matrix1.length === _matrix2[0].length, _t("In [[FUNCTION_NAME]], the number of columns of the first matrix (%s) must be equal to the \
11802
11820
  number of rows of the second matrix (%s).", _matrix1.length.toString(), _matrix2[0].length.toString()));
11803
11821
  return multiplyMatrices(_matrix1, _matrix2);
@@ -13593,6 +13611,7 @@ stores.inject(MyMetaStore, storeInstance);
13593
13611
  ],
13594
13612
  compute: function (x, dataY, dataX) {
13595
13613
  const { flatDataX, flatDataY } = filterAndFlatData(dataY, dataX);
13614
+ assertNonEmpty(flatDataX, flatDataY);
13596
13615
  return predictLinearValues([flatDataY], [flatDataX], matrixMap(toMatrix(x), (value) => toNumber(value, this.locale)), true);
13597
13616
  },
13598
13617
  isExported: true,
@@ -13609,6 +13628,7 @@ stores.inject(MyMetaStore, storeInstance);
13609
13628
  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.")),
13610
13629
  ],
13611
13630
  compute: function (knownDataY, knownDataX = [[]], newDataX = [[]], b = { value: true }) {
13631
+ assertNonEmptyMatrix(knownDataY, "known_data_y");
13612
13632
  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)));
13613
13633
  },
13614
13634
  };
@@ -13623,6 +13643,7 @@ stores.inject(MyMetaStore, storeInstance);
13623
13643
  ],
13624
13644
  compute: function (dataY, dataX) {
13625
13645
  const { flatDataX, flatDataY } = filterAndFlatData(dataY, dataX);
13646
+ assertNonEmpty(flatDataX, flatDataY);
13626
13647
  const [[], [intercept]] = fullLinearRegression([flatDataX], [flatDataY]);
13627
13648
  return intercept;
13628
13649
  },
@@ -13672,6 +13693,7 @@ stores.inject(MyMetaStore, storeInstance);
13672
13693
  arg("verbose (boolean, default=FALSE)", _t("A flag specifying whether to return additional regression statistics or only the linear coefficients and the y-intercept")),
13673
13694
  ],
13674
13695
  compute: function (dataY, dataX = [[]], calculateB = { value: true }, verbose = { value: false }) {
13696
+ assertNonEmptyMatrix(dataY, "data_y");
13675
13697
  return fullLinearRegression(toNumberMatrix(dataX, "the first argument (data_y)"), toNumberMatrix(dataY, "the second argument (data_x)"), toBoolean(calculateB), toBoolean(verbose));
13676
13698
  },
13677
13699
  isExported: true,
@@ -13688,6 +13710,7 @@ stores.inject(MyMetaStore, storeInstance);
13688
13710
  arg("verbose (boolean, default=FALSE)", _t("A flag specifying whether to return additional regression statistics or only the linear coefficients and the y-intercept")),
13689
13711
  ],
13690
13712
  compute: function (dataY, dataX = [[]], calculateB = { value: true }, verbose = { value: false }) {
13713
+ assertNonEmptyMatrix(dataY, "data_y");
13691
13714
  const coeffs = fullLinearRegression(toNumberMatrix(dataX, "the second argument (data_x)"), logM(toNumberMatrix(dataY, "the first argument (data_y)")), toBoolean(calculateB), toBoolean(verbose));
13692
13715
  for (let i = 0; i < coeffs.length; i++) {
13693
13716
  coeffs[i][0] = Math.exp(coeffs[i][0]);
@@ -13709,9 +13732,7 @@ stores.inject(MyMetaStore, storeInstance);
13709
13732
  const flatX = dataX.flat();
13710
13733
  const flatY = dataY.flat();
13711
13734
  assertSameNumberOfElements(flatX, flatY);
13712
- if (flatX.length === 0) {
13713
- return new EvaluationError(_t("[[FUNCTION_NAME]] expects non-empty ranges for both parameters."));
13714
- }
13735
+ assertNonEmpty(flatX, flatY);
13715
13736
  const n = flatX.length;
13716
13737
  let trueN = 0, trueP = 0, falseP = 0, falseN = 0;
13717
13738
  for (let i = 0; i < n; ++i) {
@@ -13875,12 +13896,7 @@ stores.inject(MyMetaStore, storeInstance);
13875
13896
  // -----------------------------------------------------------------------------
13876
13897
  function pearson(dataY, dataX) {
13877
13898
  const { flatDataX, flatDataY } = filterAndFlatData(dataY, dataX);
13878
- if (flatDataX.length === 0) {
13879
- throw new EvaluationError(_t("[[FUNCTION_NAME]] expects non-empty ranges for both parameters."));
13880
- }
13881
- if (flatDataX.length < 2) {
13882
- throw new EvaluationError(_t("[[FUNCTION_NAME]] needs at least two values for both parameters."));
13883
- }
13899
+ assertNonEmpty(flatDataX, flatDataY);
13884
13900
  const n = flatDataX.length;
13885
13901
  let sumX = 0, sumY = 0, sumXY = 0, sumXX = 0, sumYY = 0;
13886
13902
  for (let i = 0; i < n; i++) {
@@ -13969,6 +13985,7 @@ stores.inject(MyMetaStore, storeInstance);
13969
13985
  ],
13970
13986
  compute: function (dataY, dataX, order, intercept = { value: true }) {
13971
13987
  const { flatDataX, flatDataY } = filterAndFlatData(dataY, dataX);
13988
+ assertNonEmpty(flatDataX, flatDataY);
13972
13989
  return polynomialRegression(flatDataY, flatDataX, toNumber(order, this.locale), toBoolean(intercept));
13973
13990
  },
13974
13991
  isExported: false,
@@ -13988,6 +14005,7 @@ stores.inject(MyMetaStore, storeInstance);
13988
14005
  compute: function (x, dataY, dataX, order, intercept = { value: true }) {
13989
14006
  const _order = toNumber(order, this.locale);
13990
14007
  const { flatDataX, flatDataY } = filterAndFlatData(dataY, dataX);
14008
+ assertNonEmpty(flatDataX, flatDataY);
13991
14009
  const coeffs = polynomialRegression(flatDataY, flatDataX, _order, toBoolean(intercept)).flat();
13992
14010
  return matrixMap(toMatrix(x), (xij) => evaluatePolynomial(coeffs, toNumber(xij, this.locale), _order));
13993
14011
  },
@@ -14104,6 +14122,7 @@ stores.inject(MyMetaStore, storeInstance);
14104
14122
  ],
14105
14123
  compute: function (dataY, dataX) {
14106
14124
  const { flatDataX, flatDataY } = filterAndFlatData(dataY, dataX);
14125
+ assertNonEmpty(flatDataX, flatDataY);
14107
14126
  const [[slope]] = fullLinearRegression([flatDataX], [flatDataY]);
14108
14127
  return slope;
14109
14128
  },
@@ -14152,6 +14171,7 @@ stores.inject(MyMetaStore, storeInstance);
14152
14171
  ],
14153
14172
  compute: function (dataX, dataY) {
14154
14173
  const { flatDataX, flatDataY } = filterAndFlatData(dataY, dataX);
14174
+ assertNonEmpty(flatDataX, flatDataY);
14155
14175
  const n = flatDataX.length;
14156
14176
  const order = flatDataX.map((e, i) => [e, flatDataY[i]]);
14157
14177
  order.sort((a, b) => a[0] - b[0]);
@@ -14262,6 +14282,7 @@ stores.inject(MyMetaStore, storeInstance);
14262
14282
  ],
14263
14283
  compute: function (dataY, dataX) {
14264
14284
  const { flatDataX, flatDataY } = filterAndFlatData(dataY, dataX);
14285
+ assertNonEmpty(flatDataX, flatDataY);
14265
14286
  const data = fullLinearRegression([flatDataX], [flatDataY], true, true);
14266
14287
  return data[1][2];
14267
14288
  },
@@ -14279,6 +14300,7 @@ stores.inject(MyMetaStore, storeInstance);
14279
14300
  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.")),
14280
14301
  ],
14281
14302
  compute: function (knownDataY, knownDataX = [[]], newDataX = [[]], b = { value: true }) {
14303
+ assertNonEmptyMatrix(knownDataY, "known_data_y");
14282
14304
  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));
14283
14305
  },
14284
14306
  };
@@ -24485,9 +24507,9 @@ stores.inject(MyMetaStore, storeInstance);
24485
24507
  if (!rule.operator || !rule.formula || rule.formula.length === 0)
24486
24508
  continue;
24487
24509
  operator = convertCFCellIsOperator(rule.operator);
24488
- values.push(rule.formula[0]);
24510
+ values.push(prefixFormula(rule.formula[0]));
24489
24511
  if (rule.formula.length === 2) {
24490
- values.push(rule.formula[1]);
24512
+ values.push(prefixFormula(rule.formula[1]));
24491
24513
  }
24492
24514
  break;
24493
24515
  }
@@ -24645,6 +24667,11 @@ stores.inject(MyMetaStore, storeInstance);
24645
24667
  ? ICON_SETS[iconSet].neutral
24646
24668
  : ICON_SETS[iconSet].good;
24647
24669
  }
24670
+ /** Prefix the string by "=" if the string looks like a formula */
24671
+ function prefixFormula(formula) {
24672
+ const tokens = tokenize(formula);
24673
+ return tokens.length === 1 && tokens[0].type !== "REFERENCE" ? formula : "=" + formula;
24674
+ }
24648
24675
  // ---------------------------------------------------------------------------
24649
24676
  // Warnings
24650
24677
  // ---------------------------------------------------------------------------
@@ -36169,7 +36196,7 @@ stores.inject(MyMetaStore, storeInstance);
36169
36196
  const reinsertDynamicPivotMenu = {
36170
36197
  id: "reinsert_dynamic_pivot",
36171
36198
  name: _t("Re-insert dynamic pivot"),
36172
- sequence: 1020,
36199
+ sequence: 60,
36173
36200
  icon: "o-spreadsheet-Icon.INSERT_PIVOT",
36174
36201
  children: [REINSERT_DYNAMIC_PIVOT_CHILDREN],
36175
36202
  isVisible: (env) => env.model.getters.getPivotIds().some((id) => env.model.getters.getPivot(id).isValid()),
@@ -36177,7 +36204,7 @@ stores.inject(MyMetaStore, storeInstance);
36177
36204
  const reinsertStaticPivotMenu = {
36178
36205
  id: "reinsert_static_pivot",
36179
36206
  name: _t("Re-insert static pivot"),
36180
- sequence: 1020,
36207
+ sequence: 70,
36181
36208
  icon: "o-spreadsheet-Icon.INSERT_PIVOT",
36182
36209
  children: [REINSERT_STATIC_PIVOT_CHILDREN],
36183
36210
  isVisible: (env) => env.model.getters.getPivotIds().some((id) => env.model.getters.getPivot(id).isValid()),
@@ -37915,8 +37942,9 @@ stores.inject(MyMetaStore, storeInstance);
37915
37942
  sequence: 40,
37916
37943
  separator: true,
37917
37944
  })
37918
- .addChild("data_sources_data", ["data"], (env) => {
37945
+ .addChild("pivot_data_sources", ["data"], (env) => {
37919
37946
  const sequence = 50;
37947
+ const numberOfPivots = env.model.getters.getPivotIds().length;
37920
37948
  return env.model.getters.getPivotIds().map((pivotId, index) => {
37921
37949
  const highlightProvider = {
37922
37950
  get highlights() {
@@ -37926,7 +37954,7 @@ stores.inject(MyMetaStore, storeInstance);
37926
37954
  return {
37927
37955
  id: `item_pivot_${env.model.getters.getPivotFormulaId(pivotId)}`,
37928
37956
  name: env.model.getters.getPivotDisplayName(pivotId),
37929
- sequence: sequence + index,
37957
+ sequence: sequence + index / numberOfPivots,
37930
37958
  isReadonlyAllowed: true,
37931
37959
  execute: (env) => env.openSidePanel("PivotSidePanel", { pivotId }),
37932
37960
  onStartHover: (env) => env.getStore(HighlightStore).register(highlightProvider),
@@ -63898,7 +63926,7 @@ stores.inject(MyMetaStore, storeInstance);
63898
63926
  if (!result) {
63899
63927
  return EMPTY_PIVOT_CELL;
63900
63928
  }
63901
- const { functionName, args } = result;
63929
+ let { functionName, args } = result;
63902
63930
  const formulaId = args[0];
63903
63931
  if (!formulaId) {
63904
63932
  return EMPTY_PIVOT_CELL;
@@ -63928,6 +63956,9 @@ stores.inject(MyMetaStore, storeInstance);
63928
63956
  return pivotCells[pivotCol][pivotRow];
63929
63957
  }
63930
63958
  try {
63959
+ const offsetRow = position.row - mainPosition.row;
63960
+ const offsetCol = position.col - mainPosition.col;
63961
+ args = args.map((arg) => (isMatrix(arg) ? arg[offsetCol][offsetRow] : arg));
63931
63962
  if (functionName === "PIVOT.HEADER" && args.at(-2) === "measure") {
63932
63963
  const domain = pivot.parseArgsToPivotDomain(args.slice(1, -2).map((value) => ({ value })));
63933
63964
  return {
@@ -76548,7 +76579,7 @@ stores.inject(MyMetaStore, storeInstance);
76548
76579
  handlers = [];
76549
76580
  uiHandlers = [];
76550
76581
  coreHandlers = [];
76551
- constructor(data = {}, config = {}, stateUpdateMessages = [], uuidGenerator = new UuidGenerator(), verboseImport = false) {
76582
+ constructor(data = {}, config = {}, stateUpdateMessages = [], uuidGenerator = new UuidGenerator(), verboseImport = true) {
76552
76583
  const start = performance.now();
76553
76584
  console.debug("##### Model creation #####");
76554
76585
  super();
@@ -77284,9 +77315,9 @@ stores.inject(MyMetaStore, storeInstance);
77284
77315
  exports.tokenize = tokenize;
77285
77316
 
77286
77317
 
77287
- __info__.version = "18.2.27";
77288
- __info__.date = "2025-08-26T10:07:14.516Z";
77289
- __info__.hash = "fbf9445";
77318
+ __info__.version = "18.2.28";
77319
+ __info__.date = "2025-09-05T07:38:26.582Z";
77320
+ __info__.hash = "84335fb";
77290
77321
 
77291
77322
 
77292
77323
  })(this.o_spreadsheet = this.o_spreadsheet || {}, owl);