@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.
@@ -309,15 +309,76 @@ for (let font of fontSizes) {
309
309
  // -----------------------------------------------------------------------------
310
310
  // Date Type
311
311
  // -----------------------------------------------------------------------------
312
+ /**
313
+ * A DateTime object that can be used to manipulate spreadsheet dates.
314
+ * Conceptually, a spreadsheet date is simply a number with a date format,
315
+ * and it is timezone-agnostic.
316
+ * This DateTime object consistently uses UTC time to represent a naive date and time.
317
+ */
318
+ class DateTime {
319
+ constructor(year, month, day, hours = 0, minutes = 0, seconds = 0) {
320
+ this.jsDate = new Date(Date.UTC(year, month, day, hours, minutes, seconds, 0));
321
+ }
322
+ static fromTimestamp(timestamp) {
323
+ const date = new Date(timestamp);
324
+ return new DateTime(date.getUTCFullYear(), date.getUTCMonth(), date.getUTCDate(), date.getUTCHours(), date.getUTCMinutes(), date.getUTCSeconds());
325
+ }
326
+ static now() {
327
+ const now = new Date();
328
+ return new DateTime(now.getFullYear(), now.getMonth(), now.getDate(), now.getHours(), now.getMinutes(), now.getSeconds());
329
+ }
330
+ toString() {
331
+ return this.jsDate.toString();
332
+ }
333
+ getTime() {
334
+ return this.jsDate.getTime();
335
+ }
336
+ getFullYear() {
337
+ return this.jsDate.getUTCFullYear();
338
+ }
339
+ getMonth() {
340
+ return this.jsDate.getUTCMonth();
341
+ }
342
+ getDate() {
343
+ return this.jsDate.getUTCDate();
344
+ }
345
+ getDay() {
346
+ return this.jsDate.getUTCDay();
347
+ }
348
+ getHours() {
349
+ return this.jsDate.getUTCHours();
350
+ }
351
+ getMinutes() {
352
+ return this.jsDate.getUTCMinutes();
353
+ }
354
+ getSeconds() {
355
+ return this.jsDate.getUTCSeconds();
356
+ }
357
+ setFullYear(year) {
358
+ this.jsDate.setFullYear(year);
359
+ }
360
+ setDate(date) {
361
+ this.jsDate.setUTCDate(date);
362
+ }
363
+ setHours(hours) {
364
+ this.jsDate.setUTCHours(hours);
365
+ }
366
+ setMinutes(minutes) {
367
+ this.jsDate.setUTCMinutes(minutes);
368
+ }
369
+ setSeconds(seconds) {
370
+ this.jsDate.setUTCSeconds(seconds);
371
+ }
372
+ }
312
373
  // -----------------------------------------------------------------------------
313
374
  // Parsing
314
375
  // -----------------------------------------------------------------------------
315
- const INITIAL_1900_DAY = new Date(1899, 11, 30);
376
+ const INITIAL_1900_DAY = new DateTime(1899, 11, 30);
316
377
  const MS_PER_DAY = 24 * 60 * 60 * 1000;
317
378
  const CURRENT_MILLENIAL = 2000; // note: don't forget to update this in 2999
318
- const CURRENT_YEAR = new Date().getFullYear();
319
- const INITIAL_JS_DAY = new Date(0);
320
- const DATE_JS_1900_OFFSET = INITIAL_JS_DAY - INITIAL_1900_DAY;
379
+ const CURRENT_YEAR = DateTime.now().getFullYear();
380
+ const INITIAL_JS_DAY = DateTime.fromTimestamp(0);
381
+ const DATE_JS_1900_OFFSET = INITIAL_JS_DAY.getTime() - INITIAL_1900_DAY.getTime();
321
382
  const mdyDateRegexp = /^\d{1,2}(\/|-|\s)\d{1,2}((\/|-|\s)\d{1,4})?$/;
322
383
  const ymdDateRegexp = /^\d{3,4}(\/|-|\s)\d{1,2}(\/|-|\s)\d{1,2}$/;
323
384
  const timeRegexp = /((\d+(:\d+)?(:\d+)?\s*(AM|PM))|(\d+:\d+(:\d+)?))$/;
@@ -366,7 +427,7 @@ function _parseDateTime(str) {
366
427
  return {
367
428
  value: date.value + time.value,
368
429
  format: date.format + " " + (time.format === "hhhh:mm:ss" ? "hh:mm:ss" : time.format),
369
- 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()),
430
+ 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()),
370
431
  };
371
432
  }
372
433
  return date || time;
@@ -383,13 +444,13 @@ function parseDate(str, dateFormat) {
383
444
  const day = Number(parts[dayIndex]);
384
445
  const leadingZero = (parts[monthIndex].length === 2 && month < 10) || (parts[dayIndex].length === 2 && day < 10);
385
446
  const year = parts[yearIndex] ? inferYear(parts[yearIndex]) : CURRENT_YEAR;
386
- const jsDate = new Date(year, month - 1, day);
447
+ const jsDate = new DateTime(year, month - 1, day);
387
448
  const sep = str.match(/\/|-|\s/)[0];
388
449
  if (jsDate.getMonth() !== month - 1 || jsDate.getDate() !== day) {
389
450
  // invalid date
390
451
  return null;
391
452
  }
392
- const delta = jsDate - INITIAL_1900_DAY;
453
+ const delta = jsDate.getTime() - INITIAL_1900_DAY.getTime();
393
454
  let format = leadingZero ? `mm${sep}dd` : `m${sep}d`;
394
455
  if (parts[yearIndex]) {
395
456
  format = isMDY ? format + sep + "yyyy" : "yyyy" + sep + format;
@@ -449,7 +510,7 @@ function parseTime(str) {
449
510
  if (hours >= 24) {
450
511
  format = "hhhh:mm:ss";
451
512
  }
452
- const jsDate = new Date(1899, 11, 30, hours, minutes, seconds);
513
+ const jsDate = new DateTime(1899, 11, 30, hours, minutes, seconds);
453
514
  return {
454
515
  value: hours / 24 + minutes / 1440 + seconds / 86400,
455
516
  format: format,
@@ -463,7 +524,7 @@ function parseTime(str) {
463
524
  // -----------------------------------------------------------------------------
464
525
  function numberToJsDate(value) {
465
526
  const truncValue = Math.trunc(value);
466
- let date = new Date(truncValue * MS_PER_DAY - DATE_JS_1900_OFFSET);
527
+ let date = DateTime.fromTimestamp(truncValue * MS_PER_DAY - DATE_JS_1900_OFFSET);
467
528
  let time = value - truncValue;
468
529
  time = time < 0 ? 1 + time : time;
469
530
  const hours = Math.round(time * 24);
@@ -480,7 +541,7 @@ function jsDateToRoundNumber(date) {
480
541
  }
481
542
  /** Return the number of days in the current month of the given date */
482
543
  function getDaysInMonth(date) {
483
- return new Date(date.getFullYear(), date.getMonth() + 1, 0).getDate();
544
+ return new DateTime(date.getFullYear(), date.getMonth() + 1, 0).getDate();
484
545
  }
485
546
  function isLastDayOfMonth(date) {
486
547
  return getDaysInMonth(date) === date.getDate();
@@ -498,7 +559,7 @@ function addMonthsToDate(date, months, keepEndOfMonth) {
498
559
  const yStart = date.getFullYear();
499
560
  const mStart = date.getMonth();
500
561
  const dStart = date.getDate();
501
- const jsDate = new Date(yStart, mStart + months);
562
+ const jsDate = new DateTime(yStart, mStart + months, 1);
502
563
  if (keepEndOfMonth && dStart === getDaysInMonth(date)) {
503
564
  jsDate.setDate(getDaysInMonth(jsDate));
504
565
  }
@@ -626,13 +687,6 @@ function getYearFrac(startDate, endDate, _dayCountConvention) {
626
687
  }
627
688
 
628
689
  //------------------------------------------------------------------------------
629
- /**
630
- * Stringify an object, like JSON.stringify, except that the first level of keys
631
- * is ordered.
632
- */
633
- function stringify(obj) {
634
- return JSON.stringify(obj, Object.keys(obj).sort());
635
- }
636
690
  /**
637
691
  * Remove quotes from a quoted string
638
692
  * ```js
@@ -928,7 +982,7 @@ function isObjectEmptyRecursive(argument) {
928
982
  */
929
983
  function getItemId(item, itemsDic) {
930
984
  for (let [key, value] of Object.entries(itemsDic)) {
931
- if (stringify(value) === stringify(item)) {
985
+ if (deepEquals(value, item)) {
932
986
  return parseInt(key, 10);
933
987
  }
934
988
  }
@@ -1064,6 +1118,22 @@ function transpose2dArray(matrix) {
1064
1118
  if (!matrix.length)
1065
1119
  return matrix;
1066
1120
  return matrix[0].map((_, i) => matrix.map((row) => row[i]));
1121
+ }
1122
+ /**
1123
+ * Creates a version of the function that's memoized on the value of its first
1124
+ * argument, if any.
1125
+ */
1126
+ function memoize(func) {
1127
+ const cache = new Map();
1128
+ const funcName = func.name ? func.name + " (memoized)" : "memoized";
1129
+ return {
1130
+ [funcName](...args) {
1131
+ if (!cache.has(args[0])) {
1132
+ cache.set(args[0], func(...args));
1133
+ }
1134
+ return cache.get(args[0]);
1135
+ },
1136
+ }[funcName];
1067
1137
  }
1068
1138
 
1069
1139
  const RBA_REGEX = /rgba?\(|\s+|\)/gi;
@@ -1338,8 +1408,10 @@ function isSameColor(color1, color2) {
1338
1408
  const cellReference = new RegExp(/\$?([A-Z]{1,3})\$?([0-9]{1,7})/, "i");
1339
1409
  // Same as above, but matches the exact string (nothing before or after)
1340
1410
  const singleCellReference = new RegExp(/^\$?([A-Z]{1,3})\$?([0-9]{1,7})$/, "i");
1341
- /** Reference of a column header (eg. A, AB) */
1342
- const colHeader = new RegExp(/^([A-Z]{1,3})+$/, "i");
1411
+ /** Reference of a column header (eg. A, AB, $A) */
1412
+ const colHeader = new RegExp(/^\$?([A-Z]{1,3})+$/, "i");
1413
+ /** Reference of a row header (eg. 1, $1) */
1414
+ const rowHeader = new RegExp(/^\$?([0-9]{1,7})+$/, "i");
1343
1415
  /** Reference of a column (eg. A, $CA, Sheet1!B) */
1344
1416
  const colReference = new RegExp(/^\s*('.+'!|[^']+!)?\$?([A-Z]{1,3})$/, "i");
1345
1417
  /** Reference of a row (eg. 1, 59, Sheet1!9) */
@@ -1369,6 +1441,9 @@ function isRowReference(xc) {
1369
1441
  function isColHeader(str) {
1370
1442
  return colHeader.test(str);
1371
1443
  }
1444
+ function isRowHeader(str) {
1445
+ return rowHeader.test(str);
1446
+ }
1372
1447
  /**
1373
1448
  * Return true if the given xc is the reference of a single cell,
1374
1449
  * without any specified sheet (e.g. A1)
@@ -1867,7 +1942,7 @@ function formatJSTime(jsDate, format) {
1867
1942
  .map((p) => {
1868
1943
  switch (p) {
1869
1944
  case "hhhh":
1870
- const helapsedHours = Math.floor((jsDate.getTime() - INITIAL_1900_DAY) / (60 * 60 * 1000));
1945
+ const helapsedHours = Math.floor((jsDate.getTime() - INITIAL_1900_DAY.getTime()) / (60 * 60 * 1000));
1871
1946
  return helapsedHours.toString();
1872
1947
  case "hh":
1873
1948
  return hours.toString().padStart(2, "0");
@@ -2158,18 +2233,10 @@ class RangeImpl {
2158
2233
  if (isFullCol) {
2159
2234
  parts[0].rowFixed = parts[0].rowFixed || parts[1].rowFixed;
2160
2235
  parts[1].rowFixed = parts[0].rowFixed || parts[1].rowFixed;
2161
- if (zone.left === zone.right) {
2162
- parts[0].colFixed = parts[0].colFixed || parts[1].colFixed;
2163
- parts[1].colFixed = parts[0].colFixed || parts[1].colFixed;
2164
- }
2165
2236
  }
2166
2237
  if (isFullRow) {
2167
2238
  parts[0].colFixed = parts[0].colFixed || parts[1].colFixed;
2168
2239
  parts[1].colFixed = parts[0].colFixed || parts[1].colFixed;
2169
- if (zone.top === zone.bottom) {
2170
- parts[0].rowFixed = parts[0].rowFixed || parts[1].rowFixed;
2171
- parts[1].rowFixed = parts[0].rowFixed || parts[1].rowFixed;
2172
- }
2173
2240
  }
2174
2241
  return parts;
2175
2242
  }
@@ -3660,6 +3727,7 @@ exports.CommandResult = void 0;
3660
3727
  CommandResult[CommandResult["ChartDoesNotExist"] = 85] = "ChartDoesNotExist";
3661
3728
  CommandResult[CommandResult["InvalidHeaderIndex"] = 86] = "InvalidHeaderIndex";
3662
3729
  CommandResult[CommandResult["InvalidQuantity"] = 87] = "InvalidQuantity";
3730
+ CommandResult[CommandResult["NoChanges"] = 88] = "NoChanges";
3663
3731
  })(exports.CommandResult || (exports.CommandResult = {}));
3664
3732
 
3665
3733
  function isMatrix(x) {
@@ -5695,6 +5763,7 @@ function getMenuDescription(node) {
5695
5763
  return node.description ? node.description : "";
5696
5764
  }
5697
5765
 
5766
+ const macRegex = /Mac/i;
5698
5767
  /**
5699
5768
  * Return true if the event was triggered from
5700
5769
  * a child element.
@@ -5712,6 +5781,17 @@ function gridOverlayPosition() {
5712
5781
  }
5713
5782
  function getOpenedMenus() {
5714
5783
  return Array.from(document.querySelectorAll(".o-spreadsheet .o-menu"));
5784
+ }
5785
+ function isMacOS() {
5786
+ return Boolean(macRegex.test(navigator.userAgent));
5787
+ }
5788
+ /**
5789
+ * @param {KeyboardEvent | MouseEvent} ev
5790
+ * @returns Returns true if the event was triggered with the "ctrl" modifier pressed.
5791
+ * On Mac, this is the "meta" or "command" key.
5792
+ */
5793
+ function isCtrlKey(ev) {
5794
+ return isMacOS() ? ev.metaKey : ev.ctrlKey;
5715
5795
  }
5716
5796
 
5717
5797
  /**
@@ -8023,12 +8103,12 @@ function toString(value) {
8023
8103
  }
8024
8104
  }
8025
8105
  /** Normalize string by setting it to lowercase and replacing accent letters with plain letters */
8026
- function normalizeString(str) {
8106
+ const normalizeString = memoize(function normalizeString(str) {
8027
8107
  return str
8028
8108
  .toLowerCase()
8029
8109
  .normalize("NFD")
8030
8110
  .replace(/[\u0300-\u036f]/g, "");
8031
- }
8111
+ });
8032
8112
  /**
8033
8113
  * Normalize a value.
8034
8114
  * If the cell value is a string, this will set it to lowercase and replacing accent letters with plain letters
@@ -10872,7 +10952,7 @@ const DATE = {
10872
10952
  if (_year < 1900) {
10873
10953
  _year += 1900;
10874
10954
  }
10875
- const jsDate = new Date(_year, _month - 1, _day);
10955
+ const jsDate = new DateTime(_year, _month - 1, _day);
10876
10956
  const result = jsDateToRoundNumber(jsDate);
10877
10957
  assert(() => result >= 0, _lt(`The function [[FUNCTION_NAME]] result must be greater than or equal 01/01/1900.`));
10878
10958
  return result;
@@ -10984,7 +11064,7 @@ const EOMONTH = {
10984
11064
  const _months = Math.trunc(toNumber(months));
10985
11065
  const yStart = _startDate.getFullYear();
10986
11066
  const mStart = _startDate.getMonth();
10987
- const jsDate = new Date(yStart, mStart + _months + 1, 0);
11067
+ const jsDate = new DateTime(yStart, mStart + _months + 1, 0);
10988
11068
  return jsDateToRoundNumber(jsDate);
10989
11069
  },
10990
11070
  isExported: true,
@@ -11023,17 +11103,17 @@ const ISOWEEKNUM = {
11023
11103
  // The first week of the year is the week that contains the first
11024
11104
  // Thursday of the year.
11025
11105
  let firstThursday = 1;
11026
- while (new Date(y, 0, firstThursday).getDay() !== 4) {
11106
+ while (new DateTime(y, 0, firstThursday).getDay() !== 4) {
11027
11107
  firstThursday += 1;
11028
11108
  }
11029
- const firstDayOfFirstWeek = new Date(y, 0, firstThursday - 3);
11109
+ const firstDayOfFirstWeek = new DateTime(y, 0, firstThursday - 3);
11030
11110
  // The last week of the year is the week that contains the last Thursday of
11031
11111
  // the year.
11032
11112
  let lastThursday = 31;
11033
- while (new Date(y, 11, lastThursday).getDay() !== 4) {
11113
+ while (new DateTime(y, 11, lastThursday).getDay() !== 4) {
11034
11114
  lastThursday -= 1;
11035
11115
  }
11036
- const lastDayOfLastWeek = new Date(y, 11, lastThursday + 3);
11116
+ const lastDayOfLastWeek = new DateTime(y, 11, lastThursday + 3);
11037
11117
  // B - If our date > lastDayOfLastWeek then it's in the weeks of the year after
11038
11118
  // If our date < firstDayOfFirstWeek then it's in the weeks of the year before
11039
11119
  let offsetYear;
@@ -11059,17 +11139,17 @@ const ISOWEEKNUM = {
11059
11139
  case 1:
11060
11140
  // firstDay is the 1st day of the 1st week of the year after
11061
11141
  // firstDay = lastDayOfLastWeek + 1 Day
11062
- firstDay = new Date(y, 11, lastThursday + 3 + 1);
11142
+ firstDay = new DateTime(y, 11, lastThursday + 3 + 1);
11063
11143
  break;
11064
11144
  case -1:
11065
11145
  // firstDay is the 1st day of the 1st week of the previous year.
11066
11146
  // The first week of the previous year is the week that contains the
11067
11147
  // first Thursday of the previous year.
11068
11148
  let firstThursdayPreviousYear = 1;
11069
- while (new Date(y - 1, 0, firstThursdayPreviousYear).getDay() !== 4) {
11149
+ while (new DateTime(y - 1, 0, firstThursdayPreviousYear).getDay() !== 4) {
11070
11150
  firstThursdayPreviousYear += 1;
11071
11151
  }
11072
- firstDay = new Date(y - 1, 0, firstThursdayPreviousYear - 3);
11152
+ firstDay = new DateTime(y - 1, 0, firstThursdayPreviousYear - 3);
11073
11153
  break;
11074
11154
  }
11075
11155
  const diff = (_date.getTime() - firstDay.getTime()) / MS_PER_DAY;
@@ -11208,8 +11288,8 @@ const NETWORKDAYS_INTL = {
11208
11288
  });
11209
11289
  }
11210
11290
  const invertDate = _startDate.getTime() > _endDate.getTime();
11211
- const stopDate = new Date((invertDate ? _startDate : _endDate).getTime());
11212
- let stepDate = new Date((invertDate ? _endDate : _startDate).getTime());
11291
+ const stopDate = DateTime.fromTimestamp((invertDate ? _startDate : _endDate).getTime());
11292
+ let stepDate = DateTime.fromTimestamp((invertDate ? _endDate : _startDate).getTime());
11213
11293
  const timeStopDate = stopDate.getTime();
11214
11294
  let timeStepDate = stepDate.getTime();
11215
11295
  let netWorkingDay = 0;
@@ -11233,8 +11313,7 @@ const NOW = {
11233
11313
  returns: ["DATE"],
11234
11314
  computeFormat: () => "m/d/yyyy hh:mm:ss",
11235
11315
  compute: function () {
11236
- let today = new Date();
11237
- today.setMilliseconds(0);
11316
+ let today = DateTime.now();
11238
11317
  const delta = today.getTime() - INITIAL_1900_DAY.getTime();
11239
11318
  const time = today.getHours() / 24 + today.getMinutes() / 1440 + today.getSeconds() / 86400;
11240
11319
  return Math.floor(delta / MS_PER_DAY) + time;
@@ -11308,8 +11387,8 @@ const TODAY = {
11308
11387
  returns: ["DATE"],
11309
11388
  computeFormat: () => "m/d/yyyy",
11310
11389
  compute: function () {
11311
- const today = new Date();
11312
- const jsDate = new Date(today.getFullYear(), today.getMonth(), today.getDate());
11390
+ const today = DateTime.now();
11391
+ const jsDate = new DateTime(today.getFullYear(), today.getMonth(), today.getDate());
11313
11392
  return jsDateToRoundNumber(jsDate);
11314
11393
  },
11315
11394
  isExported: true,
@@ -11364,10 +11443,10 @@ const WEEKNUM = {
11364
11443
  }
11365
11444
  const y = _date.getFullYear();
11366
11445
  let dayStart = 1;
11367
- let startDayOfFirstWeek = new Date(y, 0, dayStart);
11446
+ let startDayOfFirstWeek = new DateTime(y, 0, dayStart);
11368
11447
  while (startDayOfFirstWeek.getDay() !== startDayOfWeek) {
11369
11448
  dayStart += 1;
11370
- startDayOfFirstWeek = new Date(y, 0, dayStart);
11449
+ startDayOfFirstWeek = new DateTime(y, 0, dayStart);
11371
11450
  }
11372
11451
  const dif = (_date.getTime() - startDayOfFirstWeek.getTime()) / MS_PER_DAY;
11373
11452
  if (dif < 0) {
@@ -11421,7 +11500,7 @@ const WORKDAY_INTL = {
11421
11500
  timesHoliday.add(holiday.getTime());
11422
11501
  });
11423
11502
  }
11424
- let stepDate = new Date(_startDate.getTime());
11503
+ let stepDate = DateTime.fromTimestamp(_startDate.getTime());
11425
11504
  let timeStepDate = stepDate.getTime();
11426
11505
  const unitDay = Math.sign(_numDays);
11427
11506
  let stepDay = Math.abs(_numDays);
@@ -11487,7 +11566,7 @@ const MONTH_START = {
11487
11566
  const _startDate = toJsDate(date);
11488
11567
  const yStart = _startDate.getFullYear();
11489
11568
  const mStart = _startDate.getMonth();
11490
- const jsDate = new Date(yStart, mStart, 1);
11569
+ const jsDate = new DateTime(yStart, mStart, 1);
11491
11570
  return jsDateToRoundNumber(jsDate);
11492
11571
  },
11493
11572
  };
@@ -11531,7 +11610,7 @@ const QUARTER_START = {
11531
11610
  compute: function (date) {
11532
11611
  const quarter = QUARTER.compute(date);
11533
11612
  const year = YEAR.compute(date);
11534
- const jsDate = new Date(year, (quarter - 1) * 3, 1);
11613
+ const jsDate = new DateTime(year, (quarter - 1) * 3, 1);
11535
11614
  return jsDateToRoundNumber(jsDate);
11536
11615
  },
11537
11616
  };
@@ -11548,7 +11627,7 @@ const QUARTER_END = {
11548
11627
  compute: function (date) {
11549
11628
  const quarter = QUARTER.compute(date);
11550
11629
  const year = YEAR.compute(date);
11551
- const jsDate = new Date(year, quarter * 3, 0);
11630
+ const jsDate = new DateTime(year, quarter * 3, 0);
11552
11631
  return jsDateToRoundNumber(jsDate);
11553
11632
  },
11554
11633
  };
@@ -11564,7 +11643,7 @@ const YEAR_START = {
11564
11643
  computeFormat: () => "m/d/yyyy",
11565
11644
  compute: function (date) {
11566
11645
  const year = YEAR.compute(date);
11567
- const jsDate = new Date(year, 0, 1);
11646
+ const jsDate = new DateTime(year, 0, 1);
11568
11647
  return jsDateToRoundNumber(jsDate);
11569
11648
  },
11570
11649
  };
@@ -11580,7 +11659,7 @@ const YEAR_END = {
11580
11659
  computeFormat: () => "m/d/yyyy",
11581
11660
  compute: function (date) {
11582
11661
  const year = YEAR.compute(date);
11583
- const jsDate = new Date(year + 1, 0, 0);
11662
+ const jsDate = new DateTime(year + 1, 0, 0);
11584
11663
  return jsDateToRoundNumber(jsDate);
11585
11664
  },
11586
11665
  };
@@ -11714,7 +11793,7 @@ function assertDeprecationFactorStrictlyPositive(factor) {
11714
11793
  function assertSettlementLessThanOneYearBeforeMaturity(settlement, maturity) {
11715
11794
  const startDate = toJsDate(settlement);
11716
11795
  const endDate = toJsDate(maturity);
11717
- const startDatePlusOneYear = new Date(startDate);
11796
+ const startDatePlusOneYear = toJsDate(settlement);
11718
11797
  startDatePlusOneYear.setFullYear(startDate.getFullYear() + 1);
11719
11798
  assert(() => endDate.getTime() <= startDatePlusOneYear.getTime(), _lt("The settlement date (%s) must at most one year after the maturity date (%s).", settlement.toString(), maturity.toString()));
11720
11799
  }
@@ -15640,7 +15719,7 @@ const machine = {
15640
15719
  SPACE: goTo(State.RightRef),
15641
15720
  NUMBER: goTo(State.Found),
15642
15721
  REFERENCE: goTo(State.Found, (token) => isSingleCellReference(token.value)),
15643
- SYMBOL: goTo(State.Found, (token) => isColHeader(token.value)),
15722
+ SYMBOL: goTo(State.Found, (token) => isColHeader(token.value) || isRowHeader(token.value)),
15644
15723
  },
15645
15724
  [State.RightColumnRef]: {
15646
15725
  SPACE: goTo(State.RightColumnRef),
@@ -15651,6 +15730,7 @@ const machine = {
15651
15730
  SPACE: goTo(State.RightRowRef),
15652
15731
  NUMBER: goTo(State.Found),
15653
15732
  REFERENCE: goTo(State.Found, (token) => isSingleCellReference(token.value)),
15733
+ SYMBOL: goTo(State.Found, (token) => isRowHeader(token.value)),
15654
15734
  },
15655
15735
  [State.Found]: {},
15656
15736
  };
@@ -16160,6 +16240,10 @@ function setXcToReferenceType(xc, referenceType) {
16160
16240
  return xc.slice(0, indexOfNumber) + "$" + xc.slice(indexOfNumber);
16161
16241
  case "colrow":
16162
16242
  indexOfNumber = xc.search(/[0-9]/);
16243
+ if (indexOfNumber === -1 || indexOfNumber === 0) {
16244
+ // no row number (eg. A) or no column (eg. 1)
16245
+ return "$" + xc;
16246
+ }
16163
16247
  xc = xc.slice(0, indexOfNumber) + "$" + xc.slice(indexOfNumber);
16164
16248
  return "$" + xc;
16165
16249
  case "none":
@@ -16933,10 +17017,10 @@ const arrowMap = {
16933
17017
  function updateSelectionWithArrowKeys(ev, selection) {
16934
17018
  const direction = arrowMap[ev.key];
16935
17019
  if (ev.shiftKey) {
16936
- selection.resizeAnchorZone(direction, ev.ctrlKey ? "end" : 1);
17020
+ selection.resizeAnchorZone(direction, isCtrlKey(ev) ? "end" : 1);
16937
17021
  }
16938
17022
  else {
16939
- selection.moveAnchorCell(direction, ev.ctrlKey ? "end" : 1);
17023
+ selection.moveAnchorCell(direction, isCtrlKey(ev) ? "end" : 1);
16940
17024
  }
16941
17025
  }
16942
17026
 
@@ -22593,7 +22677,10 @@ class GridOverlay extends owl.Component {
22593
22677
  return;
22594
22678
  }
22595
22679
  const [col, row] = this.getCartesianCoordinates(ev);
22596
- this.props.onCellClicked(col, row, { shiftKey: ev.shiftKey, ctrlKey: ev.ctrlKey });
22680
+ this.props.onCellClicked(col, row, {
22681
+ expandZone: ev.shiftKey,
22682
+ addZone: isCtrlKey(ev),
22683
+ });
22597
22684
  }
22598
22685
  onDoubleClick(ev) {
22599
22686
  const [col, row] = this.getCartesianCoordinates(ev);
@@ -22681,6 +22768,7 @@ class AbstractResizer extends owl.Component {
22681
22768
  draggerShadowThickness: 0,
22682
22769
  delta: 0,
22683
22770
  base: 0,
22771
+ position: "before",
22684
22772
  });
22685
22773
  }
22686
22774
  _computeHandleDisplay(ev) {
@@ -22806,11 +22894,13 @@ class AbstractResizer extends owl.Component {
22806
22894
  this.state.draggerLinePosition = dimensions.start;
22807
22895
  this.state.draggerShadowPosition = dimensions.start;
22808
22896
  this.state.base = elementIndex;
22897
+ this.state.position = "before";
22809
22898
  }
22810
22899
  else if (this._getSelectedZoneEnd() < elementIndex) {
22811
22900
  this.state.draggerLinePosition = dimensions.end;
22812
22901
  this.state.draggerShadowPosition = dimensions.end - this.state.draggerShadowThickness;
22813
- this.state.base = elementIndex + 1;
22902
+ this.state.base = elementIndex;
22903
+ this.state.position = "after";
22814
22904
  }
22815
22905
  else {
22816
22906
  this.state.draggerLinePosition = startDimensions.start;
@@ -22834,7 +22924,7 @@ class AbstractResizer extends owl.Component {
22834
22924
  this._increaseSelection(index);
22835
22925
  }
22836
22926
  else {
22837
- this._selectElement(index, ev.ctrlKey);
22927
+ this._selectElement(index, isCtrlKey(ev));
22838
22928
  }
22839
22929
  this.lastSelectedElementIndex = index;
22840
22930
  const mouseMoveSelect = (col, row) => {
@@ -22847,7 +22937,7 @@ class AbstractResizer extends owl.Component {
22847
22937
  const mouseUpSelect = () => {
22848
22938
  this.state.isSelecting = false;
22849
22939
  this.lastSelectedElementIndex = null;
22850
- this.env.model.dispatch(ev.ctrlKey ? "PREPARE_SELECTION_INPUT_EXPANSION" : "STOP_SELECTION_INPUT");
22940
+ this.env.model.dispatch(isCtrlKey(ev) ? "PREPARE_SELECTION_INPUT_EXPANSION" : "STOP_SELECTION_INPUT");
22851
22941
  this._computeGrabDisplay(ev);
22852
22942
  };
22853
22943
  dragAndDropBeyondTheViewport(this.env, mouseMoveSelect, mouseUpSelect);
@@ -22991,13 +23081,14 @@ class ColResizer extends AbstractResizer {
22991
23081
  dimension: "COL",
22992
23082
  base: this.state.base,
22993
23083
  elements,
23084
+ position: this.state.position,
22994
23085
  });
22995
23086
  if (!result.isSuccessful && result.reasons.includes(2 /* CommandResult.WillRemoveExistingMerge */)) {
22996
23087
  this.env.raiseError(MergeErrorMessage);
22997
23088
  }
22998
23089
  }
22999
- _selectElement(index, ctrlKey) {
23000
- this.env.model.selection.selectColumn(index, ctrlKey ? "newAnchor" : "overrideSelection");
23090
+ _selectElement(index, addDistinctHeader) {
23091
+ this.env.model.selection.selectColumn(index, addDistinctHeader ? "newAnchor" : "overrideSelection");
23001
23092
  }
23002
23093
  _increaseSelection(index) {
23003
23094
  this.env.model.selection.selectColumn(index, "updateAnchor");
@@ -23163,13 +23254,14 @@ class RowResizer extends AbstractResizer {
23163
23254
  dimension: "ROW",
23164
23255
  base: this.state.base,
23165
23256
  elements,
23257
+ position: this.state.position,
23166
23258
  });
23167
23259
  if (!result.isSuccessful && result.reasons.includes(2 /* CommandResult.WillRemoveExistingMerge */)) {
23168
23260
  this.env.raiseError(MergeErrorMessage);
23169
23261
  }
23170
23262
  }
23171
- _selectElement(index, ctrlKey) {
23172
- this.env.model.selection.selectRow(index, ctrlKey ? "newAnchor" : "overrideSelection");
23263
+ _selectElement(index, addDistinctHeader) {
23264
+ this.env.model.selection.selectRow(index, addDistinctHeader ? "newAnchor" : "overrideSelection");
23173
23265
  }
23174
23266
  _increaseSelection(index) {
23175
23267
  this.env.model.selection.selectRow(index, "updateAnchor");
@@ -23271,8 +23363,8 @@ function useWheelHandler(handler) {
23271
23363
  return val * (deltaMode === 0 ? 1 : DEFAULT_CELL_HEIGHT);
23272
23364
  }
23273
23365
  const onMouseWheel = (ev) => {
23274
- const deltaX = normalize(ev.shiftKey ? ev.deltaY : ev.deltaX, ev.deltaMode);
23275
- const deltaY = normalize(ev.shiftKey ? ev.deltaX : ev.deltaY, ev.deltaMode);
23366
+ const deltaX = normalize(ev.shiftKey && !isMacOS() ? ev.deltaY : ev.deltaX, ev.deltaMode);
23367
+ const deltaY = normalize(ev.shiftKey && !isMacOS() ? ev.deltaX : ev.deltaY, ev.deltaMode);
23276
23368
  handler(deltaX, deltaY);
23277
23369
  };
23278
23370
  return onMouseWheel;
@@ -23916,8 +24008,8 @@ class Grid extends owl.Component {
23916
24008
  // ---------------------------------------------------------------------------
23917
24009
  // Zone selection with mouse
23918
24010
  // ---------------------------------------------------------------------------
23919
- onCellClicked(col, row, { ctrlKey, shiftKey }) {
23920
- if (ctrlKey) {
24011
+ onCellClicked(col, row, { addZone, expandZone }) {
24012
+ if (addZone) {
23921
24013
  this.env.model.dispatch("PREPARE_SELECTION_INPUT_EXPANSION");
23922
24014
  }
23923
24015
  if (this.env.model.getters.hasOpenedPopover()) {
@@ -23926,10 +24018,10 @@ class Grid extends owl.Component {
23926
24018
  if (this.env.model.getters.getEditionMode() === "editing") {
23927
24019
  this.env.model.dispatch("STOP_EDITION");
23928
24020
  }
23929
- if (shiftKey) {
24021
+ if (expandZone) {
23930
24022
  this.env.model.selection.setAnchorCorner(col, row);
23931
24023
  }
23932
- else if (ctrlKey) {
24024
+ else if (addZone) {
23933
24025
  this.env.model.selection.addCellToSelection(col, row);
23934
24026
  }
23935
24027
  else {
@@ -23991,9 +24083,7 @@ class Grid extends owl.Component {
23991
24083
  return;
23992
24084
  }
23993
24085
  let keyDownString = "";
23994
- if (ev.ctrlKey)
23995
- keyDownString += "CTRL+";
23996
- if (ev.metaKey)
24086
+ if (isCtrlKey(ev))
23997
24087
  keyDownString += "CTRL+";
23998
24088
  if (ev.altKey)
23999
24089
  keyDownString += "ALT+";
@@ -27945,6 +28035,14 @@ class BordersPlugin extends CorePlugin {
27945
28035
  // ---------------------------------------------------------------------------
27946
28036
  // Command Handling
27947
28037
  // ---------------------------------------------------------------------------
28038
+ allowDispatch(cmd) {
28039
+ switch (cmd.type) {
28040
+ case "SET_BORDER":
28041
+ return this.checkBordersUnchanged(cmd);
28042
+ default:
28043
+ return 0 /* CommandResult.Success */;
28044
+ }
28045
+ }
27948
28046
  handle(cmd) {
27949
28047
  switch (cmd.type) {
27950
28048
  case "ADD_MERGE":
@@ -28340,6 +28438,15 @@ class BordersPlugin extends CorePlugin {
28340
28438
  this.setBorders(sheetId, [{ ...zone, left: right }], "right");
28341
28439
  }
28342
28440
  }
28441
+ checkBordersUnchanged(cmd) {
28442
+ var _a, _b, _c, _d;
28443
+ const currentBorder = this.getCellBorder(cmd);
28444
+ 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);
28445
+ if ((!currentBorder && areAllNewBordersUndefined) || deepEquals(currentBorder, cmd.border)) {
28446
+ return 88 /* CommandResult.NoChanges */;
28447
+ }
28448
+ return 0 /* CommandResult.Success */;
28449
+ }
28343
28450
  // ---------------------------------------------------------------------------
28344
28451
  // Import/Export
28345
28452
  // ---------------------------------------------------------------------------
@@ -28375,7 +28482,7 @@ class BordersPlugin extends CorePlugin {
28375
28482
  */
28376
28483
  function getBorderId(border) {
28377
28484
  for (let [key, value] of Object.entries(borders)) {
28378
- if (stringify(value) === stringify(border)) {
28485
+ if (deepEquals(value, border)) {
28379
28486
  return parseInt(key, 10);
28380
28487
  }
28381
28488
  }
@@ -28443,8 +28550,9 @@ class CellPlugin extends CorePlugin {
28443
28550
  allowDispatch(cmd) {
28444
28551
  switch (cmd.type) {
28445
28552
  case "UPDATE_CELL":
28553
+ return this.checkCellOutOfSheet(cmd);
28446
28554
  case "CLEAR_CELL":
28447
- return this.checkCellOutOfSheet(cmd.sheetId, cmd.col, cmd.row);
28555
+ return this.checkValidations(cmd, this.chainValidations(this.checkCellOutOfSheet, this.checkUselessClearCell));
28448
28556
  default:
28449
28557
  return 0 /* CommandResult.Success */;
28450
28558
  }
@@ -28603,14 +28711,9 @@ class CellPlugin extends CorePlugin {
28603
28711
  */
28604
28712
  getCellById(cellId) {
28605
28713
  // this must be as fast as possible
28606
- for (const sheetId in this.cells) {
28607
- const sheet = this.cells[sheetId];
28608
- const cell = sheet[cellId];
28609
- if (cell) {
28610
- return cell;
28611
- }
28612
- }
28613
- return undefined;
28714
+ const position = this.getters.getCellPosition(cellId);
28715
+ const sheet = this.cells[position.sheetId];
28716
+ return sheet[cellId];
28614
28717
  }
28615
28718
  /*
28616
28719
  * Reconstructs the original formula string based on a normalized form and its dependencies
@@ -28851,13 +28954,23 @@ class CellPlugin extends CorePlugin {
28851
28954
  dependencies: [],
28852
28955
  };
28853
28956
  }
28854
- checkCellOutOfSheet(sheetId, col, row) {
28957
+ checkCellOutOfSheet(cmd) {
28958
+ const { sheetId, col, row } = cmd;
28855
28959
  const sheet = this.getters.tryGetSheet(sheetId);
28856
28960
  if (!sheet)
28857
28961
  return 27 /* CommandResult.InvalidSheetId */;
28858
28962
  const sheetZone = this.getters.getSheetZone(sheetId);
28859
28963
  return isInside(col, row, sheetZone) ? 0 /* CommandResult.Success */ : 18 /* CommandResult.TargetOutOfSheet */;
28860
28964
  }
28965
+ checkUselessClearCell(cmd) {
28966
+ const cell = this.getters.getCell(cmd);
28967
+ if (!cell)
28968
+ return 88 /* CommandResult.NoChanges */;
28969
+ if (!cell.content && !cell.style && !cell.format) {
28970
+ return 88 /* CommandResult.NoChanges */;
28971
+ }
28972
+ return 0 /* CommandResult.Success */;
28973
+ }
28861
28974
  }
28862
28975
  CellPlugin.getters = [
28863
28976
  "zoneToXC",
@@ -31318,7 +31431,7 @@ class SheetPlugin extends CorePlugin {
31318
31431
  const elements = cmd.dimension === "COL"
31319
31432
  ? this.getNumberCols(cmd.sheetId)
31320
31433
  : this.getNumberRows(cmd.sheetId);
31321
- if (cmd.base < 0 || cmd.base > elements) {
31434
+ if (cmd.base < 0 || cmd.base >= elements) {
31322
31435
  return 86 /* CommandResult.InvalidHeaderIndex */;
31323
31436
  }
31324
31437
  else if (cmd.quantity <= 0) {
@@ -31918,10 +32031,13 @@ class SheetPlugin extends CorePlugin {
31918
32031
  // begin with the end.
31919
32032
  rows.sort((a, b) => b - a);
31920
32033
  for (let group of groupConsecutive(rows)) {
32034
+ // indexes are sorted in the descending order
32035
+ const from = group[group.length - 1];
32036
+ const to = group[0];
31921
32037
  // Move the cells.
31922
- this.moveCellOnRowsDeletion(sheet, group[group.length - 1], group[0]);
31923
- // Effectively delete the element and recompute the left-right/top-bottom.
31924
- group.map((row) => this.updateRowsStructureOnDeletion(row, sheet));
32038
+ this.moveCellOnRowsDeletion(sheet, from, to);
32039
+ // Effectively delete the rows
32040
+ this.updateRowsStructureOnDeletion(sheet, from, to);
31925
32041
  }
31926
32042
  const count = rows.filter((row) => row < sheet.panes.ySplit).length;
31927
32043
  if (count) {
@@ -31943,8 +32059,6 @@ class SheetPlugin extends CorePlugin {
31943
32059
  this.addEmptyRows(sheet, quantity);
31944
32060
  // Move the cells.
31945
32061
  this.moveCellsOnAddition(sheet, index, quantity, "rows");
31946
- // Recompute the left-right/top-bottom.
31947
- this.updateRowsStructureOnAddition(sheet, row, quantity);
31948
32062
  if (index < sheet.panes.ySplit) {
31949
32063
  this.setPaneDivisions(sheet.id, sheet.panes.ySplit + quantity, "ROW");
31950
32064
  }
@@ -32045,35 +32159,20 @@ class SheetPlugin extends CorePlugin {
32045
32159
  }
32046
32160
  }
32047
32161
  }
32048
- updateRowsStructureOnDeletion(index, sheet) {
32162
+ updateRowsStructureOnDeletion(sheet, deleteFromRow, deleteToRow) {
32049
32163
  const rows = [];
32050
- const cellsQueue = sheet.rows.map((row) => row.cells);
32164
+ const cellsQueue = sheet.rows.map((row) => row.cells).reverse();
32051
32165
  for (let i in sheet.rows) {
32052
- if (Number(i) === index) {
32166
+ const row = Number(i);
32167
+ if (row >= deleteFromRow && row <= deleteToRow) {
32053
32168
  continue;
32054
32169
  }
32055
32170
  rows.push({
32056
- cells: cellsQueue.shift(),
32171
+ cells: cellsQueue.pop(),
32057
32172
  });
32058
32173
  }
32059
32174
  this.history.update("sheets", sheet.id, "rows", rows);
32060
32175
  }
32061
- /**
32062
- * Update the rows of the sheet after an addition:
32063
- * - Rename the rows
32064
- *
32065
- * @param sheet Sheet on which the deletion occurs
32066
- * @param addedRow Index of the added row
32067
- * @param rowsToAdd Number of the rows to add
32068
- */
32069
- updateRowsStructureOnAddition(sheet, addedRow, rowsToAdd) {
32070
- const rows = [];
32071
- const cellsQueue = sheet.rows.map((row) => row.cells);
32072
- sheet.rows.forEach(() => rows.push({
32073
- cells: cellsQueue.shift(),
32074
- }));
32075
- this.history.update("sheets", sheet.id, "rows", rows);
32076
- }
32077
32176
  /**
32078
32177
  * Add empty rows at the end of the rows
32079
32178
  *
@@ -32355,13 +32454,18 @@ class EvaluationPlugin extends UIPlugin {
32355
32454
  // Getters
32356
32455
  // ---------------------------------------------------------------------------
32357
32456
  evaluateFormula(formulaString, sheetId = this.getters.getActiveSheetId()) {
32358
- const compiledFormula = compile(formulaString);
32359
- const params = this.getCompilationParameters((cell) => this.getEvaluatedCell(this.getters.getCellPosition(cell.id)));
32360
- const ranges = [];
32361
- for (let xc of compiledFormula.dependencies) {
32362
- ranges.push(this.getters.getRangeFromSheetXC(sheetId, xc));
32457
+ try {
32458
+ const compiledFormula = compile(formulaString);
32459
+ const params = this.getCompilationParameters((cell) => this.getEvaluatedCell(this.getters.getCellPosition(cell.id)));
32460
+ const ranges = [];
32461
+ for (let xc of compiledFormula.dependencies) {
32462
+ ranges.push(this.getters.getRangeFromSheetXC(sheetId, xc));
32463
+ }
32464
+ return compiledFormula.execute(ranges, ...params).value;
32465
+ }
32466
+ catch (error) {
32467
+ return error instanceof EvaluationError ? error.errorType : CellErrorType.GenericError;
32363
32468
  }
32364
- return compiledFormula.execute(ranges, ...params).value;
32365
32469
  }
32366
32470
  /**
32367
32471
  * Return the value of each cell in the range as they are displayed in the grid.
@@ -32530,6 +32634,7 @@ class EvaluationPlugin extends UIPlugin {
32530
32634
  }
32531
32635
  return evaluatedCell;
32532
32636
  };
32637
+ const rangeCache = {};
32533
32638
  /**
32534
32639
  * Return the values of the cell(s) used in reference, but always in the format of a range even
32535
32640
  * if a single cell is referenced. It is a list of col values. This is useful for the formulas that describe parameters as
@@ -32546,21 +32651,26 @@ class EvaluationPlugin extends UIPlugin {
32546
32651
  // Performance issue: Avoid fetching data on positions that are out of the spreadsheet
32547
32652
  // e.g. A1:ZZZ9999 in a sheet with 10 cols and 10 rows should ignore everything past J10 and return a 10x10 array
32548
32653
  const sheetZone = getters.getSheetZone(sheetId);
32549
- const result = [];
32550
32654
  const zone = intersection(range.zone, sheetZone);
32551
32655
  if (!zone) {
32552
- result.push([]);
32553
- return result;
32656
+ return [[]];
32554
32657
  }
32658
+ const { top, left, bottom, right } = zone;
32659
+ const cacheKey = `${sheetId}-${top}-${left}-${bottom}-${right}`;
32660
+ if (cacheKey in rangeCache) {
32661
+ return rangeCache[cacheKey];
32662
+ }
32663
+ const result = new Array(right - left + 1);
32555
32664
  // Performance issue: nested loop is faster than a map here
32556
- for (let col = zone.left; col <= zone.right; col++) {
32557
- const rowValues = [];
32558
- for (let row = zone.top; row <= zone.bottom; row++) {
32665
+ for (let col = left; col <= right; col++) {
32666
+ const rowValues = new Array(bottom - top + 1);
32667
+ for (let row = top; row <= bottom; row++) {
32559
32668
  const cell = evalContext.getters.getCell({ sheetId: range.sheetId, col, row });
32560
- rowValues.push(cell ? getEvaluatedCell(cell) : undefined);
32669
+ rowValues[row - top] = cell ? getEvaluatedCell(cell) : undefined;
32561
32670
  }
32562
- result.push(rowValues);
32671
+ result[col - left] = rowValues;
32563
32672
  }
32673
+ rangeCache[cacheKey] = result;
32564
32674
  return result;
32565
32675
  }
32566
32676
  /**
@@ -32847,39 +32957,34 @@ class EvaluationConditionalFormatPlugin extends UIPlugin {
32847
32957
  this.computedIcons[sheetId] = {};
32848
32958
  const computedStyle = this.computedStyles[sheetId];
32849
32959
  for (let cf of this.getters.getConditionalFormats(sheetId).reverse()) {
32850
- try {
32851
- switch (cf.rule.type) {
32852
- case "ColorScaleRule":
32853
- for (let range of cf.ranges) {
32854
- this.applyColorScale(range, cf.rule);
32855
- }
32856
- break;
32857
- case "IconSetRule":
32858
- for (let range of cf.ranges) {
32859
- this.applyIcon(range, cf.rule);
32860
- }
32861
- break;
32862
- default:
32863
- for (let ref of cf.ranges) {
32864
- const zone = this.getters.getRangeFromSheetXC(sheetId, ref).zone;
32865
- for (let row = zone.top; row <= zone.bottom; row++) {
32866
- for (let col = zone.left; col <= zone.right; col++) {
32867
- const pr = this.rulePredicate[cf.rule.type];
32868
- let cell = this.getters.getEvaluatedCell({ sheetId, col, row });
32869
- if (pr && pr(cell, cf.rule)) {
32870
- if (!computedStyle[col])
32871
- computedStyle[col] = [];
32872
- // we must combine all the properties of all the CF rules applied to the given cell
32873
- computedStyle[col][row] = Object.assign(((_a = computedStyle[col]) === null || _a === void 0 ? void 0 : _a[row]) || {}, cf.rule.style);
32874
- }
32960
+ switch (cf.rule.type) {
32961
+ case "ColorScaleRule":
32962
+ for (let range of cf.ranges) {
32963
+ this.applyColorScale(range, cf.rule);
32964
+ }
32965
+ break;
32966
+ case "IconSetRule":
32967
+ for (let range of cf.ranges) {
32968
+ this.applyIcon(range, cf.rule);
32969
+ }
32970
+ break;
32971
+ default:
32972
+ for (let ref of cf.ranges) {
32973
+ const zone = this.getters.getRangeFromSheetXC(sheetId, ref).zone;
32974
+ for (let row = zone.top; row <= zone.bottom; row++) {
32975
+ for (let col = zone.left; col <= zone.right; col++) {
32976
+ const pr = this.rulePredicate[cf.rule.type];
32977
+ let cell = this.getters.getEvaluatedCell({ sheetId, col, row });
32978
+ if (pr && pr(cell, cf.rule)) {
32979
+ if (!computedStyle[col])
32980
+ computedStyle[col] = [];
32981
+ // we must combine all the properties of all the CF rules applied to the given cell
32982
+ computedStyle[col][row] = Object.assign(((_a = computedStyle[col]) === null || _a === void 0 ? void 0 : _a[row]) || {}, cf.rule.style);
32875
32983
  }
32876
32984
  }
32877
32985
  }
32878
- break;
32879
- }
32880
- }
32881
- catch (_) {
32882
- // we don't care about the errors within the evaluation of a rule
32986
+ }
32987
+ break;
32883
32988
  }
32884
32989
  }
32885
32990
  }
@@ -32904,7 +33009,7 @@ class EvaluationConditionalFormatPlugin extends UIPlugin {
32904
33009
  return percentile(rangeValues, Number(threshold.value) / 100, true);
32905
33010
  case "formula":
32906
33011
  const value = threshold.value && this.getters.evaluateFormula(threshold.value);
32907
- return !(value instanceof Promise) ? value : null;
33012
+ return typeof value === "number" ? value : null;
32908
33013
  default:
32909
33014
  return null;
32910
33015
  }
@@ -34466,24 +34571,27 @@ class AutofillPlugin extends UIPlugin {
34466
34571
  * autofiller
34467
34572
  */
34468
34573
  autofillAuto() {
34574
+ var _a, _b;
34469
34575
  const zone = this.getters.getSelectedZone();
34470
34576
  const sheetId = this.getters.getActiveSheetId();
34471
34577
  let col = zone.left;
34472
34578
  let row = zone.bottom;
34473
34579
  if (col > 0) {
34474
- let left = this.getters.getEvaluatedCell({ sheetId, col: col - 1, row });
34475
- while (left.type !== CellValueType.empty) {
34580
+ let leftPosition = { sheetId, col: col - 1, row };
34581
+ while (this.getters.getEvaluatedCell(leftPosition).type !== CellValueType.empty ||
34582
+ ((_a = this.getters.getCell(leftPosition)) === null || _a === void 0 ? void 0 : _a.content)) {
34476
34583
  row += 1;
34477
- left = this.getters.getEvaluatedCell({ sheetId, col: col - 1, row });
34584
+ leftPosition = { sheetId, col: col - 1, row };
34478
34585
  }
34479
34586
  }
34480
34587
  if (row === zone.bottom) {
34481
34588
  col = zone.right;
34482
34589
  if (col <= this.getters.getNumberCols(sheetId)) {
34483
- let right = this.getters.getEvaluatedCell({ sheetId, col: col + 1, row });
34484
- while (right.type !== CellValueType.empty) {
34590
+ let rightPosition = { sheetId, col: col + 1, row };
34591
+ while (this.getters.getEvaluatedCell(rightPosition).type !== CellValueType.empty ||
34592
+ ((_b = this.getters.getCell(rightPosition)) === null || _b === void 0 ? void 0 : _b.content)) {
34485
34593
  row += 1;
34486
- right = this.getters.getEvaluatedCell({ sheetId, col: col + 1, row });
34594
+ rightPosition = { sheetId, col: col + 1, row };
34487
34595
  }
34488
34596
  }
34489
34597
  }
@@ -39215,7 +39323,7 @@ class GridSelectionPlugin extends UIPlugin {
39215
39323
  sheetId: cmd.sheetId,
39216
39324
  base: cmd.base,
39217
39325
  quantity: thickness,
39218
- position: "before",
39326
+ position: cmd.position,
39219
39327
  });
39220
39328
  const isCol = cmd.dimension === "COL";
39221
39329
  const start = cmd.elements[0];
@@ -39232,12 +39340,13 @@ class GridSelectionPlugin extends UIPlugin {
39232
39340
  },
39233
39341
  ];
39234
39342
  const state = new ClipboardCellsState(target, "CUT", this.getters, this.dispatch, this.selection);
39343
+ const base = isBasedBefore ? cmd.base : cmd.base + 1;
39235
39344
  const pasteTarget = [
39236
39345
  {
39237
- left: isCol ? cmd.base : 0,
39238
- right: isCol ? cmd.base + thickness - 1 : this.getters.getNumberCols(cmd.sheetId) - 1,
39239
- top: !isCol ? cmd.base : 0,
39240
- bottom: !isCol ? cmd.base + thickness - 1 : this.getters.getNumberRows(cmd.sheetId) - 1,
39346
+ left: isCol ? base : 0,
39347
+ right: isCol ? base + thickness - 1 : this.getters.getNumberCols(cmd.sheetId) - 1,
39348
+ top: !isCol ? base : 0,
39349
+ bottom: !isCol ? base + thickness - 1 : this.getters.getNumberRows(cmd.sheetId) - 1,
39241
39350
  },
39242
39351
  ];
39243
39352
  state.paste(pasteTarget, { selectTarget: true });
@@ -39274,6 +39383,11 @@ class GridSelectionPlugin extends UIPlugin {
39274
39383
  doesElementsHaveCommonMerges(id, cmd.base - 1, cmd.base)) {
39275
39384
  return 2 /* CommandResult.WillRemoveExistingMerge */;
39276
39385
  }
39386
+ const headers = [cmd.base, ...cmd.elements];
39387
+ const maxHeaderValue = isCol ? this.getters.getNumberCols(id) : this.getters.getNumberRows(id);
39388
+ if (headers.some((h) => h < 0 || h >= maxHeaderValue)) {
39389
+ return 86 /* CommandResult.InvalidHeaderIndex */;
39390
+ }
39277
39391
  return 0 /* CommandResult.Success */;
39278
39392
  }
39279
39393
  //-------------------------------------------
@@ -40827,7 +40941,7 @@ class Spreadsheet extends owl.Component {
40827
40941
  }
40828
40942
  onKeydown(ev) {
40829
40943
  let keyDownString = "";
40830
- if (ev.ctrlKey || ev.metaKey) {
40944
+ if (isCtrlKey(ev)) {
40831
40945
  keyDownString += "CTRL+";
40832
40946
  }
40833
40947
  keyDownString += ev.key.toUpperCase();
@@ -41674,9 +41788,9 @@ function revertChanges(revisions) {
41674
41788
  * Apply the changes of the given HistoryChange to the state
41675
41789
  */
41676
41790
  function applyChange(change, target) {
41677
- let val = change.root;
41678
- let key = change.path[change.path.length - 1];
41679
- for (let pathIndex = 0; pathIndex < change.path.slice(0, -1).length; pathIndex++) {
41791
+ let val = change.path[0];
41792
+ const key = change.path[change.path.length - 1];
41793
+ for (let pathIndex = 1; pathIndex < change.path.slice(0, -1).length; pathIndex++) {
41680
41794
  const p = change.path[pathIndex];
41681
41795
  if (val[p] === undefined) {
41682
41796
  const nextPath = change.path[pathIndex + 1];
@@ -42431,13 +42545,14 @@ class StateObserver {
42431
42545
  }
42432
42546
  addChange(...args) {
42433
42547
  const val = args.pop();
42434
- const [root, ...path] = args;
42548
+ const root = args[0];
42435
42549
  let value = root;
42436
- let key = path[path.length - 1];
42437
- for (let pathIndex = 0; pathIndex <= path.length - 2; pathIndex++) {
42438
- const p = path[pathIndex];
42550
+ let key = args[args.length - 1];
42551
+ const pathLength = args.length - 2;
42552
+ for (let pathIndex = 1; pathIndex <= pathLength; pathIndex++) {
42553
+ const p = args[pathIndex];
42439
42554
  if (value[p] === undefined) {
42440
- const nextPath = path[pathIndex + 1];
42555
+ const nextPath = args[pathIndex + 1];
42441
42556
  value[p] = createEmptyStructure(nextPath);
42442
42557
  }
42443
42558
  value = value[p];
@@ -42446,8 +42561,7 @@ class StateObserver {
42446
42561
  return;
42447
42562
  }
42448
42563
  this.changes.push({
42449
- root,
42450
- path,
42564
+ path: args,
42451
42565
  before: value[key],
42452
42566
  after: val,
42453
42567
  });
@@ -44221,7 +44335,10 @@ class Model extends EventBus {
44221
44335
  */
44222
44336
  checkDispatchAllowed(command) {
44223
44337
  const results = this.handlers.map((handler) => handler.allowDispatch(command));
44224
- return new DispatchResult(results.flat());
44338
+ if (results.some((r) => r !== 0 /* CommandResult.Success */)) {
44339
+ return new DispatchResult(results.flat());
44340
+ }
44341
+ return DispatchResult.Success;
44225
44342
  }
44226
44343
  finalize() {
44227
44344
  this.status = 3 /* Status.Finalizing */;
@@ -44449,6 +44566,6 @@ exports.setTranslationMethod = setTranslationMethod;
44449
44566
  exports.tokenize = tokenize;
44450
44567
 
44451
44568
 
44452
- __info__.version = '16.1.27';
44453
- __info__.date = '2023-11-16T11:57:02.419Z';
44454
- __info__.hash = '81f7214';
44569
+ __info__.version = '16.1.31';
44570
+ __info__.date = '2024-01-12T13:42:11.001Z';
44571
+ __info__.hash = 'f51befa';