@odoo/o-spreadsheet 15.2.12 → 15.2.14

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.
@@ -131,14 +131,72 @@ const FORMULA_REF_IDENTIFIER = "|";
131
131
  // -----------------------------------------------------------------------------
132
132
  // Date Type
133
133
  // -----------------------------------------------------------------------------
134
+ /**
135
+ * A DateTime object that can be used to manipulate spreadsheet dates.
136
+ * Conceptually, a spreadsheet date is simply a number with a date format,
137
+ * and it is timezone-agnostic.
138
+ * This DateTime object consistently uses UTC time to represent a naive date and time.
139
+ */
140
+ class DateTime {
141
+ constructor(year, month, day, hours = 0, minutes = 0, seconds = 0) {
142
+ this.jsDate = new Date(Date.UTC(year, month, day, hours, minutes, seconds, 0));
143
+ }
144
+ static fromTimestamp(timestamp) {
145
+ const date = new Date(timestamp);
146
+ return new DateTime(date.getUTCFullYear(), date.getUTCMonth(), date.getUTCDate(), date.getUTCHours(), date.getUTCMinutes(), date.getUTCSeconds());
147
+ }
148
+ static now() {
149
+ const now = new Date();
150
+ return new DateTime(now.getFullYear(), now.getMonth(), now.getDate(), now.getHours(), now.getMinutes(), now.getSeconds());
151
+ }
152
+ toString() {
153
+ return this.jsDate.toString();
154
+ }
155
+ getTime() {
156
+ return this.jsDate.getTime();
157
+ }
158
+ getFullYear() {
159
+ return this.jsDate.getUTCFullYear();
160
+ }
161
+ getMonth() {
162
+ return this.jsDate.getUTCMonth();
163
+ }
164
+ getDate() {
165
+ return this.jsDate.getUTCDate();
166
+ }
167
+ getDay() {
168
+ return this.jsDate.getUTCDay();
169
+ }
170
+ getHours() {
171
+ return this.jsDate.getUTCHours();
172
+ }
173
+ getMinutes() {
174
+ return this.jsDate.getUTCMinutes();
175
+ }
176
+ getSeconds() {
177
+ return this.jsDate.getUTCSeconds();
178
+ }
179
+ setDate(date) {
180
+ this.jsDate.setUTCDate(date);
181
+ }
182
+ setHours(hours) {
183
+ this.jsDate.setUTCHours(hours);
184
+ }
185
+ setMinutes(minutes) {
186
+ this.jsDate.setUTCMinutes(minutes);
187
+ }
188
+ setSeconds(seconds) {
189
+ this.jsDate.setUTCSeconds(seconds);
190
+ }
191
+ }
134
192
  // -----------------------------------------------------------------------------
135
193
  // Parsing
136
194
  // -----------------------------------------------------------------------------
137
195
  const CURRENT_MILLENIAL = 2000; // note: don't forget to update this in 2999
138
- const CURRENT_YEAR = new Date().getFullYear();
139
- const INITIAL_1900_DAY$1 = new Date(1899, 11, 30);
140
- const INITIAL_JS_DAY = new Date(0);
141
- const DATE_JS_1900_OFFSET = INITIAL_JS_DAY - INITIAL_1900_DAY$1;
196
+ const CURRENT_YEAR = DateTime.now().getFullYear();
197
+ const INITIAL_1900_DAY$1 = new DateTime(1899, 11, 30);
198
+ const INITIAL_JS_DAY = DateTime.fromTimestamp(0);
199
+ const DATE_JS_1900_OFFSET = INITIAL_JS_DAY.getTime() - INITIAL_1900_DAY$1.getTime();
142
200
  const mdyDateRegexp = /^\d{1,2}(\/|-|\s)\d{1,2}((\/|-|\s)\d{1,4})?$/;
143
201
  const ymdDateRegexp = /^\d{3,4}(\/|-|\s)\d{1,2}(\/|-|\s)\d{1,2}$/;
144
202
  const timeRegexp = /((\d+(:\d+)?(:\d+)?\s*(AM|PM))|(\d+:\d+(:\d+)?))$/;
@@ -178,7 +236,7 @@ function parseDateTime(str) {
178
236
  return {
179
237
  value: date.value + time.value,
180
238
  format: date.format + " " + (time.format === "hhhh:mm:ss" ? "hh:mm:ss" : time.format),
181
- 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()),
239
+ 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()),
182
240
  };
183
241
  }
184
242
  return date || time;
@@ -195,13 +253,13 @@ function parseDate(str, dateFormat) {
195
253
  const day = Number(parts[dayIndex]);
196
254
  const leadingZero = (parts[monthIndex].length === 2 && month < 10) || (parts[dayIndex].length === 2 && day < 10);
197
255
  const year = parts[yearIndex] ? inferYear(parts[yearIndex]) : CURRENT_YEAR;
198
- const jsDate = new Date(year, month - 1, day);
256
+ const jsDate = new DateTime(year, month - 1, day);
199
257
  const sep = str.match(/\/|-|\s/)[0];
200
258
  if (jsDate.getMonth() !== month - 1 || jsDate.getDate() !== day) {
201
259
  // invalid date
202
260
  return null;
203
261
  }
204
- const delta = jsDate - INITIAL_1900_DAY$1;
262
+ const delta = jsDate.getTime() - INITIAL_1900_DAY$1.getTime();
205
263
  let format = leadingZero ? `mm${sep}dd` : `m${sep}d`;
206
264
  if (parts[yearIndex]) {
207
265
  format = isMDY ? format + sep + "yyyy" : "yyyy" + sep + format;
@@ -261,7 +319,7 @@ function parseTime(str) {
261
319
  if (hours >= 24) {
262
320
  format = "hhhh:mm:ss";
263
321
  }
264
- const jsDate = new Date(1899, 11, 30, hours, minutes, seconds);
322
+ const jsDate = new DateTime(1899, 11, 30, hours, minutes, seconds);
265
323
  return {
266
324
  value: hours / 24 + minutes / 1440 + seconds / 86400,
267
325
  format: format,
@@ -275,7 +333,7 @@ function parseTime(str) {
275
333
  // -----------------------------------------------------------------------------
276
334
  function numberToJsDate(value) {
277
335
  const truncValue = Math.trunc(value);
278
- let date = new Date(truncValue * 86400 * 1000 - DATE_JS_1900_OFFSET);
336
+ let date = DateTime.fromTimestamp(truncValue * 86400 * 1000 - DATE_JS_1900_OFFSET);
279
337
  let time = value - truncValue;
280
338
  time = time < 0 ? 1 + time : time;
281
339
  const hours = Math.round(time * 24);
@@ -348,7 +406,7 @@ function formatJSTime(jsDate, format) {
348
406
  .map((p) => {
349
407
  switch (p) {
350
408
  case "hhhh":
351
- const helapsedHours = Math.floor((jsDate.getTime() - INITIAL_1900_DAY$1) / (60 * 60 * 1000));
409
+ const helapsedHours = Math.floor((jsDate.getTime() - INITIAL_1900_DAY$1.getTime()) / (60 * 60 * 1000));
352
410
  return helapsedHours.toString();
353
411
  case "hh":
354
412
  return hours.toString().padStart(2, "0");
@@ -2709,13 +2767,6 @@ for (let font of fontSizes) {
2709
2767
  }
2710
2768
 
2711
2769
  //------------------------------------------------------------------------------
2712
- /**
2713
- * Stringify an object, like JSON.stringify, except that the first level of keys
2714
- * is ordered.
2715
- */
2716
- function stringify(obj) {
2717
- return JSON.stringify(obj, Object.keys(obj).sort());
2718
- }
2719
2770
  /**
2720
2771
  * Remove quotes from a quoted string
2721
2772
  * ```js
@@ -2874,7 +2925,7 @@ function isBoolean(str) {
2874
2925
  function isDateTime(str) {
2875
2926
  return parseDateTime(str) !== null;
2876
2927
  }
2877
- const MARKDOWN_LINK_REGEX = /^\[([^\[]+)\]\((.+)\)$/;
2928
+ const MARKDOWN_LINK_REGEX = /^\[(.+)\]\((.+)\)$/;
2878
2929
  //link must start with http or https
2879
2930
  //https://stackoverflow.com/a/3809435/4760614
2880
2931
  const WEB_LINK_REGEX = /^https?:\/\/(www\.)?[-a-zA-Z0-9@:%._\+~#=]{2,256}\.[a-z]{2,4}\b([-a-zA-Z0-9@:%_\+.~#?&//=]*)$/;
@@ -2940,7 +2991,7 @@ const DEBUG = {};
2940
2991
  */
2941
2992
  function getItemId(item, itemsDic) {
2942
2993
  for (let [key, value] of Object.entries(itemsDic)) {
2943
- if (stringify(value) === stringify(item)) {
2994
+ if (deepEquals(value, item)) {
2944
2995
  return parseInt(key, 10);
2945
2996
  }
2946
2997
  }
@@ -4750,7 +4801,7 @@ var database = /*#__PURE__*/Object.freeze({
4750
4801
  DVARP: DVARP
4751
4802
  });
4752
4803
 
4753
- const INITIAL_1900_DAY = new Date(1899, 11, 30);
4804
+ const INITIAL_1900_DAY = new DateTime(1899, 11, 30);
4754
4805
  const DEFAULT_TYPE = 1;
4755
4806
  const DEFAULT_WEEKEND = 1;
4756
4807
  function isLeapYear(year) {
@@ -4779,7 +4830,7 @@ const DATE = {
4779
4830
  if (_year < 1900) {
4780
4831
  _year += 1900;
4781
4832
  }
4782
- const jsDate = new Date(_year, _month - 1, _day);
4833
+ const jsDate = new DateTime(_year, _month - 1, _day);
4783
4834
  const delta = jsDate.getTime() - INITIAL_1900_DAY.getTime();
4784
4835
  assert(() => delta >= 0, _lt(`The function [[FUNCTION_NAME]] result must be greater than or equal 01/01/1900.`));
4785
4836
  return Math.round(delta / 86400000);
@@ -4852,7 +4903,7 @@ const EDATE = {
4852
4903
  const yStart = _startDate.getFullYear();
4853
4904
  const mStart = _startDate.getMonth();
4854
4905
  const dStart = _startDate.getDate();
4855
- const jsDate = new Date(yStart, mStart + _months, dStart);
4906
+ const jsDate = new DateTime(yStart, mStart + _months, dStart);
4856
4907
  const delta = jsDate.getTime() - INITIAL_1900_DAY.getTime();
4857
4908
  return Math.round(delta / 86400000);
4858
4909
  },
@@ -4874,7 +4925,7 @@ const EOMONTH = {
4874
4925
  const _months = Math.trunc(toNumber(months));
4875
4926
  const yStart = _startDate.getFullYear();
4876
4927
  const mStart = _startDate.getMonth();
4877
- const jsDate = new Date(yStart, mStart + _months + 1, 0);
4928
+ const jsDate = new DateTime(yStart, mStart + _months + 1, 0);
4878
4929
  const delta = jsDate.getTime() - INITIAL_1900_DAY.getTime();
4879
4930
  return Math.round(delta / 86400000);
4880
4931
  },
@@ -4914,17 +4965,17 @@ const ISOWEEKNUM = {
4914
4965
  // The first week of the year is the week that contains the first
4915
4966
  // Thursday of the year.
4916
4967
  let firstThursday = 1;
4917
- while (new Date(y, 0, firstThursday).getDay() !== 4) {
4968
+ while (new DateTime(y, 0, firstThursday).getDay() !== 4) {
4918
4969
  firstThursday += 1;
4919
4970
  }
4920
- const firstDayOfFirstWeek = new Date(y, 0, firstThursday - 3);
4971
+ const firstDayOfFirstWeek = new DateTime(y, 0, firstThursday - 3);
4921
4972
  // The last week of the year is the week that contains the last Thursday of
4922
4973
  // the year.
4923
4974
  let lastThursday = 31;
4924
- while (new Date(y, 11, lastThursday).getDay() !== 4) {
4975
+ while (new DateTime(y, 11, lastThursday).getDay() !== 4) {
4925
4976
  lastThursday -= 1;
4926
4977
  }
4927
- const lastDayOfLastWeek = new Date(y, 11, lastThursday + 3);
4978
+ const lastDayOfLastWeek = new DateTime(y, 11, lastThursday + 3);
4928
4979
  // B - If our date > lastDayOfLastWeek then it's in the weeks of the year after
4929
4980
  // If our date < firstDayOfFirstWeek then it's in the weeks of the year before
4930
4981
  let offsetYear;
@@ -4950,17 +5001,17 @@ const ISOWEEKNUM = {
4950
5001
  case 1:
4951
5002
  // firstDay is the 1st day of the 1st week of the year after
4952
5003
  // firstDay = lastDayOfLastWeek + 1 Day
4953
- firstDay = new Date(y, 11, lastThursday + 3 + 1);
5004
+ firstDay = new DateTime(y, 11, lastThursday + 3 + 1);
4954
5005
  break;
4955
5006
  case -1:
4956
5007
  // firstDay is the 1st day of the 1st week of the previous year.
4957
5008
  // The first week of the previous year is the week that contains the
4958
5009
  // first Thursday of the previous year.
4959
5010
  let firstThursdayPreviousYear = 1;
4960
- while (new Date(y - 1, 0, firstThursdayPreviousYear).getDay() !== 4) {
5011
+ while (new DateTime(y - 1, 0, firstThursdayPreviousYear).getDay() !== 4) {
4961
5012
  firstThursdayPreviousYear += 1;
4962
5013
  }
4963
- firstDay = new Date(y - 1, 0, firstThursdayPreviousYear - 3);
5014
+ firstDay = new DateTime(y - 1, 0, firstThursdayPreviousYear - 3);
4964
5015
  break;
4965
5016
  }
4966
5017
  const dif = (_date.getTime() - firstDay.getTime()) / 86400000;
@@ -5099,8 +5150,8 @@ const NETWORKDAYS_INTL = {
5099
5150
  });
5100
5151
  }
5101
5152
  const invertDate = _startDate.getTime() > _endDate.getTime();
5102
- const stopDate = new Date((invertDate ? _startDate : _endDate).getTime());
5103
- let stepDate = new Date((invertDate ? _endDate : _startDate).getTime());
5153
+ const stopDate = DateTime.fromTimestamp((invertDate ? _startDate : _endDate).getTime());
5154
+ let stepDate = DateTime.fromTimestamp((invertDate ? _endDate : _startDate).getTime());
5104
5155
  const timeStopDate = stopDate.getTime();
5105
5156
  let timeStepDate = stepDate.getTime();
5106
5157
  let netWorkingDay = 0;
@@ -5124,8 +5175,7 @@ const NOW = {
5124
5175
  returns: ["DATE"],
5125
5176
  returnFormat: { specificFormat: "m/d/yyyy hh:mm:ss" },
5126
5177
  compute: function () {
5127
- let today = new Date();
5128
- today.setMilliseconds(0);
5178
+ let today = DateTime.now();
5129
5179
  const delta = today.getTime() - INITIAL_1900_DAY.getTime();
5130
5180
  const time = today.getHours() / 24 + today.getMinutes() / 1440 + today.getSeconds() / 86400;
5131
5181
  return Math.floor(delta / 86400000) + time;
@@ -5199,8 +5249,8 @@ const TODAY = {
5199
5249
  returns: ["DATE"],
5200
5250
  returnFormat: { specificFormat: "m/d/yyyy" },
5201
5251
  compute: function () {
5202
- const today = new Date();
5203
- const jsDate = new Date(today.getFullYear(), today.getMonth(), today.getDate());
5252
+ const today = DateTime.now();
5253
+ const jsDate = new DateTime(today.getFullYear(), today.getMonth(), today.getDate());
5204
5254
  const delta = jsDate.getTime() - INITIAL_1900_DAY.getTime();
5205
5255
  return Math.round(delta / 86400000);
5206
5256
  },
@@ -5256,10 +5306,10 @@ const WEEKNUM = {
5256
5306
  }
5257
5307
  const y = _date.getFullYear();
5258
5308
  let dayStart = 1;
5259
- let startDayOfFirstWeek = new Date(y, 0, dayStart);
5309
+ let startDayOfFirstWeek = new DateTime(y, 0, dayStart);
5260
5310
  while (startDayOfFirstWeek.getDay() !== startDayOfWeek) {
5261
5311
  dayStart += 1;
5262
- startDayOfFirstWeek = new Date(y, 0, dayStart);
5312
+ startDayOfFirstWeek = new DateTime(y, 0, dayStart);
5263
5313
  }
5264
5314
  const dif = (_date.getTime() - startDayOfFirstWeek.getTime()) / 86400000;
5265
5315
  if (dif < 0) {
@@ -5313,7 +5363,7 @@ const WORKDAY_INTL = {
5313
5363
  timesHoliday.add(holiday.getTime());
5314
5364
  });
5315
5365
  }
5316
- let stepDate = new Date(_startDate.getTime());
5366
+ let stepDate = DateTime.fromTimestamp(_startDate.getTime());
5317
5367
  let timeStepDate = stepDate.getTime();
5318
5368
  const unitDay = Math.sign(_numDays);
5319
5369
  let stepDay = Math.abs(_numDays);
@@ -9405,7 +9455,7 @@ class BordersPlugin extends CorePlugin {
9405
9455
  */
9406
9456
  function getBorderId(border) {
9407
9457
  for (let [key, value] of Object.entries(borders)) {
9408
- if (stringify(value) === stringify(border)) {
9458
+ if (deepEquals(value, border)) {
9409
9459
  return parseInt(key, 10);
9410
9460
  }
9411
9461
  }
@@ -9643,12 +9693,10 @@ class CellPlugin extends CorePlugin {
9643
9693
  * starting an async evaluation even if it has been moved or re-allocated
9644
9694
  */
9645
9695
  getCellById(cellId) {
9646
- for (const sheet of Object.values(this.cells)) {
9647
- if (sheet[cellId]) {
9648
- return sheet[cellId];
9649
- }
9650
- }
9651
- return undefined;
9696
+ // this must be as fast as possible
9697
+ const position = this.getters.getCellPosition(cellId);
9698
+ const sheet = this.cells[position.sheetId];
9699
+ return sheet[cellId];
9652
9700
  }
9653
9701
  /**
9654
9702
  * Try to infer the cell format based on the formula dependencies.
@@ -23413,9 +23461,9 @@ function revertChanges(revisions) {
23413
23461
  * Apply the changes of the given HistoryChange to the state
23414
23462
  */
23415
23463
  function applyChange(change, target) {
23416
- let val = change.root;
23417
- let key = change.path[change.path.length - 1];
23418
- for (let pathIndex = 0; pathIndex < change.path.slice(0, -1).length; pathIndex++) {
23464
+ let val = change.path[0];
23465
+ const key = change.path[change.path.length - 1];
23466
+ for (let pathIndex = 1; pathIndex < change.path.slice(0, -1).length; pathIndex++) {
23419
23467
  const p = change.path[pathIndex];
23420
23468
  if (val[p] === undefined) {
23421
23469
  const nextPath = change.path[pathIndex + 1];
@@ -23871,13 +23919,14 @@ class StateObserver {
23871
23919
  }
23872
23920
  addChange(...args) {
23873
23921
  const val = args.pop();
23874
- const [root, ...path] = args;
23922
+ const root = args[0];
23875
23923
  let value = root;
23876
- let key = path[path.length - 1];
23877
- for (let pathIndex = 0; pathIndex <= path.length - 2; pathIndex++) {
23878
- const p = path[pathIndex];
23924
+ let key = args[args.length - 1];
23925
+ const pathLength = args.length - 2;
23926
+ for (let pathIndex = 1; pathIndex <= pathLength; pathIndex++) {
23927
+ const p = args[pathIndex];
23879
23928
  if (value[p] === undefined) {
23880
- const nextPath = path[pathIndex + 1];
23929
+ const nextPath = args[pathIndex + 1];
23881
23930
  value[p] = createEmptyStructure(nextPath);
23882
23931
  }
23883
23932
  value = value[p];
@@ -23886,8 +23935,7 @@ class StateObserver {
23886
23935
  return;
23887
23936
  }
23888
23937
  this.changes.push({
23889
- root,
23890
- path,
23938
+ path: args,
23891
23939
  before: value[key],
23892
23940
  after: val,
23893
23941
  });
@@ -25927,7 +25975,10 @@ class Model extends owl__namespace.core.EventBus {
25927
25975
  */
25928
25976
  checkDispatchAllowed(command) {
25929
25977
  const results = this.handlers.map((handler) => handler.allowDispatch(command));
25930
- return new DispatchResult(results.flat());
25978
+ if (results.some((r) => r !== 0 /* Success */)) {
25979
+ return new DispatchResult(results.flat());
25980
+ }
25981
+ return DispatchResult.Success;
25931
25982
  }
25932
25983
  finalize() {
25933
25984
  this.status = 3 /* Finalizing */;
@@ -31389,6 +31440,6 @@ exports.registries = registries;
31389
31440
  exports.setTranslationMethod = setTranslationMethod;
31390
31441
 
31391
31442
 
31392
- __info__.version = '15.2.12';
31393
- __info__.date = '2023-11-16T11:57:04.728Z';
31394
- __info__.hash = 'd0fd7c1';
31443
+ __info__.version = '15.2.14';
31444
+ __info__.date = '2024-01-12T13:37:05.872Z';
31445
+ __info__.hash = '6515f7c';
@@ -888,7 +888,7 @@ declare class StateObserver {
888
888
  commands: CoreCommand[];
889
889
  };
890
890
  addCommand(command: CoreCommand): void;
891
- addChange(...args: any[]): void;
891
+ addChange(...args: [...HistoryChange["path"], any]): void;
892
892
  }
893
893
 
894
894
  /**
@@ -3803,8 +3803,7 @@ declare type EvalContext = {
3803
3803
  };
3804
3804
 
3805
3805
  interface HistoryChange {
3806
- root: any;
3807
- path: (string | number)[];
3806
+ path: [any, ...(number | string)[]];
3808
3807
  before: any;
3809
3808
  after: any;
3810
3809
  }