@odoo/o-spreadsheet 17.0.31 → 17.0.33

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.
@@ -2,9 +2,9 @@
2
2
  /**
3
3
  * This file is generated by o-spreadsheet build tools. Do not edit it.
4
4
  * @see https://github.com/odoo/o-spreadsheet
5
- * @version 17.0.31
6
- * @date 2024-07-18T14:22:00.324Z
7
- * @hash c64db0d
5
+ * @version 17.0.33
6
+ * @date 2024-08-02T08:18:55.061Z
7
+ * @hash 4ad4049
8
8
  */
9
9
 
10
10
  import { Component, useRef, onMounted, useEffect, onWillPatch, useState, onWillUpdateProps, onPatched, useComponent, useExternalListener, onWillUnmount, onWillStart, xml, useChildSubEnv, useSubEnv, markRaw } from '@odoo/owl';
@@ -592,7 +592,7 @@ function debounce(func, wait, immediate) {
592
592
  let timeout;
593
593
  return function () {
594
594
  const context = this;
595
- const args = arguments;
595
+ const args = Array.from(arguments);
596
596
  function later() {
597
597
  timeout = null;
598
598
  if (!immediate) {
@@ -795,7 +795,7 @@ function isNumberBetween(value, min, max) {
795
795
  */
796
796
  function largeMax(array) {
797
797
  let len = array.length;
798
- if (len < 100000)
798
+ if (len < 100_000)
799
799
  return Math.max(...array);
800
800
  let max = -Infinity;
801
801
  while (len--) {
@@ -809,7 +809,7 @@ function largeMax(array) {
809
809
  */
810
810
  function largeMin(array) {
811
811
  let len = array.length;
812
- if (len < 100000)
812
+ if (len < 100_000)
813
813
  return Math.min(...array);
814
814
  let min = +Infinity;
815
815
  while (len--) {
@@ -2564,13 +2564,16 @@ function operandToRegExp(operand) {
2564
2564
  }
2565
2565
  return new RegExp("^" + exp + "$", "i");
2566
2566
  }
2567
- function evaluatePredicate(value = "", criterion) {
2567
+ function evaluatePredicate(value = "", criterion, locale) {
2568
2568
  const { operator, operand } = criterion;
2569
2569
  if (operand === undefined || value === null || operand === null) {
2570
2570
  return false;
2571
2571
  }
2572
2572
  if (typeof operand === "number" && operator === "=") {
2573
- return toString(value) === toString(operand);
2573
+ if (typeof value === "string" && (isNumber(value, locale) || isDateTime(value, locale))) {
2574
+ return toNumber(value, locale) === operand;
2575
+ }
2576
+ return value === operand;
2574
2577
  }
2575
2578
  if (operator === "<>" || operator === "=") {
2576
2579
  let result;
@@ -2649,7 +2652,7 @@ function visitMatchingRanges(args, cb, locale, isQuery = false) {
2649
2652
  for (let k = 0; k < countArg - 1; k += 2) {
2650
2653
  const criteriaValue = args[k][i][j];
2651
2654
  const criterion = predicates[k / 2];
2652
- validatedPredicates = evaluatePredicate(criteriaValue ?? undefined, criterion);
2655
+ validatedPredicates = evaluatePredicate(criteriaValue ?? undefined, criterion, locale);
2653
2656
  if (!validatedPredicates) {
2654
2657
  break;
2655
2658
  }
@@ -5898,7 +5901,7 @@ function textCell(value, localeFormat) {
5898
5901
  function numberCell(value, localeFormat) {
5899
5902
  return {
5900
5903
  type: CellValueType.number,
5901
- value: value || 0,
5904
+ value: value || 0, // necessary to avoid "-0" and NaN values,
5902
5905
  format: localeFormat.format,
5903
5906
  isAutoSummable: true,
5904
5907
  defaultAlign: "right",
@@ -8431,8 +8434,8 @@ function truncateLabel(label) {
8431
8434
  function getDefaultChartJsRuntime(chart, labels, fontColor, { format, locale, truncateLabels = true }) {
8432
8435
  const options = {
8433
8436
  // https://www.chartjs.org/docs/latest/general/responsive.html
8434
- responsive: true,
8435
- maintainAspectRatio: false,
8437
+ responsive: true, // will resize when its container is resized
8438
+ maintainAspectRatio: false, // doesn't maintain the aspect ration (width/height =2 by default) so the user has the choice of the exact layout
8436
8439
  layout: {
8437
8440
  padding: { left: 20, right: 20, top: chart.title ? 10 : 25, bottom: 10 },
8438
8441
  },
@@ -8741,7 +8744,7 @@ function getBarConfiguration(chart, labels, localeFormat) {
8741
8744
  },
8742
8745
  y: {
8743
8746
  position: chart.verticalAxisPosition,
8744
- beginAtZero: true,
8747
+ beginAtZero: true, // the origin of the y axis is always zero
8745
8748
  ticks: {
8746
8749
  color: fontColor,
8747
8750
  callback: (value) => {
@@ -9395,7 +9398,7 @@ function getLineConfiguration(chart, labels, options) {
9395
9398
  },
9396
9399
  y: {
9397
9400
  position: chart.verticalAxisPosition,
9398
- beginAtZero: true,
9401
+ beginAtZero: true, // the origin of the y axis is always zero
9399
9402
  ticks: {
9400
9403
  color: fontColor,
9401
9404
  callback: (value) => {
@@ -9483,7 +9486,7 @@ function createLineChartRuntime(chart, getters) {
9483
9486
  const dataset = {
9484
9487
  label,
9485
9488
  data,
9486
- tension: 0,
9489
+ tension: 0, // 0 -> render straight lines, which is much faster
9487
9490
  borderColor: color,
9488
9491
  backgroundColor,
9489
9492
  pointBackgroundColor: color,
@@ -9594,7 +9597,7 @@ class PieChart extends AbstractChart {
9594
9597
  ...this.getDefinition(),
9595
9598
  backgroundColor: toXlsxHexColor(this.background || BACKGROUND_CHART_COLOR),
9596
9599
  fontColor: toXlsxHexColor(chartFontColor(this.background)),
9597
- verticalAxisPosition: "left",
9600
+ verticalAxisPosition: "left", //TODO ExcelChartDefinition should be adapted, but can be done later
9598
9601
  dataSets,
9599
9602
  labelRange,
9600
9603
  };
@@ -26698,7 +26701,9 @@ class Composer extends Component {
26698
26701
  argToFocus: 0,
26699
26702
  });
26700
26703
  compositionActive = false;
26704
+ spreadsheetRect = useSpreadsheetRect();
26701
26705
  get assistantStyle() {
26706
+ const composerRect = this.composerRef.el.getBoundingClientRect();
26702
26707
  const assistantStyle = {};
26703
26708
  assistantStyle["min-width"] = `${this.props.rect?.width || ASSISTANT_WIDTH}px`;
26704
26709
  if (this.autoCompleteState.type === "function") {
@@ -26721,6 +26726,10 @@ class Composer extends Component {
26721
26726
  assistantStyle.right = `0px`;
26722
26727
  }
26723
26728
  }
26729
+ if (this.props.focus === "contentFocus" &&
26730
+ composerRect.left + ASSISTANT_WIDTH > this.spreadsheetRect.width) {
26731
+ assistantStyle.right = "0px";
26732
+ }
26724
26733
  return cssPropertiesToCss(assistantStyle);
26725
26734
  }
26726
26735
  // we can't allow input events to be triggered while we remove and add back the content of the composer in processContent
@@ -28369,10 +28378,13 @@ function useCellHovered(env, gridRef, callback) {
28369
28378
  row: undefined,
28370
28379
  };
28371
28380
  const { Date } = window;
28372
- let x = 0;
28373
- let y = 0;
28381
+ let x = undefined;
28382
+ let y = undefined;
28374
28383
  let lastMoved = 0;
28375
28384
  function getPosition() {
28385
+ if (x === undefined || y === undefined) {
28386
+ return { col: -1, row: -1 };
28387
+ }
28376
28388
  const col = env.model.getters.getColIndex(x);
28377
28389
  const row = env.model.getters.getRowIndex(y);
28378
28390
  return { col, row };
@@ -29576,7 +29588,7 @@ class VerticalScrollBar extends Component {
29576
29588
  onScroll(offset) {
29577
29589
  const { scrollX } = this.env.model.getters.getActiveSheetDOMScrollInfo();
29578
29590
  this.env.model.dispatch("SET_VIEWPORT_OFFSET", {
29579
- offsetX: scrollX,
29591
+ offsetX: scrollX, // offsetX is the same
29580
29592
  offsetY: offset,
29581
29593
  });
29582
29594
  }
@@ -29787,8 +29799,8 @@ class Grid extends Component {
29787
29799
  "CTRL+SHIFT+L": () => this.setHorizontalAlign("left"),
29788
29800
  "CTRL+SHIFT+R": () => this.setHorizontalAlign("right"),
29789
29801
  "CTRL+SHIFT+V": () => PASTE_VALUE_ACTION(this.env),
29790
- "CTRL+SHIFT+<": () => this.clearFormatting(),
29791
- "CTRL+<": () => this.clearFormatting(),
29802
+ "CTRL+SHIFT+<": () => this.clearFormatting(), // for qwerty
29803
+ "CTRL+<": () => this.clearFormatting(), // for azerty
29792
29804
  "CTRL+SHIFT+ ": () => {
29793
29805
  this.env.model.selection.selectAll();
29794
29806
  },
@@ -30602,10 +30614,10 @@ function convertCFCellIsOperator(xlsxCfOperator) {
30602
30614
  const CF_TYPE_CONVERSION_MAP = {
30603
30615
  aboveAverage: undefined,
30604
30616
  expression: undefined,
30605
- cellIs: undefined,
30606
- colorScale: undefined,
30617
+ cellIs: undefined, // exist but isn't an operator in o_spreadsheet
30618
+ colorScale: undefined, // exist but isn't an operator in o_spreadsheet
30607
30619
  dataBar: undefined,
30608
- iconSet: undefined,
30620
+ iconSet: undefined, // exist but isn't an operator in o_spreadsheet
30609
30621
  top10: undefined,
30610
30622
  uniqueValues: undefined,
30611
30623
  duplicateValues: undefined,
@@ -30840,7 +30852,7 @@ const XLSX_INDEXED_COLORS = {
30840
30852
  61: "993366",
30841
30853
  62: "333399",
30842
30854
  63: "333333",
30843
- 64: "000000",
30855
+ 64: "000000", // system foreground
30844
30856
  65: "FFFFFF", // system background
30845
30857
  };
30846
30858
  const IMAGE_MIMETYPE_TO_EXTENSION_MAPPING = {
@@ -34481,12 +34493,13 @@ class BordersPlugin extends CorePlugin {
34481
34493
  this.clearBorders(cmd.sheetId, cmd.target);
34482
34494
  break;
34483
34495
  case "REMOVE_COLUMNS_ROWS":
34484
- for (let el of [...cmd.elements].sort((a, b) => b - a)) {
34496
+ const elements = [...cmd.elements].sort((a, b) => b - a);
34497
+ for (const group of groupConsecutive(elements)) {
34485
34498
  if (cmd.dimension === "COL") {
34486
- this.shiftBordersHorizontally(cmd.sheetId, el + 1, -1);
34499
+ this.shiftBordersHorizontally(cmd.sheetId, group[group.length - 1] + 1, -group.length);
34487
34500
  }
34488
34501
  else {
34489
- this.shiftBordersVertically(cmd.sheetId, el + 1, -1);
34502
+ this.shiftBordersVertically(cmd.sheetId, group[group.length - 1] + 1, -group.length);
34490
34503
  }
34491
34504
  }
34492
34505
  break;
@@ -39547,12 +39560,7 @@ class SheetPlugin extends CorePlugin {
39547
39560
  });
39548
39561
  }
39549
39562
  if (colIndex > deletedColumn) {
39550
- this.dispatch("UPDATE_CELL_POSITION", {
39551
- sheetId: sheet.id,
39552
- cellId: cellId,
39553
- col: colIndex - 1,
39554
- row: rowIndex,
39555
- });
39563
+ this.setNewPosition(cellId, sheet.id, colIndex - 1, rowIndex);
39556
39564
  }
39557
39565
  }
39558
39566
  }
@@ -39562,7 +39570,7 @@ class SheetPlugin extends CorePlugin {
39562
39570
  * Move the cells after a column or rows insertion
39563
39571
  */
39564
39572
  moveCellsOnAddition(sheet, addedElement, quantity, dimension) {
39565
- const commands = [];
39573
+ const updates = [];
39566
39574
  for (let rowIndex = 0; rowIndex < sheet.rows.length; rowIndex++) {
39567
39575
  const row = sheet.rows[rowIndex];
39568
39576
  if (dimension !== "rows" || rowIndex >= addedElement) {
@@ -39571,20 +39579,20 @@ class SheetPlugin extends CorePlugin {
39571
39579
  const cellId = row.cells[i];
39572
39580
  if (cellId) {
39573
39581
  if (dimension === "rows" || colIndex >= addedElement) {
39574
- commands.push({
39575
- type: "UPDATE_CELL_POSITION",
39582
+ updates.push({
39576
39583
  sheetId: sheet.id,
39577
39584
  cellId: cellId,
39578
39585
  col: colIndex + (dimension === "columns" ? quantity : 0),
39579
39586
  row: rowIndex + (dimension === "rows" ? quantity : 0),
39587
+ type: "UPDATE_CELL_POSITION",
39580
39588
  });
39581
39589
  }
39582
39590
  }
39583
39591
  }
39584
39592
  }
39585
39593
  }
39586
- for (let cmd of commands.reverse()) {
39587
- this.dispatch(cmd.type, cmd);
39594
+ for (let update of updates.reverse()) {
39595
+ this.updateCellPosition(update);
39588
39596
  }
39589
39597
  }
39590
39598
  /**
@@ -39617,12 +39625,7 @@ class SheetPlugin extends CorePlugin {
39617
39625
  const colIndex = Number(i);
39618
39626
  const cellId = row.cells[i];
39619
39627
  if (cellId) {
39620
- this.dispatch("UPDATE_CELL_POSITION", {
39621
- sheetId: sheet.id,
39622
- cellId: cellId,
39623
- col: colIndex,
39624
- row: rowIndex - numberRows,
39625
- });
39628
+ this.setNewPosition(cellId, sheet.id, colIndex, rowIndex - numberRows);
39626
39629
  }
39627
39630
  }
39628
39631
  }
@@ -42235,17 +42238,17 @@ function isBadExpression(tokens) {
42235
42238
  */
42236
42239
  function sortWithClusters(colorsToSort) {
42237
42240
  const clusters = [
42238
- { leadColor: rgba(255, 0, 0), colors: [] },
42239
- { leadColor: rgba(255, 128, 0), colors: [] },
42240
- { leadColor: rgba(128, 128, 0), colors: [] },
42241
- { leadColor: rgba(128, 255, 0), colors: [] },
42242
- { leadColor: rgba(0, 255, 0), colors: [] },
42243
- { leadColor: rgba(0, 255, 128), colors: [] },
42244
- { leadColor: rgba(0, 255, 255), colors: [] },
42245
- { leadColor: rgba(0, 127, 255), colors: [] },
42246
- { leadColor: rgba(0, 0, 255), colors: [] },
42247
- { leadColor: rgba(127, 0, 255), colors: [] },
42248
- { leadColor: rgba(128, 0, 128), colors: [] },
42241
+ { leadColor: rgba(255, 0, 0), colors: [] }, // red
42242
+ { leadColor: rgba(255, 128, 0), colors: [] }, // orange
42243
+ { leadColor: rgba(128, 128, 0), colors: [] }, // yellow
42244
+ { leadColor: rgba(128, 255, 0), colors: [] }, // chartreuse
42245
+ { leadColor: rgba(0, 255, 0), colors: [] }, // green
42246
+ { leadColor: rgba(0, 255, 128), colors: [] }, // spring green
42247
+ { leadColor: rgba(0, 255, 255), colors: [] }, // cyan
42248
+ { leadColor: rgba(0, 127, 255), colors: [] }, // azure
42249
+ { leadColor: rgba(0, 0, 255), colors: [] }, // blue
42250
+ { leadColor: rgba(127, 0, 255), colors: [] }, // violet
42251
+ { leadColor: rgba(128, 0, 128), colors: [] }, // magenta
42249
42252
  { leadColor: rgba(255, 0, 128), colors: [] }, // rose
42250
42253
  ];
42251
42254
  for (const color of colorsToSort.map(colorToRGBA)) {
@@ -42551,40 +42554,45 @@ class EvaluationConditionalFormatPlugin extends UIPlugin {
42551
42554
  getComputedStyles(sheetId) {
42552
42555
  const computedStyle = {};
42553
42556
  for (let cf of this.getters.getConditionalFormats(sheetId).reverse()) {
42554
- switch (cf.rule.type) {
42555
- case "ColorScaleRule":
42556
- for (let range of cf.ranges) {
42557
- this.applyColorScale(sheetId, range, cf.rule, computedStyle);
42558
- }
42559
- break;
42560
- case "CellIsRule":
42561
- const formulas = cf.rule.values.map((value) => value.startsWith("=") ? compile(value) : undefined);
42562
- for (let ref of cf.ranges) {
42563
- const zone = this.getters.getRangeFromSheetXC(sheetId, ref).zone;
42564
- for (let row = zone.top; row <= zone.bottom; row++) {
42565
- for (let col = zone.left; col <= zone.right; col++) {
42566
- const predicate = this.rulePredicate[cf.rule.type];
42567
- const target = { sheetId, col, row };
42568
- const values = cf.rule.values.map((value, i) => {
42569
- const compiledFormula = formulas[i];
42570
- if (compiledFormula) {
42571
- return this.getters.getTranslatedCellFormula(sheetId, col - zone.left, row - zone.top, {
42572
- ...compiledFormula,
42573
- dependencies: compiledFormula.dependencies.map((d) => this.getters.getRangeFromSheetXC(sheetId, d)),
42574
- });
42557
+ try {
42558
+ switch (cf.rule.type) {
42559
+ case "ColorScaleRule":
42560
+ for (let range of cf.ranges) {
42561
+ this.applyColorScale(sheetId, range, cf.rule, computedStyle);
42562
+ }
42563
+ break;
42564
+ case "CellIsRule":
42565
+ const formulas = cf.rule.values.map((value) => value.startsWith("=") ? compile(value) : undefined);
42566
+ for (let ref of cf.ranges) {
42567
+ const zone = this.getters.getRangeFromSheetXC(sheetId, ref).zone;
42568
+ for (let row = zone.top; row <= zone.bottom; row++) {
42569
+ for (let col = zone.left; col <= zone.right; col++) {
42570
+ const predicate = this.rulePredicate[cf.rule.type];
42571
+ const target = { sheetId, col, row };
42572
+ const values = cf.rule.values.map((value, i) => {
42573
+ const compiledFormula = formulas[i];
42574
+ if (compiledFormula) {
42575
+ return this.getters.getTranslatedCellFormula(sheetId, col - zone.left, row - zone.top, {
42576
+ ...compiledFormula,
42577
+ dependencies: compiledFormula.dependencies.map((d) => this.getters.getRangeFromSheetXC(sheetId, d)),
42578
+ });
42579
+ }
42580
+ return value;
42581
+ });
42582
+ if (predicate && predicate(target, { ...cf.rule, values })) {
42583
+ if (!computedStyle[col])
42584
+ computedStyle[col] = [];
42585
+ // we must combine all the properties of all the CF rules applied to the given cell
42586
+ computedStyle[col][row] = Object.assign(computedStyle[col]?.[row] || {}, cf.rule.style);
42575
42587
  }
42576
- return value;
42577
- });
42578
- if (predicate && predicate(target, { ...cf.rule, values })) {
42579
- if (!computedStyle[col])
42580
- computedStyle[col] = [];
42581
- // we must combine all the properties of all the CF rules applied to the given cell
42582
- computedStyle[col][row] = Object.assign(computedStyle[col]?.[row] || {}, cf.rule.style);
42583
42588
  }
42584
42589
  }
42585
42590
  }
42586
- }
42587
- break;
42591
+ break;
42592
+ }
42593
+ }
42594
+ catch (_) {
42595
+ // we ignore the errors within the evaluation of a rule
42588
42596
  }
42589
42597
  }
42590
42598
  return computedStyle;
@@ -52150,7 +52158,7 @@ class BottomBar extends Component {
52150
52158
  .map((sheetEl) => sheetEl.getBoundingClientRect())
52151
52159
  .map((rect) => ({
52152
52160
  x: rect.x,
52153
- width: rect.width - 1,
52161
+ width: rect.width - 1, // -1 to compensate negative margin
52154
52162
  y: rect.y,
52155
52163
  height: rect.height,
52156
52164
  }));
@@ -52375,7 +52383,7 @@ class RowGroup extends AbstractHeaderGroup {
52375
52383
  }
52376
52384
  return cssPropertiesToCss({
52377
52385
  top: `${groupBox.headerRect.height / 2}px`,
52378
- left: `calc(50% - 1px)`,
52386
+ left: `calc(50% - 1px)`, // -1px: we want the border to be on the center
52379
52387
  width: `30%`,
52380
52388
  height: `calc(100% - ${groupBox.headerRect.height / 2}px)`,
52381
52389
  "border-left": `1px solid ${HEADER_GROUPING_BORDER_COLOR}`,
@@ -52427,7 +52435,7 @@ class ColGroup extends AbstractHeaderGroup {
52427
52435
  return "";
52428
52436
  }
52429
52437
  return cssPropertiesToCss({
52430
- top: `calc(50% - 1px)`,
52438
+ top: `calc(50% - 1px)`, // -1px: we want the border to be on the center
52431
52439
  left: `${groupBox.headerRect.width / 2}px`,
52432
52440
  width: `calc(100% - ${groupBox.headerRect.width / 2}px)`,
52433
52441
  height: `30%`,
@@ -53828,7 +53836,7 @@ class Spreadsheet extends Component {
53828
53836
  const gridColSize = GROUP_LAYER_WIDTH * this.rowLayers.length;
53829
53837
  const gridRowSize = GROUP_LAYER_WIDTH * this.colLayers.length;
53830
53838
  return cssPropertiesToCss({
53831
- "grid-template-columns": `${gridColSize ? gridColSize + 2 : 0}px auto`,
53839
+ "grid-template-columns": `${gridColSize ? gridColSize + 2 : 0}px auto`, // +2: margins
53832
53840
  "grid-template-rows": `${gridRowSize ? gridRowSize + 2 : 0}px auto`,
53833
53841
  });
53834
53842
  }
@@ -55331,7 +55339,7 @@ class SelectionStreamProcessorImpl {
55331
55339
  }
55332
55340
 
55333
55341
  class StateObserver {
55334
- changes = [];
55342
+ changes;
55335
55343
  commands = [];
55336
55344
  /**
55337
55345
  * Record the changes which could happen in the given callback, save them in a
@@ -55363,7 +55371,7 @@ class StateObserver {
55363
55371
  if (value[key] === val) {
55364
55372
  return;
55365
55373
  }
55366
- this.changes.push({
55374
+ this.changes?.push({
55367
55375
  path: args,
55368
55376
  before: value[key],
55369
55377
  after: val,
@@ -56494,7 +56502,7 @@ function addTableColumns(table, sheetData) {
56494
56502
  const colHeaderXc = toXC(tableZone.left + i, tableZone.top);
56495
56503
  const colName = sheetData.cells[colHeaderXc]?.content || `col${i}`;
56496
56504
  const colAttributes = [
56497
- ["id", i + 1],
56505
+ ["id", i + 1], // id cannot be 0
56498
56506
  ["name", colName],
56499
56507
  ];
56500
56508
  columns.push(escapeXml /*xml*/ `<tableColumn ${formatAttributes(colAttributes)}/>`);
@@ -57541,6 +57549,6 @@ const constants = {
57541
57549
  export { AbstractChart, CommandResult, CorePlugin, DispatchResult, EvaluationError, Model, Registry, Revision, SPREADSHEET_DIMENSIONS, Spreadsheet, UIPlugin, __info__, addFunction, astToFormula, compile, compileTokens, components, constants, convertAstNodes, coreTypes, findCellInNewZone, functionCache, helpers, hooks, invalidateCFEvaluationCommands, invalidateDependenciesCommands, invalidateEvaluationCommands, iterateAstNodes, links, load, parse, parseTokens, readonlyAllowedCommands, registries, setDefaultSheetViewSize, setTranslationMethod, tokenize };
57542
57550
 
57543
57551
 
57544
- __info__.version = "17.0.31";
57545
- __info__.date = "2024-07-18T14:22:00.324Z";
57546
- __info__.hash = "c64db0d";
57552
+ __info__.version = "17.0.33";
57553
+ __info__.date = "2024-08-02T08:18:55.061Z";
57554
+ __info__.hash = "4ad4049";