@odoo/o-spreadsheet 16.1.27 → 16.1.31

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.
@@ -305,15 +305,76 @@ for (let font of fontSizes) {
305
305
  // -----------------------------------------------------------------------------
306
306
  // Date Type
307
307
  // -----------------------------------------------------------------------------
308
+ /**
309
+ * A DateTime object that can be used to manipulate spreadsheet dates.
310
+ * Conceptually, a spreadsheet date is simply a number with a date format,
311
+ * and it is timezone-agnostic.
312
+ * This DateTime object consistently uses UTC time to represent a naive date and time.
313
+ */
314
+ class DateTime {
315
+ constructor(year, month, day, hours = 0, minutes = 0, seconds = 0) {
316
+ this.jsDate = new Date(Date.UTC(year, month, day, hours, minutes, seconds, 0));
317
+ }
318
+ static fromTimestamp(timestamp) {
319
+ const date = new Date(timestamp);
320
+ return new DateTime(date.getUTCFullYear(), date.getUTCMonth(), date.getUTCDate(), date.getUTCHours(), date.getUTCMinutes(), date.getUTCSeconds());
321
+ }
322
+ static now() {
323
+ const now = new Date();
324
+ return new DateTime(now.getFullYear(), now.getMonth(), now.getDate(), now.getHours(), now.getMinutes(), now.getSeconds());
325
+ }
326
+ toString() {
327
+ return this.jsDate.toString();
328
+ }
329
+ getTime() {
330
+ return this.jsDate.getTime();
331
+ }
332
+ getFullYear() {
333
+ return this.jsDate.getUTCFullYear();
334
+ }
335
+ getMonth() {
336
+ return this.jsDate.getUTCMonth();
337
+ }
338
+ getDate() {
339
+ return this.jsDate.getUTCDate();
340
+ }
341
+ getDay() {
342
+ return this.jsDate.getUTCDay();
343
+ }
344
+ getHours() {
345
+ return this.jsDate.getUTCHours();
346
+ }
347
+ getMinutes() {
348
+ return this.jsDate.getUTCMinutes();
349
+ }
350
+ getSeconds() {
351
+ return this.jsDate.getUTCSeconds();
352
+ }
353
+ setFullYear(year) {
354
+ this.jsDate.setFullYear(year);
355
+ }
356
+ setDate(date) {
357
+ this.jsDate.setUTCDate(date);
358
+ }
359
+ setHours(hours) {
360
+ this.jsDate.setUTCHours(hours);
361
+ }
362
+ setMinutes(minutes) {
363
+ this.jsDate.setUTCMinutes(minutes);
364
+ }
365
+ setSeconds(seconds) {
366
+ this.jsDate.setUTCSeconds(seconds);
367
+ }
368
+ }
308
369
  // -----------------------------------------------------------------------------
309
370
  // Parsing
310
371
  // -----------------------------------------------------------------------------
311
- const INITIAL_1900_DAY = new Date(1899, 11, 30);
372
+ const INITIAL_1900_DAY = new DateTime(1899, 11, 30);
312
373
  const MS_PER_DAY = 24 * 60 * 60 * 1000;
313
374
  const CURRENT_MILLENIAL = 2000; // note: don't forget to update this in 2999
314
- const CURRENT_YEAR = new Date().getFullYear();
315
- const INITIAL_JS_DAY = new Date(0);
316
- const DATE_JS_1900_OFFSET = INITIAL_JS_DAY - INITIAL_1900_DAY;
375
+ const CURRENT_YEAR = DateTime.now().getFullYear();
376
+ const INITIAL_JS_DAY = DateTime.fromTimestamp(0);
377
+ const DATE_JS_1900_OFFSET = INITIAL_JS_DAY.getTime() - INITIAL_1900_DAY.getTime();
317
378
  const mdyDateRegexp = /^\d{1,2}(\/|-|\s)\d{1,2}((\/|-|\s)\d{1,4})?$/;
318
379
  const ymdDateRegexp = /^\d{3,4}(\/|-|\s)\d{1,2}(\/|-|\s)\d{1,2}$/;
319
380
  const timeRegexp = /((\d+(:\d+)?(:\d+)?\s*(AM|PM))|(\d+:\d+(:\d+)?))$/;
@@ -362,7 +423,7 @@ function _parseDateTime(str) {
362
423
  return {
363
424
  value: date.value + time.value,
364
425
  format: date.format + " " + (time.format === "hhhh:mm:ss" ? "hh:mm:ss" : time.format),
365
- jsDate: new Date(date.jsDate.getFullYear() + time.jsDate.getFullYear() - 1899, date.jsDate.getMonth() + time.jsDate.getMonth() - 11, date.jsDate.getDate() + time.jsDate.getDate() - 30, date.jsDate.getHours() + time.jsDate.getHours(), date.jsDate.getMinutes() + time.jsDate.getMinutes(), date.jsDate.getSeconds() + time.jsDate.getSeconds()),
426
+ jsDate: new DateTime(date.jsDate.getFullYear() + time.jsDate.getFullYear() - 1899, date.jsDate.getMonth() + time.jsDate.getMonth() - 11, date.jsDate.getDate() + time.jsDate.getDate() - 30, date.jsDate.getHours() + time.jsDate.getHours(), date.jsDate.getMinutes() + time.jsDate.getMinutes(), date.jsDate.getSeconds() + time.jsDate.getSeconds()),
366
427
  };
367
428
  }
368
429
  return date || time;
@@ -379,13 +440,13 @@ function parseDate(str, dateFormat) {
379
440
  const day = Number(parts[dayIndex]);
380
441
  const leadingZero = (parts[monthIndex].length === 2 && month < 10) || (parts[dayIndex].length === 2 && day < 10);
381
442
  const year = parts[yearIndex] ? inferYear(parts[yearIndex]) : CURRENT_YEAR;
382
- const jsDate = new Date(year, month - 1, day);
443
+ const jsDate = new DateTime(year, month - 1, day);
383
444
  const sep = str.match(/\/|-|\s/)[0];
384
445
  if (jsDate.getMonth() !== month - 1 || jsDate.getDate() !== day) {
385
446
  // invalid date
386
447
  return null;
387
448
  }
388
- const delta = jsDate - INITIAL_1900_DAY;
449
+ const delta = jsDate.getTime() - INITIAL_1900_DAY.getTime();
389
450
  let format = leadingZero ? `mm${sep}dd` : `m${sep}d`;
390
451
  if (parts[yearIndex]) {
391
452
  format = isMDY ? format + sep + "yyyy" : "yyyy" + sep + format;
@@ -445,7 +506,7 @@ function parseTime(str) {
445
506
  if (hours >= 24) {
446
507
  format = "hhhh:mm:ss";
447
508
  }
448
- const jsDate = new Date(1899, 11, 30, hours, minutes, seconds);
509
+ const jsDate = new DateTime(1899, 11, 30, hours, minutes, seconds);
449
510
  return {
450
511
  value: hours / 24 + minutes / 1440 + seconds / 86400,
451
512
  format: format,
@@ -459,7 +520,7 @@ function parseTime(str) {
459
520
  // -----------------------------------------------------------------------------
460
521
  function numberToJsDate(value) {
461
522
  const truncValue = Math.trunc(value);
462
- let date = new Date(truncValue * MS_PER_DAY - DATE_JS_1900_OFFSET);
523
+ let date = DateTime.fromTimestamp(truncValue * MS_PER_DAY - DATE_JS_1900_OFFSET);
463
524
  let time = value - truncValue;
464
525
  time = time < 0 ? 1 + time : time;
465
526
  const hours = Math.round(time * 24);
@@ -476,7 +537,7 @@ function jsDateToRoundNumber(date) {
476
537
  }
477
538
  /** Return the number of days in the current month of the given date */
478
539
  function getDaysInMonth(date) {
479
- return new Date(date.getFullYear(), date.getMonth() + 1, 0).getDate();
540
+ return new DateTime(date.getFullYear(), date.getMonth() + 1, 0).getDate();
480
541
  }
481
542
  function isLastDayOfMonth(date) {
482
543
  return getDaysInMonth(date) === date.getDate();
@@ -494,7 +555,7 @@ function addMonthsToDate(date, months, keepEndOfMonth) {
494
555
  const yStart = date.getFullYear();
495
556
  const mStart = date.getMonth();
496
557
  const dStart = date.getDate();
497
- const jsDate = new Date(yStart, mStart + months);
558
+ const jsDate = new DateTime(yStart, mStart + months, 1);
498
559
  if (keepEndOfMonth && dStart === getDaysInMonth(date)) {
499
560
  jsDate.setDate(getDaysInMonth(jsDate));
500
561
  }
@@ -622,13 +683,6 @@ function getYearFrac(startDate, endDate, _dayCountConvention) {
622
683
  }
623
684
 
624
685
  //------------------------------------------------------------------------------
625
- /**
626
- * Stringify an object, like JSON.stringify, except that the first level of keys
627
- * is ordered.
628
- */
629
- function stringify(obj) {
630
- return JSON.stringify(obj, Object.keys(obj).sort());
631
- }
632
686
  /**
633
687
  * Remove quotes from a quoted string
634
688
  * ```js
@@ -924,7 +978,7 @@ function isObjectEmptyRecursive(argument) {
924
978
  */
925
979
  function getItemId(item, itemsDic) {
926
980
  for (let [key, value] of Object.entries(itemsDic)) {
927
- if (stringify(value) === stringify(item)) {
981
+ if (deepEquals(value, item)) {
928
982
  return parseInt(key, 10);
929
983
  }
930
984
  }
@@ -1060,6 +1114,22 @@ function transpose2dArray(matrix) {
1060
1114
  if (!matrix.length)
1061
1115
  return matrix;
1062
1116
  return matrix[0].map((_, i) => matrix.map((row) => row[i]));
1117
+ }
1118
+ /**
1119
+ * Creates a version of the function that's memoized on the value of its first
1120
+ * argument, if any.
1121
+ */
1122
+ function memoize(func) {
1123
+ const cache = new Map();
1124
+ const funcName = func.name ? func.name + " (memoized)" : "memoized";
1125
+ return {
1126
+ [funcName](...args) {
1127
+ if (!cache.has(args[0])) {
1128
+ cache.set(args[0], func(...args));
1129
+ }
1130
+ return cache.get(args[0]);
1131
+ },
1132
+ }[funcName];
1063
1133
  }
1064
1134
 
1065
1135
  const RBA_REGEX = /rgba?\(|\s+|\)/gi;
@@ -1334,8 +1404,10 @@ function isSameColor(color1, color2) {
1334
1404
  const cellReference = new RegExp(/\$?([A-Z]{1,3})\$?([0-9]{1,7})/, "i");
1335
1405
  // Same as above, but matches the exact string (nothing before or after)
1336
1406
  const singleCellReference = new RegExp(/^\$?([A-Z]{1,3})\$?([0-9]{1,7})$/, "i");
1337
- /** Reference of a column header (eg. A, AB) */
1338
- const colHeader = new RegExp(/^([A-Z]{1,3})+$/, "i");
1407
+ /** Reference of a column header (eg. A, AB, $A) */
1408
+ const colHeader = new RegExp(/^\$?([A-Z]{1,3})+$/, "i");
1409
+ /** Reference of a row header (eg. 1, $1) */
1410
+ const rowHeader = new RegExp(/^\$?([0-9]{1,7})+$/, "i");
1339
1411
  /** Reference of a column (eg. A, $CA, Sheet1!B) */
1340
1412
  const colReference = new RegExp(/^\s*('.+'!|[^']+!)?\$?([A-Z]{1,3})$/, "i");
1341
1413
  /** Reference of a row (eg. 1, 59, Sheet1!9) */
@@ -1365,6 +1437,9 @@ function isRowReference(xc) {
1365
1437
  function isColHeader(str) {
1366
1438
  return colHeader.test(str);
1367
1439
  }
1440
+ function isRowHeader(str) {
1441
+ return rowHeader.test(str);
1442
+ }
1368
1443
  /**
1369
1444
  * Return true if the given xc is the reference of a single cell,
1370
1445
  * without any specified sheet (e.g. A1)
@@ -1863,7 +1938,7 @@ function formatJSTime(jsDate, format) {
1863
1938
  .map((p) => {
1864
1939
  switch (p) {
1865
1940
  case "hhhh":
1866
- const helapsedHours = Math.floor((jsDate.getTime() - INITIAL_1900_DAY) / (60 * 60 * 1000));
1941
+ const helapsedHours = Math.floor((jsDate.getTime() - INITIAL_1900_DAY.getTime()) / (60 * 60 * 1000));
1867
1942
  return helapsedHours.toString();
1868
1943
  case "hh":
1869
1944
  return hours.toString().padStart(2, "0");
@@ -2154,18 +2229,10 @@ class RangeImpl {
2154
2229
  if (isFullCol) {
2155
2230
  parts[0].rowFixed = parts[0].rowFixed || parts[1].rowFixed;
2156
2231
  parts[1].rowFixed = parts[0].rowFixed || parts[1].rowFixed;
2157
- if (zone.left === zone.right) {
2158
- parts[0].colFixed = parts[0].colFixed || parts[1].colFixed;
2159
- parts[1].colFixed = parts[0].colFixed || parts[1].colFixed;
2160
- }
2161
2232
  }
2162
2233
  if (isFullRow) {
2163
2234
  parts[0].colFixed = parts[0].colFixed || parts[1].colFixed;
2164
2235
  parts[1].colFixed = parts[0].colFixed || parts[1].colFixed;
2165
- if (zone.top === zone.bottom) {
2166
- parts[0].rowFixed = parts[0].rowFixed || parts[1].rowFixed;
2167
- parts[1].rowFixed = parts[0].rowFixed || parts[1].rowFixed;
2168
- }
2169
2236
  }
2170
2237
  return parts;
2171
2238
  }
@@ -3656,6 +3723,7 @@ var CommandResult;
3656
3723
  CommandResult[CommandResult["ChartDoesNotExist"] = 85] = "ChartDoesNotExist";
3657
3724
  CommandResult[CommandResult["InvalidHeaderIndex"] = 86] = "InvalidHeaderIndex";
3658
3725
  CommandResult[CommandResult["InvalidQuantity"] = 87] = "InvalidQuantity";
3726
+ CommandResult[CommandResult["NoChanges"] = 88] = "NoChanges";
3659
3727
  })(CommandResult || (CommandResult = {}));
3660
3728
 
3661
3729
  function isMatrix(x) {
@@ -5691,6 +5759,7 @@ function getMenuDescription(node) {
5691
5759
  return node.description ? node.description : "";
5692
5760
  }
5693
5761
 
5762
+ const macRegex = /Mac/i;
5694
5763
  /**
5695
5764
  * Return true if the event was triggered from
5696
5765
  * a child element.
@@ -5708,6 +5777,17 @@ function gridOverlayPosition() {
5708
5777
  }
5709
5778
  function getOpenedMenus() {
5710
5779
  return Array.from(document.querySelectorAll(".o-spreadsheet .o-menu"));
5780
+ }
5781
+ function isMacOS() {
5782
+ return Boolean(macRegex.test(navigator.userAgent));
5783
+ }
5784
+ /**
5785
+ * @param {KeyboardEvent | MouseEvent} ev
5786
+ * @returns Returns true if the event was triggered with the "ctrl" modifier pressed.
5787
+ * On Mac, this is the "meta" or "command" key.
5788
+ */
5789
+ function isCtrlKey(ev) {
5790
+ return isMacOS() ? ev.metaKey : ev.ctrlKey;
5711
5791
  }
5712
5792
 
5713
5793
  /**
@@ -8019,12 +8099,12 @@ function toString(value) {
8019
8099
  }
8020
8100
  }
8021
8101
  /** Normalize string by setting it to lowercase and replacing accent letters with plain letters */
8022
- function normalizeString(str) {
8102
+ const normalizeString = memoize(function normalizeString(str) {
8023
8103
  return str
8024
8104
  .toLowerCase()
8025
8105
  .normalize("NFD")
8026
8106
  .replace(/[\u0300-\u036f]/g, "");
8027
- }
8107
+ });
8028
8108
  /**
8029
8109
  * Normalize a value.
8030
8110
  * If the cell value is a string, this will set it to lowercase and replacing accent letters with plain letters
@@ -10868,7 +10948,7 @@ const DATE = {
10868
10948
  if (_year < 1900) {
10869
10949
  _year += 1900;
10870
10950
  }
10871
- const jsDate = new Date(_year, _month - 1, _day);
10951
+ const jsDate = new DateTime(_year, _month - 1, _day);
10872
10952
  const result = jsDateToRoundNumber(jsDate);
10873
10953
  assert(() => result >= 0, _lt(`The function [[FUNCTION_NAME]] result must be greater than or equal 01/01/1900.`));
10874
10954
  return result;
@@ -10980,7 +11060,7 @@ const EOMONTH = {
10980
11060
  const _months = Math.trunc(toNumber(months));
10981
11061
  const yStart = _startDate.getFullYear();
10982
11062
  const mStart = _startDate.getMonth();
10983
- const jsDate = new Date(yStart, mStart + _months + 1, 0);
11063
+ const jsDate = new DateTime(yStart, mStart + _months + 1, 0);
10984
11064
  return jsDateToRoundNumber(jsDate);
10985
11065
  },
10986
11066
  isExported: true,
@@ -11019,17 +11099,17 @@ const ISOWEEKNUM = {
11019
11099
  // The first week of the year is the week that contains the first
11020
11100
  // Thursday of the year.
11021
11101
  let firstThursday = 1;
11022
- while (new Date(y, 0, firstThursday).getDay() !== 4) {
11102
+ while (new DateTime(y, 0, firstThursday).getDay() !== 4) {
11023
11103
  firstThursday += 1;
11024
11104
  }
11025
- const firstDayOfFirstWeek = new Date(y, 0, firstThursday - 3);
11105
+ const firstDayOfFirstWeek = new DateTime(y, 0, firstThursday - 3);
11026
11106
  // The last week of the year is the week that contains the last Thursday of
11027
11107
  // the year.
11028
11108
  let lastThursday = 31;
11029
- while (new Date(y, 11, lastThursday).getDay() !== 4) {
11109
+ while (new DateTime(y, 11, lastThursday).getDay() !== 4) {
11030
11110
  lastThursday -= 1;
11031
11111
  }
11032
- const lastDayOfLastWeek = new Date(y, 11, lastThursday + 3);
11112
+ const lastDayOfLastWeek = new DateTime(y, 11, lastThursday + 3);
11033
11113
  // B - If our date > lastDayOfLastWeek then it's in the weeks of the year after
11034
11114
  // If our date < firstDayOfFirstWeek then it's in the weeks of the year before
11035
11115
  let offsetYear;
@@ -11055,17 +11135,17 @@ const ISOWEEKNUM = {
11055
11135
  case 1:
11056
11136
  // firstDay is the 1st day of the 1st week of the year after
11057
11137
  // firstDay = lastDayOfLastWeek + 1 Day
11058
- firstDay = new Date(y, 11, lastThursday + 3 + 1);
11138
+ firstDay = new DateTime(y, 11, lastThursday + 3 + 1);
11059
11139
  break;
11060
11140
  case -1:
11061
11141
  // firstDay is the 1st day of the 1st week of the previous year.
11062
11142
  // The first week of the previous year is the week that contains the
11063
11143
  // first Thursday of the previous year.
11064
11144
  let firstThursdayPreviousYear = 1;
11065
- while (new Date(y - 1, 0, firstThursdayPreviousYear).getDay() !== 4) {
11145
+ while (new DateTime(y - 1, 0, firstThursdayPreviousYear).getDay() !== 4) {
11066
11146
  firstThursdayPreviousYear += 1;
11067
11147
  }
11068
- firstDay = new Date(y - 1, 0, firstThursdayPreviousYear - 3);
11148
+ firstDay = new DateTime(y - 1, 0, firstThursdayPreviousYear - 3);
11069
11149
  break;
11070
11150
  }
11071
11151
  const diff = (_date.getTime() - firstDay.getTime()) / MS_PER_DAY;
@@ -11204,8 +11284,8 @@ const NETWORKDAYS_INTL = {
11204
11284
  });
11205
11285
  }
11206
11286
  const invertDate = _startDate.getTime() > _endDate.getTime();
11207
- const stopDate = new Date((invertDate ? _startDate : _endDate).getTime());
11208
- let stepDate = new Date((invertDate ? _endDate : _startDate).getTime());
11287
+ const stopDate = DateTime.fromTimestamp((invertDate ? _startDate : _endDate).getTime());
11288
+ let stepDate = DateTime.fromTimestamp((invertDate ? _endDate : _startDate).getTime());
11209
11289
  const timeStopDate = stopDate.getTime();
11210
11290
  let timeStepDate = stepDate.getTime();
11211
11291
  let netWorkingDay = 0;
@@ -11229,8 +11309,7 @@ const NOW = {
11229
11309
  returns: ["DATE"],
11230
11310
  computeFormat: () => "m/d/yyyy hh:mm:ss",
11231
11311
  compute: function () {
11232
- let today = new Date();
11233
- today.setMilliseconds(0);
11312
+ let today = DateTime.now();
11234
11313
  const delta = today.getTime() - INITIAL_1900_DAY.getTime();
11235
11314
  const time = today.getHours() / 24 + today.getMinutes() / 1440 + today.getSeconds() / 86400;
11236
11315
  return Math.floor(delta / MS_PER_DAY) + time;
@@ -11304,8 +11383,8 @@ const TODAY = {
11304
11383
  returns: ["DATE"],
11305
11384
  computeFormat: () => "m/d/yyyy",
11306
11385
  compute: function () {
11307
- const today = new Date();
11308
- const jsDate = new Date(today.getFullYear(), today.getMonth(), today.getDate());
11386
+ const today = DateTime.now();
11387
+ const jsDate = new DateTime(today.getFullYear(), today.getMonth(), today.getDate());
11309
11388
  return jsDateToRoundNumber(jsDate);
11310
11389
  },
11311
11390
  isExported: true,
@@ -11360,10 +11439,10 @@ const WEEKNUM = {
11360
11439
  }
11361
11440
  const y = _date.getFullYear();
11362
11441
  let dayStart = 1;
11363
- let startDayOfFirstWeek = new Date(y, 0, dayStart);
11442
+ let startDayOfFirstWeek = new DateTime(y, 0, dayStart);
11364
11443
  while (startDayOfFirstWeek.getDay() !== startDayOfWeek) {
11365
11444
  dayStart += 1;
11366
- startDayOfFirstWeek = new Date(y, 0, dayStart);
11445
+ startDayOfFirstWeek = new DateTime(y, 0, dayStart);
11367
11446
  }
11368
11447
  const dif = (_date.getTime() - startDayOfFirstWeek.getTime()) / MS_PER_DAY;
11369
11448
  if (dif < 0) {
@@ -11417,7 +11496,7 @@ const WORKDAY_INTL = {
11417
11496
  timesHoliday.add(holiday.getTime());
11418
11497
  });
11419
11498
  }
11420
- let stepDate = new Date(_startDate.getTime());
11499
+ let stepDate = DateTime.fromTimestamp(_startDate.getTime());
11421
11500
  let timeStepDate = stepDate.getTime();
11422
11501
  const unitDay = Math.sign(_numDays);
11423
11502
  let stepDay = Math.abs(_numDays);
@@ -11483,7 +11562,7 @@ const MONTH_START = {
11483
11562
  const _startDate = toJsDate(date);
11484
11563
  const yStart = _startDate.getFullYear();
11485
11564
  const mStart = _startDate.getMonth();
11486
- const jsDate = new Date(yStart, mStart, 1);
11565
+ const jsDate = new DateTime(yStart, mStart, 1);
11487
11566
  return jsDateToRoundNumber(jsDate);
11488
11567
  },
11489
11568
  };
@@ -11527,7 +11606,7 @@ const QUARTER_START = {
11527
11606
  compute: function (date) {
11528
11607
  const quarter = QUARTER.compute(date);
11529
11608
  const year = YEAR.compute(date);
11530
- const jsDate = new Date(year, (quarter - 1) * 3, 1);
11609
+ const jsDate = new DateTime(year, (quarter - 1) * 3, 1);
11531
11610
  return jsDateToRoundNumber(jsDate);
11532
11611
  },
11533
11612
  };
@@ -11544,7 +11623,7 @@ const QUARTER_END = {
11544
11623
  compute: function (date) {
11545
11624
  const quarter = QUARTER.compute(date);
11546
11625
  const year = YEAR.compute(date);
11547
- const jsDate = new Date(year, quarter * 3, 0);
11626
+ const jsDate = new DateTime(year, quarter * 3, 0);
11548
11627
  return jsDateToRoundNumber(jsDate);
11549
11628
  },
11550
11629
  };
@@ -11560,7 +11639,7 @@ const YEAR_START = {
11560
11639
  computeFormat: () => "m/d/yyyy",
11561
11640
  compute: function (date) {
11562
11641
  const year = YEAR.compute(date);
11563
- const jsDate = new Date(year, 0, 1);
11642
+ const jsDate = new DateTime(year, 0, 1);
11564
11643
  return jsDateToRoundNumber(jsDate);
11565
11644
  },
11566
11645
  };
@@ -11576,7 +11655,7 @@ const YEAR_END = {
11576
11655
  computeFormat: () => "m/d/yyyy",
11577
11656
  compute: function (date) {
11578
11657
  const year = YEAR.compute(date);
11579
- const jsDate = new Date(year + 1, 0, 0);
11658
+ const jsDate = new DateTime(year + 1, 0, 0);
11580
11659
  return jsDateToRoundNumber(jsDate);
11581
11660
  },
11582
11661
  };
@@ -11710,7 +11789,7 @@ function assertDeprecationFactorStrictlyPositive(factor) {
11710
11789
  function assertSettlementLessThanOneYearBeforeMaturity(settlement, maturity) {
11711
11790
  const startDate = toJsDate(settlement);
11712
11791
  const endDate = toJsDate(maturity);
11713
- const startDatePlusOneYear = new Date(startDate);
11792
+ const startDatePlusOneYear = toJsDate(settlement);
11714
11793
  startDatePlusOneYear.setFullYear(startDate.getFullYear() + 1);
11715
11794
  assert(() => endDate.getTime() <= startDatePlusOneYear.getTime(), _lt("The settlement date (%s) must at most one year after the maturity date (%s).", settlement.toString(), maturity.toString()));
11716
11795
  }
@@ -15636,7 +15715,7 @@ const machine = {
15636
15715
  SPACE: goTo(State.RightRef),
15637
15716
  NUMBER: goTo(State.Found),
15638
15717
  REFERENCE: goTo(State.Found, (token) => isSingleCellReference(token.value)),
15639
- SYMBOL: goTo(State.Found, (token) => isColHeader(token.value)),
15718
+ SYMBOL: goTo(State.Found, (token) => isColHeader(token.value) || isRowHeader(token.value)),
15640
15719
  },
15641
15720
  [State.RightColumnRef]: {
15642
15721
  SPACE: goTo(State.RightColumnRef),
@@ -15647,6 +15726,7 @@ const machine = {
15647
15726
  SPACE: goTo(State.RightRowRef),
15648
15727
  NUMBER: goTo(State.Found),
15649
15728
  REFERENCE: goTo(State.Found, (token) => isSingleCellReference(token.value)),
15729
+ SYMBOL: goTo(State.Found, (token) => isRowHeader(token.value)),
15650
15730
  },
15651
15731
  [State.Found]: {},
15652
15732
  };
@@ -16156,6 +16236,10 @@ function setXcToReferenceType(xc, referenceType) {
16156
16236
  return xc.slice(0, indexOfNumber) + "$" + xc.slice(indexOfNumber);
16157
16237
  case "colrow":
16158
16238
  indexOfNumber = xc.search(/[0-9]/);
16239
+ if (indexOfNumber === -1 || indexOfNumber === 0) {
16240
+ // no row number (eg. A) or no column (eg. 1)
16241
+ return "$" + xc;
16242
+ }
16159
16243
  xc = xc.slice(0, indexOfNumber) + "$" + xc.slice(indexOfNumber);
16160
16244
  return "$" + xc;
16161
16245
  case "none":
@@ -16929,10 +17013,10 @@ const arrowMap = {
16929
17013
  function updateSelectionWithArrowKeys(ev, selection) {
16930
17014
  const direction = arrowMap[ev.key];
16931
17015
  if (ev.shiftKey) {
16932
- selection.resizeAnchorZone(direction, ev.ctrlKey ? "end" : 1);
17016
+ selection.resizeAnchorZone(direction, isCtrlKey(ev) ? "end" : 1);
16933
17017
  }
16934
17018
  else {
16935
- selection.moveAnchorCell(direction, ev.ctrlKey ? "end" : 1);
17019
+ selection.moveAnchorCell(direction, isCtrlKey(ev) ? "end" : 1);
16936
17020
  }
16937
17021
  }
16938
17022
 
@@ -22589,7 +22673,10 @@ class GridOverlay extends Component {
22589
22673
  return;
22590
22674
  }
22591
22675
  const [col, row] = this.getCartesianCoordinates(ev);
22592
- this.props.onCellClicked(col, row, { shiftKey: ev.shiftKey, ctrlKey: ev.ctrlKey });
22676
+ this.props.onCellClicked(col, row, {
22677
+ expandZone: ev.shiftKey,
22678
+ addZone: isCtrlKey(ev),
22679
+ });
22593
22680
  }
22594
22681
  onDoubleClick(ev) {
22595
22682
  const [col, row] = this.getCartesianCoordinates(ev);
@@ -22677,6 +22764,7 @@ class AbstractResizer extends Component {
22677
22764
  draggerShadowThickness: 0,
22678
22765
  delta: 0,
22679
22766
  base: 0,
22767
+ position: "before",
22680
22768
  });
22681
22769
  }
22682
22770
  _computeHandleDisplay(ev) {
@@ -22802,11 +22890,13 @@ class AbstractResizer extends Component {
22802
22890
  this.state.draggerLinePosition = dimensions.start;
22803
22891
  this.state.draggerShadowPosition = dimensions.start;
22804
22892
  this.state.base = elementIndex;
22893
+ this.state.position = "before";
22805
22894
  }
22806
22895
  else if (this._getSelectedZoneEnd() < elementIndex) {
22807
22896
  this.state.draggerLinePosition = dimensions.end;
22808
22897
  this.state.draggerShadowPosition = dimensions.end - this.state.draggerShadowThickness;
22809
- this.state.base = elementIndex + 1;
22898
+ this.state.base = elementIndex;
22899
+ this.state.position = "after";
22810
22900
  }
22811
22901
  else {
22812
22902
  this.state.draggerLinePosition = startDimensions.start;
@@ -22830,7 +22920,7 @@ class AbstractResizer extends Component {
22830
22920
  this._increaseSelection(index);
22831
22921
  }
22832
22922
  else {
22833
- this._selectElement(index, ev.ctrlKey);
22923
+ this._selectElement(index, isCtrlKey(ev));
22834
22924
  }
22835
22925
  this.lastSelectedElementIndex = index;
22836
22926
  const mouseMoveSelect = (col, row) => {
@@ -22843,7 +22933,7 @@ class AbstractResizer extends Component {
22843
22933
  const mouseUpSelect = () => {
22844
22934
  this.state.isSelecting = false;
22845
22935
  this.lastSelectedElementIndex = null;
22846
- this.env.model.dispatch(ev.ctrlKey ? "PREPARE_SELECTION_INPUT_EXPANSION" : "STOP_SELECTION_INPUT");
22936
+ this.env.model.dispatch(isCtrlKey(ev) ? "PREPARE_SELECTION_INPUT_EXPANSION" : "STOP_SELECTION_INPUT");
22847
22937
  this._computeGrabDisplay(ev);
22848
22938
  };
22849
22939
  dragAndDropBeyondTheViewport(this.env, mouseMoveSelect, mouseUpSelect);
@@ -22987,13 +23077,14 @@ class ColResizer extends AbstractResizer {
22987
23077
  dimension: "COL",
22988
23078
  base: this.state.base,
22989
23079
  elements,
23080
+ position: this.state.position,
22990
23081
  });
22991
23082
  if (!result.isSuccessful && result.reasons.includes(2 /* CommandResult.WillRemoveExistingMerge */)) {
22992
23083
  this.env.raiseError(MergeErrorMessage);
22993
23084
  }
22994
23085
  }
22995
- _selectElement(index, ctrlKey) {
22996
- this.env.model.selection.selectColumn(index, ctrlKey ? "newAnchor" : "overrideSelection");
23086
+ _selectElement(index, addDistinctHeader) {
23087
+ this.env.model.selection.selectColumn(index, addDistinctHeader ? "newAnchor" : "overrideSelection");
22997
23088
  }
22998
23089
  _increaseSelection(index) {
22999
23090
  this.env.model.selection.selectColumn(index, "updateAnchor");
@@ -23159,13 +23250,14 @@ class RowResizer extends AbstractResizer {
23159
23250
  dimension: "ROW",
23160
23251
  base: this.state.base,
23161
23252
  elements,
23253
+ position: this.state.position,
23162
23254
  });
23163
23255
  if (!result.isSuccessful && result.reasons.includes(2 /* CommandResult.WillRemoveExistingMerge */)) {
23164
23256
  this.env.raiseError(MergeErrorMessage);
23165
23257
  }
23166
23258
  }
23167
- _selectElement(index, ctrlKey) {
23168
- this.env.model.selection.selectRow(index, ctrlKey ? "newAnchor" : "overrideSelection");
23259
+ _selectElement(index, addDistinctHeader) {
23260
+ this.env.model.selection.selectRow(index, addDistinctHeader ? "newAnchor" : "overrideSelection");
23169
23261
  }
23170
23262
  _increaseSelection(index) {
23171
23263
  this.env.model.selection.selectRow(index, "updateAnchor");
@@ -23267,8 +23359,8 @@ function useWheelHandler(handler) {
23267
23359
  return val * (deltaMode === 0 ? 1 : DEFAULT_CELL_HEIGHT);
23268
23360
  }
23269
23361
  const onMouseWheel = (ev) => {
23270
- const deltaX = normalize(ev.shiftKey ? ev.deltaY : ev.deltaX, ev.deltaMode);
23271
- const deltaY = normalize(ev.shiftKey ? ev.deltaX : ev.deltaY, ev.deltaMode);
23362
+ const deltaX = normalize(ev.shiftKey && !isMacOS() ? ev.deltaY : ev.deltaX, ev.deltaMode);
23363
+ const deltaY = normalize(ev.shiftKey && !isMacOS() ? ev.deltaX : ev.deltaY, ev.deltaMode);
23272
23364
  handler(deltaX, deltaY);
23273
23365
  };
23274
23366
  return onMouseWheel;
@@ -23912,8 +24004,8 @@ class Grid extends Component {
23912
24004
  // ---------------------------------------------------------------------------
23913
24005
  // Zone selection with mouse
23914
24006
  // ---------------------------------------------------------------------------
23915
- onCellClicked(col, row, { ctrlKey, shiftKey }) {
23916
- if (ctrlKey) {
24007
+ onCellClicked(col, row, { addZone, expandZone }) {
24008
+ if (addZone) {
23917
24009
  this.env.model.dispatch("PREPARE_SELECTION_INPUT_EXPANSION");
23918
24010
  }
23919
24011
  if (this.env.model.getters.hasOpenedPopover()) {
@@ -23922,10 +24014,10 @@ class Grid extends Component {
23922
24014
  if (this.env.model.getters.getEditionMode() === "editing") {
23923
24015
  this.env.model.dispatch("STOP_EDITION");
23924
24016
  }
23925
- if (shiftKey) {
24017
+ if (expandZone) {
23926
24018
  this.env.model.selection.setAnchorCorner(col, row);
23927
24019
  }
23928
- else if (ctrlKey) {
24020
+ else if (addZone) {
23929
24021
  this.env.model.selection.addCellToSelection(col, row);
23930
24022
  }
23931
24023
  else {
@@ -23987,9 +24079,7 @@ class Grid extends Component {
23987
24079
  return;
23988
24080
  }
23989
24081
  let keyDownString = "";
23990
- if (ev.ctrlKey)
23991
- keyDownString += "CTRL+";
23992
- if (ev.metaKey)
24082
+ if (isCtrlKey(ev))
23993
24083
  keyDownString += "CTRL+";
23994
24084
  if (ev.altKey)
23995
24085
  keyDownString += "ALT+";
@@ -27941,6 +28031,14 @@ class BordersPlugin extends CorePlugin {
27941
28031
  // ---------------------------------------------------------------------------
27942
28032
  // Command Handling
27943
28033
  // ---------------------------------------------------------------------------
28034
+ allowDispatch(cmd) {
28035
+ switch (cmd.type) {
28036
+ case "SET_BORDER":
28037
+ return this.checkBordersUnchanged(cmd);
28038
+ default:
28039
+ return 0 /* CommandResult.Success */;
28040
+ }
28041
+ }
27944
28042
  handle(cmd) {
27945
28043
  switch (cmd.type) {
27946
28044
  case "ADD_MERGE":
@@ -28336,6 +28434,15 @@ class BordersPlugin extends CorePlugin {
28336
28434
  this.setBorders(sheetId, [{ ...zone, left: right }], "right");
28337
28435
  }
28338
28436
  }
28437
+ checkBordersUnchanged(cmd) {
28438
+ var _a, _b, _c, _d;
28439
+ const currentBorder = this.getCellBorder(cmd);
28440
+ const areAllNewBordersUndefined = !((_a = cmd.border) === null || _a === void 0 ? void 0 : _a.bottom) && !((_b = cmd.border) === null || _b === void 0 ? void 0 : _b.left) && !((_c = cmd.border) === null || _c === void 0 ? void 0 : _c.right) && !((_d = cmd.border) === null || _d === void 0 ? void 0 : _d.top);
28441
+ if ((!currentBorder && areAllNewBordersUndefined) || deepEquals(currentBorder, cmd.border)) {
28442
+ return 88 /* CommandResult.NoChanges */;
28443
+ }
28444
+ return 0 /* CommandResult.Success */;
28445
+ }
28339
28446
  // ---------------------------------------------------------------------------
28340
28447
  // Import/Export
28341
28448
  // ---------------------------------------------------------------------------
@@ -28371,7 +28478,7 @@ class BordersPlugin extends CorePlugin {
28371
28478
  */
28372
28479
  function getBorderId(border) {
28373
28480
  for (let [key, value] of Object.entries(borders)) {
28374
- if (stringify(value) === stringify(border)) {
28481
+ if (deepEquals(value, border)) {
28375
28482
  return parseInt(key, 10);
28376
28483
  }
28377
28484
  }
@@ -28439,8 +28546,9 @@ class CellPlugin extends CorePlugin {
28439
28546
  allowDispatch(cmd) {
28440
28547
  switch (cmd.type) {
28441
28548
  case "UPDATE_CELL":
28549
+ return this.checkCellOutOfSheet(cmd);
28442
28550
  case "CLEAR_CELL":
28443
- return this.checkCellOutOfSheet(cmd.sheetId, cmd.col, cmd.row);
28551
+ return this.checkValidations(cmd, this.chainValidations(this.checkCellOutOfSheet, this.checkUselessClearCell));
28444
28552
  default:
28445
28553
  return 0 /* CommandResult.Success */;
28446
28554
  }
@@ -28599,14 +28707,9 @@ class CellPlugin extends CorePlugin {
28599
28707
  */
28600
28708
  getCellById(cellId) {
28601
28709
  // this must be as fast as possible
28602
- for (const sheetId in this.cells) {
28603
- const sheet = this.cells[sheetId];
28604
- const cell = sheet[cellId];
28605
- if (cell) {
28606
- return cell;
28607
- }
28608
- }
28609
- return undefined;
28710
+ const position = this.getters.getCellPosition(cellId);
28711
+ const sheet = this.cells[position.sheetId];
28712
+ return sheet[cellId];
28610
28713
  }
28611
28714
  /*
28612
28715
  * Reconstructs the original formula string based on a normalized form and its dependencies
@@ -28847,13 +28950,23 @@ class CellPlugin extends CorePlugin {
28847
28950
  dependencies: [],
28848
28951
  };
28849
28952
  }
28850
- checkCellOutOfSheet(sheetId, col, row) {
28953
+ checkCellOutOfSheet(cmd) {
28954
+ const { sheetId, col, row } = cmd;
28851
28955
  const sheet = this.getters.tryGetSheet(sheetId);
28852
28956
  if (!sheet)
28853
28957
  return 27 /* CommandResult.InvalidSheetId */;
28854
28958
  const sheetZone = this.getters.getSheetZone(sheetId);
28855
28959
  return isInside(col, row, sheetZone) ? 0 /* CommandResult.Success */ : 18 /* CommandResult.TargetOutOfSheet */;
28856
28960
  }
28961
+ checkUselessClearCell(cmd) {
28962
+ const cell = this.getters.getCell(cmd);
28963
+ if (!cell)
28964
+ return 88 /* CommandResult.NoChanges */;
28965
+ if (!cell.content && !cell.style && !cell.format) {
28966
+ return 88 /* CommandResult.NoChanges */;
28967
+ }
28968
+ return 0 /* CommandResult.Success */;
28969
+ }
28857
28970
  }
28858
28971
  CellPlugin.getters = [
28859
28972
  "zoneToXC",
@@ -31314,7 +31427,7 @@ class SheetPlugin extends CorePlugin {
31314
31427
  const elements = cmd.dimension === "COL"
31315
31428
  ? this.getNumberCols(cmd.sheetId)
31316
31429
  : this.getNumberRows(cmd.sheetId);
31317
- if (cmd.base < 0 || cmd.base > elements) {
31430
+ if (cmd.base < 0 || cmd.base >= elements) {
31318
31431
  return 86 /* CommandResult.InvalidHeaderIndex */;
31319
31432
  }
31320
31433
  else if (cmd.quantity <= 0) {
@@ -31914,10 +32027,13 @@ class SheetPlugin extends CorePlugin {
31914
32027
  // begin with the end.
31915
32028
  rows.sort((a, b) => b - a);
31916
32029
  for (let group of groupConsecutive(rows)) {
32030
+ // indexes are sorted in the descending order
32031
+ const from = group[group.length - 1];
32032
+ const to = group[0];
31917
32033
  // Move the cells.
31918
- this.moveCellOnRowsDeletion(sheet, group[group.length - 1], group[0]);
31919
- // Effectively delete the element and recompute the left-right/top-bottom.
31920
- group.map((row) => this.updateRowsStructureOnDeletion(row, sheet));
32034
+ this.moveCellOnRowsDeletion(sheet, from, to);
32035
+ // Effectively delete the rows
32036
+ this.updateRowsStructureOnDeletion(sheet, from, to);
31921
32037
  }
31922
32038
  const count = rows.filter((row) => row < sheet.panes.ySplit).length;
31923
32039
  if (count) {
@@ -31939,8 +32055,6 @@ class SheetPlugin extends CorePlugin {
31939
32055
  this.addEmptyRows(sheet, quantity);
31940
32056
  // Move the cells.
31941
32057
  this.moveCellsOnAddition(sheet, index, quantity, "rows");
31942
- // Recompute the left-right/top-bottom.
31943
- this.updateRowsStructureOnAddition(sheet, row, quantity);
31944
32058
  if (index < sheet.panes.ySplit) {
31945
32059
  this.setPaneDivisions(sheet.id, sheet.panes.ySplit + quantity, "ROW");
31946
32060
  }
@@ -32041,35 +32155,20 @@ class SheetPlugin extends CorePlugin {
32041
32155
  }
32042
32156
  }
32043
32157
  }
32044
- updateRowsStructureOnDeletion(index, sheet) {
32158
+ updateRowsStructureOnDeletion(sheet, deleteFromRow, deleteToRow) {
32045
32159
  const rows = [];
32046
- const cellsQueue = sheet.rows.map((row) => row.cells);
32160
+ const cellsQueue = sheet.rows.map((row) => row.cells).reverse();
32047
32161
  for (let i in sheet.rows) {
32048
- if (Number(i) === index) {
32162
+ const row = Number(i);
32163
+ if (row >= deleteFromRow && row <= deleteToRow) {
32049
32164
  continue;
32050
32165
  }
32051
32166
  rows.push({
32052
- cells: cellsQueue.shift(),
32167
+ cells: cellsQueue.pop(),
32053
32168
  });
32054
32169
  }
32055
32170
  this.history.update("sheets", sheet.id, "rows", rows);
32056
32171
  }
32057
- /**
32058
- * Update the rows of the sheet after an addition:
32059
- * - Rename the rows
32060
- *
32061
- * @param sheet Sheet on which the deletion occurs
32062
- * @param addedRow Index of the added row
32063
- * @param rowsToAdd Number of the rows to add
32064
- */
32065
- updateRowsStructureOnAddition(sheet, addedRow, rowsToAdd) {
32066
- const rows = [];
32067
- const cellsQueue = sheet.rows.map((row) => row.cells);
32068
- sheet.rows.forEach(() => rows.push({
32069
- cells: cellsQueue.shift(),
32070
- }));
32071
- this.history.update("sheets", sheet.id, "rows", rows);
32072
- }
32073
32172
  /**
32074
32173
  * Add empty rows at the end of the rows
32075
32174
  *
@@ -32351,13 +32450,18 @@ class EvaluationPlugin extends UIPlugin {
32351
32450
  // Getters
32352
32451
  // ---------------------------------------------------------------------------
32353
32452
  evaluateFormula(formulaString, sheetId = this.getters.getActiveSheetId()) {
32354
- const compiledFormula = compile(formulaString);
32355
- const params = this.getCompilationParameters((cell) => this.getEvaluatedCell(this.getters.getCellPosition(cell.id)));
32356
- const ranges = [];
32357
- for (let xc of compiledFormula.dependencies) {
32358
- ranges.push(this.getters.getRangeFromSheetXC(sheetId, xc));
32453
+ try {
32454
+ const compiledFormula = compile(formulaString);
32455
+ const params = this.getCompilationParameters((cell) => this.getEvaluatedCell(this.getters.getCellPosition(cell.id)));
32456
+ const ranges = [];
32457
+ for (let xc of compiledFormula.dependencies) {
32458
+ ranges.push(this.getters.getRangeFromSheetXC(sheetId, xc));
32459
+ }
32460
+ return compiledFormula.execute(ranges, ...params).value;
32461
+ }
32462
+ catch (error) {
32463
+ return error instanceof EvaluationError ? error.errorType : CellErrorType.GenericError;
32359
32464
  }
32360
- return compiledFormula.execute(ranges, ...params).value;
32361
32465
  }
32362
32466
  /**
32363
32467
  * Return the value of each cell in the range as they are displayed in the grid.
@@ -32526,6 +32630,7 @@ class EvaluationPlugin extends UIPlugin {
32526
32630
  }
32527
32631
  return evaluatedCell;
32528
32632
  };
32633
+ const rangeCache = {};
32529
32634
  /**
32530
32635
  * Return the values of the cell(s) used in reference, but always in the format of a range even
32531
32636
  * if a single cell is referenced. It is a list of col values. This is useful for the formulas that describe parameters as
@@ -32542,21 +32647,26 @@ class EvaluationPlugin extends UIPlugin {
32542
32647
  // Performance issue: Avoid fetching data on positions that are out of the spreadsheet
32543
32648
  // e.g. A1:ZZZ9999 in a sheet with 10 cols and 10 rows should ignore everything past J10 and return a 10x10 array
32544
32649
  const sheetZone = getters.getSheetZone(sheetId);
32545
- const result = [];
32546
32650
  const zone = intersection(range.zone, sheetZone);
32547
32651
  if (!zone) {
32548
- result.push([]);
32549
- return result;
32652
+ return [[]];
32550
32653
  }
32654
+ const { top, left, bottom, right } = zone;
32655
+ const cacheKey = `${sheetId}-${top}-${left}-${bottom}-${right}`;
32656
+ if (cacheKey in rangeCache) {
32657
+ return rangeCache[cacheKey];
32658
+ }
32659
+ const result = new Array(right - left + 1);
32551
32660
  // Performance issue: nested loop is faster than a map here
32552
- for (let col = zone.left; col <= zone.right; col++) {
32553
- const rowValues = [];
32554
- for (let row = zone.top; row <= zone.bottom; row++) {
32661
+ for (let col = left; col <= right; col++) {
32662
+ const rowValues = new Array(bottom - top + 1);
32663
+ for (let row = top; row <= bottom; row++) {
32555
32664
  const cell = evalContext.getters.getCell({ sheetId: range.sheetId, col, row });
32556
- rowValues.push(cell ? getEvaluatedCell(cell) : undefined);
32665
+ rowValues[row - top] = cell ? getEvaluatedCell(cell) : undefined;
32557
32666
  }
32558
- result.push(rowValues);
32667
+ result[col - left] = rowValues;
32559
32668
  }
32669
+ rangeCache[cacheKey] = result;
32560
32670
  return result;
32561
32671
  }
32562
32672
  /**
@@ -32843,39 +32953,34 @@ class EvaluationConditionalFormatPlugin extends UIPlugin {
32843
32953
  this.computedIcons[sheetId] = {};
32844
32954
  const computedStyle = this.computedStyles[sheetId];
32845
32955
  for (let cf of this.getters.getConditionalFormats(sheetId).reverse()) {
32846
- try {
32847
- switch (cf.rule.type) {
32848
- case "ColorScaleRule":
32849
- for (let range of cf.ranges) {
32850
- this.applyColorScale(range, cf.rule);
32851
- }
32852
- break;
32853
- case "IconSetRule":
32854
- for (let range of cf.ranges) {
32855
- this.applyIcon(range, cf.rule);
32856
- }
32857
- break;
32858
- default:
32859
- for (let ref of cf.ranges) {
32860
- const zone = this.getters.getRangeFromSheetXC(sheetId, ref).zone;
32861
- for (let row = zone.top; row <= zone.bottom; row++) {
32862
- for (let col = zone.left; col <= zone.right; col++) {
32863
- const pr = this.rulePredicate[cf.rule.type];
32864
- let cell = this.getters.getEvaluatedCell({ sheetId, col, row });
32865
- if (pr && pr(cell, cf.rule)) {
32866
- if (!computedStyle[col])
32867
- computedStyle[col] = [];
32868
- // we must combine all the properties of all the CF rules applied to the given cell
32869
- computedStyle[col][row] = Object.assign(((_a = computedStyle[col]) === null || _a === void 0 ? void 0 : _a[row]) || {}, cf.rule.style);
32870
- }
32956
+ switch (cf.rule.type) {
32957
+ case "ColorScaleRule":
32958
+ for (let range of cf.ranges) {
32959
+ this.applyColorScale(range, cf.rule);
32960
+ }
32961
+ break;
32962
+ case "IconSetRule":
32963
+ for (let range of cf.ranges) {
32964
+ this.applyIcon(range, cf.rule);
32965
+ }
32966
+ break;
32967
+ default:
32968
+ for (let ref of cf.ranges) {
32969
+ const zone = this.getters.getRangeFromSheetXC(sheetId, ref).zone;
32970
+ for (let row = zone.top; row <= zone.bottom; row++) {
32971
+ for (let col = zone.left; col <= zone.right; col++) {
32972
+ const pr = this.rulePredicate[cf.rule.type];
32973
+ let cell = this.getters.getEvaluatedCell({ sheetId, col, row });
32974
+ if (pr && pr(cell, cf.rule)) {
32975
+ if (!computedStyle[col])
32976
+ computedStyle[col] = [];
32977
+ // we must combine all the properties of all the CF rules applied to the given cell
32978
+ computedStyle[col][row] = Object.assign(((_a = computedStyle[col]) === null || _a === void 0 ? void 0 : _a[row]) || {}, cf.rule.style);
32871
32979
  }
32872
32980
  }
32873
32981
  }
32874
- break;
32875
- }
32876
- }
32877
- catch (_) {
32878
- // we don't care about the errors within the evaluation of a rule
32982
+ }
32983
+ break;
32879
32984
  }
32880
32985
  }
32881
32986
  }
@@ -32900,7 +33005,7 @@ class EvaluationConditionalFormatPlugin extends UIPlugin {
32900
33005
  return percentile(rangeValues, Number(threshold.value) / 100, true);
32901
33006
  case "formula":
32902
33007
  const value = threshold.value && this.getters.evaluateFormula(threshold.value);
32903
- return !(value instanceof Promise) ? value : null;
33008
+ return typeof value === "number" ? value : null;
32904
33009
  default:
32905
33010
  return null;
32906
33011
  }
@@ -34462,24 +34567,27 @@ class AutofillPlugin extends UIPlugin {
34462
34567
  * autofiller
34463
34568
  */
34464
34569
  autofillAuto() {
34570
+ var _a, _b;
34465
34571
  const zone = this.getters.getSelectedZone();
34466
34572
  const sheetId = this.getters.getActiveSheetId();
34467
34573
  let col = zone.left;
34468
34574
  let row = zone.bottom;
34469
34575
  if (col > 0) {
34470
- let left = this.getters.getEvaluatedCell({ sheetId, col: col - 1, row });
34471
- while (left.type !== CellValueType.empty) {
34576
+ let leftPosition = { sheetId, col: col - 1, row };
34577
+ while (this.getters.getEvaluatedCell(leftPosition).type !== CellValueType.empty ||
34578
+ ((_a = this.getters.getCell(leftPosition)) === null || _a === void 0 ? void 0 : _a.content)) {
34472
34579
  row += 1;
34473
- left = this.getters.getEvaluatedCell({ sheetId, col: col - 1, row });
34580
+ leftPosition = { sheetId, col: col - 1, row };
34474
34581
  }
34475
34582
  }
34476
34583
  if (row === zone.bottom) {
34477
34584
  col = zone.right;
34478
34585
  if (col <= this.getters.getNumberCols(sheetId)) {
34479
- let right = this.getters.getEvaluatedCell({ sheetId, col: col + 1, row });
34480
- while (right.type !== CellValueType.empty) {
34586
+ let rightPosition = { sheetId, col: col + 1, row };
34587
+ while (this.getters.getEvaluatedCell(rightPosition).type !== CellValueType.empty ||
34588
+ ((_b = this.getters.getCell(rightPosition)) === null || _b === void 0 ? void 0 : _b.content)) {
34481
34589
  row += 1;
34482
- right = this.getters.getEvaluatedCell({ sheetId, col: col + 1, row });
34590
+ rightPosition = { sheetId, col: col + 1, row };
34483
34591
  }
34484
34592
  }
34485
34593
  }
@@ -39211,7 +39319,7 @@ class GridSelectionPlugin extends UIPlugin {
39211
39319
  sheetId: cmd.sheetId,
39212
39320
  base: cmd.base,
39213
39321
  quantity: thickness,
39214
- position: "before",
39322
+ position: cmd.position,
39215
39323
  });
39216
39324
  const isCol = cmd.dimension === "COL";
39217
39325
  const start = cmd.elements[0];
@@ -39228,12 +39336,13 @@ class GridSelectionPlugin extends UIPlugin {
39228
39336
  },
39229
39337
  ];
39230
39338
  const state = new ClipboardCellsState(target, "CUT", this.getters, this.dispatch, this.selection);
39339
+ const base = isBasedBefore ? cmd.base : cmd.base + 1;
39231
39340
  const pasteTarget = [
39232
39341
  {
39233
- left: isCol ? cmd.base : 0,
39234
- right: isCol ? cmd.base + thickness - 1 : this.getters.getNumberCols(cmd.sheetId) - 1,
39235
- top: !isCol ? cmd.base : 0,
39236
- bottom: !isCol ? cmd.base + thickness - 1 : this.getters.getNumberRows(cmd.sheetId) - 1,
39342
+ left: isCol ? base : 0,
39343
+ right: isCol ? base + thickness - 1 : this.getters.getNumberCols(cmd.sheetId) - 1,
39344
+ top: !isCol ? base : 0,
39345
+ bottom: !isCol ? base + thickness - 1 : this.getters.getNumberRows(cmd.sheetId) - 1,
39237
39346
  },
39238
39347
  ];
39239
39348
  state.paste(pasteTarget, { selectTarget: true });
@@ -39270,6 +39379,11 @@ class GridSelectionPlugin extends UIPlugin {
39270
39379
  doesElementsHaveCommonMerges(id, cmd.base - 1, cmd.base)) {
39271
39380
  return 2 /* CommandResult.WillRemoveExistingMerge */;
39272
39381
  }
39382
+ const headers = [cmd.base, ...cmd.elements];
39383
+ const maxHeaderValue = isCol ? this.getters.getNumberCols(id) : this.getters.getNumberRows(id);
39384
+ if (headers.some((h) => h < 0 || h >= maxHeaderValue)) {
39385
+ return 86 /* CommandResult.InvalidHeaderIndex */;
39386
+ }
39273
39387
  return 0 /* CommandResult.Success */;
39274
39388
  }
39275
39389
  //-------------------------------------------
@@ -40823,7 +40937,7 @@ class Spreadsheet extends Component {
40823
40937
  }
40824
40938
  onKeydown(ev) {
40825
40939
  let keyDownString = "";
40826
- if (ev.ctrlKey || ev.metaKey) {
40940
+ if (isCtrlKey(ev)) {
40827
40941
  keyDownString += "CTRL+";
40828
40942
  }
40829
40943
  keyDownString += ev.key.toUpperCase();
@@ -41670,9 +41784,9 @@ function revertChanges(revisions) {
41670
41784
  * Apply the changes of the given HistoryChange to the state
41671
41785
  */
41672
41786
  function applyChange(change, target) {
41673
- let val = change.root;
41674
- let key = change.path[change.path.length - 1];
41675
- for (let pathIndex = 0; pathIndex < change.path.slice(0, -1).length; pathIndex++) {
41787
+ let val = change.path[0];
41788
+ const key = change.path[change.path.length - 1];
41789
+ for (let pathIndex = 1; pathIndex < change.path.slice(0, -1).length; pathIndex++) {
41676
41790
  const p = change.path[pathIndex];
41677
41791
  if (val[p] === undefined) {
41678
41792
  const nextPath = change.path[pathIndex + 1];
@@ -42427,13 +42541,14 @@ class StateObserver {
42427
42541
  }
42428
42542
  addChange(...args) {
42429
42543
  const val = args.pop();
42430
- const [root, ...path] = args;
42544
+ const root = args[0];
42431
42545
  let value = root;
42432
- let key = path[path.length - 1];
42433
- for (let pathIndex = 0; pathIndex <= path.length - 2; pathIndex++) {
42434
- const p = path[pathIndex];
42546
+ let key = args[args.length - 1];
42547
+ const pathLength = args.length - 2;
42548
+ for (let pathIndex = 1; pathIndex <= pathLength; pathIndex++) {
42549
+ const p = args[pathIndex];
42435
42550
  if (value[p] === undefined) {
42436
- const nextPath = path[pathIndex + 1];
42551
+ const nextPath = args[pathIndex + 1];
42437
42552
  value[p] = createEmptyStructure(nextPath);
42438
42553
  }
42439
42554
  value = value[p];
@@ -42442,8 +42557,7 @@ class StateObserver {
42442
42557
  return;
42443
42558
  }
42444
42559
  this.changes.push({
42445
- root,
42446
- path,
42560
+ path: args,
42447
42561
  before: value[key],
42448
42562
  after: val,
42449
42563
  });
@@ -44217,7 +44331,10 @@ class Model extends EventBus {
44217
44331
  */
44218
44332
  checkDispatchAllowed(command) {
44219
44333
  const results = this.handlers.map((handler) => handler.allowDispatch(command));
44220
- return new DispatchResult(results.flat());
44334
+ if (results.some((r) => r !== 0 /* CommandResult.Success */)) {
44335
+ return new DispatchResult(results.flat());
44336
+ }
44337
+ return DispatchResult.Success;
44221
44338
  }
44222
44339
  finalize() {
44223
44340
  this.status = 3 /* Status.Finalizing */;
@@ -44417,6 +44534,6 @@ function addFunction(functionName, functionDescription) {
44417
44534
  export { AbstractChart, CommandResult, CorePlugin, DATETIME_FORMAT, DispatchResult, EvaluationError, Model, Registry, Revision, SPREADSHEET_DIMENSIONS, Spreadsheet, UIPlugin, __info__, addFunction, astToFormula, compile, components, convertAstNodes, coreTypes, findCellInNewZone, functionCache, helpers, invalidateEvaluationCommands, links, load, parse, readonlyAllowedCommands, registries, setTranslationMethod, tokenize };
44418
44535
 
44419
44536
 
44420
- __info__.version = '16.1.27';
44421
- __info__.date = '2023-11-16T11:57:02.419Z';
44422
- __info__.hash = '81f7214';
44537
+ __info__.version = '16.1.31';
44538
+ __info__.date = '2024-01-12T13:42:11.001Z';
44539
+ __info__.hash = 'f51befa';