@odoo/o-spreadsheet 17.4.9 → 17.4.11
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.
- package/dist/o-spreadsheet.cjs.js +150 -82
- package/dist/o-spreadsheet.d.ts +3 -0
- package/dist/o-spreadsheet.esm.js +150 -82
- package/dist/o-spreadsheet.iife.js +150 -82
- package/dist/o-spreadsheet.iife.min.js +345 -345
- package/dist/o_spreadsheet.xml +4 -4
- package/package.json +1 -1
|
@@ -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.4.
|
|
6
|
-
* @date 2024-
|
|
7
|
-
* @hash
|
|
5
|
+
* @version 17.4.11
|
|
6
|
+
* @date 2024-11-08T12:15:26.239Z
|
|
7
|
+
* @hash 2eb3f1c
|
|
8
8
|
*/
|
|
9
9
|
|
|
10
10
|
import { useEnv, useSubEnv, onWillUnmount, useComponent, status, Component, useRef, onMounted, useEffect, useState, onPatched, onWillPatch, onWillUpdateProps, useExternalListener, onWillStart, xml, useChildSubEnv, markRaw, toRaw } from '@odoo/owl';
|
|
@@ -7625,7 +7625,10 @@ function parseOperand(tokens) {
|
|
|
7625
7625
|
case "STRING":
|
|
7626
7626
|
return { type: "STRING", value: removeStringQuotes(current.value) };
|
|
7627
7627
|
case "INVALID_REFERENCE":
|
|
7628
|
-
|
|
7628
|
+
return {
|
|
7629
|
+
type: "REFERENCE",
|
|
7630
|
+
value: CellErrorType.InvalidReference,
|
|
7631
|
+
};
|
|
7629
7632
|
case "REFERENCE":
|
|
7630
7633
|
if (tokens[0]?.value === ":" && tokens[1]?.type === "REFERENCE") {
|
|
7631
7634
|
tokens.shift();
|
|
@@ -8429,6 +8432,7 @@ const ChartTerms = {
|
|
|
8429
8432
|
StackedBarChart: _t("Stacked bar chart"),
|
|
8430
8433
|
StackedLineChart: _t("Stacked line chart"),
|
|
8431
8434
|
StackedAreaChart: _t("Stacked area chart"),
|
|
8435
|
+
StackedColumnChart: _t("Stacked column chart"),
|
|
8432
8436
|
CumulativeData: _t("Cumulative data"),
|
|
8433
8437
|
TreatLabelsAsText: _t("Treat labels as text"),
|
|
8434
8438
|
AggregatedChart: _t("Aggregate"),
|
|
@@ -10623,67 +10627,112 @@ const chartShowValuesPlugin = {
|
|
|
10623
10627
|
ctx.save();
|
|
10624
10628
|
ctx.textAlign = "center";
|
|
10625
10629
|
ctx.textBaseline = "middle";
|
|
10626
|
-
ctx.
|
|
10627
|
-
|
|
10628
|
-
|
|
10629
|
-
|
|
10630
|
-
|
|
10631
|
-
|
|
10632
|
-
|
|
10633
|
-
|
|
10634
|
-
|
|
10635
|
-
|
|
10636
|
-
|
|
10637
|
-
|
|
10638
|
-
|
|
10639
|
-
ctx.fillStyle = chartFontColor(bar.options.backgroundColor);
|
|
10640
|
-
ctx.strokeStyle = chartFontColor(ctx.fillStyle);
|
|
10641
|
-
const value = options.callback(dataset._parsed[i]);
|
|
10642
|
-
ctx.strokeText(value, x, y);
|
|
10643
|
-
ctx.fillText(value, x, y);
|
|
10644
|
-
}
|
|
10645
|
-
break;
|
|
10646
|
-
}
|
|
10647
|
-
case "bar":
|
|
10648
|
-
case "line": {
|
|
10649
|
-
const yOffset = dataset.type === "bar" && !options.horizontal ? 0 : 3;
|
|
10650
|
-
for (let i = 0; i < dataset._parsed.length; i++) {
|
|
10651
|
-
const point = dataset.data[i];
|
|
10652
|
-
const value = options.horizontal ? dataset._parsed[i].x : dataset._parsed[i].y;
|
|
10653
|
-
const displayedValue = options.callback(value - 0);
|
|
10654
|
-
let xPosition = 0, yPosition = 0;
|
|
10655
|
-
if (options.horizontal) {
|
|
10656
|
-
yPosition = point.y;
|
|
10657
|
-
if (value < 0) {
|
|
10658
|
-
ctx.textAlign = "right";
|
|
10659
|
-
xPosition = point.x - yOffset;
|
|
10660
|
-
}
|
|
10661
|
-
else {
|
|
10662
|
-
ctx.textAlign = "left";
|
|
10663
|
-
xPosition = point.x + yOffset;
|
|
10664
|
-
}
|
|
10665
|
-
}
|
|
10666
|
-
else {
|
|
10667
|
-
xPosition = point.x;
|
|
10668
|
-
if (value < 0) {
|
|
10669
|
-
ctx.textBaseline = "top";
|
|
10670
|
-
yPosition = point.y + yOffset;
|
|
10671
|
-
}
|
|
10672
|
-
else {
|
|
10673
|
-
ctx.textBaseline = "bottom";
|
|
10674
|
-
yPosition = point.y - yOffset;
|
|
10675
|
-
}
|
|
10676
|
-
}
|
|
10677
|
-
ctx.strokeText(displayedValue, xPosition, yPosition);
|
|
10678
|
-
ctx.fillText(displayedValue, xPosition, yPosition);
|
|
10679
|
-
}
|
|
10680
|
-
break;
|
|
10681
|
-
}
|
|
10682
|
-
}
|
|
10683
|
-
});
|
|
10630
|
+
ctx.miterLimit = 1; // Avoid sharp artifacts on strokeText
|
|
10631
|
+
switch (chart.config.type) {
|
|
10632
|
+
case "pie":
|
|
10633
|
+
case "doughnut":
|
|
10634
|
+
drawPieChartValues(chart, options, ctx);
|
|
10635
|
+
break;
|
|
10636
|
+
case "bar":
|
|
10637
|
+
case "line":
|
|
10638
|
+
options.horizontal
|
|
10639
|
+
? drawHorizontalBarChartValues(chart, options, ctx)
|
|
10640
|
+
: drawLineOrBarChartValues(chart, options, ctx);
|
|
10641
|
+
break;
|
|
10642
|
+
}
|
|
10684
10643
|
ctx.restore();
|
|
10685
10644
|
},
|
|
10686
10645
|
};
|
|
10646
|
+
function drawTextWithBackground(text, x, y, ctx) {
|
|
10647
|
+
ctx.lineWidth = 3; // Stroke the text with a big lineWidth width to have some kind of background
|
|
10648
|
+
ctx.strokeText(text, x, y);
|
|
10649
|
+
ctx.lineWidth = 1;
|
|
10650
|
+
ctx.fillText(text, x, y);
|
|
10651
|
+
}
|
|
10652
|
+
function drawLineOrBarChartValues(chart, options, ctx) {
|
|
10653
|
+
const yMax = chart.chartArea.bottom;
|
|
10654
|
+
const yMin = chart.chartArea.top;
|
|
10655
|
+
const textsPositions = {};
|
|
10656
|
+
for (const dataset of chart._metasets) {
|
|
10657
|
+
for (let i = 0; i < dataset._parsed.length; i++) {
|
|
10658
|
+
const value = dataset._parsed[i].y;
|
|
10659
|
+
const point = dataset.data[i];
|
|
10660
|
+
const xPosition = point.x;
|
|
10661
|
+
let yPosition = 0;
|
|
10662
|
+
if (chart.config.type === "line") {
|
|
10663
|
+
yPosition = point.y - 10;
|
|
10664
|
+
}
|
|
10665
|
+
else {
|
|
10666
|
+
yPosition = value < 0 ? point.y - point.height / 2 : point.y + point.height / 2;
|
|
10667
|
+
}
|
|
10668
|
+
yPosition = Math.min(yPosition, yMax);
|
|
10669
|
+
yPosition = Math.max(yPosition, yMin);
|
|
10670
|
+
// Avoid overlapping texts with same X
|
|
10671
|
+
if (!textsPositions[xPosition]) {
|
|
10672
|
+
textsPositions[xPosition] = [];
|
|
10673
|
+
}
|
|
10674
|
+
for (const otherPosition of textsPositions[xPosition] || []) {
|
|
10675
|
+
if (Math.abs(otherPosition - yPosition) < 13) {
|
|
10676
|
+
yPosition = otherPosition - 13;
|
|
10677
|
+
}
|
|
10678
|
+
}
|
|
10679
|
+
textsPositions[xPosition].push(yPosition);
|
|
10680
|
+
ctx.fillStyle = point.options.backgroundColor;
|
|
10681
|
+
ctx.strokeStyle = options.background || "#ffffff";
|
|
10682
|
+
drawTextWithBackground(options.callback(value - 0), xPosition, yPosition, ctx);
|
|
10683
|
+
}
|
|
10684
|
+
}
|
|
10685
|
+
}
|
|
10686
|
+
function drawHorizontalBarChartValues(chart, options, ctx) {
|
|
10687
|
+
const xMax = chart.chartArea.right;
|
|
10688
|
+
const xMin = chart.chartArea.left;
|
|
10689
|
+
const textsPositions = {};
|
|
10690
|
+
for (const dataset of chart._metasets) {
|
|
10691
|
+
for (let i = 0; i < dataset._parsed.length; i++) {
|
|
10692
|
+
const value = dataset._parsed[i].x;
|
|
10693
|
+
const displayValue = options.callback(value - 0);
|
|
10694
|
+
const point = dataset.data[i];
|
|
10695
|
+
const yPosition = point.y;
|
|
10696
|
+
let xPosition = value < 0 ? point.x + point.width / 2 : point.x - point.width / 2;
|
|
10697
|
+
xPosition = Math.min(xPosition, xMax);
|
|
10698
|
+
xPosition = Math.max(xPosition, xMin);
|
|
10699
|
+
// Avoid overlapping texts with same Y
|
|
10700
|
+
if (!textsPositions[yPosition]) {
|
|
10701
|
+
textsPositions[yPosition] = [];
|
|
10702
|
+
}
|
|
10703
|
+
const textWidth = computeTextWidth(ctx, displayValue, { fontSize: 12 }, "px");
|
|
10704
|
+
for (const otherPosition of textsPositions[yPosition]) {
|
|
10705
|
+
if (Math.abs(otherPosition - xPosition) < textWidth) {
|
|
10706
|
+
xPosition = otherPosition + textWidth + 3;
|
|
10707
|
+
}
|
|
10708
|
+
}
|
|
10709
|
+
textsPositions[yPosition].push(xPosition);
|
|
10710
|
+
ctx.fillStyle = point.options.backgroundColor;
|
|
10711
|
+
ctx.strokeStyle = options.background || "#ffffff";
|
|
10712
|
+
drawTextWithBackground(displayValue, xPosition, yPosition, ctx);
|
|
10713
|
+
}
|
|
10714
|
+
}
|
|
10715
|
+
}
|
|
10716
|
+
function drawPieChartValues(chart, options, ctx) {
|
|
10717
|
+
for (const dataset of chart._metasets) {
|
|
10718
|
+
for (let i = 0; i < dataset._parsed.length; i++) {
|
|
10719
|
+
const value = Number(dataset._parsed[i]);
|
|
10720
|
+
if (isNaN(value) || value === 0) {
|
|
10721
|
+
continue;
|
|
10722
|
+
}
|
|
10723
|
+
const bar = dataset.data[i];
|
|
10724
|
+
const { startAngle, endAngle, innerRadius, outerRadius } = bar;
|
|
10725
|
+
const midAngle = (startAngle + endAngle) / 2;
|
|
10726
|
+
const midRadius = (innerRadius + outerRadius) / 2;
|
|
10727
|
+
const x = bar.x + midRadius * Math.cos(midAngle);
|
|
10728
|
+
const y = bar.y + midRadius * Math.sin(midAngle) + 7;
|
|
10729
|
+
ctx.fillStyle = chartFontColor(options.background);
|
|
10730
|
+
ctx.strokeStyle = options.background || "#ffffff";
|
|
10731
|
+
const displayValue = options.callback(value);
|
|
10732
|
+
drawTextWithBackground(displayValue, x, y, ctx);
|
|
10733
|
+
}
|
|
10734
|
+
}
|
|
10735
|
+
}
|
|
10687
10736
|
|
|
10688
10737
|
/** This is a chartJS plugin that will draw connector lines between the bars of a Waterfall chart */
|
|
10689
10738
|
const waterfallLinesPlugin = {
|
|
@@ -23413,7 +23462,7 @@ function getDefaultChartJsRuntime(chart, labels, fontColor, { format, locale, tr
|
|
|
23413
23462
|
const xLabel = tooltipItem.dataset?.label || tooltipItem.label;
|
|
23414
23463
|
// tooltipItem.parsed can be an object or a number for pie charts
|
|
23415
23464
|
let yLabel = horizontalChart ? tooltipItem.parsed.x : tooltipItem.parsed.y;
|
|
23416
|
-
if (
|
|
23465
|
+
if (yLabel === undefined || yLabel === null) {
|
|
23417
23466
|
yLabel = tooltipItem.parsed;
|
|
23418
23467
|
}
|
|
23419
23468
|
const toolTipFormat = !format && Math.abs(yLabel) >= 1000 ? "#,##" : format;
|
|
@@ -31584,6 +31633,12 @@ class GenericChartConfigPanel extends Component {
|
|
|
31584
31633
|
|
|
31585
31634
|
class BarConfigPanel extends GenericChartConfigPanel {
|
|
31586
31635
|
static template = "o-spreadsheet-BarConfigPanel";
|
|
31636
|
+
get stackedLabel() {
|
|
31637
|
+
const definition = this.props.definition;
|
|
31638
|
+
return definition.horizontal
|
|
31639
|
+
? this.chartTerms.StackedBarChart
|
|
31640
|
+
: this.chartTerms.StackedColumnChart;
|
|
31641
|
+
}
|
|
31587
31642
|
onUpdateStacked(stacked) {
|
|
31588
31643
|
this.props.updateChart(this.props.figureId, {
|
|
31589
31644
|
stacked,
|
|
@@ -32752,7 +32807,9 @@ class LineConfigPanel extends GenericChartConfigPanel {
|
|
|
32752
32807
|
}
|
|
32753
32808
|
get stackedLabel() {
|
|
32754
32809
|
const definition = this.props.definition;
|
|
32755
|
-
return definition.fillArea
|
|
32810
|
+
return definition.fillArea
|
|
32811
|
+
? this.chartTerms.StackedAreaChart
|
|
32812
|
+
: this.chartTerms.StackedLineChart;
|
|
32756
32813
|
}
|
|
32757
32814
|
getLabelRangeOptions() {
|
|
32758
32815
|
const options = super.getLabelRangeOptions();
|
|
@@ -46530,7 +46587,7 @@ function load(data, verboseImport) {
|
|
|
46530
46587
|
if (!data) {
|
|
46531
46588
|
return createEmptyWorkbookData();
|
|
46532
46589
|
}
|
|
46533
|
-
console.
|
|
46590
|
+
console.debug("### Loading data ###");
|
|
46534
46591
|
const start = performance.now();
|
|
46535
46592
|
if (data["[Content_Types].xml"]) {
|
|
46536
46593
|
const reader = new XlsxReader(data);
|
|
@@ -46544,13 +46601,13 @@ function load(data, verboseImport) {
|
|
|
46544
46601
|
// apply migrations, if needed
|
|
46545
46602
|
if ("version" in data) {
|
|
46546
46603
|
if (data.version < CURRENT_VERSION) {
|
|
46547
|
-
console.
|
|
46604
|
+
console.debug("Migrating data from version", data.version);
|
|
46548
46605
|
data = migrate(data);
|
|
46549
46606
|
}
|
|
46550
46607
|
}
|
|
46551
46608
|
data = repairData(data);
|
|
46552
|
-
console.
|
|
46553
|
-
console.
|
|
46609
|
+
console.debug("Data loaded in", performance.now() - start, "ms");
|
|
46610
|
+
console.debug("###");
|
|
46554
46611
|
return data;
|
|
46555
46612
|
}
|
|
46556
46613
|
function migrate(data) {
|
|
@@ -46559,7 +46616,7 @@ function migrate(data) {
|
|
|
46559
46616
|
for (let i = index; i < MIGRATIONS.length; i++) {
|
|
46560
46617
|
data = MIGRATIONS[i].applyMigration(data);
|
|
46561
46618
|
}
|
|
46562
|
-
console.
|
|
46619
|
+
console.debug("Data migrated in", performance.now() - start, "ms");
|
|
46563
46620
|
return data;
|
|
46564
46621
|
}
|
|
46565
46622
|
const MIGRATIONS = [
|
|
@@ -54119,7 +54176,7 @@ class Evaluator {
|
|
|
54119
54176
|
cellsToCompute.addMany(arrayFormulasPositions);
|
|
54120
54177
|
cellsToCompute.addMany(this.getCellsDependingOn(arrayFormulasPositions));
|
|
54121
54178
|
this.evaluate(cellsToCompute);
|
|
54122
|
-
console.
|
|
54179
|
+
console.debug("evaluate Cells", performance.now() - start, "ms");
|
|
54123
54180
|
}
|
|
54124
54181
|
getArrayFormulasImpactedByChangesOf(positions) {
|
|
54125
54182
|
const impactedPositions = this.createEmptyPositionSet();
|
|
@@ -54158,7 +54215,7 @@ class Evaluator {
|
|
|
54158
54215
|
const start = performance.now();
|
|
54159
54216
|
this.evaluatedCells = new PositionMap();
|
|
54160
54217
|
this.evaluate(this.getAllCells());
|
|
54161
|
-
console.
|
|
54218
|
+
console.debug("evaluate all cells", performance.now() - start, "ms");
|
|
54162
54219
|
}
|
|
54163
54220
|
evaluateFormulaResult(sheetId, formulaString) {
|
|
54164
54221
|
try {
|
|
@@ -55980,6 +56037,10 @@ class PivotUIPlugin extends UIPlugin {
|
|
|
55980
56037
|
if (!pivot.isValid()) {
|
|
55981
56038
|
return EMPTY_PIVOT_CELL;
|
|
55982
56039
|
}
|
|
56040
|
+
if (functionName === "PIVOT" &&
|
|
56041
|
+
!cell.content.replaceAll(" ", "").toUpperCase().startsWith("=PIVOT")) {
|
|
56042
|
+
return EMPTY_PIVOT_CELL;
|
|
56043
|
+
}
|
|
55983
56044
|
if (functionName === "PIVOT") {
|
|
55984
56045
|
const includeTotal = args[2] === false ? false : undefined;
|
|
55985
56046
|
const includeColumnHeaders = args[3] === false ? false : undefined;
|
|
@@ -57308,7 +57369,7 @@ class Session extends EventBus {
|
|
|
57308
57369
|
this.onMessageReceived(message);
|
|
57309
57370
|
}
|
|
57310
57371
|
this.isReplayingInitialRevisions = false;
|
|
57311
|
-
console.
|
|
57372
|
+
console.debug("Replayed", numberOfCommands, "commands in", performance.now() - start, "ms");
|
|
57312
57373
|
}
|
|
57313
57374
|
/**
|
|
57314
57375
|
* Notify the server that the user client left the collaborative session
|
|
@@ -66571,7 +66632,8 @@ function addFormula(cell) {
|
|
|
66571
66632
|
}
|
|
66572
66633
|
const attrs = [["t", type]];
|
|
66573
66634
|
const XlsxFormula = adaptFormulaToExcel(formula);
|
|
66574
|
-
const
|
|
66635
|
+
const exportedValue = adaptFormulaValueToExcel(cell.value);
|
|
66636
|
+
const node = escapeXml /*xml*/ `<f>${XlsxFormula}</f><v>${exportedValue}</v>`;
|
|
66575
66637
|
return { attrs, node };
|
|
66576
66638
|
}
|
|
66577
66639
|
function addContent(content, sharedStrings, forceString = false) {
|
|
@@ -66606,8 +66668,14 @@ function adaptFormulaToExcel(formulaText) {
|
|
|
66606
66668
|
ast = addMissingRequiredArgs(ast);
|
|
66607
66669
|
return ast;
|
|
66608
66670
|
});
|
|
66671
|
+
ast = convertAstNodes(ast, "REFERENCE", (ast) => {
|
|
66672
|
+
return ast.value === CellErrorType.InvalidReference ? { ...ast, value: "#REF!" } : ast;
|
|
66673
|
+
});
|
|
66609
66674
|
return ast ? astToFormula(ast) : formulaText;
|
|
66610
66675
|
}
|
|
66676
|
+
function adaptFormulaValueToExcel(formulaValue) {
|
|
66677
|
+
return formulaValue === CellErrorType.InvalidReference ? "#REF!" : formulaValue;
|
|
66678
|
+
}
|
|
66611
66679
|
/**
|
|
66612
66680
|
* Some Excel function need required args that might not be mandatory in o-spreadsheet.
|
|
66613
66681
|
* This adds those missing args.
|
|
@@ -67918,7 +67986,7 @@ class Model extends EventBus {
|
|
|
67918
67986
|
coreHandlers = [];
|
|
67919
67987
|
constructor(data = {}, config = {}, stateUpdateMessages = [], uuidGenerator = new UuidGenerator(), verboseImport = true) {
|
|
67920
67988
|
const start = performance.now();
|
|
67921
|
-
console.
|
|
67989
|
+
console.debug("##### Model creation #####");
|
|
67922
67990
|
super();
|
|
67923
67991
|
setDefaultTranslationMethod();
|
|
67924
67992
|
stateUpdateMessages = repairInitialMessages(data, stateUpdateMessages);
|
|
@@ -67990,16 +68058,16 @@ class Model extends EventBus {
|
|
|
67990
68058
|
this.joinSession();
|
|
67991
68059
|
if (config.snapshotRequested) {
|
|
67992
68060
|
const startSnapshot = performance.now();
|
|
67993
|
-
console.
|
|
68061
|
+
console.debug("Snapshot requested");
|
|
67994
68062
|
this.session.snapshot(this.exportData());
|
|
67995
68063
|
this.garbageCollectExternalResources();
|
|
67996
|
-
console.
|
|
68064
|
+
console.debug("Snapshot taken in", performance.now() - startSnapshot, "ms");
|
|
67997
68065
|
}
|
|
67998
68066
|
// mark all models as "raw", so they will not be turned into reactive objects
|
|
67999
68067
|
// by owl, since we do not rely on reactivity
|
|
68000
68068
|
markRaw(this);
|
|
68001
|
-
console.
|
|
68002
|
-
console.
|
|
68069
|
+
console.debug("Model created in", performance.now() - start, "ms");
|
|
68070
|
+
console.debug("######");
|
|
68003
68071
|
}
|
|
68004
68072
|
joinSession() {
|
|
68005
68073
|
this.session.join(this.config.client);
|
|
@@ -68222,7 +68290,7 @@ class Model extends EventBus {
|
|
|
68222
68290
|
this.finalize();
|
|
68223
68291
|
const time = performance.now() - start;
|
|
68224
68292
|
if (time > 5) {
|
|
68225
|
-
console.
|
|
68293
|
+
console.debug(type, time, "ms");
|
|
68226
68294
|
}
|
|
68227
68295
|
});
|
|
68228
68296
|
this.session.save(command, commands, changes);
|
|
@@ -68565,6 +68633,6 @@ const constants = {
|
|
|
68565
68633
|
export { AbstractCellClipboardHandler, AbstractChart, AbstractFigureClipboardHandler, CellErrorType, CommandResult, CorePlugin, DispatchResult, EvaluationError, Model, PivotRuntimeDefinition, Registry, Revision, SPREADSHEET_DIMENSIONS, Spreadsheet, SpreadsheetPivotTable, UIPlugin, __info__, addFunction, addRenderingLayer, astToFormula, compile, compileTokens, components, constants, convertAstNodes, coreTypes, findCellInNewZone, functionCache, helpers, hooks, invalidateCFEvaluationCommands, invalidateDependenciesCommands, invalidateEvaluationCommands, iterateAstNodes, links, load, parse, parseTokens, readonlyAllowedCommands, registries, setDefaultSheetViewSize, setTranslationMethod, stores, tokenColors, tokenize };
|
|
68566
68634
|
|
|
68567
68635
|
|
|
68568
|
-
__info__.version = "17.4.
|
|
68569
|
-
__info__.date = "2024-
|
|
68570
|
-
__info__.hash = "
|
|
68636
|
+
__info__.version = "17.4.11";
|
|
68637
|
+
__info__.date = "2024-11-08T12:15:26.239Z";
|
|
68638
|
+
__info__.hash = "2eb3f1c";
|