@odoo/o-spreadsheet 18.2.26 → 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.26
6
- * @date 2025-08-21T06:39:39.007Z
7
- * @hash d23c340
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",
@@ -6821,15 +6822,22 @@ function parseOSClipboardContent(content) {
6821
6822
  };
6822
6823
  }
6823
6824
  const htmlDocument = new DOMParser().parseFromString(content[ClipboardMIMEType.Html], "text/html");
6824
- const oSheetClipboardData = htmlDocument
6825
- .querySelector("div")
6826
- ?.getAttribute("data-osheet-clipboard");
6827
- const spreadsheetContent = oSheetClipboardData && JSON.parse(oSheetClipboardData);
6828
6825
  return {
6829
6826
  text: content[ClipboardMIMEType.PlainText],
6830
- data: spreadsheetContent,
6827
+ data: getOSheetDataFromHTML(htmlDocument),
6831
6828
  };
6832
6829
  }
6830
+ function getOSheetDataFromHTML(htmlDocument) {
6831
+ const attributes = [...htmlDocument.documentElement.attributes];
6832
+ // Check if it's a Microsoft Office clipboard data (it will have some namespaces defined in the root element)
6833
+ if (attributes.some((attr) => attr.value.includes("microsoft"))) {
6834
+ return undefined;
6835
+ }
6836
+ const oSheetClipboardData = htmlDocument
6837
+ .querySelector("div")
6838
+ ?.getAttribute("data-osheet-clipboard");
6839
+ return oSheetClipboardData && JSON.parse(oSheetClipboardData);
6840
+ }
6833
6841
  /**
6834
6842
  * Applies each clipboard handler to paste its corresponding data into the target.
6835
6843
  */
@@ -7519,8 +7527,11 @@ function invertMatrix(M) {
7519
7527
  // (a) Swap 2 rows. This multiply the determinant by -1.
7520
7528
  // (b) Multiply a row by a scalar. This multiply the determinant by that scalar.
7521
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
+ }
7522
7533
  if (M.length !== M[0].length) {
7523
- 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");
7524
7535
  }
7525
7536
  let determinant = 1;
7526
7537
  const dim = M.length;
@@ -7589,8 +7600,11 @@ function swapMatrixRows(matrix, row1, row2) {
7589
7600
  * Note: we use indexing [col][row] instead of the standard mathematical notation [row][col]
7590
7601
  */
7591
7602
  function multiplyMatrices(matrix1, matrix2) {
7603
+ if (matrix1.length < 1 || matrix2.length < 1) {
7604
+ throw new Error("multiplyMatrices: empty matrices cannot be multiplied.");
7605
+ }
7592
7606
  if (matrix1.length !== matrix2[0].length) {
7593
- throw new EvaluationError(_t("Cannot multiply matrices : incompatible matrices size."));
7607
+ throw new Error("multiplyMatrices: incompatible matrices size.");
7594
7608
  }
7595
7609
  const rowsM1 = matrix1[0].length;
7596
7610
  const colsM2 = matrix2.length;
@@ -7616,7 +7630,7 @@ function toScalar(arg) {
7616
7630
  return arg;
7617
7631
  }
7618
7632
  if (!isSingleElementMatrix(arg)) {
7619
- 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");
7620
7634
  }
7621
7635
  return arg[0][0];
7622
7636
  }
@@ -7853,6 +7867,16 @@ function getMovingAverageValues(dataset, labels, windowSize = DEFAULT_WINDOW_SIZ
7853
7867
  }
7854
7868
  return values;
7855
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
+ }
7856
7880
 
7857
7881
  const PREVIOUS_VALUE = "(previous)";
7858
7882
  const NEXT_VALUE = "(next)";
@@ -11790,6 +11814,7 @@ const MMULT = {
11790
11814
  compute: function (matrix1, matrix2) {
11791
11815
  const _matrix1 = toNumberMatrix(matrix1, "matrix1");
11792
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."));
11793
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 \
11794
11819
  number of rows of the second matrix (%s).", _matrix1.length.toString(), _matrix2[0].length.toString()));
11795
11820
  return multiplyMatrices(_matrix1, _matrix2);
@@ -13585,6 +13610,7 @@ const FORECAST = {
13585
13610
  ],
13586
13611
  compute: function (x, dataY, dataX) {
13587
13612
  const { flatDataX, flatDataY } = filterAndFlatData(dataY, dataX);
13613
+ assertNonEmpty(flatDataX, flatDataY);
13588
13614
  return predictLinearValues([flatDataY], [flatDataX], matrixMap(toMatrix(x), (value) => toNumber(value, this.locale)), true);
13589
13615
  },
13590
13616
  isExported: true,
@@ -13601,6 +13627,7 @@ const GROWTH = {
13601
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.")),
13602
13628
  ],
13603
13629
  compute: function (knownDataY, knownDataX = [[]], newDataX = [[]], b = { value: true }) {
13630
+ assertNonEmptyMatrix(knownDataY, "known_data_y");
13604
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)));
13605
13632
  },
13606
13633
  };
@@ -13615,6 +13642,7 @@ const INTERCEPT = {
13615
13642
  ],
13616
13643
  compute: function (dataY, dataX) {
13617
13644
  const { flatDataX, flatDataY } = filterAndFlatData(dataY, dataX);
13645
+ assertNonEmpty(flatDataX, flatDataY);
13618
13646
  const [[], [intercept]] = fullLinearRegression([flatDataX], [flatDataY]);
13619
13647
  return intercept;
13620
13648
  },
@@ -13664,6 +13692,7 @@ const LINEST = {
13664
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")),
13665
13693
  ],
13666
13694
  compute: function (dataY, dataX = [[]], calculateB = { value: true }, verbose = { value: false }) {
13695
+ assertNonEmptyMatrix(dataY, "data_y");
13667
13696
  return fullLinearRegression(toNumberMatrix(dataX, "the first argument (data_y)"), toNumberMatrix(dataY, "the second argument (data_x)"), toBoolean(calculateB), toBoolean(verbose));
13668
13697
  },
13669
13698
  isExported: true,
@@ -13680,6 +13709,7 @@ const LOGEST = {
13680
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")),
13681
13710
  ],
13682
13711
  compute: function (dataY, dataX = [[]], calculateB = { value: true }, verbose = { value: false }) {
13712
+ assertNonEmptyMatrix(dataY, "data_y");
13683
13713
  const coeffs = fullLinearRegression(toNumberMatrix(dataX, "the second argument (data_x)"), logM(toNumberMatrix(dataY, "the first argument (data_y)")), toBoolean(calculateB), toBoolean(verbose));
13684
13714
  for (let i = 0; i < coeffs.length; i++) {
13685
13715
  coeffs[i][0] = Math.exp(coeffs[i][0]);
@@ -13701,9 +13731,7 @@ const MATTHEWS = {
13701
13731
  const flatX = dataX.flat();
13702
13732
  const flatY = dataY.flat();
13703
13733
  assertSameNumberOfElements(flatX, flatY);
13704
- if (flatX.length === 0) {
13705
- return new EvaluationError(_t("[[FUNCTION_NAME]] expects non-empty ranges for both parameters."));
13706
- }
13734
+ assertNonEmpty(flatX, flatY);
13707
13735
  const n = flatX.length;
13708
13736
  let trueN = 0, trueP = 0, falseP = 0, falseN = 0;
13709
13737
  for (let i = 0; i < n; ++i) {
@@ -13867,12 +13895,7 @@ const MINIFS = {
13867
13895
  // -----------------------------------------------------------------------------
13868
13896
  function pearson(dataY, dataX) {
13869
13897
  const { flatDataX, flatDataY } = filterAndFlatData(dataY, dataX);
13870
- if (flatDataX.length === 0) {
13871
- throw new EvaluationError(_t("[[FUNCTION_NAME]] expects non-empty ranges for both parameters."));
13872
- }
13873
- if (flatDataX.length < 2) {
13874
- throw new EvaluationError(_t("[[FUNCTION_NAME]] needs at least two values for both parameters."));
13875
- }
13898
+ assertNonEmpty(flatDataX, flatDataY);
13876
13899
  const n = flatDataX.length;
13877
13900
  let sumX = 0, sumY = 0, sumXY = 0, sumXX = 0, sumYY = 0;
13878
13901
  for (let i = 0; i < n; i++) {
@@ -13961,6 +13984,7 @@ const POLYFIT_COEFFS = {
13961
13984
  ],
13962
13985
  compute: function (dataY, dataX, order, intercept = { value: true }) {
13963
13986
  const { flatDataX, flatDataY } = filterAndFlatData(dataY, dataX);
13987
+ assertNonEmpty(flatDataX, flatDataY);
13964
13988
  return polynomialRegression(flatDataY, flatDataX, toNumber(order, this.locale), toBoolean(intercept));
13965
13989
  },
13966
13990
  isExported: false,
@@ -13980,6 +14004,7 @@ const POLYFIT_FORECAST = {
13980
14004
  compute: function (x, dataY, dataX, order, intercept = { value: true }) {
13981
14005
  const _order = toNumber(order, this.locale);
13982
14006
  const { flatDataX, flatDataY } = filterAndFlatData(dataY, dataX);
14007
+ assertNonEmpty(flatDataX, flatDataY);
13983
14008
  const coeffs = polynomialRegression(flatDataY, flatDataX, _order, toBoolean(intercept)).flat();
13984
14009
  return matrixMap(toMatrix(x), (xij) => evaluatePolynomial(coeffs, toNumber(xij, this.locale), _order));
13985
14010
  },
@@ -14096,6 +14121,7 @@ const SLOPE = {
14096
14121
  ],
14097
14122
  compute: function (dataY, dataX) {
14098
14123
  const { flatDataX, flatDataY } = filterAndFlatData(dataY, dataX);
14124
+ assertNonEmpty(flatDataX, flatDataY);
14099
14125
  const [[slope]] = fullLinearRegression([flatDataX], [flatDataY]);
14100
14126
  return slope;
14101
14127
  },
@@ -14144,6 +14170,7 @@ const SPEARMAN = {
14144
14170
  ],
14145
14171
  compute: function (dataX, dataY) {
14146
14172
  const { flatDataX, flatDataY } = filterAndFlatData(dataY, dataX);
14173
+ assertNonEmpty(flatDataX, flatDataY);
14147
14174
  const n = flatDataX.length;
14148
14175
  const order = flatDataX.map((e, i) => [e, flatDataY[i]]);
14149
14176
  order.sort((a, b) => a[0] - b[0]);
@@ -14254,6 +14281,7 @@ const STEYX = {
14254
14281
  ],
14255
14282
  compute: function (dataY, dataX) {
14256
14283
  const { flatDataX, flatDataY } = filterAndFlatData(dataY, dataX);
14284
+ assertNonEmpty(flatDataX, flatDataY);
14257
14285
  const data = fullLinearRegression([flatDataX], [flatDataY], true, true);
14258
14286
  return data[1][2];
14259
14287
  },
@@ -14271,6 +14299,7 @@ const TREND = {
14271
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.")),
14272
14300
  ],
14273
14301
  compute: function (knownDataY, knownDataX = [[]], newDataX = [[]], b = { value: true }) {
14302
+ assertNonEmptyMatrix(knownDataY, "known_data_y");
14274
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));
14275
14304
  },
14276
14305
  };
@@ -24477,9 +24506,9 @@ function convertConditionalFormats(xlsxCfs, dxfs, warningManager) {
24477
24506
  if (!rule.operator || !rule.formula || rule.formula.length === 0)
24478
24507
  continue;
24479
24508
  operator = convertCFCellIsOperator(rule.operator);
24480
- values.push(rule.formula[0]);
24509
+ values.push(prefixFormula(rule.formula[0]));
24481
24510
  if (rule.formula.length === 2) {
24482
- values.push(rule.formula[1]);
24511
+ values.push(prefixFormula(rule.formula[1]));
24483
24512
  }
24484
24513
  break;
24485
24514
  }
@@ -24637,6 +24666,11 @@ function convertIcons(xlsxIconSet, index) {
24637
24666
  ? ICON_SETS[iconSet].neutral
24638
24667
  : ICON_SETS[iconSet].good;
24639
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
+ }
24640
24674
  // ---------------------------------------------------------------------------
24641
24675
  // Warnings
24642
24676
  // ---------------------------------------------------------------------------
@@ -30894,10 +30928,6 @@ class ComboChart extends AbstractChart {
30894
30928
  };
30895
30929
  }
30896
30930
  getDefinitionForExcel() {
30897
- // Excel does not support aggregating labels
30898
- if (this.aggregated) {
30899
- return undefined;
30900
- }
30901
30931
  const dataSets = this.dataSets
30902
30932
  .map((ds) => toExcelDataset(this.getters, ds))
30903
30933
  .filter((ds) => ds.range !== "" && ds.range !== CellErrorType.InvalidReference);
@@ -32049,10 +32079,6 @@ class ScatterChart extends AbstractChart {
32049
32079
  return new ScatterChart(definition, this.sheetId, this.getters);
32050
32080
  }
32051
32081
  getDefinitionForExcel() {
32052
- // Excel does not support aggregating labels
32053
- if (this.aggregated) {
32054
- return undefined;
32055
- }
32056
32082
  const dataSets = this.dataSets
32057
32083
  .map((ds) => toExcelDataset(this.getters, ds))
32058
32084
  .filter((ds) => ds.range !== "");
@@ -36169,7 +36195,7 @@ const splitToColumns = {
36169
36195
  const reinsertDynamicPivotMenu = {
36170
36196
  id: "reinsert_dynamic_pivot",
36171
36197
  name: _t("Re-insert dynamic pivot"),
36172
- sequence: 1020,
36198
+ sequence: 60,
36173
36199
  icon: "o-spreadsheet-Icon.INSERT_PIVOT",
36174
36200
  children: [REINSERT_DYNAMIC_PIVOT_CHILDREN],
36175
36201
  isVisible: (env) => env.model.getters.getPivotIds().some((id) => env.model.getters.getPivot(id).isValid()),
@@ -36177,7 +36203,7 @@ const reinsertDynamicPivotMenu = {
36177
36203
  const reinsertStaticPivotMenu = {
36178
36204
  id: "reinsert_static_pivot",
36179
36205
  name: _t("Re-insert static pivot"),
36180
- sequence: 1020,
36206
+ sequence: 70,
36181
36207
  icon: "o-spreadsheet-Icon.INSERT_PIVOT",
36182
36208
  children: [REINSERT_STATIC_PIVOT_CHILDREN],
36183
36209
  isVisible: (env) => env.model.getters.getPivotIds().some((id) => env.model.getters.getPivot(id).isValid()),
@@ -37915,8 +37941,9 @@ topbarMenuRegistry
37915
37941
  sequence: 40,
37916
37942
  separator: true,
37917
37943
  })
37918
- .addChild("data_sources_data", ["data"], (env) => {
37944
+ .addChild("pivot_data_sources", ["data"], (env) => {
37919
37945
  const sequence = 50;
37946
+ const numberOfPivots = env.model.getters.getPivotIds().length;
37920
37947
  return env.model.getters.getPivotIds().map((pivotId, index) => {
37921
37948
  const highlightProvider = {
37922
37949
  get highlights() {
@@ -37926,7 +37953,7 @@ topbarMenuRegistry
37926
37953
  return {
37927
37954
  id: `item_pivot_${env.model.getters.getPivotFormulaId(pivotId)}`,
37928
37955
  name: env.model.getters.getPivotDisplayName(pivotId),
37929
- sequence: sequence + index,
37956
+ sequence: sequence + index / numberOfPivots,
37930
37957
  isReadonlyAllowed: true,
37931
37958
  execute: (env) => env.openSidePanel("PivotSidePanel", { pivotId }),
37932
37959
  onStartHover: (env) => env.getStore(HighlightStore).register(highlightProvider),
@@ -63898,7 +63925,7 @@ class PivotUIPlugin extends CoreViewPlugin {
63898
63925
  if (!result) {
63899
63926
  return EMPTY_PIVOT_CELL;
63900
63927
  }
63901
- const { functionName, args } = result;
63928
+ let { functionName, args } = result;
63902
63929
  const formulaId = args[0];
63903
63930
  if (!formulaId) {
63904
63931
  return EMPTY_PIVOT_CELL;
@@ -63928,6 +63955,9 @@ class PivotUIPlugin extends CoreViewPlugin {
63928
63955
  return pivotCells[pivotCol][pivotRow];
63929
63956
  }
63930
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));
63931
63961
  if (functionName === "PIVOT.HEADER" && args.at(-2) === "measure") {
63932
63962
  const domain = pivot.parseArgsToPivotDomain(args.slice(1, -2).map((value) => ({ value })));
63933
63963
  return {
@@ -76548,7 +76578,7 @@ class Model extends EventBus {
76548
76578
  handlers = [];
76549
76579
  uiHandlers = [];
76550
76580
  coreHandlers = [];
76551
- constructor(data = {}, config = {}, stateUpdateMessages = [], uuidGenerator = new UuidGenerator(), verboseImport = false) {
76581
+ constructor(data = {}, config = {}, stateUpdateMessages = [], uuidGenerator = new UuidGenerator(), verboseImport = true) {
76552
76582
  const start = performance.now();
76553
76583
  console.debug("##### Model creation #####");
76554
76584
  super();
@@ -77239,6 +77269,6 @@ const chartHelpers = { ...CHART_HELPERS, ...CHART_RUNTIME_HELPERS };
77239
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 };
77240
77270
 
77241
77271
 
77242
- __info__.version = "18.2.26";
77243
- __info__.date = "2025-08-21T06:39:39.007Z";
77244
- __info__.hash = "d23c340";
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.26
6
- * @date 2025-08-21T06:39:39.007Z
7
- * @hash d23c340
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",
@@ -6822,15 +6823,22 @@
6822
6823
  };
6823
6824
  }
6824
6825
  const htmlDocument = new DOMParser().parseFromString(content[ClipboardMIMEType.Html], "text/html");
6825
- const oSheetClipboardData = htmlDocument
6826
- .querySelector("div")
6827
- ?.getAttribute("data-osheet-clipboard");
6828
- const spreadsheetContent = oSheetClipboardData && JSON.parse(oSheetClipboardData);
6829
6826
  return {
6830
6827
  text: content[ClipboardMIMEType.PlainText],
6831
- data: spreadsheetContent,
6828
+ data: getOSheetDataFromHTML(htmlDocument),
6832
6829
  };
6833
6830
  }
6831
+ function getOSheetDataFromHTML(htmlDocument) {
6832
+ const attributes = [...htmlDocument.documentElement.attributes];
6833
+ // Check if it's a Microsoft Office clipboard data (it will have some namespaces defined in the root element)
6834
+ if (attributes.some((attr) => attr.value.includes("microsoft"))) {
6835
+ return undefined;
6836
+ }
6837
+ const oSheetClipboardData = htmlDocument
6838
+ .querySelector("div")
6839
+ ?.getAttribute("data-osheet-clipboard");
6840
+ return oSheetClipboardData && JSON.parse(oSheetClipboardData);
6841
+ }
6834
6842
  /**
6835
6843
  * Applies each clipboard handler to paste its corresponding data into the target.
6836
6844
  */
@@ -7520,8 +7528,11 @@
7520
7528
  // (a) Swap 2 rows. This multiply the determinant by -1.
7521
7529
  // (b) Multiply a row by a scalar. This multiply the determinant by that scalar.
7522
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
+ }
7523
7534
  if (M.length !== M[0].length) {
7524
- 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");
7525
7536
  }
7526
7537
  let determinant = 1;
7527
7538
  const dim = M.length;
@@ -7590,8 +7601,11 @@
7590
7601
  * Note: we use indexing [col][row] instead of the standard mathematical notation [row][col]
7591
7602
  */
7592
7603
  function multiplyMatrices(matrix1, matrix2) {
7604
+ if (matrix1.length < 1 || matrix2.length < 1) {
7605
+ throw new Error("multiplyMatrices: empty matrices cannot be multiplied.");
7606
+ }
7593
7607
  if (matrix1.length !== matrix2[0].length) {
7594
- throw new EvaluationError(_t("Cannot multiply matrices : incompatible matrices size."));
7608
+ throw new Error("multiplyMatrices: incompatible matrices size.");
7595
7609
  }
7596
7610
  const rowsM1 = matrix1[0].length;
7597
7611
  const colsM2 = matrix2.length;
@@ -7617,7 +7631,7 @@
7617
7631
  return arg;
7618
7632
  }
7619
7633
  if (!isSingleElementMatrix(arg)) {
7620
- 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");
7621
7635
  }
7622
7636
  return arg[0][0];
7623
7637
  }
@@ -7854,6 +7868,16 @@
7854
7868
  }
7855
7869
  return values;
7856
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
+ }
7857
7881
 
7858
7882
  const PREVIOUS_VALUE = "(previous)";
7859
7883
  const NEXT_VALUE = "(next)";
@@ -11791,6 +11815,7 @@ stores.inject(MyMetaStore, storeInstance);
11791
11815
  compute: function (matrix1, matrix2) {
11792
11816
  const _matrix1 = toNumberMatrix(matrix1, "matrix1");
11793
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."));
11794
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 \
11795
11820
  number of rows of the second matrix (%s).", _matrix1.length.toString(), _matrix2[0].length.toString()));
11796
11821
  return multiplyMatrices(_matrix1, _matrix2);
@@ -13586,6 +13611,7 @@ stores.inject(MyMetaStore, storeInstance);
13586
13611
  ],
13587
13612
  compute: function (x, dataY, dataX) {
13588
13613
  const { flatDataX, flatDataY } = filterAndFlatData(dataY, dataX);
13614
+ assertNonEmpty(flatDataX, flatDataY);
13589
13615
  return predictLinearValues([flatDataY], [flatDataX], matrixMap(toMatrix(x), (value) => toNumber(value, this.locale)), true);
13590
13616
  },
13591
13617
  isExported: true,
@@ -13602,6 +13628,7 @@ stores.inject(MyMetaStore, storeInstance);
13602
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.")),
13603
13629
  ],
13604
13630
  compute: function (knownDataY, knownDataX = [[]], newDataX = [[]], b = { value: true }) {
13631
+ assertNonEmptyMatrix(knownDataY, "known_data_y");
13605
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)));
13606
13633
  },
13607
13634
  };
@@ -13616,6 +13643,7 @@ stores.inject(MyMetaStore, storeInstance);
13616
13643
  ],
13617
13644
  compute: function (dataY, dataX) {
13618
13645
  const { flatDataX, flatDataY } = filterAndFlatData(dataY, dataX);
13646
+ assertNonEmpty(flatDataX, flatDataY);
13619
13647
  const [[], [intercept]] = fullLinearRegression([flatDataX], [flatDataY]);
13620
13648
  return intercept;
13621
13649
  },
@@ -13665,6 +13693,7 @@ stores.inject(MyMetaStore, storeInstance);
13665
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")),
13666
13694
  ],
13667
13695
  compute: function (dataY, dataX = [[]], calculateB = { value: true }, verbose = { value: false }) {
13696
+ assertNonEmptyMatrix(dataY, "data_y");
13668
13697
  return fullLinearRegression(toNumberMatrix(dataX, "the first argument (data_y)"), toNumberMatrix(dataY, "the second argument (data_x)"), toBoolean(calculateB), toBoolean(verbose));
13669
13698
  },
13670
13699
  isExported: true,
@@ -13681,6 +13710,7 @@ stores.inject(MyMetaStore, storeInstance);
13681
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")),
13682
13711
  ],
13683
13712
  compute: function (dataY, dataX = [[]], calculateB = { value: true }, verbose = { value: false }) {
13713
+ assertNonEmptyMatrix(dataY, "data_y");
13684
13714
  const coeffs = fullLinearRegression(toNumberMatrix(dataX, "the second argument (data_x)"), logM(toNumberMatrix(dataY, "the first argument (data_y)")), toBoolean(calculateB), toBoolean(verbose));
13685
13715
  for (let i = 0; i < coeffs.length; i++) {
13686
13716
  coeffs[i][0] = Math.exp(coeffs[i][0]);
@@ -13702,9 +13732,7 @@ stores.inject(MyMetaStore, storeInstance);
13702
13732
  const flatX = dataX.flat();
13703
13733
  const flatY = dataY.flat();
13704
13734
  assertSameNumberOfElements(flatX, flatY);
13705
- if (flatX.length === 0) {
13706
- return new EvaluationError(_t("[[FUNCTION_NAME]] expects non-empty ranges for both parameters."));
13707
- }
13735
+ assertNonEmpty(flatX, flatY);
13708
13736
  const n = flatX.length;
13709
13737
  let trueN = 0, trueP = 0, falseP = 0, falseN = 0;
13710
13738
  for (let i = 0; i < n; ++i) {
@@ -13868,12 +13896,7 @@ stores.inject(MyMetaStore, storeInstance);
13868
13896
  // -----------------------------------------------------------------------------
13869
13897
  function pearson(dataY, dataX) {
13870
13898
  const { flatDataX, flatDataY } = filterAndFlatData(dataY, dataX);
13871
- if (flatDataX.length === 0) {
13872
- throw new EvaluationError(_t("[[FUNCTION_NAME]] expects non-empty ranges for both parameters."));
13873
- }
13874
- if (flatDataX.length < 2) {
13875
- throw new EvaluationError(_t("[[FUNCTION_NAME]] needs at least two values for both parameters."));
13876
- }
13899
+ assertNonEmpty(flatDataX, flatDataY);
13877
13900
  const n = flatDataX.length;
13878
13901
  let sumX = 0, sumY = 0, sumXY = 0, sumXX = 0, sumYY = 0;
13879
13902
  for (let i = 0; i < n; i++) {
@@ -13962,6 +13985,7 @@ stores.inject(MyMetaStore, storeInstance);
13962
13985
  ],
13963
13986
  compute: function (dataY, dataX, order, intercept = { value: true }) {
13964
13987
  const { flatDataX, flatDataY } = filterAndFlatData(dataY, dataX);
13988
+ assertNonEmpty(flatDataX, flatDataY);
13965
13989
  return polynomialRegression(flatDataY, flatDataX, toNumber(order, this.locale), toBoolean(intercept));
13966
13990
  },
13967
13991
  isExported: false,
@@ -13981,6 +14005,7 @@ stores.inject(MyMetaStore, storeInstance);
13981
14005
  compute: function (x, dataY, dataX, order, intercept = { value: true }) {
13982
14006
  const _order = toNumber(order, this.locale);
13983
14007
  const { flatDataX, flatDataY } = filterAndFlatData(dataY, dataX);
14008
+ assertNonEmpty(flatDataX, flatDataY);
13984
14009
  const coeffs = polynomialRegression(flatDataY, flatDataX, _order, toBoolean(intercept)).flat();
13985
14010
  return matrixMap(toMatrix(x), (xij) => evaluatePolynomial(coeffs, toNumber(xij, this.locale), _order));
13986
14011
  },
@@ -14097,6 +14122,7 @@ stores.inject(MyMetaStore, storeInstance);
14097
14122
  ],
14098
14123
  compute: function (dataY, dataX) {
14099
14124
  const { flatDataX, flatDataY } = filterAndFlatData(dataY, dataX);
14125
+ assertNonEmpty(flatDataX, flatDataY);
14100
14126
  const [[slope]] = fullLinearRegression([flatDataX], [flatDataY]);
14101
14127
  return slope;
14102
14128
  },
@@ -14145,6 +14171,7 @@ stores.inject(MyMetaStore, storeInstance);
14145
14171
  ],
14146
14172
  compute: function (dataX, dataY) {
14147
14173
  const { flatDataX, flatDataY } = filterAndFlatData(dataY, dataX);
14174
+ assertNonEmpty(flatDataX, flatDataY);
14148
14175
  const n = flatDataX.length;
14149
14176
  const order = flatDataX.map((e, i) => [e, flatDataY[i]]);
14150
14177
  order.sort((a, b) => a[0] - b[0]);
@@ -14255,6 +14282,7 @@ stores.inject(MyMetaStore, storeInstance);
14255
14282
  ],
14256
14283
  compute: function (dataY, dataX) {
14257
14284
  const { flatDataX, flatDataY } = filterAndFlatData(dataY, dataX);
14285
+ assertNonEmpty(flatDataX, flatDataY);
14258
14286
  const data = fullLinearRegression([flatDataX], [flatDataY], true, true);
14259
14287
  return data[1][2];
14260
14288
  },
@@ -14272,6 +14300,7 @@ stores.inject(MyMetaStore, storeInstance);
14272
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.")),
14273
14301
  ],
14274
14302
  compute: function (knownDataY, knownDataX = [[]], newDataX = [[]], b = { value: true }) {
14303
+ assertNonEmptyMatrix(knownDataY, "known_data_y");
14275
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));
14276
14305
  },
14277
14306
  };
@@ -24478,9 +24507,9 @@ stores.inject(MyMetaStore, storeInstance);
24478
24507
  if (!rule.operator || !rule.formula || rule.formula.length === 0)
24479
24508
  continue;
24480
24509
  operator = convertCFCellIsOperator(rule.operator);
24481
- values.push(rule.formula[0]);
24510
+ values.push(prefixFormula(rule.formula[0]));
24482
24511
  if (rule.formula.length === 2) {
24483
- values.push(rule.formula[1]);
24512
+ values.push(prefixFormula(rule.formula[1]));
24484
24513
  }
24485
24514
  break;
24486
24515
  }
@@ -24638,6 +24667,11 @@ stores.inject(MyMetaStore, storeInstance);
24638
24667
  ? ICON_SETS[iconSet].neutral
24639
24668
  : ICON_SETS[iconSet].good;
24640
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
+ }
24641
24675
  // ---------------------------------------------------------------------------
24642
24676
  // Warnings
24643
24677
  // ---------------------------------------------------------------------------
@@ -30895,10 +30929,6 @@ stores.inject(MyMetaStore, storeInstance);
30895
30929
  };
30896
30930
  }
30897
30931
  getDefinitionForExcel() {
30898
- // Excel does not support aggregating labels
30899
- if (this.aggregated) {
30900
- return undefined;
30901
- }
30902
30932
  const dataSets = this.dataSets
30903
30933
  .map((ds) => toExcelDataset(this.getters, ds))
30904
30934
  .filter((ds) => ds.range !== "" && ds.range !== CellErrorType.InvalidReference);
@@ -32050,10 +32080,6 @@ stores.inject(MyMetaStore, storeInstance);
32050
32080
  return new ScatterChart(definition, this.sheetId, this.getters);
32051
32081
  }
32052
32082
  getDefinitionForExcel() {
32053
- // Excel does not support aggregating labels
32054
- if (this.aggregated) {
32055
- return undefined;
32056
- }
32057
32083
  const dataSets = this.dataSets
32058
32084
  .map((ds) => toExcelDataset(this.getters, ds))
32059
32085
  .filter((ds) => ds.range !== "");
@@ -36170,7 +36196,7 @@ stores.inject(MyMetaStore, storeInstance);
36170
36196
  const reinsertDynamicPivotMenu = {
36171
36197
  id: "reinsert_dynamic_pivot",
36172
36198
  name: _t("Re-insert dynamic pivot"),
36173
- sequence: 1020,
36199
+ sequence: 60,
36174
36200
  icon: "o-spreadsheet-Icon.INSERT_PIVOT",
36175
36201
  children: [REINSERT_DYNAMIC_PIVOT_CHILDREN],
36176
36202
  isVisible: (env) => env.model.getters.getPivotIds().some((id) => env.model.getters.getPivot(id).isValid()),
@@ -36178,7 +36204,7 @@ stores.inject(MyMetaStore, storeInstance);
36178
36204
  const reinsertStaticPivotMenu = {
36179
36205
  id: "reinsert_static_pivot",
36180
36206
  name: _t("Re-insert static pivot"),
36181
- sequence: 1020,
36207
+ sequence: 70,
36182
36208
  icon: "o-spreadsheet-Icon.INSERT_PIVOT",
36183
36209
  children: [REINSERT_STATIC_PIVOT_CHILDREN],
36184
36210
  isVisible: (env) => env.model.getters.getPivotIds().some((id) => env.model.getters.getPivot(id).isValid()),
@@ -37916,8 +37942,9 @@ stores.inject(MyMetaStore, storeInstance);
37916
37942
  sequence: 40,
37917
37943
  separator: true,
37918
37944
  })
37919
- .addChild("data_sources_data", ["data"], (env) => {
37945
+ .addChild("pivot_data_sources", ["data"], (env) => {
37920
37946
  const sequence = 50;
37947
+ const numberOfPivots = env.model.getters.getPivotIds().length;
37921
37948
  return env.model.getters.getPivotIds().map((pivotId, index) => {
37922
37949
  const highlightProvider = {
37923
37950
  get highlights() {
@@ -37927,7 +37954,7 @@ stores.inject(MyMetaStore, storeInstance);
37927
37954
  return {
37928
37955
  id: `item_pivot_${env.model.getters.getPivotFormulaId(pivotId)}`,
37929
37956
  name: env.model.getters.getPivotDisplayName(pivotId),
37930
- sequence: sequence + index,
37957
+ sequence: sequence + index / numberOfPivots,
37931
37958
  isReadonlyAllowed: true,
37932
37959
  execute: (env) => env.openSidePanel("PivotSidePanel", { pivotId }),
37933
37960
  onStartHover: (env) => env.getStore(HighlightStore).register(highlightProvider),
@@ -63899,7 +63926,7 @@ stores.inject(MyMetaStore, storeInstance);
63899
63926
  if (!result) {
63900
63927
  return EMPTY_PIVOT_CELL;
63901
63928
  }
63902
- const { functionName, args } = result;
63929
+ let { functionName, args } = result;
63903
63930
  const formulaId = args[0];
63904
63931
  if (!formulaId) {
63905
63932
  return EMPTY_PIVOT_CELL;
@@ -63929,6 +63956,9 @@ stores.inject(MyMetaStore, storeInstance);
63929
63956
  return pivotCells[pivotCol][pivotRow];
63930
63957
  }
63931
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));
63932
63962
  if (functionName === "PIVOT.HEADER" && args.at(-2) === "measure") {
63933
63963
  const domain = pivot.parseArgsToPivotDomain(args.slice(1, -2).map((value) => ({ value })));
63934
63964
  return {
@@ -76549,7 +76579,7 @@ stores.inject(MyMetaStore, storeInstance);
76549
76579
  handlers = [];
76550
76580
  uiHandlers = [];
76551
76581
  coreHandlers = [];
76552
- constructor(data = {}, config = {}, stateUpdateMessages = [], uuidGenerator = new UuidGenerator(), verboseImport = false) {
76582
+ constructor(data = {}, config = {}, stateUpdateMessages = [], uuidGenerator = new UuidGenerator(), verboseImport = true) {
76553
76583
  const start = performance.now();
76554
76584
  console.debug("##### Model creation #####");
76555
76585
  super();
@@ -77285,9 +77315,9 @@ stores.inject(MyMetaStore, storeInstance);
77285
77315
  exports.tokenize = tokenize;
77286
77316
 
77287
77317
 
77288
- __info__.version = "18.2.26";
77289
- __info__.date = "2025-08-21T06:39:39.007Z";
77290
- __info__.hash = "d23c340";
77318
+ __info__.version = "18.2.28";
77319
+ __info__.date = "2025-09-05T07:38:26.582Z";
77320
+ __info__.hash = "84335fb";
77291
77321
 
77292
77322
 
77293
77323
  })(this.o_spreadsheet = this.o_spreadsheet || {}, owl);