@odoo/o-spreadsheet 17.4.0-alpha.11 → 17.4.0-alpha.13

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 17.4.0-alpha.11
6
- * @date 2024-07-02T10:38:50.116Z
7
- * @hash 9fe9477
5
+ * @version 17.4.0-alpha.13
6
+ * @date 2024-07-11T06:36:58.556Z
7
+ * @hash e0f506b
8
8
  */
9
9
 
10
10
  (function (exports, owl) {
@@ -591,7 +591,7 @@
591
591
  /**
592
592
  * Compares two objects.
593
593
  */
594
- function deepEquals(o1, o2, ignoreFunctions) {
594
+ function deepEquals(o1, o2) {
595
595
  if (o1 === o2)
596
596
  return true;
597
597
  if ((o1 && !o2) || (o2 && !o1))
@@ -607,17 +607,13 @@
607
607
  }
608
608
  }
609
609
  for (const key in o1) {
610
- const typeOfO1Key = typeof o1[key];
611
- if (typeOfO1Key !== typeof o2[key])
610
+ if (typeof o1[key] !== typeof o2[key])
612
611
  return false;
613
- if (typeOfO1Key === "object") {
614
- if (!deepEquals(o1[key], o2[key], ignoreFunctions))
612
+ if (typeof o1[key] === "object") {
613
+ if (!deepEquals(o1[key], o2[key]))
615
614
  return false;
616
615
  }
617
616
  else {
618
- if (ignoreFunctions && typeOfO1Key === "function") {
619
- continue;
620
- }
621
617
  if (o1[key] !== o2[key])
622
618
  return false;
623
619
  }
@@ -1804,7 +1800,7 @@
1804
1800
  });
1805
1801
  const getNumberRegex = memoize(function getNumberRegex(locale) {
1806
1802
  const decimalSeparator = escapeRegExp(locale.decimalSeparator);
1807
- const thousandsSeparator = escapeRegExp(locale.thousandsSeparator);
1803
+ const thousandsSeparator = escapeRegExp(locale.thousandsSeparator || "");
1808
1804
  const pIntegerAndDecimals = `(?:\\d+(?:${thousandsSeparator}\\d{3,})*(?:${decimalSeparator}\\d*)?)`; // pattern that match integer number with or without decimal digits
1809
1805
  const pOnlyDecimals = `(?:${decimalSeparator}\\d+)`; // pattern that match only expression with decimal digits
1810
1806
  const pScientificFormat = "(?:e(?:\\+|-)?\\d+)?"; // pattern that match scientific format between zero and one time (should be placed before pPercentFormat)
@@ -1837,7 +1833,7 @@
1837
1833
  return getNumberRegex(locale).test(value.trim());
1838
1834
  }
1839
1835
  const getInvaluableSymbolsRegexp = memoize(function getInvaluableSymbolsRegexp(locale) {
1840
- return new RegExp(`[\$€${escapeRegExp(locale.thousandsSeparator)}]`, "g");
1836
+ return new RegExp(`[\$€${escapeRegExp(locale.thousandsSeparator || "")}]`, "g");
1841
1837
  });
1842
1838
  /**
1843
1839
  * Convert a string into a number. It assumes that the string actually represents
@@ -2640,11 +2636,11 @@
2640
2636
  }
2641
2637
  return returned;
2642
2638
  }
2643
- function matrixMap(matrix, fn) {
2639
+ function matrixMap(matrix, callback) {
2644
2640
  if (matrix.length === 0) {
2645
2641
  return [];
2646
2642
  }
2647
- return generateMatrix(matrix.length, matrix[0].length, (col, row) => fn(matrix[col][row]));
2643
+ return generateMatrix(matrix.length, matrix[0].length, (col, row) => callback(matrix[col][row]));
2648
2644
  }
2649
2645
  function matrixForEach(matrix, fn) {
2650
2646
  const numberOfCols = matrix.length;
@@ -2744,6 +2740,9 @@
2744
2740
  * If the character is a special regular expression character, it is escaped with "\\".
2745
2741
  */
2746
2742
  const wildcardToRegExp = memoize(function wildcardToRegExp(operand) {
2743
+ if (operand === "*") {
2744
+ return /.+/;
2745
+ }
2747
2746
  let exp = "";
2748
2747
  let predecessor = "";
2749
2748
  for (let char of operand) {
@@ -2767,9 +2766,9 @@
2767
2766
  }
2768
2767
  return new RegExp("^" + exp + "$", "i");
2769
2768
  });
2770
- function evaluatePredicate(value, criterion) {
2769
+ function evaluatePredicate(value = "", criterion) {
2771
2770
  const { operator, operand } = criterion;
2772
- if (value === undefined || operand === undefined || value === null || operand === null) {
2771
+ if (operand === undefined || value === null || operand === null) {
2773
2772
  return false;
2774
2773
  }
2775
2774
  if (typeof operand === "number" && operator === "=") {
@@ -5861,19 +5860,22 @@
5861
5860
  }
5862
5861
 
5863
5862
  function isValidLocale(locale) {
5864
- if (!(locale &&
5865
- typeof locale === "object" &&
5866
- typeof locale.name === "string" &&
5867
- typeof locale.code === "string" &&
5868
- typeof locale.thousandsSeparator === "string" &&
5869
- typeof locale.decimalSeparator === "string" &&
5870
- typeof locale.dateFormat === "string" &&
5871
- typeof locale.timeFormat === "string" &&
5872
- typeof locale.formulaArgSeparator === "string")) {
5863
+ if (!locale ||
5864
+ typeof locale !== "object" ||
5865
+ !(!locale.thousandsSeparator || typeof locale.thousandsSeparator === "string")) {
5873
5866
  return false;
5874
5867
  }
5875
- if (!Object.values(locale).every((v) => v)) {
5876
- return false;
5868
+ for (const property of [
5869
+ "code",
5870
+ "name",
5871
+ "decimalSeparator",
5872
+ "dateFormat",
5873
+ "timeFormat",
5874
+ "formulaArgSeparator",
5875
+ ]) {
5876
+ if (!locale[property] || typeof locale[property] !== "string") {
5877
+ return false;
5878
+ }
5877
5879
  }
5878
5880
  if (locale.formulaArgSeparator === locale.decimalSeparator) {
5879
5881
  return false;
@@ -5971,7 +5973,10 @@
5971
5973
  if (locale.decimalSeparator === "." || !isNumber(content, locale)) {
5972
5974
  return content;
5973
5975
  }
5974
- return content.replace(locale.thousandsSeparator, "").replace(locale.decimalSeparator, ".");
5976
+ if (locale.thousandsSeparator) {
5977
+ content = content.replaceAll(locale.thousandsSeparator, "");
5978
+ }
5979
+ return content.replace(locale.decimalSeparator, ".");
5975
5980
  }
5976
5981
  /**
5977
5982
  * Change a content string from the given locale to its canonical form (en_US locale). Also convert date string.
@@ -6323,7 +6328,7 @@
6323
6328
  },
6324
6329
  toValueAndFormat(normalizedValue) {
6325
6330
  return {
6326
- value: toNumber(normalizedValue, DEFAULT_LOCALE),
6331
+ value: _t("Q%(quarter_number)s", { quarter_number: normalizedValue }),
6327
6332
  format: "0",
6328
6333
  };
6329
6334
  },
@@ -6453,9 +6458,9 @@
6453
6458
  }
6454
6459
  const ALL_PERIODS = {
6455
6460
  year: _t("Year"),
6456
- quarter: _t("Quarter"),
6457
- month: _t("Month"),
6458
- week: _t("Week"),
6461
+ quarter: _t("Quarter & Year"),
6462
+ month: _t("Month & Year"),
6463
+ week: _t("Week & Year"),
6459
6464
  day: _t("Day"),
6460
6465
  quarter_number: _t("Quarter"),
6461
6466
  month_number: _t("Month"),
@@ -6607,7 +6612,7 @@
6607
6612
  const pivotId = this.getters.getPivotIdFromPosition(position);
6608
6613
  const spreader = this.getters.getArrayFormulaSpreadingOn(position);
6609
6614
  if (pivotId) {
6610
- if (!deepEquals(spreader, position) || !isCopyingOneCell) {
6615
+ if (spreader && (!deepEquals(spreader, position) || !isCopyingOneCell)) {
6611
6616
  const pivotCell = this.getters.getPivotCellFromPosition(position);
6612
6617
  const formulaPivotId = this.getters.getPivotFormulaId(pivotId);
6613
6618
  const pivotFormula = createPivotFormula(formulaPivotId, pivotCell);
@@ -7412,6 +7417,146 @@
7412
7417
  return undefined;
7413
7418
  }
7414
7419
 
7420
+ var State;
7421
+ (function (State) {
7422
+ /**
7423
+ * Initial state.
7424
+ * Expecting any reference for the left part of a range
7425
+ * e.g. "A1", "1", "A", "Sheet1!A1", "Sheet1!A"
7426
+ */
7427
+ State[State["LeftRef"] = 0] = "LeftRef";
7428
+ /**
7429
+ * Expecting any reference for the right part of a range
7430
+ * e.g. "A1", "1", "A", "Sheet1!A1", "Sheet1!A"
7431
+ */
7432
+ State[State["RightRef"] = 1] = "RightRef";
7433
+ /**
7434
+ * Expecting the separator without any constraint on the right part
7435
+ */
7436
+ State[State["Separator"] = 2] = "Separator";
7437
+ /**
7438
+ * Expecting the separator for a full column range
7439
+ */
7440
+ State[State["FullColumnSeparator"] = 3] = "FullColumnSeparator";
7441
+ /**
7442
+ * Expecting the separator for a full row range
7443
+ */
7444
+ State[State["FullRowSeparator"] = 4] = "FullRowSeparator";
7445
+ /**
7446
+ * Expecting the right part of a full column range
7447
+ * e.g. "1", "A1"
7448
+ */
7449
+ State[State["RightColumnRef"] = 5] = "RightColumnRef";
7450
+ /**
7451
+ * Expecting the right part of a full row range
7452
+ * e.g. "A", "A1"
7453
+ */
7454
+ State[State["RightRowRef"] = 6] = "RightRowRef";
7455
+ /**
7456
+ * Final state. A range has been matched
7457
+ */
7458
+ State[State["Found"] = 7] = "Found";
7459
+ })(State || (State = {}));
7460
+ const goTo = (state, guard = () => true) => [
7461
+ {
7462
+ goTo: state,
7463
+ guard,
7464
+ },
7465
+ ];
7466
+ const goToMulti = (state, guard = () => true) => ({
7467
+ goTo: state,
7468
+ guard,
7469
+ });
7470
+ const machine = {
7471
+ [State.LeftRef]: {
7472
+ REFERENCE: goTo(State.Separator),
7473
+ NUMBER: goTo(State.FullRowSeparator),
7474
+ SYMBOL: [
7475
+ goToMulti(State.FullColumnSeparator, (token) => isColReference(token.value)),
7476
+ goToMulti(State.FullRowSeparator, (token) => isRowReference(token.value)),
7477
+ ],
7478
+ },
7479
+ [State.FullColumnSeparator]: {
7480
+ SPACE: goTo(State.FullColumnSeparator),
7481
+ OPERATOR: goTo(State.RightColumnRef, (token) => token.value === ":"),
7482
+ },
7483
+ [State.FullRowSeparator]: {
7484
+ SPACE: goTo(State.FullRowSeparator),
7485
+ OPERATOR: goTo(State.RightRowRef, (token) => token.value === ":"),
7486
+ },
7487
+ [State.Separator]: {
7488
+ SPACE: goTo(State.Separator),
7489
+ OPERATOR: goTo(State.RightRef, (token) => token.value === ":"),
7490
+ },
7491
+ [State.RightRef]: {
7492
+ SPACE: goTo(State.RightRef),
7493
+ NUMBER: goTo(State.Found),
7494
+ REFERENCE: goTo(State.Found, (token) => isSingleCellReference(token.value)),
7495
+ SYMBOL: goTo(State.Found, (token) => isColHeader(token.value) || isRowHeader(token.value)),
7496
+ },
7497
+ [State.RightColumnRef]: {
7498
+ SPACE: goTo(State.RightColumnRef),
7499
+ SYMBOL: goTo(State.Found, (token) => isColHeader(token.value)),
7500
+ REFERENCE: goTo(State.Found, (token) => isSingleCellReference(token.value)),
7501
+ },
7502
+ [State.RightRowRef]: {
7503
+ SPACE: goTo(State.RightRowRef),
7504
+ NUMBER: goTo(State.Found),
7505
+ REFERENCE: goTo(State.Found, (token) => isSingleCellReference(token.value)),
7506
+ SYMBOL: goTo(State.Found, (token) => isRowHeader(token.value)),
7507
+ },
7508
+ [State.Found]: {},
7509
+ };
7510
+ /**
7511
+ * Check if the list of tokens starts with a sequence of tokens representing
7512
+ * a range.
7513
+ * If a range is found, the sequence is removed from the list and is returned
7514
+ * as a single token.
7515
+ */
7516
+ function matchReference(tokens) {
7517
+ let head = 0;
7518
+ let transitions = machine[State.LeftRef];
7519
+ let matchedTokens = "";
7520
+ while (transitions !== undefined) {
7521
+ const token = tokens[head++];
7522
+ if (!token) {
7523
+ return null;
7524
+ }
7525
+ const transition = transitions[token.type]?.find((transition) => transition.guard(token));
7526
+ const nextState = transition ? transition.goTo : undefined;
7527
+ switch (nextState) {
7528
+ case undefined:
7529
+ return null;
7530
+ case State.Found:
7531
+ matchedTokens += token.value;
7532
+ tokens.splice(0, head);
7533
+ return {
7534
+ type: "REFERENCE",
7535
+ value: matchedTokens,
7536
+ };
7537
+ default:
7538
+ transitions = machine[nextState];
7539
+ matchedTokens += token.value;
7540
+ break;
7541
+ }
7542
+ }
7543
+ return null;
7544
+ }
7545
+ /**
7546
+ * Take the result of the tokenizer and transform it to be usable in the
7547
+ * manipulations of range
7548
+ *
7549
+ * @param formula
7550
+ */
7551
+ function rangeTokenize(formula, locale = DEFAULT_LOCALE) {
7552
+ const tokens = tokenize(formula, locale);
7553
+ const result = [];
7554
+ while (tokens.length) {
7555
+ result.push(matchReference(tokens) || tokens.shift());
7556
+ }
7557
+ return result;
7558
+ }
7559
+
7415
7560
  const functionRegex = /[a-zA-Z0-9\_]+(\.[a-zA-Z0-9\_]+)*/;
7416
7561
  const UNARY_OPERATORS_PREFIX = ["-", "+"];
7417
7562
  const UNARY_OPERATORS_POSTFIX = ["%"];
@@ -7560,7 +7705,7 @@
7560
7705
  * Parse an expression (as a string) into an AST.
7561
7706
  */
7562
7707
  function parse(str) {
7563
- return parseTokens(tokenize(str));
7708
+ return parseTokens(rangeTokenize(str));
7564
7709
  }
7565
7710
  function parseTokens(tokens) {
7566
7711
  tokens = tokens.filter((x) => x.type !== "SPACE");
@@ -7696,146 +7841,6 @@
7696
7841
  return needParenthesis ? `(${astToFormula(rightOperation)})` : astToFormula(rightOperation);
7697
7842
  }
7698
7843
 
7699
- var State;
7700
- (function (State) {
7701
- /**
7702
- * Initial state.
7703
- * Expecting any reference for the left part of a range
7704
- * e.g. "A1", "1", "A", "Sheet1!A1", "Sheet1!A"
7705
- */
7706
- State[State["LeftRef"] = 0] = "LeftRef";
7707
- /**
7708
- * Expecting any reference for the right part of a range
7709
- * e.g. "A1", "1", "A", "Sheet1!A1", "Sheet1!A"
7710
- */
7711
- State[State["RightRef"] = 1] = "RightRef";
7712
- /**
7713
- * Expecting the separator without any constraint on the right part
7714
- */
7715
- State[State["Separator"] = 2] = "Separator";
7716
- /**
7717
- * Expecting the separator for a full column range
7718
- */
7719
- State[State["FullColumnSeparator"] = 3] = "FullColumnSeparator";
7720
- /**
7721
- * Expecting the separator for a full row range
7722
- */
7723
- State[State["FullRowSeparator"] = 4] = "FullRowSeparator";
7724
- /**
7725
- * Expecting the right part of a full column range
7726
- * e.g. "1", "A1"
7727
- */
7728
- State[State["RightColumnRef"] = 5] = "RightColumnRef";
7729
- /**
7730
- * Expecting the right part of a full row range
7731
- * e.g. "A", "A1"
7732
- */
7733
- State[State["RightRowRef"] = 6] = "RightRowRef";
7734
- /**
7735
- * Final state. A range has been matched
7736
- */
7737
- State[State["Found"] = 7] = "Found";
7738
- })(State || (State = {}));
7739
- const goTo = (state, guard = () => true) => [
7740
- {
7741
- goTo: state,
7742
- guard,
7743
- },
7744
- ];
7745
- const goToMulti = (state, guard = () => true) => ({
7746
- goTo: state,
7747
- guard,
7748
- });
7749
- const machine = {
7750
- [State.LeftRef]: {
7751
- REFERENCE: goTo(State.Separator),
7752
- NUMBER: goTo(State.FullRowSeparator),
7753
- SYMBOL: [
7754
- goToMulti(State.FullColumnSeparator, (token) => isColReference(token.value)),
7755
- goToMulti(State.FullRowSeparator, (token) => isRowReference(token.value)),
7756
- ],
7757
- },
7758
- [State.FullColumnSeparator]: {
7759
- SPACE: goTo(State.FullColumnSeparator),
7760
- OPERATOR: goTo(State.RightColumnRef, (token) => token.value === ":"),
7761
- },
7762
- [State.FullRowSeparator]: {
7763
- SPACE: goTo(State.FullRowSeparator),
7764
- OPERATOR: goTo(State.RightRowRef, (token) => token.value === ":"),
7765
- },
7766
- [State.Separator]: {
7767
- SPACE: goTo(State.Separator),
7768
- OPERATOR: goTo(State.RightRef, (token) => token.value === ":"),
7769
- },
7770
- [State.RightRef]: {
7771
- SPACE: goTo(State.RightRef),
7772
- NUMBER: goTo(State.Found),
7773
- REFERENCE: goTo(State.Found, (token) => isSingleCellReference(token.value)),
7774
- SYMBOL: goTo(State.Found, (token) => isColHeader(token.value) || isRowHeader(token.value)),
7775
- },
7776
- [State.RightColumnRef]: {
7777
- SPACE: goTo(State.RightColumnRef),
7778
- SYMBOL: goTo(State.Found, (token) => isColHeader(token.value)),
7779
- REFERENCE: goTo(State.Found, (token) => isSingleCellReference(token.value)),
7780
- },
7781
- [State.RightRowRef]: {
7782
- SPACE: goTo(State.RightRowRef),
7783
- NUMBER: goTo(State.Found),
7784
- REFERENCE: goTo(State.Found, (token) => isSingleCellReference(token.value)),
7785
- SYMBOL: goTo(State.Found, (token) => isRowHeader(token.value)),
7786
- },
7787
- [State.Found]: {},
7788
- };
7789
- /**
7790
- * Check if the list of tokens starts with a sequence of tokens representing
7791
- * a range.
7792
- * If a range is found, the sequence is removed from the list and is returned
7793
- * as a single token.
7794
- */
7795
- function matchReference(tokens) {
7796
- let head = 0;
7797
- let transitions = machine[State.LeftRef];
7798
- let matchedTokens = "";
7799
- while (transitions !== undefined) {
7800
- const token = tokens[head++];
7801
- if (!token) {
7802
- return null;
7803
- }
7804
- const transition = transitions[token.type]?.find((transition) => transition.guard(token));
7805
- const nextState = transition ? transition.goTo : undefined;
7806
- switch (nextState) {
7807
- case undefined:
7808
- return null;
7809
- case State.Found:
7810
- matchedTokens += token.value;
7811
- tokens.splice(0, head);
7812
- return {
7813
- type: "REFERENCE",
7814
- value: matchedTokens,
7815
- };
7816
- default:
7817
- transitions = machine[nextState];
7818
- matchedTokens += token.value;
7819
- break;
7820
- }
7821
- }
7822
- return null;
7823
- }
7824
- /**
7825
- * Take the result of the tokenizer and transform it to be usable in the
7826
- * manipulations of range
7827
- *
7828
- * @param formula
7829
- */
7830
- function rangeTokenize(formula, locale = DEFAULT_LOCALE) {
7831
- const tokens = tokenize(formula, locale);
7832
- const result = [];
7833
- while (tokens.length) {
7834
- result.push(matchReference(tokens) || tokens.shift());
7835
- }
7836
- return result;
7837
- }
7838
-
7839
7844
  /**
7840
7845
  * Add the following information on tokens:
7841
7846
  * - length
@@ -8066,8 +8071,8 @@
8066
8071
 
8067
8072
  function evaluateLiteral(literalCell, localeFormat) {
8068
8073
  const value = localeFormat.format === PLAIN_TEXT_FORMAT ? literalCell.content : literalCell.parsedValue;
8069
- const fPayload = { value, format: localeFormat.format };
8070
- return createEvaluatedCell(fPayload, localeFormat.locale);
8074
+ const functionResult = { value, format: localeFormat.format };
8075
+ return createEvaluatedCell(functionResult, localeFormat.locale);
8071
8076
  }
8072
8077
  function parseLiteral(content, locale) {
8073
8078
  if (content.startsWith("=")) {
@@ -8088,13 +8093,13 @@
8088
8093
  }
8089
8094
  return content;
8090
8095
  }
8091
- function createEvaluatedCell(fPayload, locale = DEFAULT_LOCALE, cell) {
8092
- const link = detectLink(fPayload.value);
8096
+ function createEvaluatedCell(functionResult, locale = DEFAULT_LOCALE, cell) {
8097
+ const link = detectLink(functionResult.value);
8093
8098
  if (!link) {
8094
- return _createEvaluatedCell(fPayload, locale, cell);
8099
+ return _createEvaluatedCell(functionResult, locale, cell);
8095
8100
  }
8096
8101
  const value = parseLiteral(link.label, locale);
8097
- const format = fPayload.format ||
8102
+ const format = functionResult.format ||
8098
8103
  (typeof value === "number"
8099
8104
  ? detectDateFormat(link.label, locale) || detectNumberFormat(link.label)
8100
8105
  : undefined);
@@ -8107,8 +8112,8 @@
8107
8112
  link,
8108
8113
  };
8109
8114
  }
8110
- function _createEvaluatedCell(fPayload, locale, cell) {
8111
- let { value, format, message } = fPayload;
8115
+ function _createEvaluatedCell(functionResult, locale, cell) {
8116
+ let { value, format, message } = functionResult;
8112
8117
  format = cell?.format || format;
8113
8118
  const formattedValue = formatValue(value, { format, locale });
8114
8119
  if (isEvaluationError(value)) {
@@ -10295,7 +10300,6 @@ stores.inject(MyMetaStore, storeInstance);
10295
10300
  return bars.find((bar, i) => i > startIndex && bar.height !== 0);
10296
10301
  }
10297
10302
 
10298
- // @ts-ignore
10299
10303
  window.Chart?.register(waterfallLinesPlugin);
10300
10304
  class ChartJsComponent extends owl.Component {
10301
10305
  static template = "o-spreadsheet-ChartJsComponent";
@@ -10328,7 +10332,7 @@ stores.inject(MyMetaStore, storeInstance);
10328
10332
  owl.onWillUnmount(() => this.chart?.destroy());
10329
10333
  owl.useEffect(() => {
10330
10334
  const runtime = this.chartRuntime;
10331
- if (!deepEquals(runtime, this.currentRuntime, "ignoreFunctions")) {
10335
+ if (runtime !== this.currentRuntime) {
10332
10336
  if (runtime.chartJsConfig.type !== this.currentRuntime.chartJsConfig.type) {
10333
10337
  this.chart?.destroy();
10334
10338
  this.createChart(deepCopy(runtime.chartJsConfig));
@@ -10343,7 +10347,6 @@ stores.inject(MyMetaStore, storeInstance);
10343
10347
  createChart(chartData) {
10344
10348
  const canvas = this.canvas.el;
10345
10349
  const ctx = canvas.getContext("2d");
10346
- // @ts-ignore
10347
10350
  this.chart = new window.Chart(ctx, chartData);
10348
10351
  }
10349
10352
  updateChartJs(chartRuntime) {
@@ -19095,7 +19098,7 @@ stores.inject(MyMetaStore, storeInstance);
19095
19098
  arg("include_column_titles (boolean, default=TRUE)", _t("Whether to include the column titles or not.")),
19096
19099
  arg("column_count (number, optional)", _t("number of columns")),
19097
19100
  ],
19098
- compute: function (pivotFormulaId, rowCount = { value: Number.MAX_VALUE }, includeTotal = { value: true }, includeColumnHeaders = { value: true }, columnCount = { value: Number.MAX_VALUE }) {
19101
+ compute: function (pivotFormulaId, rowCount = { value: 10000 }, includeTotal = { value: true }, includeColumnHeaders = { value: true }, columnCount = { value: Number.MAX_VALUE }) {
19099
19102
  const _pivotFormulaId = toString(pivotFormulaId);
19100
19103
  const _rowCount = toNumber(rowCount, this.locale);
19101
19104
  if (_rowCount < 0) {
@@ -19519,6 +19522,320 @@ stores.inject(MyMetaStore, storeInstance);
19519
19522
  UPLUS: UPLUS
19520
19523
  });
19521
19524
 
19525
+ const transformFromFactor = (factor) => ({
19526
+ transform: (x) => x * factor,
19527
+ inverseTransform: (x) => x / factor,
19528
+ });
19529
+ const standard = { transform: (x) => x, inverseTransform: (x) => x };
19530
+ const ANG2M = 1e-10;
19531
+ const IN2M = 0.0254;
19532
+ const PICAPT2M = IN2M / 72;
19533
+ const FT2M = 0.3048;
19534
+ const YD2M = 0.9144;
19535
+ const MI2M = 1609.34;
19536
+ const NMI2M = 1852;
19537
+ const LY2M = 9.46073047258e15;
19538
+ const UNITS = {
19539
+ // WEIGHT UNITs : Standard = gramme
19540
+ g: { ...standard, category: "weight" },
19541
+ u: { ...transformFromFactor(1.66053e-24), category: "weight" },
19542
+ grain: { ...transformFromFactor(0.0647989), category: "weight" },
19543
+ ozm: { ...transformFromFactor(28.3495), category: "weight" },
19544
+ lbm: { ...transformFromFactor(453.592), category: "weight" },
19545
+ stone: { ...transformFromFactor(6350.29), category: "weight" },
19546
+ sg: { ...transformFromFactor(14593.90294), category: "weight" },
19547
+ cwt: { ...transformFromFactor(45359.237), category: "weight" },
19548
+ uk_cwt: { ...transformFromFactor(50802.3), category: "weight" },
19549
+ ton: { ...transformFromFactor(907184.74), category: "weight" },
19550
+ uk_ton: { ...transformFromFactor(1016046.9), category: "weight" },
19551
+ // DISTANCE UNITS : Standard = meter
19552
+ m: { ...standard, category: "distance" },
19553
+ km: { ...transformFromFactor(1000), category: "distance" },
19554
+ ang: { ...transformFromFactor(ANG2M), category: "distance" },
19555
+ Picapt: { ...transformFromFactor(PICAPT2M), category: "distance" },
19556
+ pica: { ...transformFromFactor(IN2M / 6), category: "distance" },
19557
+ in: { ...transformFromFactor(IN2M), category: "distance" },
19558
+ ft: { ...transformFromFactor(FT2M), category: "distance" },
19559
+ yd: { ...transformFromFactor(YD2M), category: "distance" },
19560
+ ell: { ...transformFromFactor(1.143), category: "distance" },
19561
+ mi: { ...transformFromFactor(MI2M), category: "distance" },
19562
+ survey_mi: { ...transformFromFactor(1609.34), category: "distance" },
19563
+ Nmi: { ...transformFromFactor(NMI2M), category: "distance" },
19564
+ ly: { ...transformFromFactor(LY2M), category: "distance" },
19565
+ parsec: { ...transformFromFactor(3.0856775814914e16), category: "distance" },
19566
+ // TIME UNITS : Standard = second
19567
+ sec: { ...standard, category: "time" },
19568
+ min: { ...transformFromFactor(60), category: "time" },
19569
+ hr: { ...transformFromFactor(3600), category: "time" },
19570
+ day: { ...transformFromFactor(86400), category: "time" },
19571
+ yr: { ...transformFromFactor(31556952), category: "time" },
19572
+ // PRESSURE UNITS : Standard = Pascal
19573
+ Pa: { ...standard, category: "pressure" },
19574
+ bar: { ...transformFromFactor(100000), category: "pressure" },
19575
+ mmHg: { ...transformFromFactor(133.322), category: "pressure" },
19576
+ Torr: { ...transformFromFactor(133.322), category: "pressure" },
19577
+ psi: { ...transformFromFactor(6894.76), category: "pressure" },
19578
+ atm: { ...transformFromFactor(101325), category: "pressure" },
19579
+ // FORCE UNITS : Standard = Newton
19580
+ N: { ...standard, category: "force" },
19581
+ dyn: { ...transformFromFactor(1e-5), category: "force" },
19582
+ pond: { ...transformFromFactor(0.00980665), category: "force" },
19583
+ lbf: { ...transformFromFactor(4.44822), category: "force" },
19584
+ // ENERGY UNITS : Standard = Joule
19585
+ J: { ...standard, category: "energy" },
19586
+ eV: { ...transformFromFactor(1.60218e-19), category: "energy" },
19587
+ e: { ...transformFromFactor(1e-7), category: "energy" },
19588
+ flb: { ...transformFromFactor(1.3558179483), category: "energy" },
19589
+ c: { ...transformFromFactor(4.184), category: "energy" },
19590
+ cal: { ...transformFromFactor(4.1868), category: "energy" },
19591
+ BTU: { ...transformFromFactor(1055.06), category: "energy" },
19592
+ Wh: { ...transformFromFactor(3600), category: "energy" },
19593
+ HPh: { ...transformFromFactor(2684520), category: "energy" },
19594
+ // POWER UNITS : Standard = Watt
19595
+ W: { ...standard, category: "power" },
19596
+ PS: { ...transformFromFactor(735.499), category: "power" },
19597
+ HP: { ...transformFromFactor(745.7), category: "power" },
19598
+ // MAGNETISM UNITS : Standard = Tesla
19599
+ T: { ...standard, category: "magnetism" },
19600
+ ga: { ...transformFromFactor(1e-4), category: "magnetism" },
19601
+ // TEMPERATURE UNITS : Standard = Kelvin
19602
+ K: { ...standard, category: "temperature" },
19603
+ C: {
19604
+ transform: (T) => T + 273.15,
19605
+ inverseTransform: (T) => T - 273.15,
19606
+ category: "temperature",
19607
+ },
19608
+ F: {
19609
+ transform: (T) => ((T - 32) * 5) / 9 + 273.15,
19610
+ inverseTransform: (T) => ((T - 273.15) * 9) / 5 + 32,
19611
+ category: "temperature",
19612
+ },
19613
+ Rank: { ...transformFromFactor(5 / 9), category: "temperature" },
19614
+ Reau: {
19615
+ transform: (T) => T * 1.25 + 273.15,
19616
+ inverseTransform: (T) => (T - 273.15) / 1.25,
19617
+ category: "temperature",
19618
+ },
19619
+ // VOLUME UNITS : Standard = cubic meter
19620
+ "m^3": { ...standard, category: "volume", order: 3 },
19621
+ "ang^3": { ...transformFromFactor(Math.pow(ANG2M, 3)), category: "volume", order: 3 },
19622
+ "Picapt^3": { ...transformFromFactor(Math.pow(PICAPT2M, 3)), category: "volume", order: 3 },
19623
+ tsp: { ...transformFromFactor(4.92892e-6), category: "volume" },
19624
+ tspm: { ...transformFromFactor(5e-6), category: "volume" },
19625
+ tbs: { ...transformFromFactor(1.4786764825785619e-5), category: "volume" },
19626
+ "in^3": { ...transformFromFactor(Math.pow(IN2M, 3)), category: "volume", order: 3 },
19627
+ oz: { ...transformFromFactor(2.95735295625e-5), category: "volume" },
19628
+ cup: { ...transformFromFactor(0.000237), category: "volume" },
19629
+ pt: { ...transformFromFactor(0.0004731765), category: "volume" },
19630
+ uk_pt: { ...transformFromFactor(0.000568261), category: "volume" },
19631
+ qt: { ...transformFromFactor(0.0009463529), category: "volume" },
19632
+ l: { ...transformFromFactor(1e-3), category: "volume" },
19633
+ uk_qt: { ...transformFromFactor(0.0011365225), category: "volume" },
19634
+ gal: { ...transformFromFactor(0.0037854118), category: "volume" },
19635
+ uk_gal: { ...transformFromFactor(0.00454609), category: "volume" },
19636
+ "ft^3": { ...transformFromFactor(Math.pow(FT2M, 3)), category: "volume", order: 3 },
19637
+ bushel: { ...transformFromFactor(0.0352390704), category: "volume" },
19638
+ barrel: { ...transformFromFactor(0.158987295), category: "volume" },
19639
+ "yd^3": { ...transformFromFactor(Math.pow(YD2M, 3)), category: "volume", order: 3 },
19640
+ MTON: { ...transformFromFactor(1.13267386368), category: "volume" },
19641
+ GRT: { ...transformFromFactor(2.83168), category: "volume" },
19642
+ "mi^3": { ...transformFromFactor(Math.pow(MI2M, 3)), category: "volume", order: 3 },
19643
+ "Nmi^3": { ...transformFromFactor(Math.pow(NMI2M, 3)), category: "volume", order: 3 },
19644
+ "ly^3": { ...transformFromFactor(Math.pow(LY2M, 3)), category: "volume", order: 3 },
19645
+ // AREA UNITS : Standard = square meter
19646
+ "m^2": { ...standard, category: "area", order: 2 },
19647
+ "ang^2": { ...transformFromFactor(Math.pow(ANG2M, 2)), category: "area", order: 2 },
19648
+ "Picapt^2": { ...transformFromFactor(Math.pow(PICAPT2M, 2)), category: "area", order: 2 },
19649
+ "in^2": { ...transformFromFactor(Math.pow(IN2M, 2)), category: "area", order: 2 },
19650
+ "ft^2": { ...transformFromFactor(Math.pow(FT2M, 2)), category: "area", order: 2 },
19651
+ "yd^2": { ...transformFromFactor(Math.pow(YD2M, 2)), category: "area", order: 2 },
19652
+ ar: { ...transformFromFactor(100), category: "area" },
19653
+ Morgen: { ...transformFromFactor(2500), category: "area" },
19654
+ uk_acre: { ...transformFromFactor(4046.8564224), category: "area" },
19655
+ us_acre: { ...transformFromFactor(4046.8726098743), category: "area" },
19656
+ ha: { ...transformFromFactor(1e4), category: "area" },
19657
+ "mi^2": { ...transformFromFactor(Math.pow(MI2M, 2)), category: "area", order: 2 },
19658
+ "Nmi^2": { ...transformFromFactor(Math.pow(NMI2M, 2)), category: "area", order: 2 },
19659
+ "ly^2": { ...transformFromFactor(Math.pow(LY2M, 2)), category: "area", order: 2 },
19660
+ // INFORMATION UNITS : Standard = bit
19661
+ bit: { ...standard, category: "information" },
19662
+ byte: { ...transformFromFactor(8), category: "information" },
19663
+ // SPEED UNITS : Standard = m/s
19664
+ "m/s": { ...standard, category: "speed" },
19665
+ "m/hr": { ...transformFromFactor(1 / 3600), category: "speed" },
19666
+ "km/hr": { ...transformFromFactor(1 / 3.6), category: "speed" },
19667
+ mph: { ...transformFromFactor(0.44704), category: "speed" },
19668
+ kn: { ...transformFromFactor(0.5144444444), category: "speed" },
19669
+ admkn: { ...transformFromFactor(0.5147733333), category: "speed" },
19670
+ };
19671
+ const UNITS_ALIASES = {
19672
+ shweight: "cwt",
19673
+ lcwt: "uk_cwt",
19674
+ hweight: "uk_cwt",
19675
+ LTON: "uk_ton",
19676
+ brton: "uk_ton",
19677
+ pc: "parsec",
19678
+ Pica: "Picapt",
19679
+ d: "day",
19680
+ mn: "min",
19681
+ s: "sec",
19682
+ p: "Pa",
19683
+ at: "atm",
19684
+ dy: "dyn",
19685
+ ev: "eV",
19686
+ hh: "HPh",
19687
+ wh: "Wh",
19688
+ btu: "BTU",
19689
+ h: "HP",
19690
+ cel: "C",
19691
+ fah: "F",
19692
+ kel: "K",
19693
+ us_pt: "pt",
19694
+ L: "l",
19695
+ lt: "l",
19696
+ ang3: "ang^3",
19697
+ ft3: "ft^3",
19698
+ in3: "in^3",
19699
+ ly3: "ly^3",
19700
+ m3: "m^3",
19701
+ mi3: "mi^3",
19702
+ yd3: "yd^3",
19703
+ Nmi3: "Nmi^3",
19704
+ Picapt3: "Picapt^3",
19705
+ "Pica^3": "Picapt^3",
19706
+ Pica3: "Picapt^3",
19707
+ regton: "GRT",
19708
+ ang2: "ang^2",
19709
+ ft2: "ft^2",
19710
+ in2: "in^2",
19711
+ ly2: "ly^2",
19712
+ m2: "m^2",
19713
+ mi2: "mi^2",
19714
+ Nmi2: "Nmi^2",
19715
+ Picapt2: "Picapt^2",
19716
+ "Pica^2": "Picapt^2",
19717
+ Pica2: "Picapt^2",
19718
+ yd2: "yd^2",
19719
+ "m/h": "m/hr",
19720
+ "m/sec": "m/s",
19721
+ };
19722
+ const UNIT_PREFIXES = {
19723
+ "": 1,
19724
+ Y: 1e24,
19725
+ Z: 1e21,
19726
+ E: 1e18,
19727
+ P: 1e15,
19728
+ T: 1e12,
19729
+ G: 1e9,
19730
+ M: 1e6,
19731
+ k: 1e3,
19732
+ h: 1e2,
19733
+ da: 1e1,
19734
+ e: 1e1,
19735
+ d: 1e-1,
19736
+ c: 1e-2,
19737
+ m: 1e-3,
19738
+ u: 1e-6,
19739
+ n: 1e-9,
19740
+ p: 1e-12,
19741
+ f: 1e-15,
19742
+ a: 1e-18,
19743
+ z: 1e-21,
19744
+ y: 1e-21,
19745
+ Yi: Math.pow(2, 80),
19746
+ Zi: Math.pow(2, 70),
19747
+ Ei: Math.pow(2, 60),
19748
+ Pi: Math.pow(2, 50),
19749
+ Ti: Math.pow(2, 40),
19750
+ Gi: Math.pow(2, 30),
19751
+ Mi: Math.pow(2, 20),
19752
+ ki: Math.pow(2, 10),
19753
+ };
19754
+ const TRANSLATED_CATEGORIES = {
19755
+ weight: _t("Weight"),
19756
+ distance: _t("Distance"),
19757
+ time: _t("Time"),
19758
+ pressure: _t("Pressure"),
19759
+ force: _t("Force"),
19760
+ energy: _t("Energy"),
19761
+ power: _t("Power"),
19762
+ magnetism: _t("Magnetism"),
19763
+ temperature: _t("Temperature"),
19764
+ volume: _t("Volume"),
19765
+ area: _t("Area"),
19766
+ information: _t("Information"),
19767
+ speed: _t("Speed"),
19768
+ };
19769
+ function getTranslatedCategory(key) {
19770
+ return TRANSLATED_CATEGORIES[key] ?? "";
19771
+ }
19772
+ function getTransformation(key) {
19773
+ for (const [prefix, value] of Object.entries(UNIT_PREFIXES)) {
19774
+ if (prefix && !key.startsWith(prefix))
19775
+ continue;
19776
+ const _key = key.slice(prefix.length);
19777
+ let conversion = UNITS[_key];
19778
+ if (!conversion && UNITS_ALIASES[_key]) {
19779
+ conversion = UNITS[UNITS_ALIASES[_key]];
19780
+ }
19781
+ if (conversion) {
19782
+ return {
19783
+ ...conversion,
19784
+ factor: conversion.order ? Math.pow(value, conversion.order) : value,
19785
+ };
19786
+ }
19787
+ }
19788
+ return;
19789
+ }
19790
+
19791
+ // -----------------------------------------------------------------------------
19792
+ // CONVERT
19793
+ // -----------------------------------------------------------------------------
19794
+ const CONVERT = {
19795
+ description: _t("Converts a numeric value to a different unit of measure."),
19796
+ args: [
19797
+ arg("value (number)", _t("the numeric value in start_unit to convert to end_unit")),
19798
+ arg("start_unit (string)", _t("The starting unit, the unit currently assigned to value")),
19799
+ arg("end_unit (string)", _t("The unit of measure into which to convert value")),
19800
+ ],
19801
+ compute: function (value, startUnit, endUnit) {
19802
+ const _value = toNumber(value, this.locale);
19803
+ const _startUnit = toString(startUnit);
19804
+ const _endUnit = toString(endUnit);
19805
+ const startConversion = getTransformation(_startUnit);
19806
+ const endConversion = getTransformation(_endUnit);
19807
+ if (!startConversion) {
19808
+ return {
19809
+ value: CellErrorType.GenericError,
19810
+ message: _t("Invalid units of measure ('%s')", _startUnit),
19811
+ };
19812
+ }
19813
+ if (!endConversion) {
19814
+ return {
19815
+ value: CellErrorType.GenericError,
19816
+ message: _t("Invalid units of measure ('%s')", _endUnit),
19817
+ };
19818
+ }
19819
+ if (startConversion.category !== endConversion.category) {
19820
+ return {
19821
+ value: CellErrorType.GenericError,
19822
+ message: _t("Incompatible units of measure ('%s' vs '%s')", getTranslatedCategory(startConversion.category), getTranslatedCategory(endConversion.category)),
19823
+ };
19824
+ }
19825
+ return {
19826
+ value: endConversion.inverseTransform(startConversion.factor * startConversion.transform(_value)) /
19827
+ endConversion.factor,
19828
+ format: value?.format,
19829
+ };
19830
+ },
19831
+ isExported: true,
19832
+ };
19833
+
19834
+ var parser = /*#__PURE__*/Object.freeze({
19835
+ __proto__: null,
19836
+ CONVERT: CONVERT
19837
+ });
19838
+
19522
19839
  const DEFAULT_STARTING_AT = 1;
19523
19840
  /** Regex matching all the words in a string */
19524
19841
  const wordRegex = /[A-Za-zÀ-ÖØ-öø-ÿ]+/g;
@@ -19936,6 +20253,7 @@ stores.inject(MyMetaStore, storeInstance);
19936
20253
  { name: _t("Text"), functions: text },
19937
20254
  { name: _t("Engineering"), functions: engineering },
19938
20255
  { name: _t("Web"), functions: web },
20256
+ { name: _t("Parser"), functions: parser },
19939
20257
  ];
19940
20258
  const functionNameRegex = /^[A-Z0-9\_\.]+$/;
19941
20259
  class FunctionRegistry extends Registry {
@@ -19947,41 +20265,127 @@ stores.inject(MyMetaStore, storeInstance);
19947
20265
  }
19948
20266
  const descr = addMetaInfoFromArg(addDescr);
19949
20267
  validateArguments(descr.args);
19950
- this.mapping[name] = addErrorHandling(addResultHandling(addInputHandling(descr), name), name);
20268
+ this.mapping[name] = createComputeFunction(descr, name);
19951
20269
  super.add(name, descr);
19952
20270
  return this;
19953
20271
  }
19954
20272
  }
19955
- function addInputHandling(descr) {
19956
- function computeWithInputHandling(...args) {
20273
+ const functionRegistry = new FunctionRegistry();
20274
+ for (let category of categories) {
20275
+ const fns = category.functions;
20276
+ for (let name in fns) {
20277
+ const addDescr = fns[name];
20278
+ addDescr.category = addDescr.category || category.name;
20279
+ name = name.replace(/_/g, ".");
20280
+ functionRegistry.add(name, { isExported: false, ...addDescr });
20281
+ }
20282
+ }
20283
+ const notAvailableError = new NotAvailableError(_t("Array arguments to [[FUNCTION_NAME]] are of different size."));
20284
+ function createComputeFunction(descr, functionName) {
20285
+ function runtimeCompute(...args) {
20286
+ try {
20287
+ return vectorizedCompute.apply(this, args);
20288
+ }
20289
+ catch (e) {
20290
+ return handleError(e, functionName);
20291
+ }
20292
+ }
20293
+ function vectorizedCompute(...args) {
20294
+ let countVectorizableCol = 1;
20295
+ let countVectorizableRow = 1;
20296
+ let vectorizableColLimit = Infinity;
20297
+ let vectorizableRowLimit = Infinity;
20298
+ let vectorArgsType = undefined;
20299
+ //#region Compute vectorisation limits
19957
20300
  for (let i = 0; i < args.length; i++) {
19958
20301
  const argDefinition = descr.args[descr.getArgToFocus(i + 1) - 1];
19959
20302
  const arg = args[i];
19960
20303
  if (isMatrix(arg) && !argDefinition.acceptMatrix) {
19961
- if (arg.length !== 1 || arg[0].length !== 1) {
19962
- throw new EvaluationError(_t("Function [[FUNCTION_NAME]] expects the parameter '%s' to be a single value or a single cell reference, not a range.", argDefinition.name));
20304
+ // if argDefinition does not accept a matrix but arg is still a matrix
20305
+ // --> triggers the arguments vectorization
20306
+ const nColumns = arg.length;
20307
+ const nRows = arg[0].length;
20308
+ if (nColumns !== 1 || nRows !== 1) {
20309
+ vectorArgsType ??= new Array(args.length);
20310
+ if (nColumns !== 1 && nRows !== 1) {
20311
+ vectorArgsType[i] = "matrix";
20312
+ countVectorizableCol = Math.max(countVectorizableCol, nColumns);
20313
+ countVectorizableRow = Math.max(countVectorizableRow, nRows);
20314
+ vectorizableColLimit = Math.min(vectorizableColLimit, nColumns);
20315
+ vectorizableRowLimit = Math.min(vectorizableRowLimit, nRows);
20316
+ }
20317
+ else if (nColumns !== 1) {
20318
+ vectorArgsType[i] = "horizontal";
20319
+ countVectorizableCol = Math.max(countVectorizableCol, nColumns);
20320
+ vectorizableColLimit = Math.min(vectorizableColLimit, nColumns);
20321
+ }
20322
+ else if (nRows !== 1) {
20323
+ vectorArgsType[i] = "vertical";
20324
+ countVectorizableRow = Math.max(countVectorizableRow, nRows);
20325
+ vectorizableRowLimit = Math.min(vectorizableRowLimit, nRows);
20326
+ }
20327
+ }
20328
+ else {
20329
+ args[i] = arg[0][0];
19963
20330
  }
19964
- args[i] = arg[0][0];
19965
20331
  }
19966
20332
  if (!isMatrix(arg) && argDefinition.acceptMatrixOnly) {
19967
20333
  throw new BadExpressionError(_t("Function [[FUNCTION_NAME]] expects the parameter '%s' to be reference to a cell or range.", (i + 1).toString()));
19968
20334
  }
19969
20335
  }
19970
- return descr.compute.apply(this, args);
20336
+ //#endregion
20337
+ if (countVectorizableCol === 1 && countVectorizableRow === 1) {
20338
+ // either this function is not vectorized or it ends up with a 1x1 dimension
20339
+ return computeFunctionToObject.apply(this, args);
20340
+ }
20341
+ const getArgOffset = (i, j) => args.map((arg, index) => {
20342
+ switch (vectorArgsType?.[index]) {
20343
+ case "matrix":
20344
+ return arg[i][j];
20345
+ case "horizontal":
20346
+ return arg[i][0];
20347
+ case "vertical":
20348
+ return arg[0][j];
20349
+ case undefined:
20350
+ return arg;
20351
+ }
20352
+ });
20353
+ return generateMatrix(countVectorizableCol, countVectorizableRow, (col, row) => {
20354
+ if (col > vectorizableColLimit - 1 || row > vectorizableRowLimit - 1) {
20355
+ return notAvailableError;
20356
+ }
20357
+ const singleCellComputeResult = computeFunctionToObject.apply(this, getArgOffset(col, row));
20358
+ // In the case where the user tries to vectorize arguments of an array formula, we will get an
20359
+ // array for every combination of the vectorized arguments, which will lead to a 3D matrix and
20360
+ // we won't be able to return the values.
20361
+ // In this case, we keep the first element of each spreading part, just as Excel does, and
20362
+ // create an array with these parts.
20363
+ // For exemple, we have MUNIT(x) that return an unitary matrix of x*x. If we use it with a
20364
+ // range, like MUNIT(A1:A2), we will get two unitary matrices (one for the value in A1 and one
20365
+ // for the value in A2). In this case, we will simply take the first value of each matrix and
20366
+ // return the array [First value of MUNIT(A1), First value of MUNIT(A2)].
20367
+ return isMatrix(singleCellComputeResult)
20368
+ ? singleCellComputeResult[0][0]
20369
+ : singleCellComputeResult;
20370
+ });
19971
20371
  }
19972
- return computeWithInputHandling;
19973
- }
19974
- function addErrorHandling(compute, functionName) {
19975
- return function (...args) {
19976
- try {
19977
- return compute.apply(this, args);
20372
+ function computeFunctionToObject(...args) {
20373
+ const result = descr.compute.apply(this, args);
20374
+ if (!isMatrix(result)) {
20375
+ if (typeof result === "object" && result !== null && "value" in result) {
20376
+ replaceFunctionNamePlaceholder(result, functionName);
20377
+ return result;
20378
+ }
20379
+ return { value: result };
19978
20380
  }
19979
- catch (e) {
19980
- return handleError(e, functionName);
20381
+ if (typeof result[0][0] === "object" && result[0][0] !== null && "value" in result[0][0]) {
20382
+ matrixForEach(result, (result) => replaceFunctionNamePlaceholder(result, functionName));
20383
+ return result;
19981
20384
  }
19982
- };
20385
+ return matrixMap(result, (row) => ({ value: row }));
20386
+ }
20387
+ return runtimeCompute;
19983
20388
  }
19984
- const implementationErrorMessage = _t("An unexpected error occurred. Submit a support ticket at odoo.com/help.");
19985
20389
  function handleError(e, functionName) {
19986
20390
  // the error could be an user error (instance of EvaluationError)
19987
20391
  // or a javascript error (instance of Error)
@@ -20000,42 +20404,16 @@ stores.inject(MyMetaStore, storeInstance);
20000
20404
  return (obj?.value !== undefined &&
20001
20405
  typeof obj.value === "string");
20002
20406
  }
20003
- function hasStringMessage(obj) {
20004
- return (obj?.message !== undefined &&
20005
- typeof obj.message === "string");
20006
- }
20007
- function addResultHandling(compute, functionName) {
20008
- return function computeWithResultHandling(...args) {
20009
- const result = compute.apply(this, args);
20010
- if (!isMatrix(result)) {
20011
- if (typeof result === "object" && result !== null && "value" in result) {
20012
- replaceFunctionNamePlaceholder(result, functionName);
20013
- return result;
20014
- }
20015
- return { value: result };
20016
- }
20017
- if (typeof result[0][0] === "object" && result[0][0] !== null && "value" in result[0][0]) {
20018
- matrixForEach(result, (result) => replaceFunctionNamePlaceholder(result, functionName));
20019
- return result;
20020
- }
20021
- return matrixMap(result, (row) => ({ value: row }));
20022
- };
20023
- }
20024
- function replaceFunctionNamePlaceholder(fPayload, functionName) {
20407
+ function replaceFunctionNamePlaceholder(functionResult, functionName) {
20025
20408
  // for performance reasons: change in place and only if needed
20026
- if (fPayload.message?.includes("[[FUNCTION_NAME]]")) {
20027
- fPayload.message = fPayload.message.replace("[[FUNCTION_NAME]]", functionName);
20409
+ if (functionResult.message?.includes("[[FUNCTION_NAME]]")) {
20410
+ functionResult.message = functionResult.message.replace("[[FUNCTION_NAME]]", functionName);
20028
20411
  }
20029
20412
  }
20030
- const functionRegistry = new FunctionRegistry();
20031
- for (let category of categories) {
20032
- const fns = category.functions;
20033
- for (let name in fns) {
20034
- const addDescr = fns[name];
20035
- addDescr.category = addDescr.category || category.name;
20036
- name = name.replace(/_/g, ".");
20037
- functionRegistry.add(name, { isExported: false, ...addDescr });
20038
- }
20413
+ const implementationErrorMessage = _t("An unexpected error occurred. Submit a support ticket at odoo.com/help.");
20414
+ function hasStringMessage(obj) {
20415
+ return (obj?.message !== undefined &&
20416
+ typeof obj.message === "string");
20039
20417
  }
20040
20418
 
20041
20419
  autoCompleteProviders.add("functions", {
@@ -21484,6 +21862,7 @@ stores.inject(MyMetaStore, storeInstance);
21484
21862
 
21485
21863
  const functions$1 = functionRegistry.content;
21486
21864
  const OPERATOR_MAP = {
21865
+ // export for test
21487
21866
  "=": "EQ",
21488
21867
  "+": "ADD",
21489
21868
  "-": "MINUS",
@@ -21498,6 +21877,7 @@ stores.inject(MyMetaStore, storeInstance);
21498
21877
  "&": "CONCATENATE",
21499
21878
  };
21500
21879
  const UNARY_OPERATOR_MAP = {
21880
+ // export for test
21501
21881
  "-": "UMINUS",
21502
21882
  "+": "UPLUS",
21503
21883
  "%": "UNARY.PERCENT",
@@ -22786,8 +23166,7 @@ stores.inject(MyMetaStore, storeInstance);
22786
23166
  /**
22787
23167
  * Get a default chart js configuration
22788
23168
  */
22789
- function getDefaultChartJsRuntime(chart, labels, fontColor, args) {
22790
- const { format, locale, truncateLabels, horizontalChart } = args;
23169
+ function getDefaultChartJsRuntime(chart, labels, fontColor, { format, locale, truncateLabels = true, horizontalChart, }) {
22791
23170
  const chartTitle = chart.title.text ? chart.title : { ...chart.title, content: "" };
22792
23171
  const options = {
22793
23172
  // https://www.chartjs.org/docs/latest/general/responsive.html
@@ -22963,7 +23342,6 @@ stores.inject(MyMetaStore, storeInstance);
22963
23342
  if ("chartJsConfig" in runtime) {
22964
23343
  const config = deepCopy(runtime.chartJsConfig);
22965
23344
  config.plugins = [backgroundColorChartJSPlugin];
22966
- // @ts-ignore
22967
23345
  const chart = new window.Chart(canvas, config);
22968
23346
  const imgContent = chart.toBase64Image();
22969
23347
  chart.destroy();
@@ -23884,13 +24262,11 @@ stores.inject(MyMetaStore, storeInstance);
23884
24262
  }
23885
24263
  let missingTimeAdapterAlreadyWarned = false;
23886
24264
  function isLuxonTimeAdapterInstalled() {
23887
- // @ts-ignore
23888
24265
  if (!window.Chart) {
23889
24266
  return false;
23890
24267
  }
23891
24268
  // @ts-ignore
23892
24269
  const adapter = new window.Chart._adapters._date({});
23893
- // @ts-ignore
23894
24270
  const isInstalled = adapter._id === "luxon";
23895
24271
  if (!isInstalled && !missingTimeAdapterAlreadyWarned) {
23896
24272
  missingTimeAdapterAlreadyWarned = true;
@@ -23907,9 +24283,7 @@ stores.inject(MyMetaStore, storeInstance);
23907
24283
  generateLabels(chart) {
23908
24284
  // color the legend labels with the dataset color, without any transparency
23909
24285
  const { data } = chart;
23910
- /** @ts-ignore */
23911
- const labels = window.Chart.defaults.plugins.legend.labels
23912
- .generateLabels(chart);
24286
+ const labels = window.Chart.defaults.plugins.legend.labels.generateLabels(chart);
23913
24287
  for (const [index, label] of labels.entries()) {
23914
24288
  label.fillStyle = data.datasets[index].borderColor;
23915
24289
  }
@@ -24026,10 +24400,6 @@ stores.inject(MyMetaStore, storeInstance);
24026
24400
  const colors = new ColorGenerator();
24027
24401
  const definition = chart.getDefinition();
24028
24402
  for (let [index, { label, data }] of dataSetsValues.entries()) {
24029
- if (["linear", "time"].includes(axisType)) {
24030
- // Replace empty string labels by undefined to make sure chartJS doesn't decide that "" is the same as 0
24031
- data = data.map((y, index) => ({ x: labels[index] || undefined, y }));
24032
- }
24033
24403
  const color = colors.next();
24034
24404
  let backgroundRGBA = colorToRGBA(color);
24035
24405
  if (areaChart) {
@@ -24045,6 +24415,10 @@ stores.inject(MyMetaStore, storeInstance);
24045
24415
  return value;
24046
24416
  });
24047
24417
  }
24418
+ if (["linear", "time"].includes(axisType)) {
24419
+ // Replace empty string labels by undefined to make sure chartJS doesn't decide that "" is the same as 0
24420
+ data = data.map((y, index) => ({ x: labels[index] || undefined, y }));
24421
+ }
24048
24422
  const backgroundColor = rgbaToHex(backgroundRGBA);
24049
24423
  const dataset = {
24050
24424
  label,
@@ -33151,21 +33525,25 @@ stores.inject(MyMetaStore, storeInstance);
33151
33525
 
33152
33526
  function useHighlightsOnHover(ref, highlightProvider) {
33153
33527
  const hoverState = useHoveredElement(ref);
33154
- const stores = useStoreProvider();
33155
33528
  useHighlights({
33156
33529
  get highlights() {
33157
33530
  return hoverState.hovered ? highlightProvider.highlights : [];
33158
33531
  },
33159
33532
  });
33160
- owl.useEffect(() => {
33161
- stores.trigger("store-updated");
33162
- }, () => [hoverState.hovered]);
33163
33533
  }
33164
33534
  function useHighlights(highlightProvider) {
33535
+ const stores = useStoreProvider();
33165
33536
  const store = useLocalStore(HighlightStore);
33166
33537
  owl.onMounted(() => {
33167
33538
  store.register(highlightProvider);
33168
33539
  });
33540
+ let currentHighlights = highlightProvider.highlights;
33541
+ owl.useEffect((highlights) => {
33542
+ if (!deepEquals(highlights, currentHighlights)) {
33543
+ currentHighlights = highlights;
33544
+ stores.trigger("store-updated");
33545
+ }
33546
+ }, () => [highlightProvider.highlights]);
33169
33547
  }
33170
33548
 
33171
33549
  css /* scss */ `
@@ -34637,6 +35015,8 @@ stores.inject(MyMetaStore, storeInstance);
34637
35015
  isSearchDirty = false;
34638
35016
  initialShowFormulaState;
34639
35017
  preserveSelectedMatchIndex = false;
35018
+ irreplaceableMatchCount = 0;
35019
+ notificationStore = this.get(NotificationStore);
34640
35020
  // fixme: why do we make selectedMatchIndex on top of a selected
34641
35021
  // property in the matches?
34642
35022
  selectedMatchIndex = null;
@@ -34713,6 +35093,11 @@ stores.inject(MyMetaStore, storeInstance);
34713
35093
  for (const match of cmd.matches) {
34714
35094
  this.replaceMatch(match, cmd.searchString, cmd.replaceWith, cmd.searchOptions);
34715
35095
  }
35096
+ if (this.irreplaceableMatchCount > 0) {
35097
+ this.showReplaceWarningMessage(cmd.matches.length, this.irreplaceableMatchCount);
35098
+ }
35099
+ this.irreplaceableMatchCount = 0;
35100
+ break;
34716
35101
  }
34717
35102
  }
34718
35103
  finalize() {
@@ -34894,12 +35279,36 @@ stores.inject(MyMetaStore, storeInstance);
34894
35279
  searchOptions: this.searchOptions,
34895
35280
  });
34896
35281
  }
35282
+ /**
35283
+ * Show a warning message based on the number of matches replaced and irreplaceable.
35284
+ */
35285
+ showReplaceWarningMessage(totalMatches, irreplaceableMatches) {
35286
+ const replaceableMatches = totalMatches - irreplaceableMatches;
35287
+ if (replaceableMatches === 0) {
35288
+ this.notificationStore.notifyUser({
35289
+ type: "warning",
35290
+ sticky: false,
35291
+ text: _t("Match(es) cannot be replaced as they are part of a formula."),
35292
+ });
35293
+ }
35294
+ else {
35295
+ this.notificationStore.notifyUser({
35296
+ type: "warning",
35297
+ sticky: false,
35298
+ text: _t("%(replaceable_count)s match(es) replaced. %(irreplaceable_count)s match(es) cannot be replaced as they are part of a formula.", {
35299
+ replaceable_count: replaceableMatches,
35300
+ irreplaceable_count: irreplaceableMatches,
35301
+ }),
35302
+ });
35303
+ }
35304
+ }
34897
35305
  replaceMatch(selectedMatch, searchString, replaceWith, searchOptions) {
34898
35306
  const cell = this.getters.getCell(selectedMatch);
34899
35307
  if (!cell?.content) {
34900
35308
  return;
34901
35309
  }
34902
35310
  if (cell?.isFormula && !searchOptions.searchFormulas) {
35311
+ this.irreplaceableMatchCount++;
34903
35312
  return;
34904
35313
  }
34905
35314
  const searchRegex = getSearchRegex(searchString, searchOptions);
@@ -36965,7 +37374,13 @@ stores.inject(MyMetaStore, storeInstance);
36965
37374
  }
36966
37375
  async loadLocales() {
36967
37376
  this.loadedLocales = (await this.env.loadLocales())
36968
- .filter(isValidLocale)
37377
+ .filter((locale) => {
37378
+ const isValid = isValidLocale(locale);
37379
+ if (!isValid) {
37380
+ console.warn(`Invalid locale: ${locale["code"]} ${locale}`);
37381
+ }
37382
+ return isValid;
37383
+ })
36969
37384
  .sort((a, b) => a.name.localeCompare(b.name));
36970
37385
  }
36971
37386
  get numberFormatPreview() {
@@ -38024,6 +38439,9 @@ stores.inject(MyMetaStore, storeInstance);
38024
38439
  const component = { ...value, id: this.uuidGenerator.uuidv4() };
38025
38440
  return super.add(name, component);
38026
38441
  }
38442
+ getAllOrdered() {
38443
+ return this.getAll().sort((a, b) => a.sequence - b.sequence);
38444
+ }
38027
38445
  }
38028
38446
  const topbarComponentRegistry = new TopBarComponentRegistry();
38029
38447
 
@@ -42134,6 +42552,7 @@ stores.inject(MyMetaStore, storeInstance);
42134
42552
  return;
42135
42553
  }
42136
42554
  if (clipboardData.types.indexOf(ClipboardMIMEType.PlainText) > -1) {
42555
+ ev.preventDefault();
42137
42556
  const content = clipboardData.getData(ClipboardMIMEType.PlainText);
42138
42557
  const target = this.env.model.getters.getSelectedZones();
42139
42558
  const clipboardString = this.env.model.getters.getClipboardTextContent();
@@ -45718,7 +46137,7 @@ stores.inject(MyMetaStore, storeInstance);
45718
46137
  return relsFile;
45719
46138
  }
45720
46139
 
45721
- const EXCEL_IMPORT_VERSION = 16;
46140
+ const EXCEL_IMPORT_VERSION = 17;
45722
46141
  class XlsxReader {
45723
46142
  warningManager;
45724
46143
  xmls;
@@ -45850,7 +46269,7 @@ stores.inject(MyMetaStore, storeInstance);
45850
46269
  * a breaking change is made in the way the state is handled, and an upgrade
45851
46270
  * function should be defined
45852
46271
  */
45853
- const CURRENT_VERSION = 16;
46272
+ const CURRENT_VERSION = 17;
45854
46273
  const INITIAL_SHEET_ID = "Sheet1";
45855
46274
  /**
45856
46275
  * This function tries to load anything that could look like a valid
@@ -46797,6 +47216,21 @@ stores.inject(MyMetaStore, storeInstance);
46797
47216
  return [];
46798
47217
  return Object.keys(sheetBorders).map((index) => parseInt(index, 10));
46799
47218
  }
47219
+ /**
47220
+ * Get all the rows which contains at least a border
47221
+ */
47222
+ getRowsWithBorders(sheetId) {
47223
+ const sheetBorders = this.borders[sheetId]?.filter(isDefined);
47224
+ if (!sheetBorders)
47225
+ return [];
47226
+ const rowsWithBorders = new Set();
47227
+ for (const rowBorders of sheetBorders) {
47228
+ for (const rowBorder in rowBorders) {
47229
+ rowsWithBorders.add(parseInt(rowBorder, 10));
47230
+ }
47231
+ }
47232
+ return Array.from(rowsWithBorders);
47233
+ }
46800
47234
  /**
46801
47235
  * Get the range of all the rows in the sheet
46802
47236
  */
@@ -46846,7 +47280,7 @@ stores.inject(MyMetaStore, storeInstance);
46846
47280
  destructive: false,
46847
47281
  });
46848
47282
  }
46849
- this.getRowsRange(sheetId)
47283
+ this.getRowsWithBorders(sheetId)
46850
47284
  .filter((row) => row >= start)
46851
47285
  .sort((a, b) => (delta < 0 ? a - b : b - a)) // start by the end when moving up
46852
47286
  .forEach((row) => {
@@ -49172,10 +49606,12 @@ stores.inject(MyMetaStore, storeInstance);
49172
49606
  if (!sheetMap)
49173
49607
  return [];
49174
49608
  const mergeIds = new Set();
49175
- for (const { col, row } of positions(zone)) {
49176
- const mergeId = sheetMap[col]?.[row];
49177
- if (mergeId) {
49178
- mergeIds.add(mergeId);
49609
+ for (let col = zone.left; col <= zone.right; col++) {
49610
+ for (let row = zone.top; row <= zone.bottom; row++) {
49611
+ const mergeId = sheetMap[col]?.[row];
49612
+ if (mergeId) {
49613
+ mergeIds.add(mergeId);
49614
+ }
49179
49615
  }
49180
49616
  }
49181
49617
  return Array.from(mergeIds)
@@ -53043,7 +53479,13 @@ stores.inject(MyMetaStore, storeInstance);
53043
53479
  const queue = Array.from(ranges).reverse();
53044
53480
  while (queue.length > 0) {
53045
53481
  const range = queue.pop();
53046
- visited.addMany(positions(range.zone).map((position) => ({ sheetId: range.sheetId, ...position })));
53482
+ const zone = range.zone;
53483
+ const sheetId = range.sheetId;
53484
+ for (let col = zone.left; col <= zone.right; col++) {
53485
+ for (let row = zone.top; row <= zone.bottom; row++) {
53486
+ visited.add({ sheetId, col, row });
53487
+ }
53488
+ }
53047
53489
  const impactedPositions = this.rTree.search(range).map((dep) => dep.data);
53048
53490
  const nextInQueue = {};
53049
53491
  for (const position of impactedPositions) {
@@ -53059,7 +53501,16 @@ stores.inject(MyMetaStore, storeInstance);
53059
53501
  queue.push(...zones.map((zone) => ({ sheetId, zone })));
53060
53502
  }
53061
53503
  }
53062
- visited.deleteMany(ranges.flatMap((r) => positions(r.zone).map((position) => ({ sheetId: r.sheetId, ...position }))));
53504
+ // remove initial ranges
53505
+ for (const range of ranges) {
53506
+ const zone = range.zone;
53507
+ const sheetId = range.sheetId;
53508
+ for (let col = zone.left; col <= zone.right; col++) {
53509
+ for (let row = zone.top; row <= zone.bottom; row++) {
53510
+ visited.delete({ sheetId, col, row });
53511
+ }
53512
+ }
53513
+ }
53063
53514
  return visited;
53064
53515
  }
53065
53516
  }
@@ -53190,7 +53641,7 @@ stores.inject(MyMetaStore, storeInstance);
53190
53641
  return this.sheets[position.sheetId].getValue(position) === 1;
53191
53642
  }
53192
53643
  clear() {
53193
- const insertions = this.insertions;
53644
+ const insertions = [...this];
53194
53645
  this.insertions = [];
53195
53646
  for (const sheetId in this.sheets) {
53196
53647
  this.sheets[sheetId].clear();
@@ -53534,6 +53985,7 @@ stores.inject(MyMetaStore, storeInstance);
53534
53985
  }
53535
53986
  finally {
53536
53987
  this.cellsBeingComputed.delete(cellId);
53988
+ this.nextPositionsToUpdate.delete(position);
53537
53989
  }
53538
53990
  }
53539
53991
  computeAndSave(position) {
@@ -53560,8 +54012,33 @@ stores.inject(MyMetaStore, storeInstance);
53560
54012
  forEachSpreadPositionInMatrix(nbColumns, nbRows,
53561
54013
  // thanks to the isMatrix check above, we know that formulaReturn is MatrixFunctionReturn
53562
54014
  this.spreadValues(formulaPosition, formulaReturn));
54015
+ this.invalidatePositionsDependingOnSpread(formulaPosition, nbColumns, nbRows);
53563
54016
  return createEvaluatedCell(nullValueToZeroValue(formulaReturn[0][0]), this.getters.getLocale(), cellData);
53564
54017
  }
54018
+ invalidatePositionsDependingOnSpread(arrayFormulaPosition, nbColumns, nbRows) {
54019
+ // the result matrix is split in 2 zones to exclude the array formula position
54020
+ const top = arrayFormulaPosition.row;
54021
+ const left = arrayFormulaPosition.col;
54022
+ const bottom = top + nbRows - 1;
54023
+ const leftColumnZone = {
54024
+ top: top + 1,
54025
+ bottom,
54026
+ left,
54027
+ right: left,
54028
+ };
54029
+ const rightPartZone = {
54030
+ top,
54031
+ bottom,
54032
+ left: left + 1,
54033
+ right: left + nbColumns - 1,
54034
+ };
54035
+ const sheetId = arrayFormulaPosition.sheetId;
54036
+ const invalidatedPositions = this.formulaDependencies().getCellsDependingOn([
54037
+ { sheetId, zone: rightPartZone },
54038
+ { sheetId, zone: leftColumnZone },
54039
+ ]);
54040
+ this.nextPositionsToUpdate.addMany(invalidatedPositions);
54041
+ }
53565
54042
  assertSheetHasEnoughSpaceToSpreadFormulaResult({ sheetId, col, row }, matrixResult) {
53566
54043
  const numberOfCols = this.getters.getNumberCols(sheetId);
53567
54044
  const numberOfRows = this.getters.getNumberRows(sheetId);
@@ -53592,14 +54069,15 @@ stores.inject(MyMetaStore, storeInstance);
53592
54069
  }
53593
54070
  updateSpreadRelation({ sheetId, col, row, }) {
53594
54071
  const arrayFormulaPosition = { sheetId, col, row };
53595
- return (i, j) => {
54072
+ const updateSpreadRelation = (i, j) => {
53596
54073
  const position = { sheetId, col: i + col, row: j + row };
53597
54074
  this.spreadingRelations.addRelation({ resultPosition: position, arrayFormulaPosition });
53598
54075
  };
54076
+ return updateSpreadRelation;
53599
54077
  }
53600
54078
  checkCollision(formulaPosition) {
53601
54079
  const { sheetId, col, row } = formulaPosition;
53602
- return (i, j) => {
54080
+ const checkCollision = (i, j) => {
53603
54081
  const position = { sheetId: sheetId, col: i + col, row: j + row };
53604
54082
  const rawCell = this.getters.getCell(position);
53605
54083
  if (rawCell?.content ||
@@ -53609,17 +54087,16 @@ stores.inject(MyMetaStore, storeInstance);
53609
54087
  }
53610
54088
  this.blockedArrayFormulas.delete(formulaPosition);
53611
54089
  };
54090
+ return checkCollision;
53612
54091
  }
53613
54092
  spreadValues({ sheetId, col, row }, matrixResult) {
53614
- return (i, j) => {
54093
+ const spreadValues = (i, j) => {
53615
54094
  const position = { sheetId, col: i + col, row: j + row };
53616
54095
  const cell = this.getters.getCell(position);
53617
54096
  const evaluatedCell = createEvaluatedCell(nullValueToZeroValue(matrixResult[i][j]), this.getters.getLocale(), cell);
53618
54097
  this.evaluatedCells.set(position, evaluatedCell);
53619
- // check if formula dependencies present in the spread zone
53620
- // if so, they need to be recomputed
53621
- this.nextPositionsToUpdate.addMany(this.getCellsDependingOn([position]));
53622
54098
  };
54099
+ return spreadValues;
53623
54100
  }
53624
54101
  invalidateSpreading(position) {
53625
54102
  if (!this.spreadingRelations.isArrayFormula(position)) {
@@ -53674,12 +54151,12 @@ stores.inject(MyMetaStore, storeInstance);
53674
54151
  * rather than appearing empty. This indicates that the
53675
54152
  * cell is the result of a non-empty content.
53676
54153
  */
53677
- function nullValueToZeroValue(fPayload) {
53678
- if (fPayload.value === null || fPayload.value === undefined) {
53679
- // 'fPayload.value === undefined' is supposed to never happen, it's a safety net for javascript use
53680
- return { ...fPayload, value: 0 };
54154
+ function nullValueToZeroValue(functionResult) {
54155
+ if (functionResult.value === null || functionResult.value === undefined) {
54156
+ // 'functionResult.value === undefined' is supposed to never happen, it's a safety net for javascript use
54157
+ return { ...functionResult, value: 0 };
53681
54158
  }
53682
- return fPayload;
54159
+ return functionResult;
53683
54160
  }
53684
54161
  function updateEvalContextAndExecute(compiledFormula, compilationParams, sheetId, cellId) {
53685
54162
  compilationParams.evalContext.__originCellXC = lazy(() => {
@@ -63139,7 +63616,7 @@ stores.inject(MyMetaStore, storeInstance);
63139
63616
  }
63140
63617
  get topbarComponents() {
63141
63618
  return topbarComponentRegistry
63142
- .getAll()
63619
+ .getAllOrdered()
63143
63620
  .filter((item) => !item.isVisible || item.isVisible(this.env));
63144
63621
  }
63145
63622
  onExternalClick(ev) {
@@ -67770,9 +68247,9 @@ stores.inject(MyMetaStore, storeInstance);
67770
68247
  exports.tokenize = tokenize;
67771
68248
 
67772
68249
 
67773
- __info__.version = "17.4.0-alpha.11";
67774
- __info__.date = "2024-07-02T10:38:50.116Z";
67775
- __info__.hash = "9fe9477";
68250
+ __info__.version = "17.4.0-alpha.13";
68251
+ __info__.date = "2024-07-11T06:36:58.556Z";
68252
+ __info__.hash = "e0f506b";
67776
68253
 
67777
68254
 
67778
68255
  })(this.o_spreadsheet = this.o_spreadsheet || {}, owl);