@odoo/o-spreadsheet 16.1.28 → 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
  }
@@ -1343,8 +1404,10 @@ function isSameColor(color1, color2) {
1343
1404
  const cellReference = new RegExp(/\$?([A-Z]{1,3})\$?([0-9]{1,7})/, "i");
1344
1405
  // Same as above, but matches the exact string (nothing before or after)
1345
1406
  const singleCellReference = new RegExp(/^\$?([A-Z]{1,3})\$?([0-9]{1,7})$/, "i");
1346
- /** Reference of a column header (eg. A, AB) */
1347
- 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");
1348
1411
  /** Reference of a column (eg. A, $CA, Sheet1!B) */
1349
1412
  const colReference = new RegExp(/^\s*('.+'!|[^']+!)?\$?([A-Z]{1,3})$/, "i");
1350
1413
  /** Reference of a row (eg. 1, 59, Sheet1!9) */
@@ -1374,6 +1437,9 @@ function isRowReference(xc) {
1374
1437
  function isColHeader(str) {
1375
1438
  return colHeader.test(str);
1376
1439
  }
1440
+ function isRowHeader(str) {
1441
+ return rowHeader.test(str);
1442
+ }
1377
1443
  /**
1378
1444
  * Return true if the given xc is the reference of a single cell,
1379
1445
  * without any specified sheet (e.g. A1)
@@ -1872,7 +1938,7 @@ function formatJSTime(jsDate, format) {
1872
1938
  .map((p) => {
1873
1939
  switch (p) {
1874
1940
  case "hhhh":
1875
- 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));
1876
1942
  return helapsedHours.toString();
1877
1943
  case "hh":
1878
1944
  return hours.toString().padStart(2, "0");
@@ -2163,18 +2229,10 @@ class RangeImpl {
2163
2229
  if (isFullCol) {
2164
2230
  parts[0].rowFixed = parts[0].rowFixed || parts[1].rowFixed;
2165
2231
  parts[1].rowFixed = parts[0].rowFixed || parts[1].rowFixed;
2166
- if (zone.left === zone.right) {
2167
- parts[0].colFixed = parts[0].colFixed || parts[1].colFixed;
2168
- parts[1].colFixed = parts[0].colFixed || parts[1].colFixed;
2169
- }
2170
2232
  }
2171
2233
  if (isFullRow) {
2172
2234
  parts[0].colFixed = parts[0].colFixed || parts[1].colFixed;
2173
2235
  parts[1].colFixed = parts[0].colFixed || parts[1].colFixed;
2174
- if (zone.top === zone.bottom) {
2175
- parts[0].rowFixed = parts[0].rowFixed || parts[1].rowFixed;
2176
- parts[1].rowFixed = parts[0].rowFixed || parts[1].rowFixed;
2177
- }
2178
2236
  }
2179
2237
  return parts;
2180
2238
  }
@@ -5701,6 +5759,7 @@ function getMenuDescription(node) {
5701
5759
  return node.description ? node.description : "";
5702
5760
  }
5703
5761
 
5762
+ const macRegex = /Mac/i;
5704
5763
  /**
5705
5764
  * Return true if the event was triggered from
5706
5765
  * a child element.
@@ -5718,6 +5777,17 @@ function gridOverlayPosition() {
5718
5777
  }
5719
5778
  function getOpenedMenus() {
5720
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;
5721
5791
  }
5722
5792
 
5723
5793
  /**
@@ -10878,7 +10948,7 @@ const DATE = {
10878
10948
  if (_year < 1900) {
10879
10949
  _year += 1900;
10880
10950
  }
10881
- const jsDate = new Date(_year, _month - 1, _day);
10951
+ const jsDate = new DateTime(_year, _month - 1, _day);
10882
10952
  const result = jsDateToRoundNumber(jsDate);
10883
10953
  assert(() => result >= 0, _lt(`The function [[FUNCTION_NAME]] result must be greater than or equal 01/01/1900.`));
10884
10954
  return result;
@@ -10990,7 +11060,7 @@ const EOMONTH = {
10990
11060
  const _months = Math.trunc(toNumber(months));
10991
11061
  const yStart = _startDate.getFullYear();
10992
11062
  const mStart = _startDate.getMonth();
10993
- const jsDate = new Date(yStart, mStart + _months + 1, 0);
11063
+ const jsDate = new DateTime(yStart, mStart + _months + 1, 0);
10994
11064
  return jsDateToRoundNumber(jsDate);
10995
11065
  },
10996
11066
  isExported: true,
@@ -11029,17 +11099,17 @@ const ISOWEEKNUM = {
11029
11099
  // The first week of the year is the week that contains the first
11030
11100
  // Thursday of the year.
11031
11101
  let firstThursday = 1;
11032
- while (new Date(y, 0, firstThursday).getDay() !== 4) {
11102
+ while (new DateTime(y, 0, firstThursday).getDay() !== 4) {
11033
11103
  firstThursday += 1;
11034
11104
  }
11035
- const firstDayOfFirstWeek = new Date(y, 0, firstThursday - 3);
11105
+ const firstDayOfFirstWeek = new DateTime(y, 0, firstThursday - 3);
11036
11106
  // The last week of the year is the week that contains the last Thursday of
11037
11107
  // the year.
11038
11108
  let lastThursday = 31;
11039
- while (new Date(y, 11, lastThursday).getDay() !== 4) {
11109
+ while (new DateTime(y, 11, lastThursday).getDay() !== 4) {
11040
11110
  lastThursday -= 1;
11041
11111
  }
11042
- const lastDayOfLastWeek = new Date(y, 11, lastThursday + 3);
11112
+ const lastDayOfLastWeek = new DateTime(y, 11, lastThursday + 3);
11043
11113
  // B - If our date > lastDayOfLastWeek then it's in the weeks of the year after
11044
11114
  // If our date < firstDayOfFirstWeek then it's in the weeks of the year before
11045
11115
  let offsetYear;
@@ -11065,17 +11135,17 @@ const ISOWEEKNUM = {
11065
11135
  case 1:
11066
11136
  // firstDay is the 1st day of the 1st week of the year after
11067
11137
  // firstDay = lastDayOfLastWeek + 1 Day
11068
- firstDay = new Date(y, 11, lastThursday + 3 + 1);
11138
+ firstDay = new DateTime(y, 11, lastThursday + 3 + 1);
11069
11139
  break;
11070
11140
  case -1:
11071
11141
  // firstDay is the 1st day of the 1st week of the previous year.
11072
11142
  // The first week of the previous year is the week that contains the
11073
11143
  // first Thursday of the previous year.
11074
11144
  let firstThursdayPreviousYear = 1;
11075
- while (new Date(y - 1, 0, firstThursdayPreviousYear).getDay() !== 4) {
11145
+ while (new DateTime(y - 1, 0, firstThursdayPreviousYear).getDay() !== 4) {
11076
11146
  firstThursdayPreviousYear += 1;
11077
11147
  }
11078
- firstDay = new Date(y - 1, 0, firstThursdayPreviousYear - 3);
11148
+ firstDay = new DateTime(y - 1, 0, firstThursdayPreviousYear - 3);
11079
11149
  break;
11080
11150
  }
11081
11151
  const diff = (_date.getTime() - firstDay.getTime()) / MS_PER_DAY;
@@ -11214,8 +11284,8 @@ const NETWORKDAYS_INTL = {
11214
11284
  });
11215
11285
  }
11216
11286
  const invertDate = _startDate.getTime() > _endDate.getTime();
11217
- const stopDate = new Date((invertDate ? _startDate : _endDate).getTime());
11218
- 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());
11219
11289
  const timeStopDate = stopDate.getTime();
11220
11290
  let timeStepDate = stepDate.getTime();
11221
11291
  let netWorkingDay = 0;
@@ -11239,8 +11309,7 @@ const NOW = {
11239
11309
  returns: ["DATE"],
11240
11310
  computeFormat: () => "m/d/yyyy hh:mm:ss",
11241
11311
  compute: function () {
11242
- let today = new Date();
11243
- today.setMilliseconds(0);
11312
+ let today = DateTime.now();
11244
11313
  const delta = today.getTime() - INITIAL_1900_DAY.getTime();
11245
11314
  const time = today.getHours() / 24 + today.getMinutes() / 1440 + today.getSeconds() / 86400;
11246
11315
  return Math.floor(delta / MS_PER_DAY) + time;
@@ -11314,8 +11383,8 @@ const TODAY = {
11314
11383
  returns: ["DATE"],
11315
11384
  computeFormat: () => "m/d/yyyy",
11316
11385
  compute: function () {
11317
- const today = new Date();
11318
- 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());
11319
11388
  return jsDateToRoundNumber(jsDate);
11320
11389
  },
11321
11390
  isExported: true,
@@ -11370,10 +11439,10 @@ const WEEKNUM = {
11370
11439
  }
11371
11440
  const y = _date.getFullYear();
11372
11441
  let dayStart = 1;
11373
- let startDayOfFirstWeek = new Date(y, 0, dayStart);
11442
+ let startDayOfFirstWeek = new DateTime(y, 0, dayStart);
11374
11443
  while (startDayOfFirstWeek.getDay() !== startDayOfWeek) {
11375
11444
  dayStart += 1;
11376
- startDayOfFirstWeek = new Date(y, 0, dayStart);
11445
+ startDayOfFirstWeek = new DateTime(y, 0, dayStart);
11377
11446
  }
11378
11447
  const dif = (_date.getTime() - startDayOfFirstWeek.getTime()) / MS_PER_DAY;
11379
11448
  if (dif < 0) {
@@ -11427,7 +11496,7 @@ const WORKDAY_INTL = {
11427
11496
  timesHoliday.add(holiday.getTime());
11428
11497
  });
11429
11498
  }
11430
- let stepDate = new Date(_startDate.getTime());
11499
+ let stepDate = DateTime.fromTimestamp(_startDate.getTime());
11431
11500
  let timeStepDate = stepDate.getTime();
11432
11501
  const unitDay = Math.sign(_numDays);
11433
11502
  let stepDay = Math.abs(_numDays);
@@ -11493,7 +11562,7 @@ const MONTH_START = {
11493
11562
  const _startDate = toJsDate(date);
11494
11563
  const yStart = _startDate.getFullYear();
11495
11564
  const mStart = _startDate.getMonth();
11496
- const jsDate = new Date(yStart, mStart, 1);
11565
+ const jsDate = new DateTime(yStart, mStart, 1);
11497
11566
  return jsDateToRoundNumber(jsDate);
11498
11567
  },
11499
11568
  };
@@ -11537,7 +11606,7 @@ const QUARTER_START = {
11537
11606
  compute: function (date) {
11538
11607
  const quarter = QUARTER.compute(date);
11539
11608
  const year = YEAR.compute(date);
11540
- const jsDate = new Date(year, (quarter - 1) * 3, 1);
11609
+ const jsDate = new DateTime(year, (quarter - 1) * 3, 1);
11541
11610
  return jsDateToRoundNumber(jsDate);
11542
11611
  },
11543
11612
  };
@@ -11554,7 +11623,7 @@ const QUARTER_END = {
11554
11623
  compute: function (date) {
11555
11624
  const quarter = QUARTER.compute(date);
11556
11625
  const year = YEAR.compute(date);
11557
- const jsDate = new Date(year, quarter * 3, 0);
11626
+ const jsDate = new DateTime(year, quarter * 3, 0);
11558
11627
  return jsDateToRoundNumber(jsDate);
11559
11628
  },
11560
11629
  };
@@ -11570,7 +11639,7 @@ const YEAR_START = {
11570
11639
  computeFormat: () => "m/d/yyyy",
11571
11640
  compute: function (date) {
11572
11641
  const year = YEAR.compute(date);
11573
- const jsDate = new Date(year, 0, 1);
11642
+ const jsDate = new DateTime(year, 0, 1);
11574
11643
  return jsDateToRoundNumber(jsDate);
11575
11644
  },
11576
11645
  };
@@ -11586,7 +11655,7 @@ const YEAR_END = {
11586
11655
  computeFormat: () => "m/d/yyyy",
11587
11656
  compute: function (date) {
11588
11657
  const year = YEAR.compute(date);
11589
- const jsDate = new Date(year + 1, 0, 0);
11658
+ const jsDate = new DateTime(year + 1, 0, 0);
11590
11659
  return jsDateToRoundNumber(jsDate);
11591
11660
  },
11592
11661
  };
@@ -11720,7 +11789,7 @@ function assertDeprecationFactorStrictlyPositive(factor) {
11720
11789
  function assertSettlementLessThanOneYearBeforeMaturity(settlement, maturity) {
11721
11790
  const startDate = toJsDate(settlement);
11722
11791
  const endDate = toJsDate(maturity);
11723
- const startDatePlusOneYear = new Date(startDate);
11792
+ const startDatePlusOneYear = toJsDate(settlement);
11724
11793
  startDatePlusOneYear.setFullYear(startDate.getFullYear() + 1);
11725
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()));
11726
11795
  }
@@ -15646,7 +15715,7 @@ const machine = {
15646
15715
  SPACE: goTo(State.RightRef),
15647
15716
  NUMBER: goTo(State.Found),
15648
15717
  REFERENCE: goTo(State.Found, (token) => isSingleCellReference(token.value)),
15649
- SYMBOL: goTo(State.Found, (token) => isColHeader(token.value)),
15718
+ SYMBOL: goTo(State.Found, (token) => isColHeader(token.value) || isRowHeader(token.value)),
15650
15719
  },
15651
15720
  [State.RightColumnRef]: {
15652
15721
  SPACE: goTo(State.RightColumnRef),
@@ -15657,6 +15726,7 @@ const machine = {
15657
15726
  SPACE: goTo(State.RightRowRef),
15658
15727
  NUMBER: goTo(State.Found),
15659
15728
  REFERENCE: goTo(State.Found, (token) => isSingleCellReference(token.value)),
15729
+ SYMBOL: goTo(State.Found, (token) => isRowHeader(token.value)),
15660
15730
  },
15661
15731
  [State.Found]: {},
15662
15732
  };
@@ -16166,6 +16236,10 @@ function setXcToReferenceType(xc, referenceType) {
16166
16236
  return xc.slice(0, indexOfNumber) + "$" + xc.slice(indexOfNumber);
16167
16237
  case "colrow":
16168
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
+ }
16169
16243
  xc = xc.slice(0, indexOfNumber) + "$" + xc.slice(indexOfNumber);
16170
16244
  return "$" + xc;
16171
16245
  case "none":
@@ -16939,10 +17013,10 @@ const arrowMap = {
16939
17013
  function updateSelectionWithArrowKeys(ev, selection) {
16940
17014
  const direction = arrowMap[ev.key];
16941
17015
  if (ev.shiftKey) {
16942
- selection.resizeAnchorZone(direction, ev.ctrlKey ? "end" : 1);
17016
+ selection.resizeAnchorZone(direction, isCtrlKey(ev) ? "end" : 1);
16943
17017
  }
16944
17018
  else {
16945
- selection.moveAnchorCell(direction, ev.ctrlKey ? "end" : 1);
17019
+ selection.moveAnchorCell(direction, isCtrlKey(ev) ? "end" : 1);
16946
17020
  }
16947
17021
  }
16948
17022
 
@@ -22599,7 +22673,10 @@ class GridOverlay extends Component {
22599
22673
  return;
22600
22674
  }
22601
22675
  const [col, row] = this.getCartesianCoordinates(ev);
22602
- 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
+ });
22603
22680
  }
22604
22681
  onDoubleClick(ev) {
22605
22682
  const [col, row] = this.getCartesianCoordinates(ev);
@@ -22687,6 +22764,7 @@ class AbstractResizer extends Component {
22687
22764
  draggerShadowThickness: 0,
22688
22765
  delta: 0,
22689
22766
  base: 0,
22767
+ position: "before",
22690
22768
  });
22691
22769
  }
22692
22770
  _computeHandleDisplay(ev) {
@@ -22812,11 +22890,13 @@ class AbstractResizer extends Component {
22812
22890
  this.state.draggerLinePosition = dimensions.start;
22813
22891
  this.state.draggerShadowPosition = dimensions.start;
22814
22892
  this.state.base = elementIndex;
22893
+ this.state.position = "before";
22815
22894
  }
22816
22895
  else if (this._getSelectedZoneEnd() < elementIndex) {
22817
22896
  this.state.draggerLinePosition = dimensions.end;
22818
22897
  this.state.draggerShadowPosition = dimensions.end - this.state.draggerShadowThickness;
22819
- this.state.base = elementIndex + 1;
22898
+ this.state.base = elementIndex;
22899
+ this.state.position = "after";
22820
22900
  }
22821
22901
  else {
22822
22902
  this.state.draggerLinePosition = startDimensions.start;
@@ -22840,7 +22920,7 @@ class AbstractResizer extends Component {
22840
22920
  this._increaseSelection(index);
22841
22921
  }
22842
22922
  else {
22843
- this._selectElement(index, ev.ctrlKey);
22923
+ this._selectElement(index, isCtrlKey(ev));
22844
22924
  }
22845
22925
  this.lastSelectedElementIndex = index;
22846
22926
  const mouseMoveSelect = (col, row) => {
@@ -22853,7 +22933,7 @@ class AbstractResizer extends Component {
22853
22933
  const mouseUpSelect = () => {
22854
22934
  this.state.isSelecting = false;
22855
22935
  this.lastSelectedElementIndex = null;
22856
- 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");
22857
22937
  this._computeGrabDisplay(ev);
22858
22938
  };
22859
22939
  dragAndDropBeyondTheViewport(this.env, mouseMoveSelect, mouseUpSelect);
@@ -22997,13 +23077,14 @@ class ColResizer extends AbstractResizer {
22997
23077
  dimension: "COL",
22998
23078
  base: this.state.base,
22999
23079
  elements,
23080
+ position: this.state.position,
23000
23081
  });
23001
23082
  if (!result.isSuccessful && result.reasons.includes(2 /* CommandResult.WillRemoveExistingMerge */)) {
23002
23083
  this.env.raiseError(MergeErrorMessage);
23003
23084
  }
23004
23085
  }
23005
- _selectElement(index, ctrlKey) {
23006
- this.env.model.selection.selectColumn(index, ctrlKey ? "newAnchor" : "overrideSelection");
23086
+ _selectElement(index, addDistinctHeader) {
23087
+ this.env.model.selection.selectColumn(index, addDistinctHeader ? "newAnchor" : "overrideSelection");
23007
23088
  }
23008
23089
  _increaseSelection(index) {
23009
23090
  this.env.model.selection.selectColumn(index, "updateAnchor");
@@ -23169,13 +23250,14 @@ class RowResizer extends AbstractResizer {
23169
23250
  dimension: "ROW",
23170
23251
  base: this.state.base,
23171
23252
  elements,
23253
+ position: this.state.position,
23172
23254
  });
23173
23255
  if (!result.isSuccessful && result.reasons.includes(2 /* CommandResult.WillRemoveExistingMerge */)) {
23174
23256
  this.env.raiseError(MergeErrorMessage);
23175
23257
  }
23176
23258
  }
23177
- _selectElement(index, ctrlKey) {
23178
- this.env.model.selection.selectRow(index, ctrlKey ? "newAnchor" : "overrideSelection");
23259
+ _selectElement(index, addDistinctHeader) {
23260
+ this.env.model.selection.selectRow(index, addDistinctHeader ? "newAnchor" : "overrideSelection");
23179
23261
  }
23180
23262
  _increaseSelection(index) {
23181
23263
  this.env.model.selection.selectRow(index, "updateAnchor");
@@ -23277,8 +23359,8 @@ function useWheelHandler(handler) {
23277
23359
  return val * (deltaMode === 0 ? 1 : DEFAULT_CELL_HEIGHT);
23278
23360
  }
23279
23361
  const onMouseWheel = (ev) => {
23280
- const deltaX = normalize(ev.shiftKey ? ev.deltaY : ev.deltaX, ev.deltaMode);
23281
- 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);
23282
23364
  handler(deltaX, deltaY);
23283
23365
  };
23284
23366
  return onMouseWheel;
@@ -23922,8 +24004,8 @@ class Grid extends Component {
23922
24004
  // ---------------------------------------------------------------------------
23923
24005
  // Zone selection with mouse
23924
24006
  // ---------------------------------------------------------------------------
23925
- onCellClicked(col, row, { ctrlKey, shiftKey }) {
23926
- if (ctrlKey) {
24007
+ onCellClicked(col, row, { addZone, expandZone }) {
24008
+ if (addZone) {
23927
24009
  this.env.model.dispatch("PREPARE_SELECTION_INPUT_EXPANSION");
23928
24010
  }
23929
24011
  if (this.env.model.getters.hasOpenedPopover()) {
@@ -23932,10 +24014,10 @@ class Grid extends Component {
23932
24014
  if (this.env.model.getters.getEditionMode() === "editing") {
23933
24015
  this.env.model.dispatch("STOP_EDITION");
23934
24016
  }
23935
- if (shiftKey) {
24017
+ if (expandZone) {
23936
24018
  this.env.model.selection.setAnchorCorner(col, row);
23937
24019
  }
23938
- else if (ctrlKey) {
24020
+ else if (addZone) {
23939
24021
  this.env.model.selection.addCellToSelection(col, row);
23940
24022
  }
23941
24023
  else {
@@ -23997,9 +24079,7 @@ class Grid extends Component {
23997
24079
  return;
23998
24080
  }
23999
24081
  let keyDownString = "";
24000
- if (ev.ctrlKey)
24001
- keyDownString += "CTRL+";
24002
- if (ev.metaKey)
24082
+ if (isCtrlKey(ev))
24003
24083
  keyDownString += "CTRL+";
24004
24084
  if (ev.altKey)
24005
24085
  keyDownString += "ALT+";
@@ -31347,7 +31427,7 @@ class SheetPlugin extends CorePlugin {
31347
31427
  const elements = cmd.dimension === "COL"
31348
31428
  ? this.getNumberCols(cmd.sheetId)
31349
31429
  : this.getNumberRows(cmd.sheetId);
31350
- if (cmd.base < 0 || cmd.base > elements) {
31430
+ if (cmd.base < 0 || cmd.base >= elements) {
31351
31431
  return 86 /* CommandResult.InvalidHeaderIndex */;
31352
31432
  }
31353
31433
  else if (cmd.quantity <= 0) {
@@ -32370,13 +32450,18 @@ class EvaluationPlugin extends UIPlugin {
32370
32450
  // Getters
32371
32451
  // ---------------------------------------------------------------------------
32372
32452
  evaluateFormula(formulaString, sheetId = this.getters.getActiveSheetId()) {
32373
- const compiledFormula = compile(formulaString);
32374
- const params = this.getCompilationParameters((cell) => this.getEvaluatedCell(this.getters.getCellPosition(cell.id)));
32375
- const ranges = [];
32376
- for (let xc of compiledFormula.dependencies) {
32377
- 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;
32378
32464
  }
32379
- return compiledFormula.execute(ranges, ...params).value;
32380
32465
  }
32381
32466
  /**
32382
32467
  * Return the value of each cell in the range as they are displayed in the grid.
@@ -32868,39 +32953,34 @@ class EvaluationConditionalFormatPlugin extends UIPlugin {
32868
32953
  this.computedIcons[sheetId] = {};
32869
32954
  const computedStyle = this.computedStyles[sheetId];
32870
32955
  for (let cf of this.getters.getConditionalFormats(sheetId).reverse()) {
32871
- try {
32872
- switch (cf.rule.type) {
32873
- case "ColorScaleRule":
32874
- for (let range of cf.ranges) {
32875
- this.applyColorScale(range, cf.rule);
32876
- }
32877
- break;
32878
- case "IconSetRule":
32879
- for (let range of cf.ranges) {
32880
- this.applyIcon(range, cf.rule);
32881
- }
32882
- break;
32883
- default:
32884
- for (let ref of cf.ranges) {
32885
- const zone = this.getters.getRangeFromSheetXC(sheetId, ref).zone;
32886
- for (let row = zone.top; row <= zone.bottom; row++) {
32887
- for (let col = zone.left; col <= zone.right; col++) {
32888
- const pr = this.rulePredicate[cf.rule.type];
32889
- let cell = this.getters.getEvaluatedCell({ sheetId, col, row });
32890
- if (pr && pr(cell, cf.rule)) {
32891
- if (!computedStyle[col])
32892
- computedStyle[col] = [];
32893
- // we must combine all the properties of all the CF rules applied to the given cell
32894
- computedStyle[col][row] = Object.assign(((_a = computedStyle[col]) === null || _a === void 0 ? void 0 : _a[row]) || {}, cf.rule.style);
32895
- }
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);
32896
32979
  }
32897
32980
  }
32898
32981
  }
32899
- break;
32900
- }
32901
- }
32902
- catch (_) {
32903
- // we don't care about the errors within the evaluation of a rule
32982
+ }
32983
+ break;
32904
32984
  }
32905
32985
  }
32906
32986
  }
@@ -32925,7 +33005,7 @@ class EvaluationConditionalFormatPlugin extends UIPlugin {
32925
33005
  return percentile(rangeValues, Number(threshold.value) / 100, true);
32926
33006
  case "formula":
32927
33007
  const value = threshold.value && this.getters.evaluateFormula(threshold.value);
32928
- return !(value instanceof Promise) ? value : null;
33008
+ return typeof value === "number" ? value : null;
32929
33009
  default:
32930
33010
  return null;
32931
33011
  }
@@ -34487,24 +34567,27 @@ class AutofillPlugin extends UIPlugin {
34487
34567
  * autofiller
34488
34568
  */
34489
34569
  autofillAuto() {
34570
+ var _a, _b;
34490
34571
  const zone = this.getters.getSelectedZone();
34491
34572
  const sheetId = this.getters.getActiveSheetId();
34492
34573
  let col = zone.left;
34493
34574
  let row = zone.bottom;
34494
34575
  if (col > 0) {
34495
- let left = this.getters.getEvaluatedCell({ sheetId, col: col - 1, row });
34496
- 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)) {
34497
34579
  row += 1;
34498
- left = this.getters.getEvaluatedCell({ sheetId, col: col - 1, row });
34580
+ leftPosition = { sheetId, col: col - 1, row };
34499
34581
  }
34500
34582
  }
34501
34583
  if (row === zone.bottom) {
34502
34584
  col = zone.right;
34503
34585
  if (col <= this.getters.getNumberCols(sheetId)) {
34504
- let right = this.getters.getEvaluatedCell({ sheetId, col: col + 1, row });
34505
- 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)) {
34506
34589
  row += 1;
34507
- right = this.getters.getEvaluatedCell({ sheetId, col: col + 1, row });
34590
+ rightPosition = { sheetId, col: col + 1, row };
34508
34591
  }
34509
34592
  }
34510
34593
  }
@@ -39236,7 +39319,7 @@ class GridSelectionPlugin extends UIPlugin {
39236
39319
  sheetId: cmd.sheetId,
39237
39320
  base: cmd.base,
39238
39321
  quantity: thickness,
39239
- position: "before",
39322
+ position: cmd.position,
39240
39323
  });
39241
39324
  const isCol = cmd.dimension === "COL";
39242
39325
  const start = cmd.elements[0];
@@ -39253,12 +39336,13 @@ class GridSelectionPlugin extends UIPlugin {
39253
39336
  },
39254
39337
  ];
39255
39338
  const state = new ClipboardCellsState(target, "CUT", this.getters, this.dispatch, this.selection);
39339
+ const base = isBasedBefore ? cmd.base : cmd.base + 1;
39256
39340
  const pasteTarget = [
39257
39341
  {
39258
- left: isCol ? cmd.base : 0,
39259
- right: isCol ? cmd.base + thickness - 1 : this.getters.getNumberCols(cmd.sheetId) - 1,
39260
- top: !isCol ? cmd.base : 0,
39261
- 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,
39262
39346
  },
39263
39347
  ];
39264
39348
  state.paste(pasteTarget, { selectTarget: true });
@@ -39295,6 +39379,11 @@ class GridSelectionPlugin extends UIPlugin {
39295
39379
  doesElementsHaveCommonMerges(id, cmd.base - 1, cmd.base)) {
39296
39380
  return 2 /* CommandResult.WillRemoveExistingMerge */;
39297
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
+ }
39298
39387
  return 0 /* CommandResult.Success */;
39299
39388
  }
39300
39389
  //-------------------------------------------
@@ -40848,7 +40937,7 @@ class Spreadsheet extends Component {
40848
40937
  }
40849
40938
  onKeydown(ev) {
40850
40939
  let keyDownString = "";
40851
- if (ev.ctrlKey || ev.metaKey) {
40940
+ if (isCtrlKey(ev)) {
40852
40941
  keyDownString += "CTRL+";
40853
40942
  }
40854
40943
  keyDownString += ev.key.toUpperCase();
@@ -44445,6 +44534,6 @@ function addFunction(functionName, functionDescription) {
44445
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 };
44446
44535
 
44447
44536
 
44448
- __info__.version = '16.1.28';
44449
- __info__.date = '2023-11-24T13:11:01.992Z';
44450
- __info__.hash = '257f369';
44537
+ __info__.version = '16.1.31';
44538
+ __info__.date = '2024-01-12T13:42:11.001Z';
44539
+ __info__.hash = 'f51befa';