@odoo/o-spreadsheet 15.0.12 → 15.0.15

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.
@@ -106,14 +106,72 @@ const FORMULA_REF_IDENTIFIER = "|";
106
106
  // -----------------------------------------------------------------------------
107
107
  // Date Type
108
108
  // -----------------------------------------------------------------------------
109
+ /**
110
+ * A DateTime object that can be used to manipulate spreadsheet dates.
111
+ * Conceptually, a spreadsheet date is simply a number with a date format,
112
+ * and it is timezone-agnostic.
113
+ * This DateTime object consistently uses UTC time to represent a naive date and time.
114
+ */
115
+ class DateTime {
116
+ constructor(year, month, day, hours = 0, minutes = 0, seconds = 0) {
117
+ this.jsDate = new Date(Date.UTC(year, month, day, hours, minutes, seconds, 0));
118
+ }
119
+ static fromTimestamp(timestamp) {
120
+ const date = new Date(timestamp);
121
+ return new DateTime(date.getUTCFullYear(), date.getUTCMonth(), date.getUTCDate(), date.getUTCHours(), date.getUTCMinutes(), date.getUTCSeconds());
122
+ }
123
+ static now() {
124
+ const now = new Date();
125
+ return new DateTime(now.getFullYear(), now.getMonth(), now.getDate(), now.getHours(), now.getMinutes(), now.getSeconds());
126
+ }
127
+ toString() {
128
+ return this.jsDate.toString();
129
+ }
130
+ getTime() {
131
+ return this.jsDate.getTime();
132
+ }
133
+ getFullYear() {
134
+ return this.jsDate.getUTCFullYear();
135
+ }
136
+ getMonth() {
137
+ return this.jsDate.getUTCMonth();
138
+ }
139
+ getDate() {
140
+ return this.jsDate.getUTCDate();
141
+ }
142
+ getDay() {
143
+ return this.jsDate.getUTCDay();
144
+ }
145
+ getHours() {
146
+ return this.jsDate.getUTCHours();
147
+ }
148
+ getMinutes() {
149
+ return this.jsDate.getUTCMinutes();
150
+ }
151
+ getSeconds() {
152
+ return this.jsDate.getUTCSeconds();
153
+ }
154
+ setDate(date) {
155
+ this.jsDate.setUTCDate(date);
156
+ }
157
+ setHours(hours) {
158
+ this.jsDate.setUTCHours(hours);
159
+ }
160
+ setMinutes(minutes) {
161
+ this.jsDate.setUTCMinutes(minutes);
162
+ }
163
+ setSeconds(seconds) {
164
+ this.jsDate.setUTCSeconds(seconds);
165
+ }
166
+ }
109
167
  // -----------------------------------------------------------------------------
110
168
  // Parsing
111
169
  // -----------------------------------------------------------------------------
112
170
  const CURRENT_MILLENIAL = 2000; // note: don't forget to update this in 2999
113
- const CURRENT_YEAR = new Date().getFullYear();
114
- const INITIAL_1900_DAY$1 = new Date(1899, 11, 30);
115
- const INITIAL_JS_DAY = new Date(0);
116
- const DATE_JS_1900_OFFSET = INITIAL_JS_DAY - INITIAL_1900_DAY$1;
171
+ const CURRENT_YEAR = DateTime.now().getFullYear();
172
+ const INITIAL_1900_DAY$1 = new DateTime(1899, 11, 30);
173
+ const INITIAL_JS_DAY = DateTime.fromTimestamp(0);
174
+ const DATE_JS_1900_OFFSET = INITIAL_JS_DAY.getTime() - INITIAL_1900_DAY$1.getTime();
117
175
  const mdyDateRegexp = /^\d{1,2}(\/|-|\s)\d{1,2}((\/|-|\s)\d{1,4})?$/;
118
176
  const ymdDateRegexp = /^\d{3,4}(\/|-|\s)\d{1,2}(\/|-|\s)\d{1,2}$/;
119
177
  const timeRegexp = /((\d+(:\d+)?(:\d+)?\s*(AM|PM))|(\d+:\d+(:\d+)?))$/;
@@ -153,7 +211,7 @@ function parseDateTime(str) {
153
211
  return {
154
212
  value: date.value + time.value,
155
213
  format: date.format + " " + (time.format === "hhhh:mm:ss" ? "hh:mm:ss" : time.format),
156
- 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()),
214
+ 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()),
157
215
  };
158
216
  }
159
217
  return date || time;
@@ -170,13 +228,13 @@ function parseDate(str, dateFormat) {
170
228
  const day = Number(parts[dayIndex]);
171
229
  const leadingZero = (parts[monthIndex].length === 2 && month < 10) || (parts[dayIndex].length === 2 && day < 10);
172
230
  const year = parts[yearIndex] ? inferYear(parts[yearIndex]) : CURRENT_YEAR;
173
- const jsDate = new Date(year, month - 1, day);
231
+ const jsDate = new DateTime(year, month - 1, day);
174
232
  const sep = str.match(/\/|-|\s/)[0];
175
233
  if (jsDate.getMonth() !== month - 1 || jsDate.getDate() !== day) {
176
234
  // invalid date
177
235
  return null;
178
236
  }
179
- const delta = jsDate - INITIAL_1900_DAY$1;
237
+ const delta = jsDate.getTime() - INITIAL_1900_DAY$1.getTime();
180
238
  let format = leadingZero ? `mm${sep}dd` : `m${sep}d`;
181
239
  if (parts[yearIndex]) {
182
240
  format = isMDY ? format + sep + "yyyy" : "yyyy" + sep + format;
@@ -236,7 +294,7 @@ function parseTime(str) {
236
294
  if (hours >= 24) {
237
295
  format = "hhhh:mm:ss";
238
296
  }
239
- const jsDate = new Date(1899, 11, 30, hours, minutes, seconds);
297
+ const jsDate = new DateTime(1899, 11, 30, hours, minutes, seconds);
240
298
  return {
241
299
  value: hours / 24 + minutes / 1440 + seconds / 86400,
242
300
  format: format,
@@ -250,7 +308,7 @@ function parseTime(str) {
250
308
  // -----------------------------------------------------------------------------
251
309
  function numberToJsDate(value) {
252
310
  const truncValue = Math.trunc(value);
253
- let date = new Date(truncValue * 86400 * 1000 - DATE_JS_1900_OFFSET);
311
+ let date = DateTime.fromTimestamp(truncValue * 86400 * 1000 - DATE_JS_1900_OFFSET);
254
312
  let time = value - truncValue;
255
313
  time = time < 0 ? 1 + time : time;
256
314
  const hours = Math.round(time * 24);
@@ -323,7 +381,7 @@ function formatJSTime(jsDate, format) {
323
381
  .map((p) => {
324
382
  switch (p) {
325
383
  case "hhhh":
326
- const helapsedHours = Math.floor((jsDate.getTime() - INITIAL_1900_DAY$1) / (60 * 60 * 1000));
384
+ const helapsedHours = Math.floor((jsDate.getTime() - INITIAL_1900_DAY$1.getTime()) / (60 * 60 * 1000));
327
385
  return helapsedHours.toString();
328
386
  case "hh":
329
387
  return hours.toString().padStart(2, "0");
@@ -2678,13 +2736,6 @@ for (let font of fontSizes) {
2678
2736
  }
2679
2737
 
2680
2738
  //------------------------------------------------------------------------------
2681
- /**
2682
- * Stringify an object, like JSON.stringify, except that the first level of keys
2683
- * is ordered.
2684
- */
2685
- function stringify(obj) {
2686
- return JSON.stringify(obj, Object.keys(obj).sort());
2687
- }
2688
2739
  /**
2689
2740
  * Escapes a string to use as a literal string in a RegExp.
2690
2741
  * @url https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Regular_Expressions#Escaping
@@ -2830,7 +2881,7 @@ function isBoolean(str) {
2830
2881
  function isDateTime(str) {
2831
2882
  return parseDateTime(str) !== null;
2832
2883
  }
2833
- const MARKDOWN_LINK_REGEX = /^\[([^\[]+)\]\((.+)\)$/;
2884
+ const MARKDOWN_LINK_REGEX = /^\[(.+)\]\((.+)\)$/;
2834
2885
  //link must start with http or https
2835
2886
  //https://stackoverflow.com/a/3809435/4760614
2836
2887
  const WEB_LINK_REGEX = /^https?:\/\/(www\.)?[-a-zA-Z0-9@:%._\+~#=]{2,256}\.[a-z]{2,4}\b([-a-zA-Z0-9@:%_\+.~#?&//=]*)$/;
@@ -4733,7 +4784,7 @@ var database = /*#__PURE__*/Object.freeze({
4733
4784
  DVARP: DVARP
4734
4785
  });
4735
4786
 
4736
- const INITIAL_1900_DAY = new Date(1899, 11, 30);
4787
+ const INITIAL_1900_DAY = new DateTime(1899, 11, 30);
4737
4788
  const DEFAULT_TYPE = 1;
4738
4789
  const DEFAULT_WEEKEND = 1;
4739
4790
  function isLeapYear(year) {
@@ -4762,7 +4813,7 @@ const DATE = {
4762
4813
  if (_year < 1900) {
4763
4814
  _year += 1900;
4764
4815
  }
4765
- const jsDate = new Date(_year, _month - 1, _day);
4816
+ const jsDate = new DateTime(_year, _month - 1, _day);
4766
4817
  const delta = jsDate.getTime() - INITIAL_1900_DAY.getTime();
4767
4818
  assert(() => delta >= 0, _lt(`The function [[FUNCTION_NAME]] result must be greater than or equal 01/01/1900.`));
4768
4819
  return Math.round(delta / 86400000);
@@ -4835,7 +4886,7 @@ const EDATE = {
4835
4886
  const yStart = _startDate.getFullYear();
4836
4887
  const mStart = _startDate.getMonth();
4837
4888
  const dStart = _startDate.getDate();
4838
- const jsDate = new Date(yStart, mStart + _months, dStart);
4889
+ const jsDate = new DateTime(yStart, mStart + _months, dStart);
4839
4890
  const delta = jsDate.getTime() - INITIAL_1900_DAY.getTime();
4840
4891
  return Math.round(delta / 86400000);
4841
4892
  },
@@ -4857,7 +4908,7 @@ const EOMONTH = {
4857
4908
  const _months = Math.trunc(toNumber(months));
4858
4909
  const yStart = _startDate.getFullYear();
4859
4910
  const mStart = _startDate.getMonth();
4860
- const jsDate = new Date(yStart, mStart + _months + 1, 0);
4911
+ const jsDate = new DateTime(yStart, mStart + _months + 1, 0);
4861
4912
  const delta = jsDate.getTime() - INITIAL_1900_DAY.getTime();
4862
4913
  return Math.round(delta / 86400000);
4863
4914
  },
@@ -4897,17 +4948,17 @@ const ISOWEEKNUM = {
4897
4948
  // The first week of the year is the week that contains the first
4898
4949
  // Thursday of the year.
4899
4950
  let firstThursday = 1;
4900
- while (new Date(y, 0, firstThursday).getDay() !== 4) {
4951
+ while (new DateTime(y, 0, firstThursday).getDay() !== 4) {
4901
4952
  firstThursday += 1;
4902
4953
  }
4903
- const firstDayOfFirstWeek = new Date(y, 0, firstThursday - 3);
4954
+ const firstDayOfFirstWeek = new DateTime(y, 0, firstThursday - 3);
4904
4955
  // The last week of the year is the week that contains the last Thursday of
4905
4956
  // the year.
4906
4957
  let lastThursday = 31;
4907
- while (new Date(y, 11, lastThursday).getDay() !== 4) {
4958
+ while (new DateTime(y, 11, lastThursday).getDay() !== 4) {
4908
4959
  lastThursday -= 1;
4909
4960
  }
4910
- const lastDayOfLastWeek = new Date(y, 11, lastThursday + 3);
4961
+ const lastDayOfLastWeek = new DateTime(y, 11, lastThursday + 3);
4911
4962
  // B - If our date > lastDayOfLastWeek then it's in the weeks of the year after
4912
4963
  // If our date < firstDayOfFirstWeek then it's in the weeks of the year before
4913
4964
  let offsetYear;
@@ -4933,17 +4984,17 @@ const ISOWEEKNUM = {
4933
4984
  case 1:
4934
4985
  // firstDay is the 1st day of the 1st week of the year after
4935
4986
  // firstDay = lastDayOfLastWeek + 1 Day
4936
- firstDay = new Date(y, 11, lastThursday + 3 + 1);
4987
+ firstDay = new DateTime(y, 11, lastThursday + 3 + 1);
4937
4988
  break;
4938
4989
  case -1:
4939
4990
  // firstDay is the 1st day of the 1st week of the previous year.
4940
4991
  // The first week of the previous year is the week that contains the
4941
4992
  // first Thursday of the previous year.
4942
4993
  let firstThursdayPreviousYear = 1;
4943
- while (new Date(y - 1, 0, firstThursdayPreviousYear).getDay() !== 4) {
4994
+ while (new DateTime(y - 1, 0, firstThursdayPreviousYear).getDay() !== 4) {
4944
4995
  firstThursdayPreviousYear += 1;
4945
4996
  }
4946
- firstDay = new Date(y - 1, 0, firstThursdayPreviousYear - 3);
4997
+ firstDay = new DateTime(y - 1, 0, firstThursdayPreviousYear - 3);
4947
4998
  break;
4948
4999
  }
4949
5000
  const dif = (_date.getTime() - firstDay.getTime()) / 86400000;
@@ -5082,8 +5133,8 @@ const NETWORKDAYS_INTL = {
5082
5133
  });
5083
5134
  }
5084
5135
  const invertDate = _startDate.getTime() > _endDate.getTime();
5085
- const stopDate = new Date((invertDate ? _startDate : _endDate).getTime());
5086
- let stepDate = new Date((invertDate ? _endDate : _startDate).getTime());
5136
+ const stopDate = DateTime.fromTimestamp((invertDate ? _startDate : _endDate).getTime());
5137
+ let stepDate = DateTime.fromTimestamp((invertDate ? _endDate : _startDate).getTime());
5087
5138
  const timeStopDate = stopDate.getTime();
5088
5139
  let timeStepDate = stepDate.getTime();
5089
5140
  let netWorkingDay = 0;
@@ -5107,8 +5158,7 @@ const NOW = {
5107
5158
  returns: ["DATE"],
5108
5159
  returnFormat: { specificFormat: "m/d/yyyy hh:mm:ss" },
5109
5160
  compute: function () {
5110
- let today = new Date();
5111
- today.setMilliseconds(0);
5161
+ let today = DateTime.now();
5112
5162
  const delta = today.getTime() - INITIAL_1900_DAY.getTime();
5113
5163
  const time = today.getHours() / 24 + today.getMinutes() / 1440 + today.getSeconds() / 86400;
5114
5164
  return Math.floor(delta / 86400000) + time;
@@ -5182,8 +5232,8 @@ const TODAY = {
5182
5232
  returns: ["DATE"],
5183
5233
  returnFormat: { specificFormat: "m/d/yyyy" },
5184
5234
  compute: function () {
5185
- const today = new Date();
5186
- const jsDate = new Date(today.getFullYear(), today.getMonth(), today.getDate());
5235
+ const today = DateTime.now();
5236
+ const jsDate = new DateTime(today.getFullYear(), today.getMonth(), today.getDate());
5187
5237
  const delta = jsDate.getTime() - INITIAL_1900_DAY.getTime();
5188
5238
  return Math.round(delta / 86400000);
5189
5239
  },
@@ -5239,10 +5289,10 @@ const WEEKNUM = {
5239
5289
  }
5240
5290
  const y = _date.getFullYear();
5241
5291
  let dayStart = 1;
5242
- let startDayOfFirstWeek = new Date(y, 0, dayStart);
5292
+ let startDayOfFirstWeek = new DateTime(y, 0, dayStart);
5243
5293
  while (startDayOfFirstWeek.getDay() !== startDayOfWeek) {
5244
5294
  dayStart += 1;
5245
- startDayOfFirstWeek = new Date(y, 0, dayStart);
5295
+ startDayOfFirstWeek = new DateTime(y, 0, dayStart);
5246
5296
  }
5247
5297
  const dif = (_date.getTime() - startDayOfFirstWeek.getTime()) / 86400000;
5248
5298
  if (dif < 0) {
@@ -5296,7 +5346,7 @@ const WORKDAY_INTL = {
5296
5346
  timesHoliday.add(holiday.getTime());
5297
5347
  });
5298
5348
  }
5299
- let stepDate = new Date(_startDate.getTime());
5349
+ let stepDate = DateTime.fromTimestamp(_startDate.getTime());
5300
5350
  let timeStepDate = stepDate.getTime();
5301
5351
  const unitDay = Math.sign(_numDays);
5302
5352
  let stepDay = Math.abs(_numDays);
@@ -9403,7 +9453,7 @@ class BordersPlugin extends CorePlugin {
9403
9453
  */
9404
9454
  function getBorderId(border) {
9405
9455
  for (let [key, value] of Object.entries(borders)) {
9406
- if (stringify(value) === stringify(border)) {
9456
+ if (deepEquals(value, border)) {
9407
9457
  return parseInt(key, 10);
9408
9458
  }
9409
9459
  }
@@ -9599,7 +9649,7 @@ class CellPlugin extends CorePlugin {
9599
9649
  */
9600
9650
  function getStyleId(style) {
9601
9651
  for (let [key, value] of Object.entries(styles)) {
9602
- if (stringify(value) === stringify(style)) {
9652
+ if (deepEquals(value, style)) {
9603
9653
  return parseInt(key, 10);
9604
9654
  }
9605
9655
  }
@@ -9653,12 +9703,10 @@ class CellPlugin extends CorePlugin {
9653
9703
  * starting an async evaluation even if it has been moved or re-allocated
9654
9704
  */
9655
9705
  getCellById(cellId) {
9656
- for (const sheet of Object.values(this.cells)) {
9657
- if (sheet[cellId]) {
9658
- return sheet[cellId];
9659
- }
9660
- }
9661
- return undefined;
9706
+ // this must be as fast as possible
9707
+ const position = this.getters.getCellPosition(cellId);
9708
+ const sheet = this.cells[position.sheetId];
9709
+ return sheet[cellId];
9662
9710
  }
9663
9711
  /**
9664
9712
  * Reconstructs the original formula string based on a normalized form and its dependencies
@@ -15827,7 +15875,7 @@ const CSS$i = css$k /* scss */ `
15827
15875
  }
15828
15876
  .o-input-count {
15829
15877
  width: fit-content;
15830
- padding: 4 0 4 4;
15878
+ padding: 4px 0 4px 4px;
15831
15879
  }
15832
15880
  }
15833
15881
  }
@@ -23479,9 +23527,9 @@ function revertChanges(revisions) {
23479
23527
  * Apply the changes of the given HistoryChange to the state
23480
23528
  */
23481
23529
  function applyChange(change, target) {
23482
- let val = change.root;
23483
- let key = change.path[change.path.length - 1];
23484
- for (let pathIndex = 0; pathIndex < change.path.slice(0, -1).length; pathIndex++) {
23530
+ let val = change.path[0];
23531
+ const key = change.path[change.path.length - 1];
23532
+ for (let pathIndex = 1; pathIndex < change.path.slice(0, -1).length; pathIndex++) {
23485
23533
  const p = change.path[pathIndex];
23486
23534
  if (val[p] === undefined) {
23487
23535
  const nextPath = change.path[pathIndex + 1];
@@ -23937,13 +23985,14 @@ class StateObserver {
23937
23985
  }
23938
23986
  addChange(...args) {
23939
23987
  const val = args.pop();
23940
- const [root, ...path] = args;
23988
+ const root = args[0];
23941
23989
  let value = root;
23942
- let key = path[path.length - 1];
23943
- for (let pathIndex = 0; pathIndex <= path.length - 2; pathIndex++) {
23944
- const p = path[pathIndex];
23990
+ let key = args[args.length - 1];
23991
+ const pathLength = args.length - 2;
23992
+ for (let pathIndex = 1; pathIndex <= pathLength; pathIndex++) {
23993
+ const p = args[pathIndex];
23945
23994
  if (value[p] === undefined) {
23946
- const nextPath = path[pathIndex + 1];
23995
+ const nextPath = args[pathIndex + 1];
23947
23996
  value[p] = createEmptyStructure(nextPath);
23948
23997
  }
23949
23998
  value = value[p];
@@ -23952,8 +24001,7 @@ class StateObserver {
23952
24001
  return;
23953
24002
  }
23954
24003
  this.changes.push({
23955
- root,
23956
- path,
24004
+ path: args,
23957
24005
  before: value[key],
23958
24006
  after: val,
23959
24007
  });
@@ -26011,7 +26059,10 @@ class Model extends owl.core.EventBus {
26011
26059
  */
26012
26060
  checkDispatchAllowed(command) {
26013
26061
  const results = this.handlers.map((handler) => handler.allowDispatch(command));
26014
- return new DispatchResult(results.flat());
26062
+ if (results.some((r) => r !== 0 /* Success */)) {
26063
+ return new DispatchResult(results.flat());
26064
+ }
26065
+ return DispatchResult.Success;
26015
26066
  }
26016
26067
  finalize() {
26017
26068
  this.status = 3 /* Finalizing */;
@@ -31411,6 +31462,6 @@ const helpers = {
31411
31462
  export { CommandResult, CorePlugin, DATETIME_FORMAT, DataSource, DispatchResult, Model, Revision, SPREADSHEET_DIMENSIONS, Spreadsheet, UIPlugin, DEBUG as __DEBUG__, __info__, astToFormula, cellTypes, convertAstNodes, coreTypes, functionCache, helpers, invalidateEvaluationCommands, normalize, parse, readonlyAllowedCommands, registries, setTranslationMethod };
31412
31463
 
31413
31464
 
31414
- __info__.version = '15.0.12';
31415
- __info__.date = '2023-11-02T09:28:12.777Z';
31416
- __info__.hash = 'e82bd99';
31465
+ __info__.version = '15.0.15';
31466
+ __info__.date = '2024-01-12T13:39:45.422Z';
31467
+ __info__.hash = 'c87764c';