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