@odoo/o-spreadsheet 17.4.15 → 17.4.17

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.15
6
- * @date 2024-12-05T10:40:45.905Z
7
- * @hash 9b13f22
5
+ * @version 17.4.17
6
+ * @date 2025-01-14T11:34:44.930Z
7
+ * @hash fd2a7ab
8
8
  */
9
9
 
10
10
  (function (exports, owl) {
@@ -767,6 +767,7 @@
767
767
  * https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Regular_Expressions/Character_Classes
768
768
  */
769
769
  const whiteSpaceSpecialCharacters = [
770
+ " ",
770
771
  "\t",
771
772
  "\f",
772
773
  "\v",
@@ -781,17 +782,15 @@
781
782
  String.fromCharCode(parseInt("3000", 16)),
782
783
  String.fromCharCode(parseInt("feff", 16)),
783
784
  ];
784
- const whiteSpaceRegexp = new RegExp(whiteSpaceSpecialCharacters.join("|") + "|(\r\n|\r|\n)", "g");
785
+ const whiteSpaceRegexp = new RegExp(whiteSpaceSpecialCharacters.join("|"), "g");
786
+ const newLineRegexp = /(\r\n|\r)/g;
785
787
  /**
786
- * Replace all the special spaces in a string (non-breaking, tabs, ...) by normal spaces, and all the
787
- * different newlines types by \n.
788
+ * Replace all different newlines characters by \n
788
789
  */
789
- function replaceSpecialSpaces(text) {
790
+ function replaceNewLines(text) {
790
791
  if (!text)
791
792
  return "";
792
- if (!whiteSpaceRegexp.test(text))
793
- return text;
794
- return text.replace(whiteSpaceRegexp, (match, newLine) => (newLine ? NEWLINE : " "));
793
+ return text.replace(newLineRegexp, NEWLINE);
795
794
  }
796
795
  /**
797
796
  * Determine if the numbers are consecutive.
@@ -5683,7 +5682,7 @@
5683
5682
  const POSTFIX_UNARY_OPERATORS = ["%"];
5684
5683
  const OPERATORS = "+,-,*,/,:,=,<>,>=,>,<=,<,^,&".split(",").concat(POSTFIX_UNARY_OPERATORS);
5685
5684
  function tokenize(str, locale = DEFAULT_LOCALE) {
5686
- str = replaceSpecialSpaces(str);
5685
+ str = replaceNewLines(str);
5687
5686
  const chars = new TokenizingChars(str);
5688
5687
  const result = [];
5689
5688
  while (!chars.isOver()) {
@@ -5830,12 +5829,12 @@
5830
5829
  if (length) {
5831
5830
  return { type: "SPACE", value: NEWLINE.repeat(length) };
5832
5831
  }
5833
- while (chars.current === " ") {
5834
- length++;
5835
- chars.shift();
5832
+ let spaces = "";
5833
+ while (chars.current && chars.current.match(whiteSpaceRegexp)) {
5834
+ spaces += chars.shift();
5836
5835
  }
5837
- if (length) {
5838
- return { type: "SPACE", value: " ".repeat(length) };
5836
+ if (spaces) {
5837
+ return { type: "SPACE", value: spaces };
5839
5838
  }
5840
5839
  return null;
5841
5840
  }
@@ -20367,6 +20366,17 @@ stores.inject(MyMetaStore, storeInstance);
20367
20366
  },
20368
20367
  isExported: true,
20369
20368
  };
20369
+ // -----------------------------------------------------------------------------
20370
+ // VALUE
20371
+ // -----------------------------------------------------------------------------
20372
+ const VALUE = {
20373
+ description: _t("Converts a string to a numeric value."),
20374
+ args: [arg("value (number)", _t("the string to be converted"))],
20375
+ compute: function (value) {
20376
+ return toNumber(value, this.locale);
20377
+ },
20378
+ isExported: true,
20379
+ };
20370
20380
 
20371
20381
  var text = /*#__PURE__*/Object.freeze({
20372
20382
  __proto__: null,
@@ -20389,7 +20399,8 @@ stores.inject(MyMetaStore, storeInstance);
20389
20399
  TEXT: TEXT,
20390
20400
  TEXTJOIN: TEXTJOIN,
20391
20401
  TRIM: TRIM,
20392
- UPPER: UPPER
20402
+ UPPER: UPPER,
20403
+ VALUE: VALUE
20393
20404
  });
20394
20405
 
20395
20406
  // -----------------------------------------------------------------------------
@@ -20646,14 +20657,11 @@ stores.inject(MyMetaStore, storeInstance);
20646
20657
  });
20647
20658
 
20648
20659
  class DOMFocusableElementStore {
20649
- mutators = ["setFocusableElement", "focus"];
20660
+ mutators = ["setFocusableElement"];
20650
20661
  focusableElement = undefined;
20651
20662
  setFocusableElement(element) {
20652
20663
  this.focusableElement = element;
20653
20664
  }
20654
- focus() {
20655
- this.focusableElement?.focus();
20656
- }
20657
20665
  }
20658
20666
 
20659
20667
  /**
@@ -24681,7 +24689,11 @@ stores.inject(MyMetaStore, storeInstance);
24681
24689
  colors.push(chartColors.upperColor);
24682
24690
  return {
24683
24691
  background: getters.getStyleOfSingleCellChart(chart.background, dataRange).background,
24684
- title: chart.title ?? { text: "" },
24692
+ title: {
24693
+ ...chart.title,
24694
+ // chart titles are extracted from .json files and they are translated at runtime here
24695
+ text: _t(chart.title.text ?? ""),
24696
+ },
24685
24697
  minValue: {
24686
24698
  value: minValue,
24687
24699
  label: formatValue(minValue, { locale, format }),
@@ -38565,6 +38577,7 @@ stores.inject(MyMetaStore, storeInstance);
38565
38577
  }
38566
38578
  }
38567
38579
  `;
38580
+ const DEFAULT_TABLE_STYLE_COLOR = "#3C78D8";
38568
38581
  class TableStyleEditorPanel extends owl.Component {
38569
38582
  static template = "o-spreadsheet-TableStyleEditorPanel";
38570
38583
  static components = { Section, RoundColorPicker, TableStylePreview };
@@ -38583,7 +38596,7 @@ stores.inject(MyMetaStore, storeInstance);
38583
38596
  : null;
38584
38597
  return {
38585
38598
  pickerOpened: false,
38586
- primaryColor: editedStyle?.primaryColor || "#3C78D8",
38599
+ primaryColor: editedStyle?.primaryColor || DEFAULT_TABLE_STYLE_COLOR,
38587
38600
  selectedTemplateName: editedStyle?.templateName || "lightColoredText",
38588
38601
  styleName: editedStyle?.displayName || this.env.model.getters.getNewCustomTableStyleName(),
38589
38602
  };
@@ -38592,7 +38605,7 @@ stores.inject(MyMetaStore, storeInstance);
38592
38605
  this.state.pickerOpened = !this.state.pickerOpened;
38593
38606
  }
38594
38607
  onColorPicked(color) {
38595
- this.state.primaryColor = color;
38608
+ this.state.primaryColor = isColorValid(color) ? color : DEFAULT_TABLE_STYLE_COLOR;
38596
38609
  this.state.pickerOpened = false;
38597
38610
  }
38598
38611
  onTemplatePicked(templateName) {
@@ -39420,7 +39433,7 @@ stores.inject(MyMetaStore, storeInstance);
39420
39433
  this.isEditing = isEditing;
39421
39434
  if (!isEditing) {
39422
39435
  this.rect = this.defaultRect;
39423
- this.DOMFocusableElementStore.focus();
39436
+ this.DOMFocusableElementStore.focusableElement?.focus();
39424
39437
  return;
39425
39438
  }
39426
39439
  const position = this.env.model.getters.getActivePosition();
@@ -39939,6 +39952,7 @@ stores.inject(MyMetaStore, storeInstance);
39939
39952
  draggedFigure: undefined,
39940
39953
  horizontalSnap: undefined,
39941
39954
  verticalSnap: undefined,
39955
+ cancelDnd: undefined,
39942
39956
  });
39943
39957
  setup() {
39944
39958
  owl.onMounted(() => {
@@ -39951,12 +39965,28 @@ stores.inject(MyMetaStore, storeInstance);
39951
39965
  // new rendering
39952
39966
  this.render();
39953
39967
  });
39968
+ owl.onWillUpdateProps(() => {
39969
+ const sheetId = this.env.model.getters.getActiveSheetId();
39970
+ const draggedFigureId = this.dnd.draggedFigure?.id;
39971
+ if (draggedFigureId && !this.env.model.getters.getFigure(sheetId, draggedFigureId)) {
39972
+ if (this.dnd.cancelDnd) {
39973
+ this.dnd.cancelDnd();
39974
+ }
39975
+ this.dnd.draggedFigure = undefined;
39976
+ this.dnd.horizontalSnap = undefined;
39977
+ this.dnd.verticalSnap = undefined;
39978
+ this.dnd.cancelDnd = undefined;
39979
+ }
39980
+ });
39954
39981
  }
39955
39982
  getVisibleFigures() {
39956
39983
  const visibleFigures = this.env.model.getters.getVisibleFigures();
39957
39984
  if (this.dnd.draggedFigure &&
39958
39985
  !visibleFigures.some((figure) => figure.id === this.dnd.draggedFigure?.id)) {
39959
- visibleFigures.push(this.env.model.getters.getFigure(this.env.model.getters.getActiveSheetId(), this.dnd.draggedFigure?.id));
39986
+ const draggedFigure = this.env.model.getters.getFigure(this.env.model.getters.getActiveSheetId(), this.dnd.draggedFigure?.id);
39987
+ if (draggedFigure) {
39988
+ visibleFigures.push(draggedFigure);
39989
+ }
39960
39990
  }
39961
39991
  return visibleFigures;
39962
39992
  }
@@ -40075,7 +40105,7 @@ stores.inject(MyMetaStore, storeInstance);
40075
40105
  this.dnd.verticalSnap = undefined;
40076
40106
  this.env.model.dispatch("UPDATE_FIGURE", { sheetId, id: figure.id, x, y });
40077
40107
  };
40078
- startDnd(onMouseMove, onMouseUp);
40108
+ this.dnd.cancelDnd = startDnd(onMouseMove, onMouseUp);
40079
40109
  }
40080
40110
  /**
40081
40111
  * Initialize the resize of a figure with mouse movements
@@ -40123,7 +40153,7 @@ stores.inject(MyMetaStore, storeInstance);
40123
40153
  this.dnd.horizontalSnap = undefined;
40124
40154
  this.dnd.verticalSnap = undefined;
40125
40155
  };
40126
- startDnd(onMouseMove, onMouseUp);
40156
+ this.dnd.cancelDnd = startDnd(onMouseMove, onMouseUp);
40127
40157
  }
40128
40158
  getOtherFigures(figId) {
40129
40159
  return this.getVisibleFigures().filter((f) => f.id !== figId);
@@ -42443,7 +42473,7 @@ stores.inject(MyMetaStore, storeInstance);
42443
42473
  this.cellPopovers = useStore(CellPopoverStore);
42444
42474
  owl.useEffect(() => {
42445
42475
  if (!this.sidePanel.isOpen) {
42446
- this.DOMFocusableElementStore.focus();
42476
+ this.DOMFocusableElementStore.focusableElement?.focus();
42447
42477
  }
42448
42478
  }, () => [this.sidePanel.isOpen]);
42449
42479
  }
@@ -42647,7 +42677,7 @@ stores.inject(MyMetaStore, storeInstance);
42647
42677
  focusDefaultElement() {
42648
42678
  if (!this.env.model.getters.getSelectedFigureId() &&
42649
42679
  this.composerStore.editionMode === "inactive") {
42650
- this.DOMFocusableElementStore.focus();
42680
+ this.DOMFocusableElementStore.focusableElement?.focus();
42651
42681
  }
42652
42682
  }
42653
42683
  get gridEl() {
@@ -44264,7 +44294,7 @@ stores.inject(MyMetaStore, storeInstance);
44264
44294
  vertical: style.verticalAlign
44265
44295
  ? V_ALIGNMENT_EXPORT_CONVERSION_MAP[style.verticalAlign]
44266
44296
  : undefined,
44267
- wrapText: style.wrapping === "wrap" || undefined,
44297
+ wrapText: style.wrapping === "wrap" || cell.content?.includes(NEWLINE) ? true : undefined,
44268
44298
  },
44269
44299
  };
44270
44300
  styles.font["strike"] = !!style?.strikethrough || undefined;
@@ -44487,7 +44517,7 @@ stores.inject(MyMetaStore, storeInstance);
44487
44517
  return undefined;
44488
44518
  }
44489
44519
  function isChartData(data) {
44490
- return "dataSets" in data;
44520
+ return "dataSets" in data && data.dataSets.length > 0;
44491
44521
  }
44492
44522
  function isImageData(data) {
44493
44523
  return "imageSrc" in data;
@@ -44725,9 +44755,8 @@ stores.inject(MyMetaStore, storeInstance);
44725
44755
  }
44726
44756
  return rows;
44727
44757
  }
44728
- /** Remove newlines (\n) in shared strings, We do not support them */
44729
44758
  function convertSharedStrings(xlsxSharedStrings) {
44730
- return xlsxSharedStrings.map((str) => str.replace(/\n/g, ""));
44759
+ return xlsxSharedStrings.map(replaceNewLines);
44731
44760
  }
44732
44761
  function convertCells(sheet, data, sheetDims, warningManager) {
44733
44762
  const cells = {};
@@ -45496,15 +45525,10 @@ stores.inject(MyMetaStore, storeInstance);
45496
45525
  getSharedStrings() {
45497
45526
  return this.mapOnElements({ parent: this.rootFile.file.xml, query: "si" }, (ssElement) => {
45498
45527
  // Shared string can either be a simple text, or a rich text (text with formatting, possibly in multiple parts)
45499
- if (ssElement.children[0].tagName === "t") {
45500
- return this.extractTextContent(ssElement) || "";
45501
- }
45502
45528
  // We don't support rich text formatting, we'll only extract the text
45503
- else {
45504
- return this.mapOnElements({ parent: ssElement, query: "t" }, (textElement) => {
45505
- return this.extractTextContent(textElement) || "";
45506
- }).join("");
45507
- }
45529
+ return this.mapOnElements({ parent: ssElement, query: "t" }, (textElement) => {
45530
+ return this.extractTextContent(textElement) || "";
45531
+ }).join("");
45508
45532
  });
45509
45533
  }
45510
45534
  }
@@ -47378,10 +47402,34 @@ stores.inject(MyMetaStore, storeInstance);
47378
47402
  const elements = [...cmd.elements].sort((a, b) => b - a);
47379
47403
  for (const group of groupConsecutive(elements)) {
47380
47404
  if (cmd.dimension === "COL") {
47381
- this.shiftBordersHorizontally(cmd.sheetId, group[group.length - 1] + 1, -group.length);
47405
+ if (group[0] >= this.getters.getNumberCols(cmd.sheetId)) {
47406
+ for (let row = 0; row < this.getters.getNumberRows(cmd.sheetId); row++) {
47407
+ this.history.update("borders", cmd.sheetId, group[0] + 1, row, "vertical", undefined);
47408
+ }
47409
+ }
47410
+ if (group[group.length - 1] === 0) {
47411
+ for (let row = 0; row < this.getters.getNumberRows(cmd.sheetId); row++) {
47412
+ this.history.update("borders", cmd.sheetId, 0, row, "vertical", undefined);
47413
+ }
47414
+ }
47415
+ const zone = this.getters.getColsZone(cmd.sheetId, group[group.length - 1] + 1, group[0]);
47416
+ this.clearInsideBorders(cmd.sheetId, [zone]);
47417
+ this.shiftBordersHorizontally(cmd.sheetId, group[0] + 1, -group.length);
47382
47418
  }
47383
47419
  else {
47384
- this.shiftBordersVertically(cmd.sheetId, group[group.length - 1] + 1, -group.length);
47420
+ if (group[0] >= this.getters.getNumberRows(cmd.sheetId)) {
47421
+ for (let col = 0; col < this.getters.getNumberCols(cmd.sheetId); col++) {
47422
+ this.history.update("borders", cmd.sheetId, col, group[0] + 1, "horizontal", undefined);
47423
+ }
47424
+ }
47425
+ if (group[group.length - 1] === 0) {
47426
+ for (let col = 0; col < this.getters.getNumberCols(cmd.sheetId); col++) {
47427
+ this.history.update("borders", cmd.sheetId, col, 0, "horizontal", undefined);
47428
+ }
47429
+ }
47430
+ const zone = this.getters.getRowsZone(cmd.sheetId, group[group.length - 1] + 1, group[0]);
47431
+ this.clearInsideBorders(cmd.sheetId, [zone]);
47432
+ this.shiftBordersVertically(cmd.sheetId, group[0] + 1, -group.length);
47385
47433
  }
47386
47434
  }
47387
47435
  break;
@@ -47688,6 +47736,18 @@ stores.inject(MyMetaStore, storeInstance);
47688
47736
  }
47689
47737
  }
47690
47738
  }
47739
+ /**
47740
+ * Remove the borders inside of a zone
47741
+ */
47742
+ clearInsideBorders(sheetId, zones) {
47743
+ for (let zone of zones) {
47744
+ for (let row = zone.top; row <= zone.bottom; row++) {
47745
+ for (let col = zone.left; col <= zone.right; col++) {
47746
+ this.history.update("borders", sheetId, col, row, undefined);
47747
+ }
47748
+ }
47749
+ }
47750
+ }
47691
47751
  /**
47692
47752
  * Add a border to the existing one to a cell
47693
47753
  */
@@ -48235,7 +48295,7 @@ stores.inject(MyMetaStore, storeInstance);
48235
48295
  const before = this.getters.getCell({ sheetId, col, row });
48236
48296
  const hasContent = "content" in after || "formula" in after;
48237
48297
  // Compute the new cell properties
48238
- const afterContent = hasContent ? replaceSpecialSpaces(after?.content) : before?.content || "";
48298
+ const afterContent = hasContent ? replaceNewLines(after?.content) : before?.content || "";
48239
48299
  let style;
48240
48300
  if (after.style !== undefined) {
48241
48301
  style = after.style || undefined;
@@ -51096,6 +51156,9 @@ stores.inject(MyMetaStore, storeInstance);
51096
51156
  };
51097
51157
  }
51098
51158
  getUnboundedZone(sheetId, zone) {
51159
+ if (zone.bottom === undefined || zone.right === undefined) {
51160
+ return zone;
51161
+ }
51099
51162
  const isFullRow = zone.left === 0 && zone.right === this.getNumberCols(sheetId) - 1;
51100
51163
  const isFullCol = zone.top === 0 && zone.bottom === this.getNumberRows(sheetId) - 1;
51101
51164
  return {
@@ -57514,6 +57577,9 @@ stores.inject(MyMetaStore, storeInstance);
57514
57577
  }
57515
57578
  }
57516
57579
  this.acknowledge(message);
57580
+ if (message.type === "REMOTE_REVISION" && message.clientId === this.clientId) {
57581
+ return;
57582
+ }
57517
57583
  this.trigger("collaborative-event-received");
57518
57584
  }
57519
57585
  onClientMoved(message) {
@@ -61305,6 +61371,7 @@ stores.inject(MyMetaStore, storeInstance);
61305
61371
  break;
61306
61372
  case "DELETE_SHEET":
61307
61373
  this.cleanViewports();
61374
+ this.sheetsWithDirtyViewports.delete(cmd.sheetId);
61308
61375
  break;
61309
61376
  case "ACTIVATE_SHEET":
61310
61377
  this.sheetsWithDirtyViewports.add(cmd.sheetIdTo);
@@ -62266,11 +62333,6 @@ stores.inject(MyMetaStore, storeInstance);
62266
62333
  editionState = "initializing";
62267
62334
  DOMFocusableElementStore;
62268
62335
  setup() {
62269
- owl.onMounted(() => {
62270
- if (this.isSheetActive) {
62271
- this.scrollToSheet();
62272
- }
62273
- });
62274
62336
  owl.onPatched(() => {
62275
62337
  if (this.sheetNameRef.el && this.state.isEditing && this.editionState === "initializing") {
62276
62338
  this.editionState = "editing";
@@ -62278,6 +62340,11 @@ stores.inject(MyMetaStore, storeInstance);
62278
62340
  }
62279
62341
  });
62280
62342
  this.DOMFocusableElementStore = useStore(DOMFocusableElementStore);
62343
+ owl.useEffect((sheetId) => {
62344
+ if (this.props.sheetId === sheetId) {
62345
+ this.scrollToSheet();
62346
+ }
62347
+ }, () => [this.env.model.getters.getActiveSheetId()]);
62281
62348
  }
62282
62349
  focusInputAndSelectContent() {
62283
62350
  if (!this.state.isEditing || !this.sheetNameRef.el)
@@ -62289,7 +62356,10 @@ stores.inject(MyMetaStore, storeInstance);
62289
62356
  }
62290
62357
  }
62291
62358
  scrollToSheet() {
62292
- this.sheetDivRef.el?.scrollIntoView?.();
62359
+ this.sheetDivRef.el?.scrollIntoView?.({
62360
+ behavior: "smooth",
62361
+ inline: "nearest",
62362
+ });
62293
62363
  }
62294
62364
  onFocusOut() {
62295
62365
  if (this.state.isEditing && this.editionState !== "initializing") {
@@ -62319,11 +62389,11 @@ stores.inject(MyMetaStore, storeInstance);
62319
62389
  if (ev.key === "Enter") {
62320
62390
  ev.preventDefault();
62321
62391
  this.stopEdition();
62322
- this.DOMFocusableElementStore.focus();
62392
+ this.DOMFocusableElementStore.focusableElement?.focus();
62323
62393
  }
62324
62394
  if (ev.key === "Escape") {
62325
62395
  this.cancelEdition();
62326
- this.DOMFocusableElementStore.focus();
62396
+ this.DOMFocusableElementStore.focusableElement?.focus();
62327
62397
  }
62328
62398
  }
62329
62399
  onMouseEventSheetName(ev) {
@@ -63728,7 +63798,6 @@ stores.inject(MyMetaStore, storeInstance);
63728
63798
  height: fit-content;
63729
63799
  margin-top: -1px;
63730
63800
  border: 1px solid;
63731
- z-index: ${ComponentsImportance.TopBarComposer};
63732
63801
 
63733
63802
  .o-composer:empty:not(:focus):not(.active)::before {
63734
63803
  content: url("data:image/svg+xml,${encodeURIComponent(FX_SVG)}");
@@ -63770,6 +63839,7 @@ stores.inject(MyMetaStore, storeInstance);
63770
63839
  }
63771
63840
  return cssPropertiesToCss({
63772
63841
  "border-color": SELECTION_BORDER_COLOR,
63842
+ "z-index": String(ComponentsImportance.TopBarComposer),
63773
63843
  });
63774
63844
  }
63775
63845
  get delimitation() {
@@ -67858,7 +67928,12 @@ stores.inject(MyMetaStore, storeInstance);
67858
67928
  ["count", strings.length],
67859
67929
  ["uniqueCount", strings.length],
67860
67930
  ];
67861
- const stringNodes = strings.map((string) => escapeXml /*xml*/ `<si><t>${string}</t></si>`);
67931
+ const stringNodes = strings.map((string) => {
67932
+ if (string.trim() !== string) {
67933
+ return escapeXml /*xml*/ `<si><t xml:space="preserve">${string}</t></si>`;
67934
+ }
67935
+ return escapeXml /*xml*/ `<si><t>${string}</t></si>`;
67936
+ });
67862
67937
  const xml = escapeXml /*xml*/ `
67863
67938
  <sst ${formatAttributes(namespaces)}>
67864
67939
  ${joinXmlNodes(stringNodes)}
@@ -68595,6 +68670,8 @@ stores.inject(MyMetaStore, storeInstance);
68595
68670
  areDomainArgsFieldsValid,
68596
68671
  formatTickValue,
68597
68672
  sanitizeSheetName,
68673
+ isNumber,
68674
+ isDateTime,
68598
68675
  };
68599
68676
  const links = {
68600
68677
  isMarkdownLink,
@@ -68725,9 +68802,9 @@ stores.inject(MyMetaStore, storeInstance);
68725
68802
  exports.tokenize = tokenize;
68726
68803
 
68727
68804
 
68728
- __info__.version = "17.4.15";
68729
- __info__.date = "2024-12-05T10:40:45.905Z";
68730
- __info__.hash = "9b13f22";
68805
+ __info__.version = "17.4.17";
68806
+ __info__.date = "2025-01-14T11:34:44.930Z";
68807
+ __info__.hash = "fd2a7ab";
68731
68808
 
68732
68809
 
68733
68810
  })(this.o_spreadsheet = this.o_spreadsheet || {}, owl);