@odoo/o-spreadsheet 16.4.14 → 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;
@@ -31166,6 +31167,14 @@ class BordersPlugin extends CorePlugin {
31166
31167
  // ---------------------------------------------------------------------------
31167
31168
  // Command Handling
31168
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
+ }
31169
31178
  handle(cmd) {
31170
31179
  switch (cmd.type) {
31171
31180
  case "ADD_MERGE":
@@ -31585,6 +31594,14 @@ class BordersPlugin extends CorePlugin {
31585
31594
  this.setBorders(sheetId, [{ ...zone, left: right }], "right", bordersTopLeft.right);
31586
31595
  }
31587
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
+ }
31588
31605
  // ---------------------------------------------------------------------------
31589
31606
  // Import/Export
31590
31607
  // ---------------------------------------------------------------------------
@@ -31692,8 +31709,9 @@ class CellPlugin extends CorePlugin {
31692
31709
  allowDispatch(cmd) {
31693
31710
  switch (cmd.type) {
31694
31711
  case "UPDATE_CELL":
31712
+ return this.checkCellOutOfSheet(cmd);
31695
31713
  case "CLEAR_CELL":
31696
- return this.checkCellOutOfSheet(cmd.sheetId, cmd.col, cmd.row);
31714
+ return this.checkValidations(cmd, this.chainValidations(this.checkCellOutOfSheet, this.checkUselessClearCell));
31697
31715
  default:
31698
31716
  return 0 /* CommandResult.Success */;
31699
31717
  }
@@ -31852,14 +31870,9 @@ class CellPlugin extends CorePlugin {
31852
31870
  */
31853
31871
  getCellById(cellId) {
31854
31872
  // this must be as fast as possible
31855
- for (const sheetId in this.cells) {
31856
- const sheet = this.cells[sheetId];
31857
- const cell = sheet[cellId];
31858
- if (cell) {
31859
- return cell;
31860
- }
31861
- }
31862
- return undefined;
31873
+ const position = this.getters.getCellPosition(cellId);
31874
+ const sheet = this.cells[position.sheetId];
31875
+ return sheet[cellId];
31863
31876
  }
31864
31877
  /*
31865
31878
  * Reconstructs the original formula string based on a normalized form and its dependencies
@@ -32085,13 +32098,23 @@ class CellPlugin extends CorePlugin {
32085
32098
  dependencies: [],
32086
32099
  };
32087
32100
  }
32088
- checkCellOutOfSheet(sheetId, col, row) {
32101
+ checkCellOutOfSheet(cmd) {
32102
+ const { sheetId, col, row } = cmd;
32089
32103
  const sheet = this.getters.tryGetSheet(sheetId);
32090
32104
  if (!sheet)
32091
32105
  return 28 /* CommandResult.InvalidSheetId */;
32092
32106
  const sheetZone = this.getters.getSheetZone(sheetId);
32093
32107
  return isInside(col, row, sheetZone) ? 0 /* CommandResult.Success */ : 19 /* CommandResult.TargetOutOfSheet */;
32094
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
+ }
32095
32118
  }
32096
32119
  class FormulaCellWithDependencies {
32097
32120
  id;
@@ -35160,10 +35183,13 @@ class SheetPlugin extends CorePlugin {
35160
35183
  // begin with the end.
35161
35184
  rows.sort((a, b) => b - a);
35162
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];
35163
35189
  // Move the cells.
35164
- this.moveCellOnRowsDeletion(sheet, group[group.length - 1], group[0]);
35165
- // Effectively delete the element and recompute the left-right/top-bottom.
35166
- group.map((row) => this.updateRowsStructureOnDeletion(row, sheet));
35190
+ this.moveCellOnRowsDeletion(sheet, from, to);
35191
+ // Effectively delete the rows
35192
+ this.updateRowsStructureOnDeletion(sheet, from, to);
35167
35193
  }
35168
35194
  const count = rows.filter((row) => row < sheet.panes.ySplit).length;
35169
35195
  if (count) {
@@ -35185,8 +35211,6 @@ class SheetPlugin extends CorePlugin {
35185
35211
  this.addEmptyRows(sheet, quantity);
35186
35212
  // Move the cells.
35187
35213
  this.moveCellsOnAddition(sheet, index, quantity, "rows");
35188
- // Recompute the left-right/top-bottom.
35189
- this.updateRowsStructureOnAddition(sheet, row, quantity);
35190
35214
  if (index < sheet.panes.ySplit) {
35191
35215
  this.setPaneDivisions(sheet.id, sheet.panes.ySplit + quantity, "ROW");
35192
35216
  }
@@ -35287,35 +35311,20 @@ class SheetPlugin extends CorePlugin {
35287
35311
  }
35288
35312
  }
35289
35313
  }
35290
- updateRowsStructureOnDeletion(index, sheet) {
35314
+ updateRowsStructureOnDeletion(sheet, deleteFromRow, deleteToRow) {
35291
35315
  const rows = [];
35292
- const cellsQueue = sheet.rows.map((row) => row.cells);
35316
+ const cellsQueue = sheet.rows.map((row) => row.cells).reverse();
35293
35317
  for (let i in sheet.rows) {
35294
- if (Number(i) === index) {
35318
+ const row = Number(i);
35319
+ if (row >= deleteFromRow && row <= deleteToRow) {
35295
35320
  continue;
35296
35321
  }
35297
35322
  rows.push({
35298
- cells: cellsQueue.shift(),
35323
+ cells: cellsQueue.pop(),
35299
35324
  });
35300
35325
  }
35301
35326
  this.history.update("sheets", sheet.id, "rows", rows);
35302
35327
  }
35303
- /**
35304
- * Update the rows of the sheet after an addition:
35305
- * - Rename the rows
35306
- *
35307
- * @param sheet Sheet on which the deletion occurs
35308
- * @param addedRow Index of the added row
35309
- * @param rowsToAdd Number of the rows to add
35310
- */
35311
- updateRowsStructureOnAddition(sheet, addedRow, rowsToAdd) {
35312
- const rows = [];
35313
- const cellsQueue = sheet.rows.map((row) => row.cells);
35314
- sheet.rows.forEach(() => rows.push({
35315
- cells: cellsQueue.shift(),
35316
- }));
35317
- this.history.update("sheets", sheet.id, "rows", rows);
35318
- }
35319
35328
  /**
35320
35329
  * Add empty rows at the end of the rows
35321
35330
  *
@@ -35431,6 +35440,7 @@ class CompilationParametersBuilder {
35431
35440
  getters;
35432
35441
  computeCell;
35433
35442
  evalContext;
35443
+ rangeCache = {};
35434
35444
  constructor(context, getters, computeCell) {
35435
35445
  this.getters = getters;
35436
35446
  this.computeCell = computeCell;
@@ -35516,25 +35526,32 @@ class CompilationParametersBuilder {
35516
35526
  if (!_zone) {
35517
35527
  return { value: [[]], format: [[]] };
35518
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
+ }
35519
35534
  const height = _zone.bottom - _zone.top + 1;
35520
35535
  const width = _zone.right - _zone.left + 1;
35521
- const value = Array.from({ length: width }, () => Array.from({ length: height }));
35522
- const format = Array.from({ length: width }, () => Array.from({ length: height }));
35536
+ const value = new Array(width);
35537
+ const format = new Array(width);
35523
35538
  // Performance issue: nested loop is faster than a map here
35524
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);
35525
35543
  for (let row = _zone.top; row <= _zone.bottom; row++) {
35526
- const evaluatedCell = this.getEvaluatedCellIfNotEmpty({ sheetId: sheetId, col, row });
35527
- if (evaluatedCell) {
35528
- const colIndex = col - _zone.left;
35529
- const rowIndex = row - _zone.top;
35530
- value[colIndex][rowIndex] = evaluatedCell.value;
35531
- if (evaluatedCell.format !== undefined) {
35532
- format[colIndex][rowIndex] = evaluatedCell.format;
35533
- }
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;
35534
35549
  }
35535
35550
  }
35536
35551
  }
35537
- return { value, format };
35552
+ const result = { value, format };
35553
+ this.rangeCache[cacheKey] = result;
35554
+ return result;
35538
35555
  }
35539
35556
  }
35540
35557
 
@@ -35732,6 +35749,7 @@ class Evaluator {
35732
35749
  this.formulaDependencies().addDependencies(positionId, dependencies);
35733
35750
  }
35734
35751
  updateCompilationParameters() {
35752
+ // rebuild the compilation parameters (with a clean cache)
35735
35753
  this.compilationParams = buildCompilationParameters(this.context, this.getters, this.computeAndSave.bind(this));
35736
35754
  }
35737
35755
  evaluateCells(positions) {
@@ -35775,6 +35793,15 @@ class Evaluator {
35775
35793
  this.evaluatedCells = new Map();
35776
35794
  this.evaluate(this.getAllCells());
35777
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
+ }
35778
35805
  getAllCells() {
35779
35806
  const positionIds = new JetSet();
35780
35807
  for (const sheetId of this.getters.getSheetIds()) {
@@ -35801,6 +35828,7 @@ class Evaluator {
35801
35828
  this.nextPositionsToUpdate = cells;
35802
35829
  let currentIteration = 0;
35803
35830
  while (this.nextPositionsToUpdate.size && currentIteration++ < MAX_ITERATION) {
35831
+ this.updateCompilationParameters();
35804
35832
  const positionIds = Array.from(this.nextPositionsToUpdate);
35805
35833
  this.nextPositionsToUpdate.clear();
35806
35834
  for (let i = 0; i < positionIds.length; ++i) {
@@ -36185,7 +36213,6 @@ class PositionBitsEncoder {
36185
36213
  // of other cells depending on it, at the next iteration.
36186
36214
  //#endregion
36187
36215
  class EvaluationPlugin extends UIPlugin {
36188
- config;
36189
36216
  static getters = [
36190
36217
  "evaluateFormula",
36191
36218
  "getRangeFormattedValues",
@@ -36197,12 +36224,9 @@ class EvaluationPlugin extends UIPlugin {
36197
36224
  ];
36198
36225
  shouldRebuildDependenciesGraph = true;
36199
36226
  evaluator;
36200
- compilationParams;
36201
36227
  positionsToUpdate = [];
36202
36228
  constructor(config) {
36203
36229
  super(config);
36204
- this.config = config;
36205
- this.compilationParams = this.getCompilationParameters();
36206
36230
  this.evaluator = new Evaluator(config.custom, this.getters);
36207
36231
  }
36208
36232
  // ---------------------------------------------------------------------------
@@ -36227,10 +36251,6 @@ class EvaluationPlugin extends UIPlugin {
36227
36251
  case "EVALUATE_CELLS":
36228
36252
  this.evaluator.evaluateAllCells();
36229
36253
  break;
36230
- case "UPDATE_LOCALE":
36231
- this.compilationParams = this.getCompilationParameters();
36232
- this.evaluator.updateCompilationParameters();
36233
- break;
36234
36254
  }
36235
36255
  }
36236
36256
  finalize() {
@@ -36248,12 +36268,7 @@ class EvaluationPlugin extends UIPlugin {
36248
36268
  // Getters
36249
36269
  // ---------------------------------------------------------------------------
36250
36270
  evaluateFormula(sheetId, formulaString) {
36251
- const compiledFormula = compile(formulaString);
36252
- const ranges = [];
36253
- for (let xc of compiledFormula.dependencies) {
36254
- ranges.push(this.getters.getRangeFromSheetXC(sheetId, xc));
36255
- }
36256
- return compiledFormula.execute(ranges, ...this.compilationParams).value;
36271
+ return this.evaluator.evaluateFormula(sheetId, formulaString);
36257
36272
  }
36258
36273
  /**
36259
36274
  * Return the value of each cell in the range as they are displayed in the grid.
@@ -36352,9 +36367,6 @@ class EvaluationPlugin extends UIPlugin {
36352
36367
  }
36353
36368
  return undefined;
36354
36369
  }
36355
- getCompilationParameters() {
36356
- return buildCompilationParameters(this.config.custom, this.getters, (position) => this.evaluator.getEvaluatedCell(position));
36357
- }
36358
36370
  }
36359
36371
  function isBadExpression(formula) {
36360
36372
  try {
@@ -42071,8 +42083,8 @@ class ClipboardPlugin extends UIPlugin {
42071
42083
  this.lastPasteState = this.state;
42072
42084
  if (this.paintFormatStatus === "oneOff") {
42073
42085
  this.paintFormatStatus = "inactive";
42074
- this.status = "invisible";
42075
42086
  }
42087
+ this.status = "invisible";
42076
42088
  break;
42077
42089
  case "CLEAN_CLIPBOARD_HIGHLIGHT":
42078
42090
  this.status = "invisible";
@@ -47889,9 +47901,9 @@ function revertChanges(revisions) {
47889
47901
  * Apply the changes of the given HistoryChange to the state
47890
47902
  */
47891
47903
  function applyChange(change, target) {
47892
- let val = change.root;
47893
- let key = change.path[change.path.length - 1];
47894
- 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++) {
47895
47907
  const p = change.path[pathIndex];
47896
47908
  if (val[p] === undefined) {
47897
47909
  const nextPath = change.path[pathIndex + 1];
@@ -48572,13 +48584,14 @@ class StateObserver {
48572
48584
  }
48573
48585
  addChange(...args) {
48574
48586
  const val = args.pop();
48575
- const [root, ...path] = args;
48587
+ const root = args[0];
48576
48588
  let value = root;
48577
- let key = path[path.length - 1];
48578
- for (let pathIndex = 0; pathIndex <= path.length - 2; pathIndex++) {
48579
- 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];
48580
48593
  if (value[p] === undefined) {
48581
- const nextPath = path[pathIndex + 1];
48594
+ const nextPath = args[pathIndex + 1];
48582
48595
  value[p] = createEmptyStructure(nextPath);
48583
48596
  }
48584
48597
  value = value[p];
@@ -48587,8 +48600,7 @@ class StateObserver {
48587
48600
  return;
48588
48601
  }
48589
48602
  this.changes.push({
48590
- root,
48591
- path,
48603
+ path: args,
48592
48604
  before: value[key],
48593
48605
  after: val,
48594
48606
  });
@@ -50438,19 +50450,22 @@ class Model extends EventBus {
50438
50450
  * Check if the given command is allowed by all the plugins and the history.
50439
50451
  */
50440
50452
  checkDispatchAllowed(command) {
50441
- if (isCoreCommand(command)) {
50442
- 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());
50443
50458
  }
50444
- return this.checkDispatchAllowedLocalCommand(command);
50459
+ return DispatchResult.Success;
50445
50460
  }
50446
50461
  checkDispatchAllowedCoreCommand(command) {
50447
50462
  const results = this.corePlugins.map((handler) => handler.allowDispatch(command));
50448
50463
  results.push(this.range.allowDispatch(command));
50449
- return new DispatchResult(results.flat());
50464
+ return results;
50450
50465
  }
50451
50466
  checkDispatchAllowedLocalCommand(command) {
50452
50467
  const results = this.uiHandlers.map((handler) => handler.allowDispatch(command));
50453
- return new DispatchResult(results.flat());
50468
+ return results;
50454
50469
  }
50455
50470
  finalize() {
50456
50471
  this.status = 3 /* Status.Finalizing */;
@@ -50742,6 +50757,6 @@ const constants = {
50742
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 };
50743
50758
 
50744
50759
 
50745
- __info__.version = '16.4.14';
50746
- __info__.date = '2023-11-16T11:59:51.900Z';
50747
- __info__.hash = '66c6f47';
50760
+ __info__.version = '16.4.15';
50761
+ __info__.date = '2023-11-24T13:12:26.608Z';
50762
+ __info__.hash = '141943d';