@odoo/o-spreadsheet 16.4.13 → 16.4.15

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.
@@ -672,8 +672,7 @@ function transpose2dArray(matrix, callback = (val) => val) {
672
672
  *
673
673
  * https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Regular_Expressions/Character_Classes
674
674
  */
675
- const whiteSpaceCharacters = [
676
- " ",
675
+ const whiteSpaceSpecialCharacters = [
677
676
  "\t",
678
677
  "\f",
679
678
  "\v",
@@ -688,8 +687,7 @@ const whiteSpaceCharacters = [
688
687
  String.fromCharCode(parseInt("3000", 16)),
689
688
  String.fromCharCode(parseInt("feff", 16)),
690
689
  ];
691
- const whiteSpaceRegexp = new RegExp(whiteSpaceCharacters.join("|"), "g");
692
- const newLineRegex = /\r\n|\r|\n/g;
690
+ const whiteSpaceRegexp = new RegExp(whiteSpaceSpecialCharacters.join("|") + "|(\r\n|\r|\n)", "g");
693
691
  /**
694
692
  * Replace all the special spaces in a string (non-breaking, tabs, ...) by normal spaces, and all the
695
693
  * different newlines types by \n.
@@ -697,9 +695,9 @@ const newLineRegex = /\r\n|\r|\n/g;
697
695
  function replaceSpecialSpaces(text) {
698
696
  if (!text)
699
697
  return "";
700
- text = text.replace(whiteSpaceRegexp, " ");
701
- text = text.replace(newLineRegex, NEWLINE);
702
- return text;
698
+ if (!whiteSpaceRegexp.test(text))
699
+ return text;
700
+ return text.replace(whiteSpaceRegexp, (match, newLine) => (newLine ? NEWLINE : " "));
703
701
  }
704
702
  /**
705
703
  * Determine if the numbers are consecutive.
@@ -4269,6 +4267,7 @@ exports.CommandResult = void 0;
4269
4267
  CommandResult[CommandResult["NoActiveSheet"] = 94] = "NoActiveSheet";
4270
4268
  CommandResult[CommandResult["InvalidLocale"] = 95] = "InvalidLocale";
4271
4269
  CommandResult[CommandResult["AlreadyInPaintingFormatMode"] = 96] = "AlreadyInPaintingFormatMode";
4270
+ CommandResult[CommandResult["NoChanges"] = 97] = "NoChanges";
4272
4271
  })(exports.CommandResult || (exports.CommandResult = {}));
4273
4272
 
4274
4273
  const borderStyles = ["thin", "medium", "thick", "dashed", "dotted"];
@@ -4358,12 +4357,12 @@ function toString(value) {
4358
4357
  }
4359
4358
  }
4360
4359
  /** Normalize string by setting it to lowercase and replacing accent letters with plain letters */
4361
- function normalizeString(str) {
4360
+ const normalizeString = memoize(function normalizeString(str) {
4362
4361
  return str
4363
4362
  .toLowerCase()
4364
4363
  .normalize("NFD")
4365
4364
  .replace(/[\u0300-\u036f]/g, "");
4366
- }
4365
+ });
4367
4366
  /**
4368
4367
  * Normalize a value.
4369
4368
  * If the cell value is a string, this will set it to lowercase and replacing accent letters with plain letters
@@ -14174,7 +14173,7 @@ function tokenize(str, locale = DEFAULT_LOCALE) {
14174
14173
  return result;
14175
14174
  }
14176
14175
  function tokenizeDebugger(chars) {
14177
- if (chars.current() === "?") {
14176
+ if (chars.current === "?") {
14178
14177
  chars.shift();
14179
14178
  return { type: "DEBUGGER", value: "?" };
14180
14179
  }
@@ -14185,7 +14184,7 @@ const misc = {
14185
14184
  ")": "RIGHT_PAREN",
14186
14185
  };
14187
14186
  function tokenizeMisc(chars) {
14188
- if (chars.current() in misc) {
14187
+ if (chars.current in misc) {
14189
14188
  const value = chars.shift();
14190
14189
  const type = misc[value];
14191
14190
  return { type, value };
@@ -14193,7 +14192,7 @@ function tokenizeMisc(chars) {
14193
14192
  return null;
14194
14193
  }
14195
14194
  function tokenizeArgsSeparator(chars, locale) {
14196
- if (chars.current() === locale.formulaArgSeparator) {
14195
+ if (chars.current === locale.formulaArgSeparator) {
14197
14196
  const value = chars.shift();
14198
14197
  const type = "ARG_SEPARATOR";
14199
14198
  return { type, value };
@@ -14218,14 +14217,13 @@ function tokenizeNumber(chars, locale) {
14218
14217
  return null;
14219
14218
  }
14220
14219
  function tokenizeString(chars) {
14221
- if (chars.current() === '"') {
14220
+ if (chars.current === '"') {
14222
14221
  const startChar = chars.shift();
14223
14222
  let letters = startChar;
14224
- while (chars.current() &&
14225
- (chars.current() !== startChar || letters[letters.length - 1] === "\\")) {
14223
+ while (chars.current && (chars.current !== startChar || letters[letters.length - 1] === "\\")) {
14226
14224
  letters += chars.shift();
14227
14225
  }
14228
- if (chars.current() === '"') {
14226
+ if (chars.current === '"') {
14229
14227
  letters += chars.shift();
14230
14228
  }
14231
14229
  return {
@@ -14252,14 +14250,14 @@ function tokenizeSymbol(chars) {
14252
14250
  let result = "";
14253
14251
  // there are two main cases to manage: either something which starts with
14254
14252
  // a ', like 'Sheet 2'A2, or a word-like element.
14255
- if (chars.current() === "'") {
14253
+ if (chars.current === "'") {
14256
14254
  let lastChar = chars.shift();
14257
14255
  result += lastChar;
14258
- while (chars.current()) {
14256
+ while (chars.current) {
14259
14257
  lastChar = chars.shift();
14260
14258
  result += lastChar;
14261
14259
  if (lastChar === "'") {
14262
- if (chars.current() && chars.current() === "'") {
14260
+ if (chars.current && chars.current === "'") {
14263
14261
  lastChar = chars.shift();
14264
14262
  result += lastChar;
14265
14263
  }
@@ -14275,7 +14273,7 @@ function tokenizeSymbol(chars) {
14275
14273
  };
14276
14274
  }
14277
14275
  }
14278
- while (chars.current() && separatorRegexp.test(chars.current())) {
14276
+ while (chars.current && separatorRegexp.test(chars.current)) {
14279
14277
  result += chars.shift();
14280
14278
  }
14281
14279
  if (result.length) {
@@ -14290,14 +14288,14 @@ function tokenizeSymbol(chars) {
14290
14288
  }
14291
14289
  function tokenizeSpace(chars) {
14292
14290
  let length = 0;
14293
- while (chars.current() === NEWLINE) {
14291
+ while (chars.current === NEWLINE) {
14294
14292
  length++;
14295
14293
  chars.shift();
14296
14294
  }
14297
14295
  if (length) {
14298
14296
  return { type: "SPACE", value: NEWLINE.repeat(length) };
14299
14297
  }
14300
- while (chars.current() === " ") {
14298
+ while (chars.current === " ") {
14301
14299
  length++;
14302
14300
  chars.shift();
14303
14301
  }
@@ -14316,17 +14314,20 @@ function tokenizeInvalidRange(chars) {
14316
14314
  class TokenizingChars {
14317
14315
  text;
14318
14316
  currentIndex = 0;
14317
+ current;
14319
14318
  constructor(text) {
14320
14319
  this.text = text;
14321
- }
14322
- current() {
14323
- return this.text[this.currentIndex];
14320
+ this.current = text[0];
14324
14321
  }
14325
14322
  shift() {
14326
- return this.text[this.currentIndex++];
14323
+ const current = this.current;
14324
+ const next = this.text[++this.currentIndex];
14325
+ this.current = next;
14326
+ return current;
14327
14327
  }
14328
14328
  advanceBy(length) {
14329
14329
  this.currentIndex += length;
14330
+ this.current = this.text[this.currentIndex];
14330
14331
  }
14331
14332
  isOver() {
14332
14333
  return this.currentIndex >= this.text.length;
@@ -18569,6 +18570,17 @@ const SELECTION_CONTAINS_FILTER = (env) => {
18569
18570
  //------------------------------------------------------------------------------
18570
18571
  const IS_ONLY_ONE_RANGE = (env) => {
18571
18572
  return env.model.getters.getSelectedZones().length === 1;
18573
+ };
18574
+ const CAN_INSERT_HEADER = (env, dimension) => {
18575
+ if (!IS_ONLY_ONE_RANGE(env)) {
18576
+ return false;
18577
+ }
18578
+ const activeHeaders = dimension === "COL" ? env.model.getters.getActiveCols() : env.model.getters.getActiveRows();
18579
+ const ortogonalActiveHeaders = dimension === "COL" ? env.model.getters.getActiveRows() : env.model.getters.getActiveCols();
18580
+ const sheetId = env.model.getters.getActiveSheetId();
18581
+ const zone = env.model.getters.getSelectedZone();
18582
+ const allSheetSelected = isEqual(zone, env.model.getters.getSheetZone(sheetId));
18583
+ return isConsecutive(activeHeaders) && (ortogonalActiveHeaders.size === 0 || allSheetSelected);
18572
18584
  };
18573
18585
 
18574
18586
  const undo = {
@@ -18758,9 +18770,7 @@ const insertRow = {
18758
18770
  const number = getRowsNumber(env);
18759
18771
  return number === 1 ? _lt("Insert row") : _lt("Insert %s rows", number.toString());
18760
18772
  },
18761
- isVisible: (env) => isConsecutive(env.model.getters.getActiveRows()) &&
18762
- IS_ONLY_ONE_RANGE(env) &&
18763
- env.model.getters.getActiveCols().size === 0,
18773
+ isVisible: (env) => CAN_INSERT_HEADER(env, "ROW"),
18764
18774
  icon: "o-spreadsheet-Icon.INSERT_ROW",
18765
18775
  };
18766
18776
  const rowInsertRowBefore = {
@@ -18769,9 +18779,7 @@ const rowInsertRowBefore = {
18769
18779
  return number === 1 ? _lt("Insert row above") : _lt("Insert %s rows above", number.toString());
18770
18780
  },
18771
18781
  execute: INSERT_ROWS_BEFORE_ACTION,
18772
- isVisible: (env) => isConsecutive(env.model.getters.getActiveRows()) &&
18773
- IS_ONLY_ONE_RANGE(env) &&
18774
- env.model.getters.getActiveCols().size === 0,
18782
+ isVisible: (env) => CAN_INSERT_HEADER(env, "ROW"),
18775
18783
  icon: "o-spreadsheet-Icon.INSERT_ROW_BEFORE",
18776
18784
  };
18777
18785
  const topBarInsertRowsBefore = {
@@ -18802,9 +18810,7 @@ const rowInsertRowsAfter = {
18802
18810
  const number = getRowsNumber(env);
18803
18811
  return number === 1 ? _lt("Insert row below") : _lt("Insert %s rows below", number.toString());
18804
18812
  },
18805
- isVisible: (env) => isConsecutive(env.model.getters.getActiveRows()) &&
18806
- IS_ONLY_ONE_RANGE(env) &&
18807
- env.model.getters.getActiveCols().size === 0,
18813
+ isVisible: (env) => CAN_INSERT_HEADER(env, "ROW"),
18808
18814
  icon: "o-spreadsheet-Icon.INSERT_ROW_AFTER",
18809
18815
  };
18810
18816
  const topBarInsertRowsAfter = {
@@ -18822,9 +18828,7 @@ const insertCol = {
18822
18828
  const number = getColumnsNumber(env);
18823
18829
  return number === 1 ? _lt("Insert column") : _lt("Insert %s columns", number.toString());
18824
18830
  },
18825
- isVisible: (env) => isConsecutive(env.model.getters.getActiveCols()) &&
18826
- IS_ONLY_ONE_RANGE(env) &&
18827
- env.model.getters.getActiveRows().size === 0,
18831
+ isVisible: (env) => CAN_INSERT_HEADER(env, "COL"),
18828
18832
  icon: "o-spreadsheet-Icon.INSERT_COL",
18829
18833
  };
18830
18834
  const colInsertColsBefore = {
@@ -18835,9 +18839,7 @@ const colInsertColsBefore = {
18835
18839
  : _lt("Insert %s columns left", number.toString());
18836
18840
  },
18837
18841
  execute: INSERT_COLUMNS_BEFORE_ACTION,
18838
- isVisible: (env) => isConsecutive(env.model.getters.getActiveCols()) &&
18839
- IS_ONLY_ONE_RANGE(env) &&
18840
- env.model.getters.getActiveRows().size === 0,
18842
+ isVisible: (env) => CAN_INSERT_HEADER(env, "COL"),
18841
18843
  icon: "o-spreadsheet-Icon.INSERT_COL_BEFORE",
18842
18844
  };
18843
18845
  const topBarInsertColsBefore = {
@@ -18870,9 +18872,7 @@ const colInsertColsAfter = {
18870
18872
  : _lt("Insert %s columns right", number.toString());
18871
18873
  },
18872
18874
  execute: INSERT_COLUMNS_AFTER_ACTION,
18873
- isVisible: (env) => isConsecutive(env.model.getters.getActiveCols()) &&
18874
- IS_ONLY_ONE_RANGE(env) &&
18875
- env.model.getters.getActiveRows().size === 0,
18875
+ isVisible: (env) => CAN_INSERT_HEADER(env, "COL"),
18876
18876
  icon: "o-spreadsheet-Icon.INSERT_COL_AFTER",
18877
18877
  };
18878
18878
  const topBarInsertColsAfter = {
@@ -22830,7 +22830,7 @@ css /* scss */ `
22830
22830
  }
22831
22831
  .o-input-count {
22832
22832
  width: fit-content;
22833
- padding: 4 0 4 4;
22833
+ padding: 4px 0 4px 4px;
22834
22834
  }
22835
22835
  }
22836
22836
  }
@@ -31171,6 +31171,14 @@ class BordersPlugin extends CorePlugin {
31171
31171
  // ---------------------------------------------------------------------------
31172
31172
  // Command Handling
31173
31173
  // ---------------------------------------------------------------------------
31174
+ allowDispatch(cmd) {
31175
+ switch (cmd.type) {
31176
+ case "SET_BORDER":
31177
+ return this.checkBordersUnchanged(cmd);
31178
+ default:
31179
+ return 0 /* CommandResult.Success */;
31180
+ }
31181
+ }
31174
31182
  handle(cmd) {
31175
31183
  switch (cmd.type) {
31176
31184
  case "ADD_MERGE":
@@ -31590,6 +31598,14 @@ class BordersPlugin extends CorePlugin {
31590
31598
  this.setBorders(sheetId, [{ ...zone, left: right }], "right", bordersTopLeft.right);
31591
31599
  }
31592
31600
  }
31601
+ checkBordersUnchanged(cmd) {
31602
+ const currentBorder = this.getCellBorder(cmd);
31603
+ const areAllNewBordersUndefined = !cmd.border?.bottom && !cmd.border?.left && !cmd.border?.right && !cmd.border?.top;
31604
+ if ((!currentBorder && areAllNewBordersUndefined) || deepEquals(currentBorder, cmd.border)) {
31605
+ return 97 /* CommandResult.NoChanges */;
31606
+ }
31607
+ return 0 /* CommandResult.Success */;
31608
+ }
31593
31609
  // ---------------------------------------------------------------------------
31594
31610
  // Import/Export
31595
31611
  // ---------------------------------------------------------------------------
@@ -31697,8 +31713,9 @@ class CellPlugin extends CorePlugin {
31697
31713
  allowDispatch(cmd) {
31698
31714
  switch (cmd.type) {
31699
31715
  case "UPDATE_CELL":
31716
+ return this.checkCellOutOfSheet(cmd);
31700
31717
  case "CLEAR_CELL":
31701
- return this.checkCellOutOfSheet(cmd.sheetId, cmd.col, cmd.row);
31718
+ return this.checkValidations(cmd, this.chainValidations(this.checkCellOutOfSheet, this.checkUselessClearCell));
31702
31719
  default:
31703
31720
  return 0 /* CommandResult.Success */;
31704
31721
  }
@@ -31857,14 +31874,9 @@ class CellPlugin extends CorePlugin {
31857
31874
  */
31858
31875
  getCellById(cellId) {
31859
31876
  // this must be as fast as possible
31860
- for (const sheetId in this.cells) {
31861
- const sheet = this.cells[sheetId];
31862
- const cell = sheet[cellId];
31863
- if (cell) {
31864
- return cell;
31865
- }
31866
- }
31867
- return undefined;
31877
+ const position = this.getters.getCellPosition(cellId);
31878
+ const sheet = this.cells[position.sheetId];
31879
+ return sheet[cellId];
31868
31880
  }
31869
31881
  /*
31870
31882
  * Reconstructs the original formula string based on a normalized form and its dependencies
@@ -32090,13 +32102,23 @@ class CellPlugin extends CorePlugin {
32090
32102
  dependencies: [],
32091
32103
  };
32092
32104
  }
32093
- checkCellOutOfSheet(sheetId, col, row) {
32105
+ checkCellOutOfSheet(cmd) {
32106
+ const { sheetId, col, row } = cmd;
32094
32107
  const sheet = this.getters.tryGetSheet(sheetId);
32095
32108
  if (!sheet)
32096
32109
  return 28 /* CommandResult.InvalidSheetId */;
32097
32110
  const sheetZone = this.getters.getSheetZone(sheetId);
32098
32111
  return isInside(col, row, sheetZone) ? 0 /* CommandResult.Success */ : 19 /* CommandResult.TargetOutOfSheet */;
32099
32112
  }
32113
+ checkUselessClearCell(cmd) {
32114
+ const cell = this.getters.getCell(cmd);
32115
+ if (!cell)
32116
+ return 97 /* CommandResult.NoChanges */;
32117
+ if (!cell.content && !cell.style && !cell.format) {
32118
+ return 97 /* CommandResult.NoChanges */;
32119
+ }
32120
+ return 0 /* CommandResult.Success */;
32121
+ }
32100
32122
  }
32101
32123
  class FormulaCellWithDependencies {
32102
32124
  id;
@@ -33077,6 +33099,13 @@ class FiltersPlugin extends CorePlugin {
33077
33099
  }
33078
33100
  onDeleteColumnsRows(cmd) {
33079
33101
  for (const table of this.getFilterTables(cmd.sheetId)) {
33102
+ // Remove the filter tables whose data filter headers are in the removed rows.
33103
+ if (cmd.dimension === "ROW" && cmd.elements.includes(table.zone.top)) {
33104
+ const tables = { ...this.tables[cmd.sheetId] };
33105
+ delete tables[table.id];
33106
+ this.history.update("tables", cmd.sheetId, tables);
33107
+ continue;
33108
+ }
33080
33109
  const zone = reduceZoneOnDeletion(table.zone, cmd.dimension === "COL" ? "left" : "top", cmd.elements);
33081
33110
  if (!zone) {
33082
33111
  const tables = { ...this.tables[cmd.sheetId] };
@@ -35158,10 +35187,13 @@ class SheetPlugin extends CorePlugin {
35158
35187
  // begin with the end.
35159
35188
  rows.sort((a, b) => b - a);
35160
35189
  for (let group of groupConsecutive(rows)) {
35190
+ // indexes are sorted in the descending order
35191
+ const from = group[group.length - 1];
35192
+ const to = group[0];
35161
35193
  // Move the cells.
35162
- this.moveCellOnRowsDeletion(sheet, group[group.length - 1], group[0]);
35163
- // Effectively delete the element and recompute the left-right/top-bottom.
35164
- group.map((row) => this.updateRowsStructureOnDeletion(row, sheet));
35194
+ this.moveCellOnRowsDeletion(sheet, from, to);
35195
+ // Effectively delete the rows
35196
+ this.updateRowsStructureOnDeletion(sheet, from, to);
35165
35197
  }
35166
35198
  const count = rows.filter((row) => row < sheet.panes.ySplit).length;
35167
35199
  if (count) {
@@ -35183,8 +35215,6 @@ class SheetPlugin extends CorePlugin {
35183
35215
  this.addEmptyRows(sheet, quantity);
35184
35216
  // Move the cells.
35185
35217
  this.moveCellsOnAddition(sheet, index, quantity, "rows");
35186
- // Recompute the left-right/top-bottom.
35187
- this.updateRowsStructureOnAddition(sheet, row, quantity);
35188
35218
  if (index < sheet.panes.ySplit) {
35189
35219
  this.setPaneDivisions(sheet.id, sheet.panes.ySplit + quantity, "ROW");
35190
35220
  }
@@ -35285,35 +35315,20 @@ class SheetPlugin extends CorePlugin {
35285
35315
  }
35286
35316
  }
35287
35317
  }
35288
- updateRowsStructureOnDeletion(index, sheet) {
35318
+ updateRowsStructureOnDeletion(sheet, deleteFromRow, deleteToRow) {
35289
35319
  const rows = [];
35290
- const cellsQueue = sheet.rows.map((row) => row.cells);
35320
+ const cellsQueue = sheet.rows.map((row) => row.cells).reverse();
35291
35321
  for (let i in sheet.rows) {
35292
- if (Number(i) === index) {
35322
+ const row = Number(i);
35323
+ if (row >= deleteFromRow && row <= deleteToRow) {
35293
35324
  continue;
35294
35325
  }
35295
35326
  rows.push({
35296
- cells: cellsQueue.shift(),
35327
+ cells: cellsQueue.pop(),
35297
35328
  });
35298
35329
  }
35299
35330
  this.history.update("sheets", sheet.id, "rows", rows);
35300
35331
  }
35301
- /**
35302
- * Update the rows of the sheet after an addition:
35303
- * - Rename the rows
35304
- *
35305
- * @param sheet Sheet on which the deletion occurs
35306
- * @param addedRow Index of the added row
35307
- * @param rowsToAdd Number of the rows to add
35308
- */
35309
- updateRowsStructureOnAddition(sheet, addedRow, rowsToAdd) {
35310
- const rows = [];
35311
- const cellsQueue = sheet.rows.map((row) => row.cells);
35312
- sheet.rows.forEach(() => rows.push({
35313
- cells: cellsQueue.shift(),
35314
- }));
35315
- this.history.update("sheets", sheet.id, "rows", rows);
35316
- }
35317
35332
  /**
35318
35333
  * Add empty rows at the end of the rows
35319
35334
  *
@@ -35429,6 +35444,7 @@ class CompilationParametersBuilder {
35429
35444
  getters;
35430
35445
  computeCell;
35431
35446
  evalContext;
35447
+ rangeCache = {};
35432
35448
  constructor(context, getters, computeCell) {
35433
35449
  this.getters = getters;
35434
35450
  this.computeCell = computeCell;
@@ -35514,25 +35530,32 @@ class CompilationParametersBuilder {
35514
35530
  if (!_zone) {
35515
35531
  return { value: [[]], format: [[]] };
35516
35532
  }
35533
+ const { top, left, bottom, right } = zone;
35534
+ const cacheKey = `${sheetId}-${top}-${left}-${bottom}-${right}`;
35535
+ if (cacheKey in this.rangeCache) {
35536
+ return this.rangeCache[cacheKey];
35537
+ }
35517
35538
  const height = _zone.bottom - _zone.top + 1;
35518
35539
  const width = _zone.right - _zone.left + 1;
35519
- const value = Array.from({ length: width }, () => Array.from({ length: height }));
35520
- const format = Array.from({ length: width }, () => Array.from({ length: height }));
35540
+ const value = new Array(width);
35541
+ const format = new Array(width);
35521
35542
  // Performance issue: nested loop is faster than a map here
35522
35543
  for (let col = _zone.left; col <= _zone.right; col++) {
35544
+ const colIndex = col - _zone.left;
35545
+ value[colIndex] = new Array(height);
35546
+ format[colIndex] = new Array(height);
35523
35547
  for (let row = _zone.top; row <= _zone.bottom; row++) {
35524
- const evaluatedCell = this.getEvaluatedCellIfNotEmpty({ sheetId: sheetId, col, row });
35525
- if (evaluatedCell) {
35526
- const colIndex = col - _zone.left;
35527
- const rowIndex = row - _zone.top;
35528
- value[colIndex][rowIndex] = evaluatedCell.value;
35529
- if (evaluatedCell.format !== undefined) {
35530
- format[colIndex][rowIndex] = evaluatedCell.format;
35531
- }
35548
+ const evaluatedCell = this.getEvaluatedCellIfNotEmpty({ sheetId, col, row });
35549
+ const rowIndex = row - _zone.top;
35550
+ value[colIndex][rowIndex] = evaluatedCell?.value;
35551
+ if (evaluatedCell?.format !== undefined) {
35552
+ format[colIndex][rowIndex] = evaluatedCell.format;
35532
35553
  }
35533
35554
  }
35534
35555
  }
35535
- return { value, format };
35556
+ const result = { value, format };
35557
+ this.rangeCache[cacheKey] = result;
35558
+ return result;
35536
35559
  }
35537
35560
  }
35538
35561
 
@@ -35730,6 +35753,7 @@ class Evaluator {
35730
35753
  this.formulaDependencies().addDependencies(positionId, dependencies);
35731
35754
  }
35732
35755
  updateCompilationParameters() {
35756
+ // rebuild the compilation parameters (with a clean cache)
35733
35757
  this.compilationParams = buildCompilationParameters(this.context, this.getters, this.computeAndSave.bind(this));
35734
35758
  }
35735
35759
  evaluateCells(positions) {
@@ -35773,6 +35797,15 @@ class Evaluator {
35773
35797
  this.evaluatedCells = new Map();
35774
35798
  this.evaluate(this.getAllCells());
35775
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
+ }
35776
35809
  getAllCells() {
35777
35810
  const positionIds = new JetSet();
35778
35811
  for (const sheetId of this.getters.getSheetIds()) {
@@ -35799,6 +35832,7 @@ class Evaluator {
35799
35832
  this.nextPositionsToUpdate = cells;
35800
35833
  let currentIteration = 0;
35801
35834
  while (this.nextPositionsToUpdate.size && currentIteration++ < MAX_ITERATION) {
35835
+ this.updateCompilationParameters();
35802
35836
  const positionIds = Array.from(this.nextPositionsToUpdate);
35803
35837
  this.nextPositionsToUpdate.clear();
35804
35838
  for (let i = 0; i < positionIds.length; ++i) {
@@ -36183,7 +36217,6 @@ class PositionBitsEncoder {
36183
36217
  // of other cells depending on it, at the next iteration.
36184
36218
  //#endregion
36185
36219
  class EvaluationPlugin extends UIPlugin {
36186
- config;
36187
36220
  static getters = [
36188
36221
  "evaluateFormula",
36189
36222
  "getRangeFormattedValues",
@@ -36195,12 +36228,9 @@ class EvaluationPlugin extends UIPlugin {
36195
36228
  ];
36196
36229
  shouldRebuildDependenciesGraph = true;
36197
36230
  evaluator;
36198
- compilationParams;
36199
36231
  positionsToUpdate = [];
36200
36232
  constructor(config) {
36201
36233
  super(config);
36202
- this.config = config;
36203
- this.compilationParams = this.getCompilationParameters();
36204
36234
  this.evaluator = new Evaluator(config.custom, this.getters);
36205
36235
  }
36206
36236
  // ---------------------------------------------------------------------------
@@ -36225,10 +36255,6 @@ class EvaluationPlugin extends UIPlugin {
36225
36255
  case "EVALUATE_CELLS":
36226
36256
  this.evaluator.evaluateAllCells();
36227
36257
  break;
36228
- case "UPDATE_LOCALE":
36229
- this.compilationParams = this.getCompilationParameters();
36230
- this.evaluator.updateCompilationParameters();
36231
- break;
36232
36258
  }
36233
36259
  }
36234
36260
  finalize() {
@@ -36246,12 +36272,7 @@ class EvaluationPlugin extends UIPlugin {
36246
36272
  // Getters
36247
36273
  // ---------------------------------------------------------------------------
36248
36274
  evaluateFormula(sheetId, formulaString) {
36249
- const compiledFormula = compile(formulaString);
36250
- const ranges = [];
36251
- for (let xc of compiledFormula.dependencies) {
36252
- ranges.push(this.getters.getRangeFromSheetXC(sheetId, xc));
36253
- }
36254
- return compiledFormula.execute(ranges, ...this.compilationParams).value;
36275
+ return this.evaluator.evaluateFormula(sheetId, formulaString);
36255
36276
  }
36256
36277
  /**
36257
36278
  * Return the value of each cell in the range as they are displayed in the grid.
@@ -36350,9 +36371,6 @@ class EvaluationPlugin extends UIPlugin {
36350
36371
  }
36351
36372
  return undefined;
36352
36373
  }
36353
- getCompilationParameters() {
36354
- return buildCompilationParameters(this.config.custom, this.getters, (position) => this.evaluator.getEvaluatedCell(position));
36355
- }
36356
36374
  }
36357
36375
  function isBadExpression(formula) {
36358
36376
  try {
@@ -36525,22 +36543,20 @@ class EvaluationChartPlugin extends UIPlugin {
36525
36543
  invalidateCFEvaluationCommands.has(cmd.type) ||
36526
36544
  cmd.type === "EVALUATE_CELLS" ||
36527
36545
  cmd.type === "UPDATE_CELL") {
36528
- if (cmd.type !== "UNDO" && cmd.type !== "REDO") {
36529
- for (const chartId in this.charts) {
36530
- this.history.update("charts", chartId, undefined);
36531
- }
36546
+ for (const chartId in this.charts) {
36547
+ this.charts[chartId] = undefined;
36532
36548
  }
36533
36549
  }
36534
36550
  switch (cmd.type) {
36535
36551
  case "UPDATE_CHART":
36536
36552
  case "CREATE_CHART":
36537
36553
  case "DELETE_FIGURE":
36538
- this.history.update("charts", cmd.id, undefined);
36554
+ this.charts[cmd.id] = undefined;
36539
36555
  break;
36540
36556
  case "DELETE_SHEET":
36541
36557
  for (let chartId in this.charts) {
36542
36558
  if (!this.getters.isChartDefined(chartId)) {
36543
- this.history.update("charts", chartId, undefined);
36559
+ this.charts[chartId] = undefined;
36544
36560
  }
36545
36561
  }
36546
36562
  break;
@@ -36552,7 +36568,7 @@ class EvaluationChartPlugin extends UIPlugin {
36552
36568
  if (!chart) {
36553
36569
  throw new Error(`No chart for the given id: ${figureId}`);
36554
36570
  }
36555
- this.history.update("charts", figureId, this.createRuntimeChart(chart));
36571
+ this.charts[figureId] = this.createRuntimeChart(chart);
36556
36572
  }
36557
36573
  return this.charts[figureId];
36558
36574
  }
@@ -42071,8 +42087,8 @@ class ClipboardPlugin extends UIPlugin {
42071
42087
  this.lastPasteState = this.state;
42072
42088
  if (this.paintFormatStatus === "oneOff") {
42073
42089
  this.paintFormatStatus = "inactive";
42074
- this.status = "invisible";
42075
42090
  }
42091
+ this.status = "invisible";
42076
42092
  break;
42077
42093
  case "CLEAN_CLIPBOARD_HIGHLIGHT":
42078
42094
  this.status = "invisible";
@@ -43016,6 +43032,8 @@ class FilterEvaluationPlugin extends UIPlugin {
43016
43032
  case "EVALUATE_CELLS":
43017
43033
  case "ACTIVATE_SHEET":
43018
43034
  case "REMOVE_FILTER_TABLE":
43035
+ case "ADD_COLUMNS_ROWS":
43036
+ case "REMOVE_COLUMNS_ROWS":
43019
43037
  this.isEvaluationDirty = true;
43020
43038
  break;
43021
43039
  case "START":
@@ -47887,9 +47905,9 @@ function revertChanges(revisions) {
47887
47905
  * Apply the changes of the given HistoryChange to the state
47888
47906
  */
47889
47907
  function applyChange(change, target) {
47890
- let val = change.root;
47891
- let key = change.path[change.path.length - 1];
47892
- for (let pathIndex = 0; pathIndex < change.path.slice(0, -1).length; pathIndex++) {
47908
+ let val = change.path[0];
47909
+ const key = change.path[change.path.length - 1];
47910
+ for (let pathIndex = 1; pathIndex < change.path.slice(0, -1).length; pathIndex++) {
47893
47911
  const p = change.path[pathIndex];
47894
47912
  if (val[p] === undefined) {
47895
47913
  const nextPath = change.path[pathIndex + 1];
@@ -48570,13 +48588,14 @@ class StateObserver {
48570
48588
  }
48571
48589
  addChange(...args) {
48572
48590
  const val = args.pop();
48573
- const [root, ...path] = args;
48591
+ const root = args[0];
48574
48592
  let value = root;
48575
- let key = path[path.length - 1];
48576
- for (let pathIndex = 0; pathIndex <= path.length - 2; pathIndex++) {
48577
- const p = path[pathIndex];
48593
+ let key = args[args.length - 1];
48594
+ const pathLength = args.length - 2;
48595
+ for (let pathIndex = 1; pathIndex <= pathLength; pathIndex++) {
48596
+ const p = args[pathIndex];
48578
48597
  if (value[p] === undefined) {
48579
- const nextPath = path[pathIndex + 1];
48598
+ const nextPath = args[pathIndex + 1];
48580
48599
  value[p] = createEmptyStructure(nextPath);
48581
48600
  }
48582
48601
  value = value[p];
@@ -48585,8 +48604,7 @@ class StateObserver {
48585
48604
  return;
48586
48605
  }
48587
48606
  this.changes.push({
48588
- root,
48589
- path,
48607
+ path: args,
48590
48608
  before: value[key],
48591
48609
  after: val,
48592
48610
  });
@@ -50436,19 +50454,22 @@ class Model extends EventBus {
50436
50454
  * Check if the given command is allowed by all the plugins and the history.
50437
50455
  */
50438
50456
  checkDispatchAllowed(command) {
50439
- if (isCoreCommand(command)) {
50440
- return this.checkDispatchAllowedCoreCommand(command);
50457
+ const results = isCoreCommand(command)
50458
+ ? this.checkDispatchAllowedCoreCommand(command)
50459
+ : this.checkDispatchAllowedLocalCommand(command);
50460
+ if (results.some((r) => r !== 0 /* CommandResult.Success */)) {
50461
+ return new DispatchResult(results.flat());
50441
50462
  }
50442
- return this.checkDispatchAllowedLocalCommand(command);
50463
+ return DispatchResult.Success;
50443
50464
  }
50444
50465
  checkDispatchAllowedCoreCommand(command) {
50445
50466
  const results = this.corePlugins.map((handler) => handler.allowDispatch(command));
50446
50467
  results.push(this.range.allowDispatch(command));
50447
- return new DispatchResult(results.flat());
50468
+ return results;
50448
50469
  }
50449
50470
  checkDispatchAllowedLocalCommand(command) {
50450
50471
  const results = this.uiHandlers.map((handler) => handler.allowDispatch(command));
50451
- return new DispatchResult(results.flat());
50472
+ return results;
50452
50473
  }
50453
50474
  finalize() {
50454
50475
  this.status = 3 /* Status.Finalizing */;
@@ -50772,6 +50793,6 @@ exports.setTranslationMethod = setTranslationMethod;
50772
50793
  exports.tokenize = tokenize;
50773
50794
 
50774
50795
 
50775
- __info__.version = '16.4.13';
50776
- __info__.date = '2023-11-03T12:21:08.124Z';
50777
- __info__.hash = '001985e';
50796
+ __info__.version = '16.4.15';
50797
+ __info__.date = '2023-11-24T13:12:26.608Z';
50798
+ __info__.hash = '141943d';