@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.
@@ -668,8 +668,7 @@ function transpose2dArray(matrix, callback = (val) => val) {
668
668
  *
669
669
  * https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Regular_Expressions/Character_Classes
670
670
  */
671
- const whiteSpaceCharacters = [
672
- " ",
671
+ const whiteSpaceSpecialCharacters = [
673
672
  "\t",
674
673
  "\f",
675
674
  "\v",
@@ -684,8 +683,7 @@ const whiteSpaceCharacters = [
684
683
  String.fromCharCode(parseInt("3000", 16)),
685
684
  String.fromCharCode(parseInt("feff", 16)),
686
685
  ];
687
- const whiteSpaceRegexp = new RegExp(whiteSpaceCharacters.join("|"), "g");
688
- const newLineRegex = /\r\n|\r|\n/g;
686
+ const whiteSpaceRegexp = new RegExp(whiteSpaceSpecialCharacters.join("|") + "|(\r\n|\r|\n)", "g");
689
687
  /**
690
688
  * Replace all the special spaces in a string (non-breaking, tabs, ...) by normal spaces, and all the
691
689
  * different newlines types by \n.
@@ -693,9 +691,9 @@ const newLineRegex = /\r\n|\r|\n/g;
693
691
  function replaceSpecialSpaces(text) {
694
692
  if (!text)
695
693
  return "";
696
- text = text.replace(whiteSpaceRegexp, " ");
697
- text = text.replace(newLineRegex, NEWLINE);
698
- return text;
694
+ if (!whiteSpaceRegexp.test(text))
695
+ return text;
696
+ return text.replace(whiteSpaceRegexp, (match, newLine) => (newLine ? NEWLINE : " "));
699
697
  }
700
698
  /**
701
699
  * Determine if the numbers are consecutive.
@@ -4265,6 +4263,7 @@ var CommandResult;
4265
4263
  CommandResult[CommandResult["NoActiveSheet"] = 94] = "NoActiveSheet";
4266
4264
  CommandResult[CommandResult["InvalidLocale"] = 95] = "InvalidLocale";
4267
4265
  CommandResult[CommandResult["AlreadyInPaintingFormatMode"] = 96] = "AlreadyInPaintingFormatMode";
4266
+ CommandResult[CommandResult["NoChanges"] = 97] = "NoChanges";
4268
4267
  })(CommandResult || (CommandResult = {}));
4269
4268
 
4270
4269
  const borderStyles = ["thin", "medium", "thick", "dashed", "dotted"];
@@ -4354,12 +4353,12 @@ function toString(value) {
4354
4353
  }
4355
4354
  }
4356
4355
  /** Normalize string by setting it to lowercase and replacing accent letters with plain letters */
4357
- function normalizeString(str) {
4356
+ const normalizeString = memoize(function normalizeString(str) {
4358
4357
  return str
4359
4358
  .toLowerCase()
4360
4359
  .normalize("NFD")
4361
4360
  .replace(/[\u0300-\u036f]/g, "");
4362
- }
4361
+ });
4363
4362
  /**
4364
4363
  * Normalize a value.
4365
4364
  * If the cell value is a string, this will set it to lowercase and replacing accent letters with plain letters
@@ -14170,7 +14169,7 @@ function tokenize(str, locale = DEFAULT_LOCALE) {
14170
14169
  return result;
14171
14170
  }
14172
14171
  function tokenizeDebugger(chars) {
14173
- if (chars.current() === "?") {
14172
+ if (chars.current === "?") {
14174
14173
  chars.shift();
14175
14174
  return { type: "DEBUGGER", value: "?" };
14176
14175
  }
@@ -14181,7 +14180,7 @@ const misc = {
14181
14180
  ")": "RIGHT_PAREN",
14182
14181
  };
14183
14182
  function tokenizeMisc(chars) {
14184
- if (chars.current() in misc) {
14183
+ if (chars.current in misc) {
14185
14184
  const value = chars.shift();
14186
14185
  const type = misc[value];
14187
14186
  return { type, value };
@@ -14189,7 +14188,7 @@ function tokenizeMisc(chars) {
14189
14188
  return null;
14190
14189
  }
14191
14190
  function tokenizeArgsSeparator(chars, locale) {
14192
- if (chars.current() === locale.formulaArgSeparator) {
14191
+ if (chars.current === locale.formulaArgSeparator) {
14193
14192
  const value = chars.shift();
14194
14193
  const type = "ARG_SEPARATOR";
14195
14194
  return { type, value };
@@ -14214,14 +14213,13 @@ function tokenizeNumber(chars, locale) {
14214
14213
  return null;
14215
14214
  }
14216
14215
  function tokenizeString(chars) {
14217
- if (chars.current() === '"') {
14216
+ if (chars.current === '"') {
14218
14217
  const startChar = chars.shift();
14219
14218
  let letters = startChar;
14220
- while (chars.current() &&
14221
- (chars.current() !== startChar || letters[letters.length - 1] === "\\")) {
14219
+ while (chars.current && (chars.current !== startChar || letters[letters.length - 1] === "\\")) {
14222
14220
  letters += chars.shift();
14223
14221
  }
14224
- if (chars.current() === '"') {
14222
+ if (chars.current === '"') {
14225
14223
  letters += chars.shift();
14226
14224
  }
14227
14225
  return {
@@ -14248,14 +14246,14 @@ function tokenizeSymbol(chars) {
14248
14246
  let result = "";
14249
14247
  // there are two main cases to manage: either something which starts with
14250
14248
  // a ', like 'Sheet 2'A2, or a word-like element.
14251
- if (chars.current() === "'") {
14249
+ if (chars.current === "'") {
14252
14250
  let lastChar = chars.shift();
14253
14251
  result += lastChar;
14254
- while (chars.current()) {
14252
+ while (chars.current) {
14255
14253
  lastChar = chars.shift();
14256
14254
  result += lastChar;
14257
14255
  if (lastChar === "'") {
14258
- if (chars.current() && chars.current() === "'") {
14256
+ if (chars.current && chars.current === "'") {
14259
14257
  lastChar = chars.shift();
14260
14258
  result += lastChar;
14261
14259
  }
@@ -14271,7 +14269,7 @@ function tokenizeSymbol(chars) {
14271
14269
  };
14272
14270
  }
14273
14271
  }
14274
- while (chars.current() && separatorRegexp.test(chars.current())) {
14272
+ while (chars.current && separatorRegexp.test(chars.current)) {
14275
14273
  result += chars.shift();
14276
14274
  }
14277
14275
  if (result.length) {
@@ -14286,14 +14284,14 @@ function tokenizeSymbol(chars) {
14286
14284
  }
14287
14285
  function tokenizeSpace(chars) {
14288
14286
  let length = 0;
14289
- while (chars.current() === NEWLINE) {
14287
+ while (chars.current === NEWLINE) {
14290
14288
  length++;
14291
14289
  chars.shift();
14292
14290
  }
14293
14291
  if (length) {
14294
14292
  return { type: "SPACE", value: NEWLINE.repeat(length) };
14295
14293
  }
14296
- while (chars.current() === " ") {
14294
+ while (chars.current === " ") {
14297
14295
  length++;
14298
14296
  chars.shift();
14299
14297
  }
@@ -14312,17 +14310,20 @@ function tokenizeInvalidRange(chars) {
14312
14310
  class TokenizingChars {
14313
14311
  text;
14314
14312
  currentIndex = 0;
14313
+ current;
14315
14314
  constructor(text) {
14316
14315
  this.text = text;
14317
- }
14318
- current() {
14319
- return this.text[this.currentIndex];
14316
+ this.current = text[0];
14320
14317
  }
14321
14318
  shift() {
14322
- return this.text[this.currentIndex++];
14319
+ const current = this.current;
14320
+ const next = this.text[++this.currentIndex];
14321
+ this.current = next;
14322
+ return current;
14323
14323
  }
14324
14324
  advanceBy(length) {
14325
14325
  this.currentIndex += length;
14326
+ this.current = this.text[this.currentIndex];
14326
14327
  }
14327
14328
  isOver() {
14328
14329
  return this.currentIndex >= this.text.length;
@@ -18565,6 +18566,17 @@ const SELECTION_CONTAINS_FILTER = (env) => {
18565
18566
  //------------------------------------------------------------------------------
18566
18567
  const IS_ONLY_ONE_RANGE = (env) => {
18567
18568
  return env.model.getters.getSelectedZones().length === 1;
18569
+ };
18570
+ const CAN_INSERT_HEADER = (env, dimension) => {
18571
+ if (!IS_ONLY_ONE_RANGE(env)) {
18572
+ return false;
18573
+ }
18574
+ const activeHeaders = dimension === "COL" ? env.model.getters.getActiveCols() : env.model.getters.getActiveRows();
18575
+ const ortogonalActiveHeaders = dimension === "COL" ? env.model.getters.getActiveRows() : env.model.getters.getActiveCols();
18576
+ const sheetId = env.model.getters.getActiveSheetId();
18577
+ const zone = env.model.getters.getSelectedZone();
18578
+ const allSheetSelected = isEqual(zone, env.model.getters.getSheetZone(sheetId));
18579
+ return isConsecutive(activeHeaders) && (ortogonalActiveHeaders.size === 0 || allSheetSelected);
18568
18580
  };
18569
18581
 
18570
18582
  const undo = {
@@ -18754,9 +18766,7 @@ const insertRow = {
18754
18766
  const number = getRowsNumber(env);
18755
18767
  return number === 1 ? _lt("Insert row") : _lt("Insert %s rows", number.toString());
18756
18768
  },
18757
- isVisible: (env) => isConsecutive(env.model.getters.getActiveRows()) &&
18758
- IS_ONLY_ONE_RANGE(env) &&
18759
- env.model.getters.getActiveCols().size === 0,
18769
+ isVisible: (env) => CAN_INSERT_HEADER(env, "ROW"),
18760
18770
  icon: "o-spreadsheet-Icon.INSERT_ROW",
18761
18771
  };
18762
18772
  const rowInsertRowBefore = {
@@ -18765,9 +18775,7 @@ const rowInsertRowBefore = {
18765
18775
  return number === 1 ? _lt("Insert row above") : _lt("Insert %s rows above", number.toString());
18766
18776
  },
18767
18777
  execute: INSERT_ROWS_BEFORE_ACTION,
18768
- isVisible: (env) => isConsecutive(env.model.getters.getActiveRows()) &&
18769
- IS_ONLY_ONE_RANGE(env) &&
18770
- env.model.getters.getActiveCols().size === 0,
18778
+ isVisible: (env) => CAN_INSERT_HEADER(env, "ROW"),
18771
18779
  icon: "o-spreadsheet-Icon.INSERT_ROW_BEFORE",
18772
18780
  };
18773
18781
  const topBarInsertRowsBefore = {
@@ -18798,9 +18806,7 @@ const rowInsertRowsAfter = {
18798
18806
  const number = getRowsNumber(env);
18799
18807
  return number === 1 ? _lt("Insert row below") : _lt("Insert %s rows below", number.toString());
18800
18808
  },
18801
- isVisible: (env) => isConsecutive(env.model.getters.getActiveRows()) &&
18802
- IS_ONLY_ONE_RANGE(env) &&
18803
- env.model.getters.getActiveCols().size === 0,
18809
+ isVisible: (env) => CAN_INSERT_HEADER(env, "ROW"),
18804
18810
  icon: "o-spreadsheet-Icon.INSERT_ROW_AFTER",
18805
18811
  };
18806
18812
  const topBarInsertRowsAfter = {
@@ -18818,9 +18824,7 @@ const insertCol = {
18818
18824
  const number = getColumnsNumber(env);
18819
18825
  return number === 1 ? _lt("Insert column") : _lt("Insert %s columns", number.toString());
18820
18826
  },
18821
- isVisible: (env) => isConsecutive(env.model.getters.getActiveCols()) &&
18822
- IS_ONLY_ONE_RANGE(env) &&
18823
- env.model.getters.getActiveRows().size === 0,
18827
+ isVisible: (env) => CAN_INSERT_HEADER(env, "COL"),
18824
18828
  icon: "o-spreadsheet-Icon.INSERT_COL",
18825
18829
  };
18826
18830
  const colInsertColsBefore = {
@@ -18831,9 +18835,7 @@ const colInsertColsBefore = {
18831
18835
  : _lt("Insert %s columns left", number.toString());
18832
18836
  },
18833
18837
  execute: INSERT_COLUMNS_BEFORE_ACTION,
18834
- isVisible: (env) => isConsecutive(env.model.getters.getActiveCols()) &&
18835
- IS_ONLY_ONE_RANGE(env) &&
18836
- env.model.getters.getActiveRows().size === 0,
18838
+ isVisible: (env) => CAN_INSERT_HEADER(env, "COL"),
18837
18839
  icon: "o-spreadsheet-Icon.INSERT_COL_BEFORE",
18838
18840
  };
18839
18841
  const topBarInsertColsBefore = {
@@ -18866,9 +18868,7 @@ const colInsertColsAfter = {
18866
18868
  : _lt("Insert %s columns right", number.toString());
18867
18869
  },
18868
18870
  execute: INSERT_COLUMNS_AFTER_ACTION,
18869
- isVisible: (env) => isConsecutive(env.model.getters.getActiveCols()) &&
18870
- IS_ONLY_ONE_RANGE(env) &&
18871
- env.model.getters.getActiveRows().size === 0,
18871
+ isVisible: (env) => CAN_INSERT_HEADER(env, "COL"),
18872
18872
  icon: "o-spreadsheet-Icon.INSERT_COL_AFTER",
18873
18873
  };
18874
18874
  const topBarInsertColsAfter = {
@@ -22826,7 +22826,7 @@ css /* scss */ `
22826
22826
  }
22827
22827
  .o-input-count {
22828
22828
  width: fit-content;
22829
- padding: 4 0 4 4;
22829
+ padding: 4px 0 4px 4px;
22830
22830
  }
22831
22831
  }
22832
22832
  }
@@ -31167,6 +31167,14 @@ class BordersPlugin extends CorePlugin {
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 @@ class BordersPlugin extends CorePlugin {
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
  // ---------------------------------------------------------------------------
@@ -31693,8 +31709,9 @@ class CellPlugin extends CorePlugin {
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 @@ class CellPlugin extends CorePlugin {
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 @@ class CellPlugin extends CorePlugin {
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;
@@ -33073,6 +33095,13 @@ class FiltersPlugin extends CorePlugin {
33073
33095
  }
33074
33096
  onDeleteColumnsRows(cmd) {
33075
33097
  for (const table of this.getFilterTables(cmd.sheetId)) {
33098
+ // Remove the filter tables whose data filter headers are in the removed rows.
33099
+ if (cmd.dimension === "ROW" && cmd.elements.includes(table.zone.top)) {
33100
+ const tables = { ...this.tables[cmd.sheetId] };
33101
+ delete tables[table.id];
33102
+ this.history.update("tables", cmd.sheetId, tables);
33103
+ continue;
33104
+ }
33076
33105
  const zone = reduceZoneOnDeletion(table.zone, cmd.dimension === "COL" ? "left" : "top", cmd.elements);
33077
33106
  if (!zone) {
33078
33107
  const tables = { ...this.tables[cmd.sheetId] };
@@ -35154,10 +35183,13 @@ class SheetPlugin extends CorePlugin {
35154
35183
  // begin with the end.
35155
35184
  rows.sort((a, b) => b - a);
35156
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];
35157
35189
  // Move the cells.
35158
- this.moveCellOnRowsDeletion(sheet, group[group.length - 1], group[0]);
35159
- // Effectively delete the element and recompute the left-right/top-bottom.
35160
- group.map((row) => this.updateRowsStructureOnDeletion(row, sheet));
35190
+ this.moveCellOnRowsDeletion(sheet, from, to);
35191
+ // Effectively delete the rows
35192
+ this.updateRowsStructureOnDeletion(sheet, from, to);
35161
35193
  }
35162
35194
  const count = rows.filter((row) => row < sheet.panes.ySplit).length;
35163
35195
  if (count) {
@@ -35179,8 +35211,6 @@ class SheetPlugin extends CorePlugin {
35179
35211
  this.addEmptyRows(sheet, quantity);
35180
35212
  // Move the cells.
35181
35213
  this.moveCellsOnAddition(sheet, index, quantity, "rows");
35182
- // Recompute the left-right/top-bottom.
35183
- this.updateRowsStructureOnAddition(sheet, row, quantity);
35184
35214
  if (index < sheet.panes.ySplit) {
35185
35215
  this.setPaneDivisions(sheet.id, sheet.panes.ySplit + quantity, "ROW");
35186
35216
  }
@@ -35281,35 +35311,20 @@ class SheetPlugin extends CorePlugin {
35281
35311
  }
35282
35312
  }
35283
35313
  }
35284
- updateRowsStructureOnDeletion(index, sheet) {
35314
+ updateRowsStructureOnDeletion(sheet, deleteFromRow, deleteToRow) {
35285
35315
  const rows = [];
35286
- const cellsQueue = sheet.rows.map((row) => row.cells);
35316
+ const cellsQueue = sheet.rows.map((row) => row.cells).reverse();
35287
35317
  for (let i in sheet.rows) {
35288
- if (Number(i) === index) {
35318
+ const row = Number(i);
35319
+ if (row >= deleteFromRow && row <= deleteToRow) {
35289
35320
  continue;
35290
35321
  }
35291
35322
  rows.push({
35292
- cells: cellsQueue.shift(),
35323
+ cells: cellsQueue.pop(),
35293
35324
  });
35294
35325
  }
35295
35326
  this.history.update("sheets", sheet.id, "rows", rows);
35296
35327
  }
35297
- /**
35298
- * Update the rows of the sheet after an addition:
35299
- * - Rename the rows
35300
- *
35301
- * @param sheet Sheet on which the deletion occurs
35302
- * @param addedRow Index of the added row
35303
- * @param rowsToAdd Number of the rows to add
35304
- */
35305
- updateRowsStructureOnAddition(sheet, addedRow, rowsToAdd) {
35306
- const rows = [];
35307
- const cellsQueue = sheet.rows.map((row) => row.cells);
35308
- sheet.rows.forEach(() => rows.push({
35309
- cells: cellsQueue.shift(),
35310
- }));
35311
- this.history.update("sheets", sheet.id, "rows", rows);
35312
- }
35313
35328
  /**
35314
35329
  * Add empty rows at the end of the rows
35315
35330
  *
@@ -35425,6 +35440,7 @@ class CompilationParametersBuilder {
35425
35440
  getters;
35426
35441
  computeCell;
35427
35442
  evalContext;
35443
+ rangeCache = {};
35428
35444
  constructor(context, getters, computeCell) {
35429
35445
  this.getters = getters;
35430
35446
  this.computeCell = computeCell;
@@ -35510,25 +35526,32 @@ class CompilationParametersBuilder {
35510
35526
  if (!_zone) {
35511
35527
  return { value: [[]], format: [[]] };
35512
35528
  }
35529
+ const { top, left, bottom, right } = zone;
35530
+ const cacheKey = `${sheetId}-${top}-${left}-${bottom}-${right}`;
35531
+ if (cacheKey in this.rangeCache) {
35532
+ return this.rangeCache[cacheKey];
35533
+ }
35513
35534
  const height = _zone.bottom - _zone.top + 1;
35514
35535
  const width = _zone.right - _zone.left + 1;
35515
- const value = Array.from({ length: width }, () => Array.from({ length: height }));
35516
- const format = Array.from({ length: width }, () => Array.from({ length: height }));
35536
+ const value = new Array(width);
35537
+ const format = new Array(width);
35517
35538
  // Performance issue: nested loop is faster than a map here
35518
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);
35519
35543
  for (let row = _zone.top; row <= _zone.bottom; row++) {
35520
- const evaluatedCell = this.getEvaluatedCellIfNotEmpty({ sheetId: sheetId, col, row });
35521
- if (evaluatedCell) {
35522
- const colIndex = col - _zone.left;
35523
- const rowIndex = row - _zone.top;
35524
- value[colIndex][rowIndex] = evaluatedCell.value;
35525
- if (evaluatedCell.format !== undefined) {
35526
- format[colIndex][rowIndex] = evaluatedCell.format;
35527
- }
35544
+ const evaluatedCell = this.getEvaluatedCellIfNotEmpty({ sheetId, col, row });
35545
+ const rowIndex = row - _zone.top;
35546
+ value[colIndex][rowIndex] = evaluatedCell?.value;
35547
+ if (evaluatedCell?.format !== undefined) {
35548
+ format[colIndex][rowIndex] = evaluatedCell.format;
35528
35549
  }
35529
35550
  }
35530
35551
  }
35531
- return { value, format };
35552
+ const result = { value, format };
35553
+ this.rangeCache[cacheKey] = result;
35554
+ return result;
35532
35555
  }
35533
35556
  }
35534
35557
 
@@ -35726,6 +35749,7 @@ class Evaluator {
35726
35749
  this.formulaDependencies().addDependencies(positionId, dependencies);
35727
35750
  }
35728
35751
  updateCompilationParameters() {
35752
+ // rebuild the compilation parameters (with a clean cache)
35729
35753
  this.compilationParams = buildCompilationParameters(this.context, this.getters, this.computeAndSave.bind(this));
35730
35754
  }
35731
35755
  evaluateCells(positions) {
@@ -35769,6 +35793,15 @@ class Evaluator {
35769
35793
  this.evaluatedCells = new Map();
35770
35794
  this.evaluate(this.getAllCells());
35771
35795
  }
35796
+ evaluateFormula(sheetId, formulaString) {
35797
+ const compiledFormula = compile(formulaString);
35798
+ const ranges = [];
35799
+ for (let xc of compiledFormula.dependencies) {
35800
+ ranges.push(this.getters.getRangeFromSheetXC(sheetId, xc));
35801
+ }
35802
+ this.updateCompilationParameters();
35803
+ return compiledFormula.execute(ranges, ...this.compilationParams).value;
35804
+ }
35772
35805
  getAllCells() {
35773
35806
  const positionIds = new JetSet();
35774
35807
  for (const sheetId of this.getters.getSheetIds()) {
@@ -35795,6 +35828,7 @@ class Evaluator {
35795
35828
  this.nextPositionsToUpdate = cells;
35796
35829
  let currentIteration = 0;
35797
35830
  while (this.nextPositionsToUpdate.size && currentIteration++ < MAX_ITERATION) {
35831
+ this.updateCompilationParameters();
35798
35832
  const positionIds = Array.from(this.nextPositionsToUpdate);
35799
35833
  this.nextPositionsToUpdate.clear();
35800
35834
  for (let i = 0; i < positionIds.length; ++i) {
@@ -36179,7 +36213,6 @@ class PositionBitsEncoder {
36179
36213
  // of other cells depending on it, at the next iteration.
36180
36214
  //#endregion
36181
36215
  class EvaluationPlugin extends UIPlugin {
36182
- config;
36183
36216
  static getters = [
36184
36217
  "evaluateFormula",
36185
36218
  "getRangeFormattedValues",
@@ -36191,12 +36224,9 @@ class EvaluationPlugin extends UIPlugin {
36191
36224
  ];
36192
36225
  shouldRebuildDependenciesGraph = true;
36193
36226
  evaluator;
36194
- compilationParams;
36195
36227
  positionsToUpdate = [];
36196
36228
  constructor(config) {
36197
36229
  super(config);
36198
- this.config = config;
36199
- this.compilationParams = this.getCompilationParameters();
36200
36230
  this.evaluator = new Evaluator(config.custom, this.getters);
36201
36231
  }
36202
36232
  // ---------------------------------------------------------------------------
@@ -36221,10 +36251,6 @@ class EvaluationPlugin extends UIPlugin {
36221
36251
  case "EVALUATE_CELLS":
36222
36252
  this.evaluator.evaluateAllCells();
36223
36253
  break;
36224
- case "UPDATE_LOCALE":
36225
- this.compilationParams = this.getCompilationParameters();
36226
- this.evaluator.updateCompilationParameters();
36227
- break;
36228
36254
  }
36229
36255
  }
36230
36256
  finalize() {
@@ -36242,12 +36268,7 @@ class EvaluationPlugin extends UIPlugin {
36242
36268
  // Getters
36243
36269
  // ---------------------------------------------------------------------------
36244
36270
  evaluateFormula(sheetId, formulaString) {
36245
- const compiledFormula = compile(formulaString);
36246
- const ranges = [];
36247
- for (let xc of compiledFormula.dependencies) {
36248
- ranges.push(this.getters.getRangeFromSheetXC(sheetId, xc));
36249
- }
36250
- return compiledFormula.execute(ranges, ...this.compilationParams).value;
36271
+ return this.evaluator.evaluateFormula(sheetId, formulaString);
36251
36272
  }
36252
36273
  /**
36253
36274
  * Return the value of each cell in the range as they are displayed in the grid.
@@ -36346,9 +36367,6 @@ class EvaluationPlugin extends UIPlugin {
36346
36367
  }
36347
36368
  return undefined;
36348
36369
  }
36349
- getCompilationParameters() {
36350
- return buildCompilationParameters(this.config.custom, this.getters, (position) => this.evaluator.getEvaluatedCell(position));
36351
- }
36352
36370
  }
36353
36371
  function isBadExpression(formula) {
36354
36372
  try {
@@ -36521,22 +36539,20 @@ class EvaluationChartPlugin extends UIPlugin {
36521
36539
  invalidateCFEvaluationCommands.has(cmd.type) ||
36522
36540
  cmd.type === "EVALUATE_CELLS" ||
36523
36541
  cmd.type === "UPDATE_CELL") {
36524
- if (cmd.type !== "UNDO" && cmd.type !== "REDO") {
36525
- for (const chartId in this.charts) {
36526
- this.history.update("charts", chartId, undefined);
36527
- }
36542
+ for (const chartId in this.charts) {
36543
+ this.charts[chartId] = undefined;
36528
36544
  }
36529
36545
  }
36530
36546
  switch (cmd.type) {
36531
36547
  case "UPDATE_CHART":
36532
36548
  case "CREATE_CHART":
36533
36549
  case "DELETE_FIGURE":
36534
- this.history.update("charts", cmd.id, undefined);
36550
+ this.charts[cmd.id] = undefined;
36535
36551
  break;
36536
36552
  case "DELETE_SHEET":
36537
36553
  for (let chartId in this.charts) {
36538
36554
  if (!this.getters.isChartDefined(chartId)) {
36539
- this.history.update("charts", chartId, undefined);
36555
+ this.charts[chartId] = undefined;
36540
36556
  }
36541
36557
  }
36542
36558
  break;
@@ -36548,7 +36564,7 @@ class EvaluationChartPlugin extends UIPlugin {
36548
36564
  if (!chart) {
36549
36565
  throw new Error(`No chart for the given id: ${figureId}`);
36550
36566
  }
36551
- this.history.update("charts", figureId, this.createRuntimeChart(chart));
36567
+ this.charts[figureId] = this.createRuntimeChart(chart);
36552
36568
  }
36553
36569
  return this.charts[figureId];
36554
36570
  }
@@ -42067,8 +42083,8 @@ class ClipboardPlugin extends UIPlugin {
42067
42083
  this.lastPasteState = this.state;
42068
42084
  if (this.paintFormatStatus === "oneOff") {
42069
42085
  this.paintFormatStatus = "inactive";
42070
- this.status = "invisible";
42071
42086
  }
42087
+ this.status = "invisible";
42072
42088
  break;
42073
42089
  case "CLEAN_CLIPBOARD_HIGHLIGHT":
42074
42090
  this.status = "invisible";
@@ -43012,6 +43028,8 @@ class FilterEvaluationPlugin extends UIPlugin {
43012
43028
  case "EVALUATE_CELLS":
43013
43029
  case "ACTIVATE_SHEET":
43014
43030
  case "REMOVE_FILTER_TABLE":
43031
+ case "ADD_COLUMNS_ROWS":
43032
+ case "REMOVE_COLUMNS_ROWS":
43015
43033
  this.isEvaluationDirty = true;
43016
43034
  break;
43017
43035
  case "START":
@@ -47883,9 +47901,9 @@ function revertChanges(revisions) {
47883
47901
  * Apply the changes of the given HistoryChange to the state
47884
47902
  */
47885
47903
  function applyChange(change, target) {
47886
- let val = change.root;
47887
- let key = change.path[change.path.length - 1];
47888
- for (let pathIndex = 0; pathIndex < change.path.slice(0, -1).length; pathIndex++) {
47904
+ let val = change.path[0];
47905
+ const key = change.path[change.path.length - 1];
47906
+ for (let pathIndex = 1; pathIndex < change.path.slice(0, -1).length; pathIndex++) {
47889
47907
  const p = change.path[pathIndex];
47890
47908
  if (val[p] === undefined) {
47891
47909
  const nextPath = change.path[pathIndex + 1];
@@ -48566,13 +48584,14 @@ class StateObserver {
48566
48584
  }
48567
48585
  addChange(...args) {
48568
48586
  const val = args.pop();
48569
- const [root, ...path] = args;
48587
+ const root = args[0];
48570
48588
  let value = root;
48571
- let key = path[path.length - 1];
48572
- for (let pathIndex = 0; pathIndex <= path.length - 2; pathIndex++) {
48573
- const p = path[pathIndex];
48589
+ let key = args[args.length - 1];
48590
+ const pathLength = args.length - 2;
48591
+ for (let pathIndex = 1; pathIndex <= pathLength; pathIndex++) {
48592
+ const p = args[pathIndex];
48574
48593
  if (value[p] === undefined) {
48575
- const nextPath = path[pathIndex + 1];
48594
+ const nextPath = args[pathIndex + 1];
48576
48595
  value[p] = createEmptyStructure(nextPath);
48577
48596
  }
48578
48597
  value = value[p];
@@ -48581,8 +48600,7 @@ class StateObserver {
48581
48600
  return;
48582
48601
  }
48583
48602
  this.changes.push({
48584
- root,
48585
- path,
48603
+ path: args,
48586
48604
  before: value[key],
48587
48605
  after: val,
48588
48606
  });
@@ -50432,19 +50450,22 @@ class Model extends EventBus {
50432
50450
  * Check if the given command is allowed by all the plugins and the history.
50433
50451
  */
50434
50452
  checkDispatchAllowed(command) {
50435
- if (isCoreCommand(command)) {
50436
- return this.checkDispatchAllowedCoreCommand(command);
50453
+ const results = isCoreCommand(command)
50454
+ ? this.checkDispatchAllowedCoreCommand(command)
50455
+ : this.checkDispatchAllowedLocalCommand(command);
50456
+ if (results.some((r) => r !== 0 /* CommandResult.Success */)) {
50457
+ return new DispatchResult(results.flat());
50437
50458
  }
50438
- return this.checkDispatchAllowedLocalCommand(command);
50459
+ return DispatchResult.Success;
50439
50460
  }
50440
50461
  checkDispatchAllowedCoreCommand(command) {
50441
50462
  const results = this.corePlugins.map((handler) => handler.allowDispatch(command));
50442
50463
  results.push(this.range.allowDispatch(command));
50443
- return new DispatchResult(results.flat());
50464
+ return results;
50444
50465
  }
50445
50466
  checkDispatchAllowedLocalCommand(command) {
50446
50467
  const results = this.uiHandlers.map((handler) => handler.allowDispatch(command));
50447
- return new DispatchResult(results.flat());
50468
+ return results;
50448
50469
  }
50449
50470
  finalize() {
50450
50471
  this.status = 3 /* Status.Finalizing */;
@@ -50736,6 +50757,6 @@ const constants = {
50736
50757
  export { AbstractChart, CommandResult, CorePlugin, DispatchResult, EvaluationError, Model, Registry, Revision, SPREADSHEET_DIMENSIONS, Spreadsheet, UIPlugin, __info__, addFunction, astToFormula, compile, components, constants, convertAstNodes, coreTypes, findCellInNewZone, functionCache, helpers, invalidateCFEvaluationCommands, invalidateDependenciesCommands, invalidateEvaluationCommands, iterateAstNodes, links, load, parse, readonlyAllowedCommands, registries, setDefaultSheetViewSize, setTranslationMethod, tokenize };
50737
50758
 
50738
50759
 
50739
- __info__.version = '16.4.13';
50740
- __info__.date = '2023-11-03T12:21:08.124Z';
50741
- __info__.hash = '001985e';
50760
+ __info__.version = '16.4.15';
50761
+ __info__.date = '2023-11-24T13:12:26.608Z';
50762
+ __info__.hash = '141943d';