@odoo/o-spreadsheet 16.4.14 → 16.4.16

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.
@@ -298,13 +298,6 @@
298
298
  const FONT_SIZES = [6, 7, 8, 9, 10, 11, 12, 14, 18, 24, 36];
299
299
 
300
300
  //------------------------------------------------------------------------------
301
- /**
302
- * Stringify an object, like JSON.stringify, except that the first level of keys
303
- * is ordered.
304
- */
305
- function stringify(obj) {
306
- return JSON.stringify(obj, Object.keys(obj).sort());
307
- }
308
301
  /**
309
302
  * Remove quotes from a quoted string
310
303
  * ```js
@@ -524,7 +517,7 @@
524
517
  */
525
518
  function getItemId(item, itemsDic) {
526
519
  for (let [key, value] of Object.entries(itemsDic)) {
527
- if (stringify(value) === stringify(item)) {
520
+ if (deepEquals(value, item)) {
528
521
  return parseInt(key, 10);
529
522
  }
530
523
  }
@@ -669,8 +662,7 @@
669
662
  *
670
663
  * https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Regular_Expressions/Character_Classes
671
664
  */
672
- const whiteSpaceCharacters = [
673
- " ",
665
+ const whiteSpaceSpecialCharacters = [
674
666
  "\t",
675
667
  "\f",
676
668
  "\v",
@@ -685,8 +677,7 @@
685
677
  String.fromCharCode(parseInt("3000", 16)),
686
678
  String.fromCharCode(parseInt("feff", 16)),
687
679
  ];
688
- const whiteSpaceRegexp = new RegExp(whiteSpaceCharacters.join("|"), "g");
689
- const newLineRegex = /\r\n|\r|\n/g;
680
+ const whiteSpaceRegexp = new RegExp(whiteSpaceSpecialCharacters.join("|") + "|(\r\n|\r|\n)", "g");
690
681
  /**
691
682
  * Replace all the special spaces in a string (non-breaking, tabs, ...) by normal spaces, and all the
692
683
  * different newlines types by \n.
@@ -694,9 +685,9 @@
694
685
  function replaceSpecialSpaces(text) {
695
686
  if (!text)
696
687
  return "";
697
- text = text.replace(whiteSpaceRegexp, " ");
698
- text = text.replace(newLineRegex, NEWLINE);
699
- return text;
688
+ if (!whiteSpaceRegexp.test(text))
689
+ return text;
690
+ return text.replace(whiteSpaceRegexp, (match, newLine) => (newLine ? NEWLINE : " "));
700
691
  }
701
692
  /**
702
693
  * Determine if the numbers are consecutive.
@@ -2344,8 +2335,10 @@
2344
2335
  const cellReference = new RegExp(/\$?([A-Z]{1,3})\$?([0-9]{1,7})/, "i");
2345
2336
  // Same as above, but matches the exact string (nothing before or after)
2346
2337
  const singleCellReference = new RegExp(/^\$?([A-Z]{1,3})\$?([0-9]{1,7})$/, "i");
2347
- /** Reference of a column header (eg. A, AB) */
2348
- const colHeader = new RegExp(/^([A-Z]{1,3})+$/, "i");
2338
+ /** Reference of a column header (eg. A, AB, $A) */
2339
+ const colHeader = new RegExp(/^\$?([A-Z]{1,3})+$/, "i");
2340
+ /** Reference of a row header (eg. 1, $1) */
2341
+ const rowHeader = new RegExp(/^\$?([0-9]{1,7})+$/, "i");
2349
2342
  /** Reference of a column (eg. A, $CA, Sheet1!B) */
2350
2343
  const colReference = new RegExp(/^\s*('.+'!|[^']+!)?\$?([A-Z]{1,3})$/, "i");
2351
2344
  /** Reference of a row (eg. 1, 59, Sheet1!9) */
@@ -2375,6 +2368,9 @@
2375
2368
  function isColHeader(str) {
2376
2369
  return colHeader.test(str);
2377
2370
  }
2371
+ function isRowHeader(str) {
2372
+ return rowHeader.test(str);
2373
+ }
2378
2374
  /**
2379
2375
  * Return true if the given xc is the reference of a single cell,
2380
2376
  * without any specified sheet (e.g. A1)
@@ -3047,18 +3043,10 @@
3047
3043
  if (isFullCol) {
3048
3044
  parts[0].rowFixed = parts[0].rowFixed || parts[1].rowFixed;
3049
3045
  parts[1].rowFixed = parts[0].rowFixed || parts[1].rowFixed;
3050
- if (zone.left === zone.right) {
3051
- parts[0].colFixed = parts[0].colFixed || parts[1].colFixed;
3052
- parts[1].colFixed = parts[0].colFixed || parts[1].colFixed;
3053
- }
3054
3046
  }
3055
3047
  if (isFullRow) {
3056
3048
  parts[0].colFixed = parts[0].colFixed || parts[1].colFixed;
3057
3049
  parts[1].colFixed = parts[0].colFixed || parts[1].colFixed;
3058
- if (zone.top === zone.bottom) {
3059
- parts[0].rowFixed = parts[0].rowFixed || parts[1].rowFixed;
3060
- parts[1].rowFixed = parts[0].rowFixed || parts[1].rowFixed;
3061
- }
3062
3050
  }
3063
3051
  return parts;
3064
3052
  }
@@ -4266,6 +4254,7 @@
4266
4254
  CommandResult[CommandResult["NoActiveSheet"] = 94] = "NoActiveSheet";
4267
4255
  CommandResult[CommandResult["InvalidLocale"] = 95] = "InvalidLocale";
4268
4256
  CommandResult[CommandResult["AlreadyInPaintingFormatMode"] = 96] = "AlreadyInPaintingFormatMode";
4257
+ CommandResult[CommandResult["NoChanges"] = 97] = "NoChanges";
4269
4258
  })(exports.CommandResult || (exports.CommandResult = {}));
4270
4259
 
4271
4260
  const borderStyles = ["thin", "medium", "thick", "dashed", "dotted"];
@@ -4355,12 +4344,12 @@
4355
4344
  }
4356
4345
  }
4357
4346
  /** Normalize string by setting it to lowercase and replacing accent letters with plain letters */
4358
- function normalizeString(str) {
4347
+ const normalizeString = memoize(function normalizeString(str) {
4359
4348
  return str
4360
4349
  .toLowerCase()
4361
4350
  .normalize("NFD")
4362
4351
  .replace(/[\u0300-\u036f]/g, "");
4363
- }
4352
+ });
4364
4353
  /**
4365
4354
  * Normalize a value.
4366
4355
  * If the cell value is a string, this will set it to lowercase and replacing accent letters with plain letters
@@ -5646,6 +5635,9 @@
5646
5635
  }
5647
5636
  function getOpenedMenus() {
5648
5637
  return Array.from(document.querySelectorAll(".o-spreadsheet .o-menu"));
5638
+ }
5639
+ function isMacOS() {
5640
+ return navigator.userAgent.toUpperCase().indexOf("MAC") >= 0;
5649
5641
  }
5650
5642
 
5651
5643
  /**
@@ -14171,7 +14163,7 @@
14171
14163
  return result;
14172
14164
  }
14173
14165
  function tokenizeDebugger(chars) {
14174
- if (chars.current() === "?") {
14166
+ if (chars.current === "?") {
14175
14167
  chars.shift();
14176
14168
  return { type: "DEBUGGER", value: "?" };
14177
14169
  }
@@ -14182,7 +14174,7 @@
14182
14174
  ")": "RIGHT_PAREN",
14183
14175
  };
14184
14176
  function tokenizeMisc(chars) {
14185
- if (chars.current() in misc) {
14177
+ if (chars.current in misc) {
14186
14178
  const value = chars.shift();
14187
14179
  const type = misc[value];
14188
14180
  return { type, value };
@@ -14190,7 +14182,7 @@
14190
14182
  return null;
14191
14183
  }
14192
14184
  function tokenizeArgsSeparator(chars, locale) {
14193
- if (chars.current() === locale.formulaArgSeparator) {
14185
+ if (chars.current === locale.formulaArgSeparator) {
14194
14186
  const value = chars.shift();
14195
14187
  const type = "ARG_SEPARATOR";
14196
14188
  return { type, value };
@@ -14215,14 +14207,13 @@
14215
14207
  return null;
14216
14208
  }
14217
14209
  function tokenizeString(chars) {
14218
- if (chars.current() === '"') {
14210
+ if (chars.current === '"') {
14219
14211
  const startChar = chars.shift();
14220
14212
  let letters = startChar;
14221
- while (chars.current() &&
14222
- (chars.current() !== startChar || letters[letters.length - 1] === "\\")) {
14213
+ while (chars.current && (chars.current !== startChar || letters[letters.length - 1] === "\\")) {
14223
14214
  letters += chars.shift();
14224
14215
  }
14225
- if (chars.current() === '"') {
14216
+ if (chars.current === '"') {
14226
14217
  letters += chars.shift();
14227
14218
  }
14228
14219
  return {
@@ -14249,14 +14240,14 @@
14249
14240
  let result = "";
14250
14241
  // there are two main cases to manage: either something which starts with
14251
14242
  // a ', like 'Sheet 2'A2, or a word-like element.
14252
- if (chars.current() === "'") {
14243
+ if (chars.current === "'") {
14253
14244
  let lastChar = chars.shift();
14254
14245
  result += lastChar;
14255
- while (chars.current()) {
14246
+ while (chars.current) {
14256
14247
  lastChar = chars.shift();
14257
14248
  result += lastChar;
14258
14249
  if (lastChar === "'") {
14259
- if (chars.current() && chars.current() === "'") {
14250
+ if (chars.current && chars.current === "'") {
14260
14251
  lastChar = chars.shift();
14261
14252
  result += lastChar;
14262
14253
  }
@@ -14272,7 +14263,7 @@
14272
14263
  };
14273
14264
  }
14274
14265
  }
14275
- while (chars.current() && separatorRegexp.test(chars.current())) {
14266
+ while (chars.current && separatorRegexp.test(chars.current)) {
14276
14267
  result += chars.shift();
14277
14268
  }
14278
14269
  if (result.length) {
@@ -14287,14 +14278,14 @@
14287
14278
  }
14288
14279
  function tokenizeSpace(chars) {
14289
14280
  let length = 0;
14290
- while (chars.current() === NEWLINE) {
14281
+ while (chars.current === NEWLINE) {
14291
14282
  length++;
14292
14283
  chars.shift();
14293
14284
  }
14294
14285
  if (length) {
14295
14286
  return { type: "SPACE", value: NEWLINE.repeat(length) };
14296
14287
  }
14297
- while (chars.current() === " ") {
14288
+ while (chars.current === " ") {
14298
14289
  length++;
14299
14290
  chars.shift();
14300
14291
  }
@@ -14313,17 +14304,20 @@
14313
14304
  class TokenizingChars {
14314
14305
  text;
14315
14306
  currentIndex = 0;
14307
+ current;
14316
14308
  constructor(text) {
14317
14309
  this.text = text;
14318
- }
14319
- current() {
14320
- return this.text[this.currentIndex];
14310
+ this.current = text[0];
14321
14311
  }
14322
14312
  shift() {
14323
- return this.text[this.currentIndex++];
14313
+ const current = this.current;
14314
+ const next = this.text[++this.currentIndex];
14315
+ this.current = next;
14316
+ return current;
14324
14317
  }
14325
14318
  advanceBy(length) {
14326
14319
  this.currentIndex += length;
14320
+ this.current = this.text[this.currentIndex];
14327
14321
  }
14328
14322
  isOver() {
14329
14323
  return this.currentIndex >= this.text.length;
@@ -14700,7 +14694,7 @@
14700
14694
  SPACE: goTo(State.RightRef),
14701
14695
  NUMBER: goTo(State.Found),
14702
14696
  REFERENCE: goTo(State.Found, (token) => isSingleCellReference(token.value)),
14703
- SYMBOL: goTo(State.Found, (token) => isColHeader(token.value)),
14697
+ SYMBOL: goTo(State.Found, (token) => isColHeader(token.value) || isRowHeader(token.value)),
14704
14698
  },
14705
14699
  [State.RightColumnRef]: {
14706
14700
  SPACE: goTo(State.RightColumnRef),
@@ -14711,6 +14705,7 @@
14711
14705
  SPACE: goTo(State.RightRowRef),
14712
14706
  NUMBER: goTo(State.Found),
14713
14707
  REFERENCE: goTo(State.Found, (token) => isSingleCellReference(token.value)),
14708
+ SYMBOL: goTo(State.Found, (token) => isRowHeader(token.value)),
14714
14709
  },
14715
14710
  [State.Found]: {},
14716
14711
  };
@@ -16868,7 +16863,7 @@
16868
16863
  });
16869
16864
  return {
16870
16865
  chartJsConfig: config,
16871
- background: getters.getBackgroundOfSingleCellChart(chart.background, dataRange),
16866
+ background: getters.getStyleOfSingleCellChart(chart.background, dataRange).background,
16872
16867
  };
16873
16868
  }
16874
16869
 
@@ -17592,7 +17587,7 @@
17592
17587
  };
17593
17588
  baselineCell = getters.getEvaluatedCell(baselinePosition);
17594
17589
  }
17595
- const background = getters.getBackgroundOfSingleCellChart(chart.background, chart.keyValue);
17590
+ const { background, fontColor } = getters.getStyleOfSingleCellChart(chart.background, chart.keyValue);
17596
17591
  const locale = getters.getLocale();
17597
17592
  return {
17598
17593
  title: _t(chart.title),
@@ -17601,7 +17596,7 @@
17601
17596
  baselineArrow: getBaselineArrowDirection(baselineCell, keyValueCell, chart.baselineMode),
17602
17597
  baselineColor: getBaselineColor(baselineCell, chart.baselineMode, keyValueCell, chart.baselineColorUp, chart.baselineColorDown),
17603
17598
  baselineDescr: chart.baselineDescr ? _t(chart.baselineDescr) : "",
17604
- fontColor: chartFontColor(background),
17599
+ fontColor,
17605
17600
  background,
17606
17601
  baselineStyle: chart.baselineMode !== "percentage" && baseline
17607
17602
  ? getters.getCellStyle({
@@ -25617,6 +25612,7 @@
25617
25612
  draggerShadowThickness: 0,
25618
25613
  delta: 0,
25619
25614
  base: 0,
25615
+ position: "before",
25620
25616
  });
25621
25617
  _computeHandleDisplay(ev) {
25622
25618
  const position = this._getEvOffset(ev);
@@ -25741,11 +25737,13 @@
25741
25737
  this.state.draggerLinePosition = dimensions.start;
25742
25738
  this.state.draggerShadowPosition = dimensions.start;
25743
25739
  this.state.base = elementIndex;
25740
+ this.state.position = "before";
25744
25741
  }
25745
25742
  else if (this._getSelectedZoneEnd() < elementIndex) {
25746
25743
  this.state.draggerLinePosition = dimensions.end;
25747
25744
  this.state.draggerShadowPosition = dimensions.end - this.state.draggerShadowThickness;
25748
- this.state.base = elementIndex + 1;
25745
+ this.state.base = elementIndex;
25746
+ this.state.position = "after";
25749
25747
  }
25750
25748
  else {
25751
25749
  this.state.draggerLinePosition = startDimensions.start;
@@ -25928,6 +25926,7 @@
25928
25926
  dimension: "COL",
25929
25927
  base: this.state.base,
25930
25928
  elements,
25929
+ position: this.state.position,
25931
25930
  });
25932
25931
  if (!result.isSuccessful && result.reasons.includes(2 /* CommandResult.WillRemoveExistingMerge */)) {
25933
25932
  this.env.raiseError(MergeErrorMessage);
@@ -26101,6 +26100,7 @@
26101
26100
  dimension: "ROW",
26102
26101
  base: this.state.base,
26103
26102
  elements,
26103
+ position: this.state.position,
26104
26104
  });
26105
26105
  if (!result.isSuccessful && result.reasons.includes(2 /* CommandResult.WillRemoveExistingMerge */)) {
26106
26106
  this.env.raiseError(MergeErrorMessage);
@@ -26208,8 +26208,8 @@
26208
26208
  return val * (deltaMode === 0 ? 1 : DEFAULT_CELL_HEIGHT);
26209
26209
  }
26210
26210
  const onMouseWheel = (ev) => {
26211
- const deltaX = normalize(ev.shiftKey ? ev.deltaY : ev.deltaX, ev.deltaMode);
26212
- const deltaY = normalize(ev.shiftKey ? ev.deltaX : ev.deltaY, ev.deltaMode);
26211
+ const deltaX = normalize(ev.shiftKey && !isMacOS() ? ev.deltaY : ev.deltaX, ev.deltaMode);
26212
+ const deltaY = normalize(ev.shiftKey && !isMacOS() ? ev.deltaX : ev.deltaY, ev.deltaMode);
26213
26213
  handler(deltaX, deltaY);
26214
26214
  };
26215
26215
  return onMouseWheel;
@@ -31167,6 +31167,14 @@
31167
31167
  // ---------------------------------------------------------------------------
31168
31168
  // Command Handling
31169
31169
  // ---------------------------------------------------------------------------
31170
+ allowDispatch(cmd) {
31171
+ switch (cmd.type) {
31172
+ case "SET_BORDER":
31173
+ return this.checkBordersUnchanged(cmd);
31174
+ default:
31175
+ return 0 /* CommandResult.Success */;
31176
+ }
31177
+ }
31170
31178
  handle(cmd) {
31171
31179
  switch (cmd.type) {
31172
31180
  case "ADD_MERGE":
@@ -31586,6 +31594,14 @@
31586
31594
  this.setBorders(sheetId, [{ ...zone, left: right }], "right", bordersTopLeft.right);
31587
31595
  }
31588
31596
  }
31597
+ checkBordersUnchanged(cmd) {
31598
+ const currentBorder = this.getCellBorder(cmd);
31599
+ const areAllNewBordersUndefined = !cmd.border?.bottom && !cmd.border?.left && !cmd.border?.right && !cmd.border?.top;
31600
+ if ((!currentBorder && areAllNewBordersUndefined) || deepEquals(currentBorder, cmd.border)) {
31601
+ return 97 /* CommandResult.NoChanges */;
31602
+ }
31603
+ return 0 /* CommandResult.Success */;
31604
+ }
31589
31605
  // ---------------------------------------------------------------------------
31590
31606
  // Import/Export
31591
31607
  // ---------------------------------------------------------------------------
@@ -31622,7 +31638,7 @@
31622
31638
  */
31623
31639
  function getBorderId(border) {
31624
31640
  for (let [key, value] of Object.entries(borders)) {
31625
- if (stringify(value) === stringify(border)) {
31641
+ if (deepEquals(value, border)) {
31626
31642
  return parseInt(key, 10);
31627
31643
  }
31628
31644
  }
@@ -31693,8 +31709,9 @@
31693
31709
  allowDispatch(cmd) {
31694
31710
  switch (cmd.type) {
31695
31711
  case "UPDATE_CELL":
31712
+ return this.checkCellOutOfSheet(cmd);
31696
31713
  case "CLEAR_CELL":
31697
- return this.checkCellOutOfSheet(cmd.sheetId, cmd.col, cmd.row);
31714
+ return this.checkValidations(cmd, this.chainValidations(this.checkCellOutOfSheet, this.checkUselessClearCell));
31698
31715
  default:
31699
31716
  return 0 /* CommandResult.Success */;
31700
31717
  }
@@ -31853,14 +31870,9 @@
31853
31870
  */
31854
31871
  getCellById(cellId) {
31855
31872
  // this must be as fast as possible
31856
- for (const sheetId in this.cells) {
31857
- const sheet = this.cells[sheetId];
31858
- const cell = sheet[cellId];
31859
- if (cell) {
31860
- return cell;
31861
- }
31862
- }
31863
- return undefined;
31873
+ const position = this.getters.getCellPosition(cellId);
31874
+ const sheet = this.cells[position.sheetId];
31875
+ return sheet[cellId];
31864
31876
  }
31865
31877
  /*
31866
31878
  * Reconstructs the original formula string based on a normalized form and its dependencies
@@ -32086,13 +32098,23 @@
32086
32098
  dependencies: [],
32087
32099
  };
32088
32100
  }
32089
- checkCellOutOfSheet(sheetId, col, row) {
32101
+ checkCellOutOfSheet(cmd) {
32102
+ const { sheetId, col, row } = cmd;
32090
32103
  const sheet = this.getters.tryGetSheet(sheetId);
32091
32104
  if (!sheet)
32092
32105
  return 28 /* CommandResult.InvalidSheetId */;
32093
32106
  const sheetZone = this.getters.getSheetZone(sheetId);
32094
32107
  return isInside(col, row, sheetZone) ? 0 /* CommandResult.Success */ : 19 /* CommandResult.TargetOutOfSheet */;
32095
32108
  }
32109
+ checkUselessClearCell(cmd) {
32110
+ const cell = this.getters.getCell(cmd);
32111
+ if (!cell)
32112
+ return 97 /* CommandResult.NoChanges */;
32113
+ if (!cell.content && !cell.style && !cell.format) {
32114
+ return 97 /* CommandResult.NoChanges */;
32115
+ }
32116
+ return 0 /* CommandResult.Success */;
32117
+ }
32096
32118
  }
32097
32119
  class FormulaCellWithDependencies {
32098
32120
  id;
@@ -34525,7 +34547,7 @@
34525
34547
  const elements = cmd.dimension === "COL"
34526
34548
  ? this.getNumberCols(cmd.sheetId)
34527
34549
  : this.getNumberRows(cmd.sheetId);
34528
- if (cmd.base < 0 || cmd.base > elements) {
34550
+ if (cmd.base < 0 || cmd.base >= elements) {
34529
34551
  return 88 /* CommandResult.InvalidHeaderIndex */;
34530
34552
  }
34531
34553
  else if (cmd.quantity <= 0) {
@@ -35161,10 +35183,13 @@
35161
35183
  // begin with the end.
35162
35184
  rows.sort((a, b) => b - a);
35163
35185
  for (let group of groupConsecutive(rows)) {
35186
+ // indexes are sorted in the descending order
35187
+ const from = group[group.length - 1];
35188
+ const to = group[0];
35164
35189
  // Move the cells.
35165
- this.moveCellOnRowsDeletion(sheet, group[group.length - 1], group[0]);
35166
- // Effectively delete the element and recompute the left-right/top-bottom.
35167
- group.map((row) => this.updateRowsStructureOnDeletion(row, sheet));
35190
+ this.moveCellOnRowsDeletion(sheet, from, to);
35191
+ // Effectively delete the rows
35192
+ this.updateRowsStructureOnDeletion(sheet, from, to);
35168
35193
  }
35169
35194
  const count = rows.filter((row) => row < sheet.panes.ySplit).length;
35170
35195
  if (count) {
@@ -35186,8 +35211,6 @@
35186
35211
  this.addEmptyRows(sheet, quantity);
35187
35212
  // Move the cells.
35188
35213
  this.moveCellsOnAddition(sheet, index, quantity, "rows");
35189
- // Recompute the left-right/top-bottom.
35190
- this.updateRowsStructureOnAddition(sheet, row, quantity);
35191
35214
  if (index < sheet.panes.ySplit) {
35192
35215
  this.setPaneDivisions(sheet.id, sheet.panes.ySplit + quantity, "ROW");
35193
35216
  }
@@ -35288,35 +35311,20 @@
35288
35311
  }
35289
35312
  }
35290
35313
  }
35291
- updateRowsStructureOnDeletion(index, sheet) {
35314
+ updateRowsStructureOnDeletion(sheet, deleteFromRow, deleteToRow) {
35292
35315
  const rows = [];
35293
- const cellsQueue = sheet.rows.map((row) => row.cells);
35316
+ const cellsQueue = sheet.rows.map((row) => row.cells).reverse();
35294
35317
  for (let i in sheet.rows) {
35295
- if (Number(i) === index) {
35318
+ const row = Number(i);
35319
+ if (row >= deleteFromRow && row <= deleteToRow) {
35296
35320
  continue;
35297
35321
  }
35298
35322
  rows.push({
35299
- cells: cellsQueue.shift(),
35323
+ cells: cellsQueue.pop(),
35300
35324
  });
35301
35325
  }
35302
35326
  this.history.update("sheets", sheet.id, "rows", rows);
35303
35327
  }
35304
- /**
35305
- * Update the rows of the sheet after an addition:
35306
- * - Rename the rows
35307
- *
35308
- * @param sheet Sheet on which the deletion occurs
35309
- * @param addedRow Index of the added row
35310
- * @param rowsToAdd Number of the rows to add
35311
- */
35312
- updateRowsStructureOnAddition(sheet, addedRow, rowsToAdd) {
35313
- const rows = [];
35314
- const cellsQueue = sheet.rows.map((row) => row.cells);
35315
- sheet.rows.forEach(() => rows.push({
35316
- cells: cellsQueue.shift(),
35317
- }));
35318
- this.history.update("sheets", sheet.id, "rows", rows);
35319
- }
35320
35328
  /**
35321
35329
  * Add empty rows at the end of the rows
35322
35330
  *
@@ -35432,6 +35440,7 @@
35432
35440
  getters;
35433
35441
  computeCell;
35434
35442
  evalContext;
35443
+ rangeCache = {};
35435
35444
  constructor(context, getters, computeCell) {
35436
35445
  this.getters = getters;
35437
35446
  this.computeCell = computeCell;
@@ -35479,10 +35488,13 @@
35479
35488
  if (evaluatedCell === undefined) {
35480
35489
  return { value: null, format: this.getters.getCell(position)?.format };
35481
35490
  }
35491
+ if (evaluatedCell.type === CellValueType.error) {
35492
+ throw evaluatedCell.error;
35493
+ }
35482
35494
  return evaluatedCell;
35483
35495
  }
35484
35496
  getEvaluatedCellIfNotEmpty(position) {
35485
- const evaluatedCell = this.getEvaluatedCell(position);
35497
+ const evaluatedCell = this.computeCell(position);
35486
35498
  if (evaluatedCell.type === CellValueType.empty) {
35487
35499
  const cell = this.getters.getCell(position);
35488
35500
  if (!cell || (!cell.isFormula && cell.content === "")) {
@@ -35491,13 +35503,6 @@
35491
35503
  }
35492
35504
  return evaluatedCell;
35493
35505
  }
35494
- getEvaluatedCell(position) {
35495
- const evaluatedCell = this.computeCell(position);
35496
- if (evaluatedCell.type === CellValueType.error) {
35497
- throw evaluatedCell.error;
35498
- }
35499
- return evaluatedCell;
35500
- }
35501
35506
  /**
35502
35507
  * Return the values of the cell(s) used in reference, but always in the format of a range even
35503
35508
  * if a single cell is referenced. It is a list of col values. This is useful for the formulas that describe parameters as
@@ -35517,25 +35522,40 @@
35517
35522
  if (!_zone) {
35518
35523
  return { value: [[]], format: [[]] };
35519
35524
  }
35525
+ const { top, left, bottom, right } = zone;
35526
+ const cacheKey = `${sheetId}-${top}-${left}-${bottom}-${right}`;
35527
+ if (cacheKey in this.rangeCache) {
35528
+ const result = this.rangeCache[cacheKey];
35529
+ if (result instanceof EvaluationError) {
35530
+ throw result;
35531
+ }
35532
+ return result;
35533
+ }
35520
35534
  const height = _zone.bottom - _zone.top + 1;
35521
35535
  const width = _zone.right - _zone.left + 1;
35522
- const value = Array.from({ length: width }, () => Array.from({ length: height }));
35523
- const format = Array.from({ length: width }, () => Array.from({ length: height }));
35536
+ const value = new Array(width);
35537
+ const format = new Array(width);
35524
35538
  // Performance issue: nested loop is faster than a map here
35525
35539
  for (let col = _zone.left; col <= _zone.right; col++) {
35540
+ const colIndex = col - _zone.left;
35541
+ value[colIndex] = new Array(height);
35542
+ format[colIndex] = new Array(height);
35526
35543
  for (let row = _zone.top; row <= _zone.bottom; row++) {
35527
- const evaluatedCell = this.getEvaluatedCellIfNotEmpty({ sheetId: sheetId, col, row });
35528
- if (evaluatedCell) {
35529
- const colIndex = col - _zone.left;
35530
- const rowIndex = row - _zone.top;
35531
- value[colIndex][rowIndex] = evaluatedCell.value;
35532
- if (evaluatedCell.format !== undefined) {
35533
- format[colIndex][rowIndex] = evaluatedCell.format;
35534
- }
35544
+ const evaluatedCell = this.getEvaluatedCellIfNotEmpty({ sheetId, col, row });
35545
+ if (evaluatedCell?.type === CellValueType.error) {
35546
+ this.rangeCache[cacheKey] = evaluatedCell.error;
35547
+ throw evaluatedCell.error;
35548
+ }
35549
+ const rowIndex = row - _zone.top;
35550
+ value[colIndex][rowIndex] = evaluatedCell?.value;
35551
+ if (evaluatedCell?.format !== undefined) {
35552
+ format[colIndex][rowIndex] = evaluatedCell.format;
35535
35553
  }
35536
35554
  }
35537
35555
  }
35538
- return { value, format };
35556
+ const result = { value, format };
35557
+ this.rangeCache[cacheKey] = result;
35558
+ return result;
35539
35559
  }
35540
35560
  }
35541
35561
 
@@ -35733,6 +35753,7 @@
35733
35753
  this.formulaDependencies().addDependencies(positionId, dependencies);
35734
35754
  }
35735
35755
  updateCompilationParameters() {
35756
+ // rebuild the compilation parameters (with a clean cache)
35736
35757
  this.compilationParams = buildCompilationParameters(this.context, this.getters, this.computeAndSave.bind(this));
35737
35758
  }
35738
35759
  evaluateCells(positions) {
@@ -35776,6 +35797,15 @@
35776
35797
  this.evaluatedCells = new Map();
35777
35798
  this.evaluate(this.getAllCells());
35778
35799
  }
35800
+ evaluateFormula(sheetId, formulaString) {
35801
+ const compiledFormula = compile(formulaString);
35802
+ const ranges = [];
35803
+ for (let xc of compiledFormula.dependencies) {
35804
+ ranges.push(this.getters.getRangeFromSheetXC(sheetId, xc));
35805
+ }
35806
+ this.updateCompilationParameters();
35807
+ return compiledFormula.execute(ranges, ...this.compilationParams).value;
35808
+ }
35779
35809
  getAllCells() {
35780
35810
  const positionIds = new JetSet();
35781
35811
  for (const sheetId of this.getters.getSheetIds()) {
@@ -35802,6 +35832,7 @@
35802
35832
  this.nextPositionsToUpdate = cells;
35803
35833
  let currentIteration = 0;
35804
35834
  while (this.nextPositionsToUpdate.size && currentIteration++ < MAX_ITERATION) {
35835
+ this.updateCompilationParameters();
35805
35836
  const positionIds = Array.from(this.nextPositionsToUpdate);
35806
35837
  this.nextPositionsToUpdate.clear();
35807
35838
  for (let i = 0; i < positionIds.length; ++i) {
@@ -36186,7 +36217,6 @@
36186
36217
  // of other cells depending on it, at the next iteration.
36187
36218
  //#endregion
36188
36219
  class EvaluationPlugin extends UIPlugin {
36189
- config;
36190
36220
  static getters = [
36191
36221
  "evaluateFormula",
36192
36222
  "getRangeFormattedValues",
@@ -36198,12 +36228,9 @@
36198
36228
  ];
36199
36229
  shouldRebuildDependenciesGraph = true;
36200
36230
  evaluator;
36201
- compilationParams;
36202
36231
  positionsToUpdate = [];
36203
36232
  constructor(config) {
36204
36233
  super(config);
36205
- this.config = config;
36206
- this.compilationParams = this.getCompilationParameters();
36207
36234
  this.evaluator = new Evaluator(config.custom, this.getters);
36208
36235
  }
36209
36236
  // ---------------------------------------------------------------------------
@@ -36228,10 +36255,6 @@
36228
36255
  case "EVALUATE_CELLS":
36229
36256
  this.evaluator.evaluateAllCells();
36230
36257
  break;
36231
- case "UPDATE_LOCALE":
36232
- this.compilationParams = this.getCompilationParameters();
36233
- this.evaluator.updateCompilationParameters();
36234
- break;
36235
36258
  }
36236
36259
  }
36237
36260
  finalize() {
@@ -36249,12 +36272,7 @@
36249
36272
  // Getters
36250
36273
  // ---------------------------------------------------------------------------
36251
36274
  evaluateFormula(sheetId, formulaString) {
36252
- const compiledFormula = compile(formulaString);
36253
- const ranges = [];
36254
- for (let xc of compiledFormula.dependencies) {
36255
- ranges.push(this.getters.getRangeFromSheetXC(sheetId, xc));
36256
- }
36257
- return compiledFormula.execute(ranges, ...this.compilationParams).value;
36275
+ return this.evaluator.evaluateFormula(sheetId, formulaString);
36258
36276
  }
36259
36277
  /**
36260
36278
  * Return the value of each cell in the range as they are displayed in the grid.
@@ -36353,9 +36371,6 @@
36353
36371
  }
36354
36372
  return undefined;
36355
36373
  }
36356
- getCompilationParameters() {
36357
- return buildCompilationParameters(this.config.custom, this.getters, (position) => this.evaluator.getEvaluatedCell(position));
36358
- }
36359
36374
  }
36360
36375
  function isBadExpression(formula) {
36361
36376
  try {
@@ -36520,7 +36535,7 @@
36520
36535
  }
36521
36536
 
36522
36537
  class EvaluationChartPlugin extends UIPlugin {
36523
- static getters = ["getChartRuntime", "getBackgroundOfSingleCellChart"];
36538
+ static getters = ["getChartRuntime", "getStyleOfSingleCellChart"];
36524
36539
  charts = {};
36525
36540
  createRuntimeChart = chartRuntimeFactory(this.getters);
36526
36541
  handle(cmd) {
@@ -36558,25 +36573,26 @@
36558
36573
  return this.charts[figureId];
36559
36574
  }
36560
36575
  /**
36561
- * Get the background color of a chart based on the color of the first cell of the main range
36562
- * of the chart. In order of priority, it will return :
36563
- *
36564
- * - the chart background color if one is defined
36565
- * - the fill color of the cell if one is defined
36566
- * - the fill color of the cell from conditional formats if one is defined
36567
- * - the default chart color if no other color is defined
36576
+ * Get the background and textColor of a chart based on the color of the first cell of the main range of the chart.
36568
36577
  */
36569
- getBackgroundOfSingleCellChart(chartBackground, mainRange) {
36578
+ getStyleOfSingleCellChart(chartBackground, mainRange) {
36570
36579
  if (chartBackground)
36571
- return chartBackground;
36580
+ return { background: chartBackground, fontColor: chartFontColor(chartBackground) };
36572
36581
  if (!mainRange) {
36573
- return BACKGROUND_CHART_COLOR;
36582
+ return {
36583
+ background: BACKGROUND_CHART_COLOR,
36584
+ fontColor: chartFontColor(BACKGROUND_CHART_COLOR),
36585
+ };
36574
36586
  }
36575
36587
  const col = mainRange.zone.left;
36576
36588
  const row = mainRange.zone.top;
36577
36589
  const sheetId = mainRange.sheetId;
36578
36590
  const style = this.getters.getCellComputedStyle({ sheetId, col, row });
36579
- return style.fillColor || BACKGROUND_CHART_COLOR;
36591
+ const background = style.fillColor || BACKGROUND_CHART_COLOR;
36592
+ return {
36593
+ background,
36594
+ fontColor: style.textColor || chartFontColor(background),
36595
+ };
36580
36596
  }
36581
36597
  }
36582
36598
 
@@ -37275,19 +37291,21 @@
37275
37291
  let col = zone.left;
37276
37292
  let row = zone.bottom;
37277
37293
  if (col > 0) {
37278
- let left = this.getters.getEvaluatedCell({ sheetId, col: col - 1, row });
37279
- while (left.type !== CellValueType.empty) {
37294
+ let leftPosition = { sheetId, col: col - 1, row };
37295
+ while (this.getters.getEvaluatedCell(leftPosition).type !== CellValueType.empty ||
37296
+ this.getters.getCell(leftPosition)?.content) {
37280
37297
  row += 1;
37281
- left = this.getters.getEvaluatedCell({ sheetId, col: col - 1, row });
37298
+ leftPosition = { sheetId, col: col - 1, row };
37282
37299
  }
37283
37300
  }
37284
37301
  if (row === zone.bottom) {
37285
37302
  col = zone.right;
37286
37303
  if (col <= this.getters.getNumberCols(sheetId)) {
37287
- let right = this.getters.getEvaluatedCell({ sheetId, col: col + 1, row });
37288
- while (right.type !== CellValueType.empty) {
37304
+ let rightPosition = { sheetId, col: col + 1, row };
37305
+ while (this.getters.getEvaluatedCell(rightPosition).type !== CellValueType.empty ||
37306
+ this.getters.getCell(rightPosition)?.content) {
37289
37307
  row += 1;
37290
- right = this.getters.getEvaluatedCell({ sheetId, col: col + 1, row });
37308
+ rightPosition = { sheetId, col: col + 1, row };
37291
37309
  }
37292
37310
  }
37293
37311
  }
@@ -40205,6 +40223,9 @@
40205
40223
  // Other
40206
40224
  // ---------------------------------------------------------------------------
40207
40225
  initInput(id, initialRanges, inputHasSingleRange = false) {
40226
+ if (this.inputs[id]) {
40227
+ this.unfocus();
40228
+ }
40208
40229
  this.inputs[id] = new SelectionInputPlugin(this.config, initialRanges, inputHasSingleRange);
40209
40230
  if (initialRanges.length === 0) {
40210
40231
  const input = this.inputs[id];
@@ -42072,8 +42093,8 @@
42072
42093
  this.lastPasteState = this.state;
42073
42094
  if (this.paintFormatStatus === "oneOff") {
42074
42095
  this.paintFormatStatus = "inactive";
42075
- this.status = "invisible";
42076
42096
  }
42097
+ this.status = "invisible";
42077
42098
  break;
42078
42099
  case "CLEAN_CLIPBOARD_HIGHLIGHT":
42079
42100
  this.status = "invisible";
@@ -42320,6 +42341,10 @@
42320
42341
  return xc.slice(0, indexOfNumber) + "$" + xc.slice(indexOfNumber);
42321
42342
  case "colrow":
42322
42343
  indexOfNumber = xc.search(/[0-9]/);
42344
+ if (indexOfNumber === -1 || indexOfNumber === 0) {
42345
+ // no row number (eg. A) or no column (eg. 1)
42346
+ return "$" + xc;
42347
+ }
42323
42348
  xc = xc.slice(0, indexOfNumber) + "$" + xc.slice(indexOfNumber);
42324
42349
  return "$" + xc;
42325
42350
  case "none":
@@ -43735,7 +43760,7 @@
43735
43760
  sheetId: cmd.sheetId,
43736
43761
  base: cmd.base,
43737
43762
  quantity: thickness,
43738
- position: "before",
43763
+ position: cmd.position,
43739
43764
  });
43740
43765
  const isCol = cmd.dimension === "COL";
43741
43766
  const start = cmd.elements[0];
@@ -43752,12 +43777,13 @@
43752
43777
  },
43753
43778
  ];
43754
43779
  const state = new ClipboardCellsState(target, "CUT", this.getters, this.dispatch, this.selection);
43780
+ const base = isBasedBefore ? cmd.base : cmd.base + 1;
43755
43781
  const pasteTarget = [
43756
43782
  {
43757
- left: isCol ? cmd.base : 0,
43758
- right: isCol ? cmd.base + thickness - 1 : this.getters.getNumberCols(cmd.sheetId) - 1,
43759
- top: !isCol ? cmd.base : 0,
43760
- bottom: !isCol ? cmd.base + thickness - 1 : this.getters.getNumberRows(cmd.sheetId) - 1,
43783
+ left: isCol ? base : 0,
43784
+ right: isCol ? base + thickness - 1 : this.getters.getNumberCols(cmd.sheetId) - 1,
43785
+ top: !isCol ? base : 0,
43786
+ bottom: !isCol ? base + thickness - 1 : this.getters.getNumberRows(cmd.sheetId) - 1,
43761
43787
  },
43762
43788
  ];
43763
43789
  state.paste(pasteTarget, { selectTarget: true });
@@ -43794,6 +43820,11 @@
43794
43820
  doesElementsHaveCommonMerges(id, cmd.base - 1, cmd.base)) {
43795
43821
  return 2 /* CommandResult.WillRemoveExistingMerge */;
43796
43822
  }
43823
+ const headers = [cmd.base, ...cmd.elements];
43824
+ const maxHeaderValue = isCol ? this.getters.getNumberCols(id) : this.getters.getNumberRows(id);
43825
+ if (headers.some((h) => h < 0 || h >= maxHeaderValue)) {
43826
+ return 88 /* CommandResult.InvalidHeaderIndex */;
43827
+ }
43797
43828
  return 0 /* CommandResult.Success */;
43798
43829
  }
43799
43830
  //-------------------------------------------
@@ -47890,9 +47921,9 @@
47890
47921
  * Apply the changes of the given HistoryChange to the state
47891
47922
  */
47892
47923
  function applyChange(change, target) {
47893
- let val = change.root;
47894
- let key = change.path[change.path.length - 1];
47895
- for (let pathIndex = 0; pathIndex < change.path.slice(0, -1).length; pathIndex++) {
47924
+ let val = change.path[0];
47925
+ const key = change.path[change.path.length - 1];
47926
+ for (let pathIndex = 1; pathIndex < change.path.slice(0, -1).length; pathIndex++) {
47896
47927
  const p = change.path[pathIndex];
47897
47928
  if (val[p] === undefined) {
47898
47929
  const nextPath = change.path[pathIndex + 1];
@@ -48573,13 +48604,14 @@
48573
48604
  }
48574
48605
  addChange(...args) {
48575
48606
  const val = args.pop();
48576
- const [root, ...path] = args;
48607
+ const root = args[0];
48577
48608
  let value = root;
48578
- let key = path[path.length - 1];
48579
- for (let pathIndex = 0; pathIndex <= path.length - 2; pathIndex++) {
48580
- const p = path[pathIndex];
48609
+ let key = args[args.length - 1];
48610
+ const pathLength = args.length - 2;
48611
+ for (let pathIndex = 1; pathIndex <= pathLength; pathIndex++) {
48612
+ const p = args[pathIndex];
48581
48613
  if (value[p] === undefined) {
48582
- const nextPath = path[pathIndex + 1];
48614
+ const nextPath = args[pathIndex + 1];
48583
48615
  value[p] = createEmptyStructure(nextPath);
48584
48616
  }
48585
48617
  value = value[p];
@@ -48588,8 +48620,7 @@
48588
48620
  return;
48589
48621
  }
48590
48622
  this.changes.push({
48591
- root,
48592
- path,
48623
+ path: args,
48593
48624
  before: value[key],
48594
48625
  after: val,
48595
48626
  });
@@ -50439,19 +50470,22 @@
50439
50470
  * Check if the given command is allowed by all the plugins and the history.
50440
50471
  */
50441
50472
  checkDispatchAllowed(command) {
50442
- if (isCoreCommand(command)) {
50443
- return this.checkDispatchAllowedCoreCommand(command);
50473
+ const results = isCoreCommand(command)
50474
+ ? this.checkDispatchAllowedCoreCommand(command)
50475
+ : this.checkDispatchAllowedLocalCommand(command);
50476
+ if (results.some((r) => r !== 0 /* CommandResult.Success */)) {
50477
+ return new DispatchResult(results.flat());
50444
50478
  }
50445
- return this.checkDispatchAllowedLocalCommand(command);
50479
+ return DispatchResult.Success;
50446
50480
  }
50447
50481
  checkDispatchAllowedCoreCommand(command) {
50448
50482
  const results = this.corePlugins.map((handler) => handler.allowDispatch(command));
50449
50483
  results.push(this.range.allowDispatch(command));
50450
- return new DispatchResult(results.flat());
50484
+ return results;
50451
50485
  }
50452
50486
  checkDispatchAllowedLocalCommand(command) {
50453
50487
  const results = this.uiHandlers.map((handler) => handler.allowDispatch(command));
50454
- return new DispatchResult(results.flat());
50488
+ return results;
50455
50489
  }
50456
50490
  finalize() {
50457
50491
  this.status = 3 /* Status.Finalizing */;
@@ -50777,9 +50811,9 @@
50777
50811
  Object.defineProperty(exports, '__esModule', { value: true });
50778
50812
 
50779
50813
 
50780
- __info__.version = '16.4.14';
50781
- __info__.date = '2023-11-16T11:59:51.900Z';
50782
- __info__.hash = '66c6f47';
50814
+ __info__.version = '16.4.16';
50815
+ __info__.date = '2023-12-05T09:37:14.210Z';
50816
+ __info__.hash = '1abbb92';
50783
50817
 
50784
50818
 
50785
50819
  })(this.o_spreadsheet = this.o_spreadsheet || {}, owl);