@odoo/o-spreadsheet 17.0.3 → 17.0.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -2,9 +2,9 @@
2
2
  /**
3
3
  * This file is generated by o-spreadsheet build tools. Do not edit it.
4
4
  * @see https://github.com/odoo/o-spreadsheet
5
- * @version 17.0.3
6
- * @date 2023-11-16T11:59:49.524Z
7
- * @hash 8bd0fb2
5
+ * @version 17.0.4
6
+ * @date 2023-11-24T13:13:42.575Z
7
+ * @hash 7346d0a
8
8
  */
9
9
 
10
10
  'use strict';
@@ -693,8 +693,7 @@ function removeFalsyAttributes(obj) {
693
693
  *
694
694
  * https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Regular_Expressions/Character_Classes
695
695
  */
696
- const whiteSpaceCharacters = [
697
- " ",
696
+ const whiteSpaceSpecialCharacters = [
698
697
  "\t",
699
698
  "\f",
700
699
  "\v",
@@ -709,8 +708,7 @@ const whiteSpaceCharacters = [
709
708
  String.fromCharCode(parseInt("3000", 16)),
710
709
  String.fromCharCode(parseInt("feff", 16)),
711
710
  ];
712
- const whiteSpaceRegexp = new RegExp(whiteSpaceCharacters.join("|"), "g");
713
- const newLineRegex = /\r\n|\r|\n/g;
711
+ const whiteSpaceRegexp = new RegExp(whiteSpaceSpecialCharacters.join("|") + "|(\r\n|\r|\n)", "g");
714
712
  /**
715
713
  * Replace all the special spaces in a string (non-breaking, tabs, ...) by normal spaces, and all the
716
714
  * different newlines types by \n.
@@ -718,9 +716,9 @@ const newLineRegex = /\r\n|\r|\n/g;
718
716
  function replaceSpecialSpaces(text) {
719
717
  if (!text)
720
718
  return "";
721
- text = text.replace(whiteSpaceRegexp, " ");
722
- text = text.replace(newLineRegex, NEWLINE);
723
- return text;
719
+ if (!whiteSpaceRegexp.test(text))
720
+ return text;
721
+ return text.replace(whiteSpaceRegexp, (match, newLine) => (newLine ? NEWLINE : " "));
724
722
  }
725
723
  /**
726
724
  * Determine if the numbers are consecutive.
@@ -2062,6 +2060,7 @@ exports.CommandResult = void 0;
2062
2060
  CommandResult["InvalidNumberOfCriterionValues"] = "InvalidNumberOfCriterionValues";
2063
2061
  CommandResult["BlockingValidationRule"] = "BlockingValidationRule";
2064
2062
  CommandResult["InvalidCopyPasteSelection"] = "InvalidCopyPasteSelection";
2063
+ CommandResult["NoChanges"] = "NoChanges";
2065
2064
  })(exports.CommandResult || (exports.CommandResult = {}));
2066
2065
 
2067
2066
  const DEFAULT_LOCALES = [
@@ -2191,12 +2190,12 @@ function toString(value) {
2191
2190
  }
2192
2191
  }
2193
2192
  /** Normalize string by setting it to lowercase and replacing accent letters with plain letters */
2194
- function normalizeString(str) {
2193
+ const normalizeString = memoize(function normalizeString(str) {
2195
2194
  return str
2196
2195
  .toLowerCase()
2197
2196
  .normalize("NFD")
2198
2197
  .replace(/[\u0300-\u036f]/g, "");
2199
- }
2198
+ });
2200
2199
  const expectBooleanValueError = (value) => _t("The function [[FUNCTION_NAME]] expects a boolean value, but '%s' is a text, and cannot be coerced to a number.", value);
2201
2200
  function toBoolean(value) {
2202
2201
  switch (typeof value) {
@@ -7331,7 +7330,7 @@ function tokenize(str, locale = DEFAULT_LOCALE) {
7331
7330
  return result;
7332
7331
  }
7333
7332
  function tokenizeDebugger(chars) {
7334
- if (chars.current() === "?") {
7333
+ if (chars.current === "?") {
7335
7334
  chars.shift();
7336
7335
  return { type: "DEBUGGER", value: "?" };
7337
7336
  }
@@ -7342,7 +7341,7 @@ const misc$1 = {
7342
7341
  ")": "RIGHT_PAREN",
7343
7342
  };
7344
7343
  function tokenizeMisc(chars) {
7345
- if (chars.current() in misc$1) {
7344
+ if (chars.current in misc$1) {
7346
7345
  const value = chars.shift();
7347
7346
  const type = misc$1[value];
7348
7347
  return { type, value };
@@ -7350,7 +7349,7 @@ function tokenizeMisc(chars) {
7350
7349
  return null;
7351
7350
  }
7352
7351
  function tokenizeArgsSeparator(chars, locale) {
7353
- if (chars.current() === locale.formulaArgSeparator) {
7352
+ if (chars.current === locale.formulaArgSeparator) {
7354
7353
  const value = chars.shift();
7355
7354
  const type = "ARG_SEPARATOR";
7356
7355
  return { type, value };
@@ -7375,14 +7374,13 @@ function tokenizeNumber(chars, locale) {
7375
7374
  return null;
7376
7375
  }
7377
7376
  function tokenizeString(chars) {
7378
- if (chars.current() === '"') {
7377
+ if (chars.current === '"') {
7379
7378
  const startChar = chars.shift();
7380
7379
  let letters = startChar;
7381
- while (chars.current() &&
7382
- (chars.current() !== startChar || letters[letters.length - 1] === "\\")) {
7380
+ while (chars.current && (chars.current !== startChar || letters[letters.length - 1] === "\\")) {
7383
7381
  letters += chars.shift();
7384
7382
  }
7385
- if (chars.current() === '"') {
7383
+ if (chars.current === '"') {
7386
7384
  letters += chars.shift();
7387
7385
  }
7388
7386
  return {
@@ -7409,14 +7407,14 @@ function tokenizeSymbol(chars) {
7409
7407
  let result = "";
7410
7408
  // there are two main cases to manage: either something which starts with
7411
7409
  // a ', like 'Sheet 2'A2, or a word-like element.
7412
- if (chars.current() === "'") {
7410
+ if (chars.current === "'") {
7413
7411
  let lastChar = chars.shift();
7414
7412
  result += lastChar;
7415
- while (chars.current()) {
7413
+ while (chars.current) {
7416
7414
  lastChar = chars.shift();
7417
7415
  result += lastChar;
7418
7416
  if (lastChar === "'") {
7419
- if (chars.current() && chars.current() === "'") {
7417
+ if (chars.current && chars.current === "'") {
7420
7418
  lastChar = chars.shift();
7421
7419
  result += lastChar;
7422
7420
  }
@@ -7432,7 +7430,7 @@ function tokenizeSymbol(chars) {
7432
7430
  };
7433
7431
  }
7434
7432
  }
7435
- while (chars.current() && separatorRegexp.test(chars.current())) {
7433
+ while (chars.current && separatorRegexp.test(chars.current)) {
7436
7434
  result += chars.shift();
7437
7435
  }
7438
7436
  if (result.length) {
@@ -7447,14 +7445,14 @@ function tokenizeSymbol(chars) {
7447
7445
  }
7448
7446
  function tokenizeSpace(chars) {
7449
7447
  let length = 0;
7450
- while (chars.current() === NEWLINE) {
7448
+ while (chars.current === NEWLINE) {
7451
7449
  length++;
7452
7450
  chars.shift();
7453
7451
  }
7454
7452
  if (length) {
7455
7453
  return { type: "SPACE", value: NEWLINE.repeat(length) };
7456
7454
  }
7457
- while (chars.current() === " ") {
7455
+ while (chars.current === " ") {
7458
7456
  length++;
7459
7457
  chars.shift();
7460
7458
  }
@@ -7473,17 +7471,20 @@ function tokenizeInvalidRange(chars) {
7473
7471
  class TokenizingChars {
7474
7472
  text;
7475
7473
  currentIndex = 0;
7474
+ current;
7476
7475
  constructor(text) {
7477
7476
  this.text = text;
7478
- }
7479
- current() {
7480
- return this.text[this.currentIndex];
7477
+ this.current = text[0];
7481
7478
  }
7482
7479
  shift() {
7483
- return this.text[this.currentIndex++];
7480
+ const current = this.current;
7481
+ const next = this.text[++this.currentIndex];
7482
+ this.current = next;
7483
+ return current;
7484
7484
  }
7485
7485
  advanceBy(length) {
7486
7486
  this.currentIndex += length;
7487
+ this.current = this.text[this.currentIndex];
7487
7488
  }
7488
7489
  isOver() {
7489
7490
  return this.currentIndex >= this.text.length;
@@ -29759,6 +29760,7 @@ class Grid extends owl.Component {
29759
29760
  const content = clipboardData.getData(ClipboardMIMEType.PlainText);
29760
29761
  const target = this.env.model.getters.getSelectedZones();
29761
29762
  const clipboardString = this.env.model.getters.getClipboardTextContent();
29763
+ const isCutOperation = this.env.model.getters.isCutOperation();
29762
29764
  if (clipboardString === content) {
29763
29765
  // the paste actually comes from o-spreadsheet itself
29764
29766
  interactivePaste(this.env, target);
@@ -29766,7 +29768,7 @@ class Grid extends owl.Component {
29766
29768
  else {
29767
29769
  interactivePasteFromOS(this.env, target, content);
29768
29770
  }
29769
- if (this.env.model.getters.isCutOperation()) {
29771
+ if (isCutOperation) {
29770
29772
  await this.env.clipboard.write({ [ClipboardMIMEType.PlainText]: "" });
29771
29773
  }
29772
29774
  }
@@ -34060,6 +34062,14 @@ class BordersPlugin extends CorePlugin {
34060
34062
  // ---------------------------------------------------------------------------
34061
34063
  // Command Handling
34062
34064
  // ---------------------------------------------------------------------------
34065
+ allowDispatch(cmd) {
34066
+ switch (cmd.type) {
34067
+ case "SET_BORDER":
34068
+ return this.checkBordersUnchanged(cmd);
34069
+ default:
34070
+ return "Success" /* CommandResult.Success */;
34071
+ }
34072
+ }
34063
34073
  handle(cmd) {
34064
34074
  switch (cmd.type) {
34065
34075
  case "ADD_MERGE":
@@ -34479,6 +34489,14 @@ class BordersPlugin extends CorePlugin {
34479
34489
  this.setBorders(sheetId, [{ ...zone, left: right }], "right", bordersTopLeft.right);
34480
34490
  }
34481
34491
  }
34492
+ checkBordersUnchanged(cmd) {
34493
+ const currentBorder = this.getCellBorder(cmd);
34494
+ const areAllNewBordersUndefined = !cmd.border?.bottom && !cmd.border?.left && !cmd.border?.right && !cmd.border?.top;
34495
+ if ((!currentBorder && areAllNewBordersUndefined) || deepEquals(currentBorder, cmd.border)) {
34496
+ return "NoChanges" /* CommandResult.NoChanges */;
34497
+ }
34498
+ return "Success" /* CommandResult.Success */;
34499
+ }
34482
34500
  // ---------------------------------------------------------------------------
34483
34501
  // Import/Export
34484
34502
  // ---------------------------------------------------------------------------
@@ -35334,8 +35352,9 @@ class CellPlugin extends CorePlugin {
35334
35352
  allowDispatch(cmd) {
35335
35353
  switch (cmd.type) {
35336
35354
  case "UPDATE_CELL":
35355
+ return this.checkCellOutOfSheet(cmd);
35337
35356
  case "CLEAR_CELL":
35338
- return this.checkCellOutOfSheet(cmd.sheetId, cmd.col, cmd.row);
35357
+ return this.checkValidations(cmd, this.chainValidations(this.checkCellOutOfSheet, this.checkUselessClearCell));
35339
35358
  default:
35340
35359
  return "Success" /* CommandResult.Success */;
35341
35360
  }
@@ -35504,14 +35523,9 @@ class CellPlugin extends CorePlugin {
35504
35523
  */
35505
35524
  getCellById(cellId) {
35506
35525
  // this must be as fast as possible
35507
- for (const sheetId in this.cells) {
35508
- const sheet = this.cells[sheetId];
35509
- const cell = sheet[cellId];
35510
- if (cell) {
35511
- return cell;
35512
- }
35513
- }
35514
- return undefined;
35526
+ const position = this.getters.getCellPosition(cellId);
35527
+ const sheet = this.cells[position.sheetId];
35528
+ return sheet[cellId];
35515
35529
  }
35516
35530
  /*
35517
35531
  * Reconstructs the original formula string based on a normalized form and its dependencies
@@ -35742,13 +35756,23 @@ class CellPlugin extends CorePlugin {
35742
35756
  },
35743
35757
  };
35744
35758
  }
35745
- checkCellOutOfSheet(sheetId, col, row) {
35759
+ checkCellOutOfSheet(cmd) {
35760
+ const { sheetId, col, row } = cmd;
35746
35761
  const sheet = this.getters.tryGetSheet(sheetId);
35747
35762
  if (!sheet)
35748
35763
  return "InvalidSheetId" /* CommandResult.InvalidSheetId */;
35749
35764
  const sheetZone = this.getters.getSheetZone(sheetId);
35750
35765
  return isInside(col, row, sheetZone) ? "Success" /* CommandResult.Success */ : "TargetOutOfSheet" /* CommandResult.TargetOutOfSheet */;
35751
35766
  }
35767
+ checkUselessClearCell(cmd) {
35768
+ const cell = this.getters.getCell(cmd);
35769
+ if (!cell)
35770
+ return "NoChanges" /* CommandResult.NoChanges */;
35771
+ if (!cell.content && !cell.style && !cell.format) {
35772
+ return "NoChanges" /* CommandResult.NoChanges */;
35773
+ }
35774
+ return "Success" /* CommandResult.Success */;
35775
+ }
35752
35776
  }
35753
35777
  class FormulaCellWithDependencies {
35754
35778
  id;
@@ -36438,6 +36462,9 @@ class DataValidationPlugin extends CorePlugin {
36438
36462
  return this.rules[sheetId].find((rule) => rule.id === id);
36439
36463
  }
36440
36464
  getValidationRuleForCell({ sheetId, col, row }) {
36465
+ if (!this.rules[sheetId]) {
36466
+ return undefined;
36467
+ }
36441
36468
  for (const rule of this.rules[sheetId]) {
36442
36469
  for (const range of rule.ranges) {
36443
36470
  if (isInside(col, row, range.zone)) {
@@ -39035,10 +39062,13 @@ class SheetPlugin extends CorePlugin {
39035
39062
  // begin with the end.
39036
39063
  rows.sort((a, b) => b - a);
39037
39064
  for (let group of groupConsecutive(rows)) {
39065
+ // indexes are sorted in the descending order
39066
+ const from = group[group.length - 1];
39067
+ const to = group[0];
39038
39068
  // Move the cells.
39039
- this.moveCellOnRowsDeletion(sheet, group[group.length - 1], group[0]);
39040
- // Effectively delete the element and recompute the left-right/top-bottom.
39041
- group.map((row) => this.updateRowsStructureOnDeletion(row, sheet));
39069
+ this.moveCellOnRowsDeletion(sheet, from, to);
39070
+ // Effectively delete the rows
39071
+ this.updateRowsStructureOnDeletion(sheet, from, to);
39042
39072
  }
39043
39073
  const count = rows.filter((row) => row < sheet.panes.ySplit).length;
39044
39074
  if (count) {
@@ -39060,8 +39090,6 @@ class SheetPlugin extends CorePlugin {
39060
39090
  this.addEmptyRows(sheet, quantity);
39061
39091
  // Move the cells.
39062
39092
  this.moveCellsOnAddition(sheet, index, quantity, "rows");
39063
- // Recompute the left-right/top-bottom.
39064
- this.updateRowsStructureOnAddition(sheet, row, quantity);
39065
39093
  if (index < sheet.panes.ySplit) {
39066
39094
  this.setPaneDivisions(sheet.id, sheet.panes.ySplit + quantity, "ROW");
39067
39095
  }
@@ -39162,35 +39190,20 @@ class SheetPlugin extends CorePlugin {
39162
39190
  }
39163
39191
  }
39164
39192
  }
39165
- updateRowsStructureOnDeletion(index, sheet) {
39193
+ updateRowsStructureOnDeletion(sheet, deleteFromRow, deleteToRow) {
39166
39194
  const rows = [];
39167
- const cellsQueue = sheet.rows.map((row) => row.cells);
39195
+ const cellsQueue = sheet.rows.map((row) => row.cells).reverse();
39168
39196
  for (let i in sheet.rows) {
39169
- if (Number(i) === index) {
39197
+ const row = Number(i);
39198
+ if (row >= deleteFromRow && row <= deleteToRow) {
39170
39199
  continue;
39171
39200
  }
39172
39201
  rows.push({
39173
- cells: cellsQueue.shift(),
39202
+ cells: cellsQueue.pop(),
39174
39203
  });
39175
39204
  }
39176
39205
  this.history.update("sheets", sheet.id, "rows", rows);
39177
39206
  }
39178
- /**
39179
- * Update the rows of the sheet after an addition:
39180
- * - Rename the rows
39181
- *
39182
- * @param sheet Sheet on which the deletion occurs
39183
- * @param addedRow Index of the added row
39184
- * @param rowsToAdd Number of the rows to add
39185
- */
39186
- updateRowsStructureOnAddition(sheet, addedRow, rowsToAdd) {
39187
- const rows = [];
39188
- const cellsQueue = sheet.rows.map((row) => row.cells);
39189
- sheet.rows.forEach(() => rows.push({
39190
- cells: cellsQueue.shift(),
39191
- }));
39192
- this.history.update("sheets", sheet.id, "rows", rows);
39193
- }
39194
39207
  /**
39195
39208
  * Add empty rows at the end of the rows
39196
39209
  *
@@ -39717,6 +39730,7 @@ class CompilationParametersBuilder {
39717
39730
  getters;
39718
39731
  computeCell;
39719
39732
  evalContext;
39733
+ rangeCache = {};
39720
39734
  constructor(context, getters, computeCell) {
39721
39735
  this.getters = getters;
39722
39736
  this.computeCell = computeCell;
@@ -39802,17 +39816,24 @@ class CompilationParametersBuilder {
39802
39816
  if (!_zone) {
39803
39817
  return [[]];
39804
39818
  }
39819
+ const { top, left, bottom, right } = zone;
39820
+ const cacheKey = `${sheetId}-${top}-${left}-${bottom}-${right}`;
39821
+ if (cacheKey in this.rangeCache) {
39822
+ return this.rangeCache[cacheKey];
39823
+ }
39805
39824
  const height = _zone.bottom - _zone.top + 1;
39806
39825
  const width = _zone.right - _zone.left + 1;
39807
- const matrix = Array.from({ length: width }, () => Array.from({ length: height }));
39826
+ const matrix = new Array(width);
39808
39827
  // Performance issue: nested loop is faster than a map here
39809
39828
  for (let col = _zone.left; col <= _zone.right; col++) {
39829
+ const colIndex = col - _zone.left;
39830
+ matrix[colIndex] = new Array(height);
39810
39831
  for (let row = _zone.top; row <= _zone.bottom; row++) {
39811
- const colIndex = col - _zone.left;
39812
39832
  const rowIndex = row - _zone.top;
39813
39833
  matrix[colIndex][rowIndex] = this.readCell({ sheetId, col, row });
39814
39834
  }
39815
39835
  }
39836
+ this.rangeCache[cacheKey] = matrix;
39816
39837
  return matrix;
39817
39838
  }
39818
39839
  }
@@ -40018,6 +40039,7 @@ class Evaluator {
40018
40039
  this.formulaDependencies().addDependencies(positionId, dependencies);
40019
40040
  }
40020
40041
  updateCompilationParameters() {
40042
+ // rebuild the compilation parameters (with a clean cache)
40021
40043
  this.compilationParams = buildCompilationParameters(this.context, this.getters, this.computeAndSave.bind(this));
40022
40044
  }
40023
40045
  evaluateCells(positions) {
@@ -40061,6 +40083,16 @@ class Evaluator {
40061
40083
  this.evaluatedCells = new Map();
40062
40084
  this.evaluate(this.getAllCells());
40063
40085
  }
40086
+ evaluateFormula(sheetId, formulaString) {
40087
+ const compiledFormula = compile(formulaString);
40088
+ const ranges = compiledFormula.dependencies.map((xc) => this.getters.getRangeFromSheetXC(sheetId, xc));
40089
+ this.updateCompilationParameters();
40090
+ const array = compiledFormula.execute(ranges, ...this.compilationParams);
40091
+ if (isMatrix(array)) {
40092
+ return matrixMap(array, (cell) => cell.value);
40093
+ }
40094
+ return array.value;
40095
+ }
40064
40096
  getAllCells() {
40065
40097
  const positionIds = new JetSet();
40066
40098
  for (const sheetId of this.getters.getSheetIds()) {
@@ -40087,6 +40119,7 @@ class Evaluator {
40087
40119
  this.nextPositionsToUpdate = cells;
40088
40120
  let currentIteration = 0;
40089
40121
  while (this.nextPositionsToUpdate.size && currentIteration++ < MAX_ITERATION) {
40122
+ this.updateCompilationParameters();
40090
40123
  const positionIds = Array.from(this.nextPositionsToUpdate);
40091
40124
  this.nextPositionsToUpdate.clear();
40092
40125
  for (let i = 0; i < positionIds.length; ++i) {
@@ -40450,7 +40483,6 @@ class PositionBitsEncoder {
40450
40483
  // of other cells depending on it, at the next iteration.
40451
40484
  //#endregion
40452
40485
  class EvaluationPlugin extends UIPlugin {
40453
- config;
40454
40486
  static getters = [
40455
40487
  "evaluateFormula",
40456
40488
  "getCorrespondingFormulaCell",
@@ -40461,15 +40493,13 @@ class EvaluationPlugin extends UIPlugin {
40461
40493
  "getEvaluatedCells",
40462
40494
  "getEvaluatedCellsInZone",
40463
40495
  "getSpreadPositionsOf",
40496
+ "getArrayFormulaSpreadingOn",
40464
40497
  ];
40465
40498
  shouldRebuildDependenciesGraph = true;
40466
40499
  evaluator;
40467
- compilationParams;
40468
40500
  positionsToUpdate = [];
40469
40501
  constructor(config) {
40470
40502
  super(config);
40471
- this.config = config;
40472
- this.compilationParams = this.getCompilationParameters();
40473
40503
  this.evaluator = new Evaluator(config.custom, this.getters);
40474
40504
  }
40475
40505
  // ---------------------------------------------------------------------------
@@ -40494,10 +40524,6 @@ class EvaluationPlugin extends UIPlugin {
40494
40524
  case "EVALUATE_CELLS":
40495
40525
  this.evaluator.evaluateAllCells();
40496
40526
  break;
40497
- case "UPDATE_LOCALE":
40498
- this.compilationParams = this.getCompilationParameters();
40499
- this.evaluator.updateCompilationParameters();
40500
- break;
40501
40527
  }
40502
40528
  }
40503
40529
  finalize() {
@@ -40515,16 +40541,7 @@ class EvaluationPlugin extends UIPlugin {
40515
40541
  // Getters
40516
40542
  // ---------------------------------------------------------------------------
40517
40543
  evaluateFormula(sheetId, formulaString) {
40518
- const compiledFormula = compile(formulaString);
40519
- const ranges = [];
40520
- for (let xc of compiledFormula.dependencies) {
40521
- ranges.push(this.getters.getRangeFromSheetXC(sheetId, xc));
40522
- }
40523
- const array = compiledFormula.execute(ranges, ...this.compilationParams);
40524
- if (isMatrix(array)) {
40525
- return array.map((col) => col.map((row) => row.value));
40526
- }
40527
- return array.value;
40544
+ return this.evaluator.evaluateFormula(sheetId, formulaString);
40528
40545
  }
40529
40546
  /**
40530
40547
  * Return the value of each cell in the range as they are displayed in the grid.
@@ -40573,6 +40590,9 @@ class EvaluationPlugin extends UIPlugin {
40573
40590
  getSpreadPositionsOf(position) {
40574
40591
  return this.evaluator.getSpreadPositionsOf(position);
40575
40592
  }
40593
+ getArrayFormulaSpreadingOn(position) {
40594
+ return this.evaluator.getArrayFormulaSpreadingOn(position);
40595
+ }
40576
40596
  // ---------------------------------------------------------------------------
40577
40597
  // Export
40578
40598
  // ---------------------------------------------------------------------------
@@ -40616,7 +40636,7 @@ class EvaluationPlugin extends UIPlugin {
40616
40636
  else if (cell && cell.content) {
40617
40637
  return undefined;
40618
40638
  }
40619
- const spreadingFormulaPosition = this.evaluator.getArrayFormulaSpreadingOn(position);
40639
+ const spreadingFormulaPosition = this.getArrayFormulaSpreadingOn(position);
40620
40640
  if (spreadingFormulaPosition === undefined) {
40621
40641
  return undefined;
40622
40642
  }
@@ -40626,9 +40646,6 @@ class EvaluationPlugin extends UIPlugin {
40626
40646
  }
40627
40647
  return undefined;
40628
40648
  }
40629
- getCompilationParameters() {
40630
- return buildCompilationParameters(this.config.custom, this.getters, (position) => this.evaluator.getEvaluatedCell(position));
40631
- }
40632
40649
  }
40633
40650
  function isBadExpression(tokens) {
40634
40651
  try {
@@ -43327,11 +43344,26 @@ class ClipboardCellsState extends ClipboardCellsAbstractState {
43327
43344
  let cellsInRow = [];
43328
43345
  for (let col of columnsIndex) {
43329
43346
  const position = { col, row, sheetId };
43347
+ const spreader = getters.getArrayFormulaSpreadingOn(position);
43348
+ let cell = getters.getCell(position);
43349
+ const evaluatedCell = getters.getEvaluatedCell(position);
43350
+ if (spreader) {
43351
+ const isSpreaderCopied = rowsIndex.includes(spreader.row) && columnsIndex.includes(spreader.col);
43352
+ const content = isSpreaderCopied
43353
+ ? ""
43354
+ : formatValue(evaluatedCell.value, { locale: getters.getLocale() });
43355
+ cell = {
43356
+ id: cell?.id || "",
43357
+ style: cell?.style,
43358
+ format: evaluatedCell.format,
43359
+ content,
43360
+ isFormula: false,
43361
+ };
43362
+ }
43330
43363
  cellsInRow.push({
43331
- cell: getters.getCell(position),
43332
- style: getters.getCellComputedStyle(position),
43333
- evaluatedCell: getters.getEvaluatedCell(position),
43364
+ cell,
43334
43365
  border: getters.getCellBorder(position) || undefined,
43366
+ evaluatedCell,
43335
43367
  position,
43336
43368
  });
43337
43369
  }
@@ -43453,13 +43485,6 @@ class ClipboardCellsState extends ClipboardCellsAbstractState {
43453
43485
  });
43454
43486
  }
43455
43487
  this.pasteCopiedTables(target);
43456
- this.cells.forEach((row) => {
43457
- row.forEach((c) => {
43458
- if (c.cell) {
43459
- c.cell = undefined;
43460
- }
43461
- });
43462
- });
43463
43488
  }
43464
43489
  /**
43465
43490
  * The clipped zone is copied as many times as it fits in the target.
@@ -43531,7 +43556,7 @@ class ClipboardCellsState extends ClipboardCellsAbstractState {
43531
43556
  clearClippedZones() {
43532
43557
  for (const row of this.cells) {
43533
43558
  for (const cell of row) {
43534
- if (cell.cell) {
43559
+ if (cell?.cell) {
43535
43560
  this.dispatch("CLEAR_CELL", cell.position);
43536
43561
  }
43537
43562
  }
@@ -43557,6 +43582,9 @@ class ClipboardCellsState extends ClipboardCellsAbstractState {
43557
43582
  const rowCells = this.cells[r];
43558
43583
  for (let c = 0; c < width; c++) {
43559
43584
  const origin = rowCells[c];
43585
+ if (!origin) {
43586
+ continue;
43587
+ }
43560
43588
  const position = { col: col + c, row: row + r, sheetId: sheetId };
43561
43589
  // TODO: refactor this part. the "Paste merge" action is also executed with
43562
43590
  // MOVE_RANGES in pasteFromCut. Adding a condition on the operation type here
@@ -43580,53 +43608,42 @@ class ClipboardCellsState extends ClipboardCellsAbstractState {
43580
43608
  pasteCell(origin, target, operation, clipboardOption) {
43581
43609
  const { sheetId, col, row } = target;
43582
43610
  const targetCell = this.getters.getEvaluatedCell(target);
43583
- if (clipboardOption?.pasteOption !== "onlyValue") {
43584
- const targetBorders = this.getters.getCellBorder(target);
43585
- const originBorders = origin.border;
43586
- const border = {
43587
- top: targetBorders?.top || originBorders?.top,
43588
- bottom: targetBorders?.bottom || originBorders?.bottom,
43589
- left: targetBorders?.left || originBorders?.left,
43590
- right: targetBorders?.right || originBorders?.right,
43591
- };
43592
- this.dispatch("SET_BORDER", { sheetId, col, row, border });
43611
+ if (clipboardOption?.pasteOption === "onlyValue") {
43612
+ const locale = this.getters.getLocale();
43613
+ const content = formatValue(origin.evaluatedCell.value, { locale });
43614
+ this.dispatch("UPDATE_CELL", { ...target, content });
43615
+ return;
43593
43616
  }
43594
- if (origin.cell) {
43595
- if (clipboardOption?.pasteOption === "onlyFormat") {
43596
- this.dispatch("UPDATE_CELL", {
43597
- ...target,
43598
- style: origin.cell.style,
43599
- format: origin.evaluatedCell.format,
43600
- });
43601
- return;
43602
- }
43603
- if (clipboardOption?.pasteOption === "onlyValue") {
43604
- const locale = this.getters.getLocale();
43605
- const content = formatValue(origin.evaluatedCell.value, { locale });
43606
- this.dispatch("UPDATE_CELL", { ...target, content });
43607
- return;
43608
- }
43609
- let content = origin.cell.content;
43610
- if (origin.cell.isFormula && operation === "COPY") {
43611
- content = this.getters.getTranslatedCellFormula(sheetId, col - origin.position.col, row - origin.position.row, origin.cell.compiledFormula);
43612
- }
43617
+ const targetBorders = this.getters.getCellBorder(target);
43618
+ const originBorders = origin.border;
43619
+ const border = {
43620
+ top: targetBorders?.top || originBorders?.top,
43621
+ bottom: targetBorders?.bottom || originBorders?.bottom,
43622
+ left: targetBorders?.left || originBorders?.left,
43623
+ right: targetBorders?.right || originBorders?.right,
43624
+ };
43625
+ this.dispatch("SET_BORDER", { sheetId, col, row, border });
43626
+ if (clipboardOption?.pasteOption === "onlyFormat") {
43627
+ this.dispatch("UPDATE_CELL", {
43628
+ ...target,
43629
+ style: origin.cell?.style ?? null,
43630
+ format: origin.cell?.format ?? origin.evaluatedCell.format ?? targetCell.format,
43631
+ });
43632
+ return;
43633
+ }
43634
+ const content = origin.cell && origin.cell.isFormula && operation === "COPY"
43635
+ ? this.getters.getTranslatedCellFormula(sheetId, col - origin.position.col, row - origin.position.row, origin.cell.compiledFormula)
43636
+ : origin.cell?.content;
43637
+ if (content !== "" || origin.cell?.format || origin.cell?.style) {
43613
43638
  this.dispatch("UPDATE_CELL", {
43614
43639
  ...target,
43615
43640
  content,
43616
- style: origin.cell.style || null,
43617
- format: origin.cell.format,
43641
+ style: origin.cell?.style || null,
43642
+ format: origin.cell?.format,
43618
43643
  });
43619
43644
  }
43620
43645
  else if (targetCell) {
43621
- if (clipboardOption?.pasteOption === "onlyValue") {
43622
- this.dispatch("UPDATE_CELL", { ...target, content: "" });
43623
- }
43624
- else if (clipboardOption?.pasteOption === "onlyFormat") {
43625
- this.dispatch("UPDATE_CELL", { ...target, style: null, format: "" });
43626
- }
43627
- else {
43628
- this.dispatch("CLEAR_CELL", target);
43629
- }
43646
+ this.dispatch("CLEAR_CELL", target);
43630
43647
  }
43631
43648
  }
43632
43649
  /**
@@ -43688,22 +43705,28 @@ class ClipboardCellsState extends ClipboardCellsAbstractState {
43688
43705
  return (this.cells
43689
43706
  .map((cells) => {
43690
43707
  return cells
43691
- .map((c) => this.getters.shouldShowFormulas() && c.cell?.isFormula
43708
+ .map((c) => this.getters.shouldShowFormulas() && c?.cell?.isFormula
43692
43709
  ? c.cell?.content || ""
43693
- : c.evaluatedCell?.formattedValue || "")
43710
+ : c?.evaluatedCell?.formattedValue || "")
43694
43711
  .join("\t");
43695
43712
  })
43696
43713
  .join("\n") || "\t");
43697
43714
  }
43698
43715
  getHTMLContent() {
43699
43716
  if (this.cells.length === 1 && this.cells[0].length === 1) {
43717
+ if (!this.cells[0][0]) {
43718
+ return "";
43719
+ }
43700
43720
  return this.getters.getCellText(this.cells[0][0].position);
43701
43721
  }
43702
43722
  let htmlTable = '<table border="1" style="border-collapse:collapse">';
43703
43723
  for (const row of this.cells) {
43704
43724
  htmlTable += "<tr>";
43705
43725
  for (const cell of row) {
43706
- const cssStyle = cssPropertiesToCss(cellStyleToCss(cell.style));
43726
+ if (!cell) {
43727
+ continue;
43728
+ }
43729
+ const cssStyle = cssPropertiesToCss(cellStyleToCss(this.getters.getCellComputedStyle(cell.position)));
43707
43730
  const cellText = this.getters.getCellText(cell.position);
43708
43731
  htmlTable += `<td style="${cssStyle}">` + xmlEscape(cellText) + "</td>";
43709
43732
  }
@@ -46833,11 +46856,14 @@ class ClipboardPlugin extends UIPlugin {
46833
46856
  }
46834
46857
  const pasteOption = cmd.pasteOption || (this.paintFormatStatus !== "inactive" ? "onlyFormat" : undefined);
46835
46858
  this.state.paste(cmd.target, { pasteOption, shouldPasteCF: true, selectTarget: true });
46859
+ if (this.state.operation === "CUT") {
46860
+ this.state = undefined;
46861
+ }
46836
46862
  this.lastPasteState = this.state;
46837
46863
  if (this.paintFormatStatus === "oneOff") {
46838
46864
  this.paintFormatStatus = "inactive";
46839
- this.status = "invisible";
46840
46865
  }
46866
+ this.status = "invisible";
46841
46867
  break;
46842
46868
  case "COPY_PASTE_CELLS_ABOVE":
46843
46869
  {
@@ -55561,19 +55587,22 @@ class Model extends EventBus {
55561
55587
  * Check if the given command is allowed by all the plugins and the history.
55562
55588
  */
55563
55589
  checkDispatchAllowed(command) {
55564
- if (isCoreCommand(command)) {
55565
- return this.checkDispatchAllowedCoreCommand(command);
55590
+ const results = isCoreCommand(command)
55591
+ ? this.checkDispatchAllowedCoreCommand(command)
55592
+ : this.checkDispatchAllowedLocalCommand(command);
55593
+ if (results.some((r) => r !== "Success" /* CommandResult.Success */)) {
55594
+ return new DispatchResult(results.flat());
55566
55595
  }
55567
- return this.checkDispatchAllowedLocalCommand(command);
55596
+ return DispatchResult.Success;
55568
55597
  }
55569
55598
  checkDispatchAllowedCoreCommand(command) {
55570
55599
  const results = this.corePlugins.map((handler) => handler.allowDispatch(command));
55571
55600
  results.push(this.range.allowDispatch(command));
55572
- return new DispatchResult(results.flat());
55601
+ return results;
55573
55602
  }
55574
55603
  checkDispatchAllowedLocalCommand(command) {
55575
55604
  const results = this.uiHandlers.map((handler) => handler.allowDispatch(command));
55576
- return new DispatchResult(results.flat());
55605
+ return results;
55577
55606
  }
55578
55607
  finalize() {
55579
55608
  this.status = 3 /* Status.Finalizing */;
@@ -55920,6 +55949,6 @@ exports.setTranslationMethod = setTranslationMethod;
55920
55949
  exports.tokenize = tokenize;
55921
55950
 
55922
55951
 
55923
- __info__.version = "17.0.3";
55924
- __info__.date = "2023-11-16T11:59:49.524Z";
55925
- __info__.hash = "8bd0fb2";
55952
+ __info__.version = "17.0.4";
55953
+ __info__.date = "2023-11-24T13:13:42.575Z";
55954
+ __info__.hash = "7346d0a";