@odoo/o-spreadsheet 18.0.20 → 18.0.22

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.0.20
6
- * @date 2025-03-19T08:21:32.426Z
7
- * @hash 3f48d8b
5
+ * @version 18.0.22
6
+ * @date 2025-04-04T08:42:35.352Z
7
+ * @hash 89a3279
8
8
  */
9
9
 
10
10
  (function (exports, owl) {
@@ -801,8 +801,7 @@
801
801
  *
802
802
  * https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Regular_Expressions/Character_Classes
803
803
  */
804
- const whiteSpaceSpecialCharacters = [
805
- " ",
804
+ const specialWhiteSpaceSpecialCharacters = [
806
805
  "\t",
807
806
  "\f",
808
807
  "\v",
@@ -817,7 +816,7 @@
817
816
  String.fromCharCode(parseInt("3000", 16)),
818
817
  String.fromCharCode(parseInt("feff", 16)),
819
818
  ];
820
- const whiteSpaceRegexp = new RegExp(whiteSpaceSpecialCharacters.join("|"), "g");
819
+ const specialWhiteSpaceRegexp = new RegExp(specialWhiteSpaceSpecialCharacters.join("|"), "g");
821
820
  const newLineRegexp = /(\r\n|\r)/g;
822
821
  /**
823
822
  * Replace all different newlines characters by \n
@@ -1112,7 +1111,10 @@
1112
1111
  }
1113
1112
  else if (stringVals.length === 4) {
1114
1113
  const alpha = parseFloat(stringVals.pop() || "1");
1115
- alphaHex = Math.round((alpha || 1) * 255);
1114
+ if (isNaN(alpha)) {
1115
+ throw new Error("invalid alpha value");
1116
+ }
1117
+ alphaHex = Math.round(alpha * 255);
1116
1118
  }
1117
1119
  const vals = stringVals.map((val) => parseInt(val, 10));
1118
1120
  if (alphaHex !== 255) {
@@ -6631,8 +6633,12 @@
6631
6633
  str = replaceNewLines(str);
6632
6634
  const chars = new TokenizingChars(str);
6633
6635
  const result = [];
6636
+ const tokenizeSpace = specialWhiteSpaceRegexp.test(str)
6637
+ ? tokenizeSpecialCharacterSpace
6638
+ : tokenizeSimpleSpace;
6634
6639
  while (!chars.isOver()) {
6635
- let token = tokenizeSpace(chars) ||
6640
+ let token = tokenizeNewLine(chars) ||
6641
+ tokenizeSpace(chars) ||
6636
6642
  tokenizeArgsSeparator(chars, locale) ||
6637
6643
  tokenizeParenthesis(chars) ||
6638
6644
  tokenizeOperator(chars) ||
@@ -6766,17 +6772,19 @@
6766
6772
  }
6767
6773
  return null;
6768
6774
  }
6769
- function tokenizeSpace(chars) {
6770
- let length = 0;
6771
- while (chars.current === NEWLINE) {
6772
- length++;
6773
- chars.shift();
6775
+ function tokenizeSpecialCharacterSpace(chars) {
6776
+ let spaces = "";
6777
+ while (chars.current === " " || (chars.current && chars.current.match(specialWhiteSpaceRegexp))) {
6778
+ spaces += chars.shift();
6774
6779
  }
6775
- if (length) {
6776
- return { type: "SPACE", value: NEWLINE.repeat(length) };
6780
+ if (spaces) {
6781
+ return { type: "SPACE", value: spaces };
6777
6782
  }
6783
+ return null;
6784
+ }
6785
+ function tokenizeSimpleSpace(chars) {
6778
6786
  let spaces = "";
6779
- while (chars.current && chars.current.match(whiteSpaceRegexp)) {
6787
+ while (chars.current === " ") {
6780
6788
  spaces += chars.shift();
6781
6789
  }
6782
6790
  if (spaces) {
@@ -6784,6 +6792,17 @@
6784
6792
  }
6785
6793
  return null;
6786
6794
  }
6795
+ function tokenizeNewLine(chars) {
6796
+ let length = 0;
6797
+ while (chars.current === NEWLINE) {
6798
+ length++;
6799
+ chars.shift();
6800
+ }
6801
+ if (length) {
6802
+ return { type: "SPACE", value: NEWLINE.repeat(length) };
6803
+ }
6804
+ return null;
6805
+ }
6787
6806
  function tokenizeInvalidRange(chars) {
6788
6807
  if (chars.currentStartsWith(CellErrorType.InvalidReference)) {
6789
6808
  chars.advanceBy(CellErrorType.InvalidReference.length);
@@ -6836,7 +6855,7 @@
6836
6855
  */
6837
6856
  function canonicalizeNumberContent(content, locale) {
6838
6857
  return content.startsWith("=")
6839
- ? canonicalizeFormula$1(content, locale)
6858
+ ? canonicalizeFormula(content, locale)
6840
6859
  : canonicalizeNumberLiteral(content, locale);
6841
6860
  }
6842
6861
  /**
@@ -6851,7 +6870,7 @@
6851
6870
  */
6852
6871
  function canonicalizeContent(content, locale) {
6853
6872
  return content.startsWith("=")
6854
- ? canonicalizeFormula$1(content, locale)
6873
+ ? canonicalizeFormula(content, locale)
6855
6874
  : canonicalizeLiteral(content, locale);
6856
6875
  }
6857
6876
  /**
@@ -6867,15 +6886,21 @@
6867
6886
  ? localizeFormula(content, locale)
6868
6887
  : localizeLiteral(content, locale);
6869
6888
  }
6889
+ /** Change a number string to its canonical form (en_US locale) */
6890
+ function canonicalizeNumberValue(content, locale) {
6891
+ return content.startsWith("=")
6892
+ ? canonicalizeFormula(content, locale)
6893
+ : canonicalizeNumberLiteral(content, locale);
6894
+ }
6870
6895
  /** Change a formula to its canonical form (en_US locale) */
6871
- function canonicalizeFormula$1(formula, locale) {
6872
- return _localizeFormula$1(formula, locale, DEFAULT_LOCALE);
6896
+ function canonicalizeFormula(formula, locale) {
6897
+ return _localizeFormula(formula, locale, DEFAULT_LOCALE);
6873
6898
  }
6874
6899
  /** Change a formula from the canonical form to the given locale */
6875
6900
  function localizeFormula(formula, locale) {
6876
- return _localizeFormula$1(formula, DEFAULT_LOCALE, locale);
6901
+ return _localizeFormula(formula, DEFAULT_LOCALE, locale);
6877
6902
  }
6878
- function _localizeFormula$1(formula, fromLocale, toLocale) {
6903
+ function _localizeFormula(formula, fromLocale, toLocale) {
6879
6904
  if (fromLocale.formulaArgSeparator === toLocale.formulaArgSeparator &&
6880
6905
  fromLocale.decimalSeparator === toLocale.decimalSeparator) {
6881
6906
  return formula;
@@ -7031,37 +7056,6 @@
7031
7056
  return locale.dateFormat + " " + locale.timeFormat;
7032
7057
  }
7033
7058
 
7034
- /** Change a number string to its canonical form (en_US locale) */
7035
- function canonicalizeNumberValue(content, locale) {
7036
- return content.startsWith("=")
7037
- ? canonicalizeFormula(content, locale)
7038
- : canonicalizeNumberLiteral(content, locale);
7039
- }
7040
- /** Change a formula to its canonical form (en_US locale) */
7041
- function canonicalizeFormula(formula, locale) {
7042
- return _localizeFormula(formula, locale, DEFAULT_LOCALE);
7043
- }
7044
- function _localizeFormula(formula, fromLocale, toLocale) {
7045
- if (fromLocale.formulaArgSeparator === toLocale.formulaArgSeparator &&
7046
- fromLocale.decimalSeparator === toLocale.decimalSeparator) {
7047
- return formula;
7048
- }
7049
- const tokens = tokenize(formula, fromLocale);
7050
- let localizedFormula = "";
7051
- for (const token of tokens) {
7052
- if (token.type === "NUMBER") {
7053
- localizedFormula += token.value.replace(fromLocale.decimalSeparator, toLocale.decimalSeparator);
7054
- }
7055
- else if (token.type === "ARG_SEPARATOR") {
7056
- localizedFormula += toLocale.formulaArgSeparator;
7057
- }
7058
- else {
7059
- localizedFormula += token.value;
7060
- }
7061
- }
7062
- return localizedFormula;
7063
- }
7064
-
7065
7059
  function boolAnd(args) {
7066
7060
  let foundBoolean = false;
7067
7061
  let acc = true;
@@ -10072,9 +10066,9 @@ stores.inject(MyMetaStore, storeInstance);
10072
10066
  /** In XLSX color format (no #) */
10073
10067
  const AUTO_COLOR = "000000";
10074
10068
  const XLSX_ICONSET_MAP = {
10075
- arrow: "3Arrows",
10069
+ arrows: "3Arrows",
10076
10070
  smiley: "3Symbols",
10077
- dot: "3TrafficLights1",
10071
+ dots: "3TrafficLights1",
10078
10072
  };
10079
10073
  const NAMESPACE = {
10080
10074
  styleSheet: "http://schemas.openxmlformats.org/spreadsheetml/2006/main",
@@ -10746,6 +10740,7 @@ stores.inject(MyMetaStore, storeInstance);
10746
10740
  };
10747
10741
  /** Map between legend position in XLSX file and human readable position */
10748
10742
  const DRAWING_LEGEND_POSITION_CONVERSION_MAP = {
10743
+ none: "none",
10749
10744
  b: "bottom",
10750
10745
  t: "top",
10751
10746
  l: "left",
@@ -13502,7 +13497,7 @@ stores.inject(MyMetaStore, storeInstance);
13502
13497
  default: "ffffff",
13503
13498
  }).asString(),
13504
13499
  legendPosition: DRAWING_LEGEND_POSITION_CONVERSION_MAP[this.extractChildAttr(rootChartElement, "c:legendPos", "val", {
13505
- default: "b",
13500
+ default: "none",
13506
13501
  }).asString()],
13507
13502
  stacked: barChartGrouping === "stacked",
13508
13503
  fontColor: "000000",
@@ -13536,7 +13531,7 @@ stores.inject(MyMetaStore, storeInstance);
13536
13531
  default: "ffffff",
13537
13532
  }).asString(),
13538
13533
  legendPosition: DRAWING_LEGEND_POSITION_CONVERSION_MAP[this.extractChildAttr(chartElement, "c:legendPos", "val", {
13539
- default: "b",
13534
+ default: "none",
13540
13535
  }).asString()],
13541
13536
  stacked: barChartGrouping === "stacked",
13542
13537
  fontColor: "000000",
@@ -16728,7 +16723,8 @@ stores.inject(MyMetaStore, storeInstance);
16728
16723
  }
16729
16724
  }
16730
16725
  else if (dataSets.length === 1) {
16731
- for (let i = 0; i < getData(getters, dataSets[0]).length; i++) {
16726
+ const dataLength = getData(getters, dataSets[0]).length;
16727
+ for (let i = 0; i < dataLength; i++) {
16732
16728
  labels.formattedValues.push("");
16733
16729
  labels.values.push("");
16734
16730
  }
@@ -25974,11 +25970,26 @@ stores.inject(MyMetaStore, storeInstance);
25974
25970
  const _searchFor = toString(searchFor).toLowerCase();
25975
25971
  const _textToSearch = toString(textToSearch).toLowerCase();
25976
25972
  const _startingAt = toNumber(startingAt, this.locale);
25977
- assert(() => _textToSearch !== "", _t("The text_to_search must be non-empty."));
25978
- assert(() => _startingAt >= 1, _t("The starting_at (%s) must be greater than or equal to 1.", _startingAt.toString()));
25973
+ if (_textToSearch === "") {
25974
+ return {
25975
+ value: CellErrorType.GenericError,
25976
+ message: _t("The text_to_search must be non-empty."),
25977
+ };
25978
+ }
25979
+ if (_startingAt < 1) {
25980
+ return {
25981
+ value: CellErrorType.GenericError,
25982
+ message: _t("The starting_at (%s) must be greater than or equal to 1.", _startingAt),
25983
+ };
25984
+ }
25979
25985
  const result = _textToSearch.indexOf(_searchFor, _startingAt - 1);
25980
- assert(() => result >= 0, _t("In [[FUNCTION_NAME]] evaluation, cannot find '%s' within '%s'.", _searchFor, _textToSearch));
25981
- return result + 1;
25986
+ if (result === -1) {
25987
+ return {
25988
+ value: CellErrorType.GenericError,
25989
+ message: _t("In [[FUNCTION_NAME]] evaluation, cannot find '%s' within '%s'.", _searchFor, _textToSearch),
25990
+ };
25991
+ }
25992
+ return { value: result + 1 };
25982
25993
  },
25983
25994
  isExported: true,
25984
25995
  };
@@ -27828,11 +27839,14 @@ stores.inject(MyMetaStore, storeInstance);
27828
27839
  }
27829
27840
  }
27830
27841
  function compileTokensOrThrow(tokens) {
27831
- const { dependencies, constantValues, symbols } = formulaArguments(tokens);
27832
- const cacheKey = compilationCacheKey(tokens, dependencies, constantValues);
27842
+ const { dependencies, literalValues, symbols } = formulaArguments(tokens);
27843
+ const cacheKey = compilationCacheKey(tokens);
27833
27844
  if (!functionCache[cacheKey]) {
27834
27845
  const ast = parseTokens([...tokens]);
27835
27846
  const scope = new Scope();
27847
+ let stringCount = 0;
27848
+ let numberCount = 0;
27849
+ let dependencyCount = 0;
27836
27850
  if (ast.type === "BIN_OPERATION" && ast.value === ":") {
27837
27851
  throw new BadExpressionError(_t("Invalid formula"));
27838
27852
  }
@@ -27905,16 +27919,15 @@ stores.inject(MyMetaStore, storeInstance);
27905
27919
  case "BOOLEAN":
27906
27920
  return code.return(`{ value: ${ast.value} }`);
27907
27921
  case "NUMBER":
27908
- return code.return(`{ value: this.constantValues.numbers[${constantValues.numbers.indexOf(ast.value)}] }`);
27922
+ return code.return(`this.literalValues.numbers[${numberCount++}]`);
27909
27923
  case "STRING":
27910
- return code.return(`{ value: this.constantValues.strings[${constantValues.strings.indexOf(ast.value)}] }`);
27924
+ return code.return(`this.literalValues.strings[${stringCount++}]`);
27911
27925
  case "REFERENCE":
27912
- const referenceIndex = dependencies.indexOf(ast.value);
27913
27926
  if ((!isMeta && ast.value.includes(":")) || hasRange) {
27914
- return code.return(`range(deps[${referenceIndex}])`);
27927
+ return code.return(`range(deps[${dependencyCount++}])`);
27915
27928
  }
27916
27929
  else {
27917
- return code.return(`ref(deps[${referenceIndex}], ${isMeta ? "true" : "false"})`);
27930
+ return code.return(`ref(deps[${dependencyCount++}], ${isMeta ? "true" : "false"})`);
27918
27931
  }
27919
27932
  case "FUNCALL":
27920
27933
  const args = compileFunctionArgs(ast).map((arg) => arg.assignResultToVariable());
@@ -27946,7 +27959,7 @@ stores.inject(MyMetaStore, storeInstance);
27946
27959
  const compiledFormula = {
27947
27960
  execute: functionCache[cacheKey],
27948
27961
  dependencies,
27949
- constantValues,
27962
+ literalValues,
27950
27963
  symbols,
27951
27964
  tokens,
27952
27965
  isBadExpression: false,
@@ -27958,33 +27971,31 @@ stores.inject(MyMetaStore, storeInstance);
27958
27971
  * References, numbers and strings are replaced with placeholders because
27959
27972
  * the compiled formula does not depend on their actual value.
27960
27973
  * Both `=A1+1+"2"` and `=A2+2+"3"` are compiled to the exact same function.
27961
- *
27962
27974
  * Spaces are also ignored to compute the cache key.
27963
27975
  *
27964
- * A formula `=A1+A2+SUM(2, 2, "2")` have the cache key `=|0|+|1|+SUM(|N0|,|N0|,|S0|)`
27976
+ * A formula `=A1+A2+SUM(2, 2, "2")` have the cache key `=|C|+|C|+SUM(|N|,|N|,|S|)`
27965
27977
  */
27966
- function compilationCacheKey(tokens, dependencies, constantValues, symbols) {
27978
+ function compilationCacheKey(tokens) {
27967
27979
  let cacheKey = "";
27968
27980
  for (const token of tokens) {
27969
27981
  switch (token.type) {
27970
27982
  case "STRING":
27971
- const value = removeStringQuotes(token.value);
27972
- cacheKey += `|S${constantValues.strings.indexOf(value)}|`;
27983
+ cacheKey += "|S|";
27973
27984
  break;
27974
27985
  case "NUMBER":
27975
- cacheKey += `|N${constantValues.numbers.indexOf(parseNumber(token.value, DEFAULT_LOCALE))}|`;
27986
+ cacheKey += "|N|";
27976
27987
  break;
27977
27988
  case "REFERENCE":
27978
27989
  case "INVALID_REFERENCE":
27979
27990
  if (token.value.includes(":")) {
27980
- cacheKey += `R|${dependencies.indexOf(token.value)}|`;
27991
+ cacheKey += "|R|";
27981
27992
  }
27982
27993
  else {
27983
- cacheKey += `C|${dependencies.indexOf(token.value)}|`;
27994
+ cacheKey += "|C|";
27984
27995
  }
27985
27996
  break;
27986
27997
  case "SPACE":
27987
- cacheKey += "";
27998
+ // ignore spaces
27988
27999
  break;
27989
28000
  default:
27990
28001
  cacheKey += token.value;
@@ -27997,7 +28008,7 @@ stores.inject(MyMetaStore, storeInstance);
27997
28008
  * Return formula arguments which are references, strings and numbers.
27998
28009
  */
27999
28010
  function formulaArguments(tokens) {
28000
- const constantValues = {
28011
+ const literalValues = {
28001
28012
  numbers: [],
28002
28013
  strings: [],
28003
28014
  };
@@ -28011,15 +28022,11 @@ stores.inject(MyMetaStore, storeInstance);
28011
28022
  break;
28012
28023
  case "STRING":
28013
28024
  const value = removeStringQuotes(token.value);
28014
- if (!constantValues.strings.includes(value)) {
28015
- constantValues.strings.push(value);
28016
- }
28025
+ literalValues.strings.push({ value });
28017
28026
  break;
28018
28027
  case "NUMBER": {
28019
28028
  const value = parseNumber(token.value, DEFAULT_LOCALE);
28020
- if (!constantValues.numbers.includes(value)) {
28021
- constantValues.numbers.push(value);
28022
- }
28029
+ literalValues.numbers.push({ value });
28023
28030
  break;
28024
28031
  }
28025
28032
  case "SYMBOL": {
@@ -28030,7 +28037,7 @@ stores.inject(MyMetaStore, storeInstance);
28030
28037
  }
28031
28038
  return {
28032
28039
  dependencies,
28033
- constantValues,
28040
+ literalValues,
28034
28041
  symbols,
28035
28042
  };
28036
28043
  }
@@ -42795,7 +42802,8 @@ stores.inject(MyMetaStore, storeInstance);
42795
42802
  &.pivot-dimension-invalid {
42796
42803
  background-color: #ffdddd;
42797
42804
  border-color: red !important;
42798
- select {
42805
+ select,
42806
+ input {
42799
42807
  background-color: #ffdddd;
42800
42808
  }
42801
42809
  }
@@ -44542,7 +44550,7 @@ stores.inject(MyMetaStore, storeInstance);
44542
44550
  this.notification.notifyUser({
44543
44551
  type: "info",
44544
44552
  text: _t("Pivot updates only work with dynamic pivot tables. Use %s or re-insert the static pivot from the Data menu.", pivotExample),
44545
- sticky: false,
44553
+ sticky: true,
44546
44554
  });
44547
44555
  }
44548
44556
  }
@@ -68298,7 +68306,9 @@ stores.inject(MyMetaStore, storeInstance);
68298
68306
  border: 1px solid;
68299
68307
  font-family: ${DEFAULT_FONT};
68300
68308
 
68301
- .o-composer:empty:not(:focus):not(.active)::before {
68309
+ /* In readonly we always show the fx icon if the composer is empty, not matter the focus */
68310
+ .o-composer:empty:not(:focus):not(.active)::before,
68311
+ &.o-topbar-composer-readonly .o-composer:empty::before {
68302
68312
  content: url("data:image/svg+xml,${encodeURIComponent(FX_SVG)}");
68303
68313
  position: relative;
68304
68314
  top: 20%;
@@ -71659,10 +71669,14 @@ stores.inject(MyMetaStore, storeInstance);
71659
71669
  continue;
71660
71670
  }
71661
71671
  const cfValueObjectNodes = cfValueObject.map((attrs) => escapeXml /*xml*/ `<cfvo ${formatAttributes(attrs)} />`);
71672
+ const iconSetAttrs = [["iconSet", getIconSet(rule.icons)]];
71673
+ if (isIconSetReversed(rule.icons)) {
71674
+ iconSetAttrs.push(["reverse", "1"]);
71675
+ }
71662
71676
  conditionalFormats.push(escapeXml /*xml*/ `
71663
71677
  <conditionalFormatting sqref="${range}">
71664
71678
  <cfRule ${formatAttributes(ruleAttributes)}>
71665
- <iconSet iconSet="${getIconSet(rule.icons)}">
71679
+ <iconSet ${formatAttributes(iconSetAttrs)}>
71666
71680
  ${joinXmlNodes(cfValueObjectNodes)}
71667
71681
  </iconSet>
71668
71682
  </cfRule>
@@ -71680,9 +71694,21 @@ stores.inject(MyMetaStore, storeInstance);
71680
71694
  ["stopIfTrue", cf.stopIfTrue ? 1 : 0],
71681
71695
  ];
71682
71696
  }
71697
+ function isIconSetReversed(iconSet) {
71698
+ const defaultIconSet = ICON_SETS[detectIconsType(iconSet)];
71699
+ return iconSet.upper === defaultIconSet.bad && iconSet.lower === defaultIconSet.good;
71700
+ }
71683
71701
  function getIconSet(iconSet) {
71684
- return XLSX_ICONSET_MAP[Object.keys(XLSX_ICONSET_MAP).find((key) => iconSet.upper.toLowerCase().startsWith(key)) ||
71685
- "dots"];
71702
+ return XLSX_ICONSET_MAP[detectIconsType(iconSet)];
71703
+ }
71704
+ /**
71705
+ * Partial detection based on "upper" point only.
71706
+ * We support any arbitrary icon in the set, while excel doesn't allow
71707
+ * mixing icons from different types.
71708
+ */
71709
+ function detectIconsType(iconSet) {
71710
+ const type = Object.keys(ICON_SETS).find((type) => Object.values(ICON_SETS[type]).includes(iconSet.upper)) || "dots";
71711
+ return type;
71686
71712
  }
71687
71713
  function thresholdAttributes(threshold, position) {
71688
71714
  const type = getExcelThresholdType(threshold.type, position);
@@ -73606,9 +73632,9 @@ stores.inject(MyMetaStore, storeInstance);
73606
73632
  exports.tokenize = tokenize;
73607
73633
 
73608
73634
 
73609
- __info__.version = "18.0.20";
73610
- __info__.date = "2025-03-19T08:21:32.426Z";
73611
- __info__.hash = "3f48d8b";
73635
+ __info__.version = "18.0.22";
73636
+ __info__.date = "2025-04-04T08:42:35.352Z";
73637
+ __info__.hash = "89a3279";
73612
73638
 
73613
73639
 
73614
73640
  })(this.o_spreadsheet = this.o_spreadsheet || {}, owl);