@odoo/o-spreadsheet 16.4.14 → 16.4.16

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