@odoo/o-spreadsheet 19.1.0-alpha.7 → 19.1.0-alpha.8
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-engine.d.ts +8 -20
- package/dist/o-spreadsheet-engine.esm.js +471 -189
- package/dist/o-spreadsheet-engine.iife.js +471 -189
- package/dist/o-spreadsheet-engine.min.iife.js +313 -313
- package/dist/o-spreadsheet.d.ts +17 -23
- package/dist/o_spreadsheet.esm.js +696 -371
- package/dist/o_spreadsheet.iife.js +696 -371
- package/dist/o_spreadsheet.min.iife.js +317 -317
- package/dist/o_spreadsheet.xml +98 -9
- package/package.json +1 -1
|
@@ -3,8 +3,8 @@
|
|
|
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
5
|
* @version 19.1.0-alpha.3
|
|
6
|
-
* @date 2025-10-
|
|
7
|
-
* @hash
|
|
6
|
+
* @date 2025-10-23T08:19:27.355Z
|
|
7
|
+
* @hash 78717d4
|
|
8
8
|
*/
|
|
9
9
|
|
|
10
10
|
(function (exports) {
|
|
@@ -5185,6 +5185,29 @@
|
|
|
5185
5185
|
removeContiguousProfiles(profilesStartingPosition, profiles, leftIndex, rightIndex);
|
|
5186
5186
|
}
|
|
5187
5187
|
}
|
|
5188
|
+
function profilesContainsZone(profilesStartingPosition, profiles, zone) {
|
|
5189
|
+
const leftValue = zone.left;
|
|
5190
|
+
const rightValue = zone.right;
|
|
5191
|
+
const topValue = zone.top;
|
|
5192
|
+
const bottomValue = zone.bottom + 1;
|
|
5193
|
+
const leftIndex = binaryPredecessorSearch(profilesStartingPosition, leftValue, 0);
|
|
5194
|
+
const rightIndex = binaryPredecessorSearch(profilesStartingPosition, rightValue, leftIndex);
|
|
5195
|
+
if (leftIndex === -1 || rightIndex === -1) {
|
|
5196
|
+
return false;
|
|
5197
|
+
}
|
|
5198
|
+
for (let i = leftIndex; i <= rightIndex; i++) {
|
|
5199
|
+
const profile = profiles.get(profilesStartingPosition[i]);
|
|
5200
|
+
const topPredIndex = binaryPredecessorSearch(profile, topValue, 0, true);
|
|
5201
|
+
const bottomSuccIndex = binarySuccessorSearch(profile, bottomValue, 0, true);
|
|
5202
|
+
if (topPredIndex === -1 || topPredIndex % 2 !== 0) {
|
|
5203
|
+
return false;
|
|
5204
|
+
}
|
|
5205
|
+
if (topValue < profile[topPredIndex] || bottomValue > profile[bottomSuccIndex]) {
|
|
5206
|
+
return false;
|
|
5207
|
+
}
|
|
5208
|
+
}
|
|
5209
|
+
return true;
|
|
5210
|
+
}
|
|
5188
5211
|
function findIndexAndCreateProfile(profilesStartingPosition, profiles, value, searchLeft, startIndex) {
|
|
5189
5212
|
if (value === undefined) {
|
|
5190
5213
|
// this is only the case when the value correspond to a bottom value that could be undefined
|
|
@@ -5269,7 +5292,18 @@
|
|
|
5269
5292
|
}
|
|
5270
5293
|
// add the top and bottom value to the profile and
|
|
5271
5294
|
// remove all information between the top and bottom index
|
|
5272
|
-
|
|
5295
|
+
const toDelete = bottomSuccIndex - topPredIndex - 1;
|
|
5296
|
+
const toInsert = newPoints.length;
|
|
5297
|
+
const start = topPredIndex + 1;
|
|
5298
|
+
// fast path and slow path
|
|
5299
|
+
if (start === profile.length - 1 && toDelete === 1 && toInsert === 1) {
|
|
5300
|
+
// fast path: we just need to replace the last element
|
|
5301
|
+
profile[start] = newPoints[0] ?? newPoints[1];
|
|
5302
|
+
}
|
|
5303
|
+
else {
|
|
5304
|
+
// equivalent but slower and with memory allocation
|
|
5305
|
+
profile.splice(start, toDelete, ...newPoints);
|
|
5306
|
+
}
|
|
5273
5307
|
}
|
|
5274
5308
|
function removeContiguousProfiles(profilesStartingPosition, profiles, leftIndex, rightIndex) {
|
|
5275
5309
|
const start = leftIndex - 1 === -1 ? 0 : leftIndex - 1;
|
|
@@ -5308,8 +5342,10 @@
|
|
|
5308
5342
|
left,
|
|
5309
5343
|
bottom,
|
|
5310
5344
|
right,
|
|
5311
|
-
hasHeader: (bottom === undefined && top !== 0) || (right === undefined && left !== 0),
|
|
5312
5345
|
};
|
|
5346
|
+
if ((bottom === undefined && top !== 0) || (right === undefined && left !== 0)) {
|
|
5347
|
+
profileZone.hasHeader = true;
|
|
5348
|
+
}
|
|
5313
5349
|
let findCorrespondingZone = false;
|
|
5314
5350
|
for (let j = pendingZones.length - 1; j >= 0; j--) {
|
|
5315
5351
|
const pendingZone = pendingZones[j];
|
|
@@ -5764,17 +5800,6 @@
|
|
|
5764
5800
|
}
|
|
5765
5801
|
return [leftColumnZone, rightPartZone];
|
|
5766
5802
|
}
|
|
5767
|
-
function aggregatePositionsToZones(positions) {
|
|
5768
|
-
const result = {};
|
|
5769
|
-
for (const position of positions) {
|
|
5770
|
-
result[position.sheetId] ??= [];
|
|
5771
|
-
result[position.sheetId].push(positionToZone(position));
|
|
5772
|
-
}
|
|
5773
|
-
for (const sheetId in result) {
|
|
5774
|
-
result[sheetId] = recomputeZones(result[sheetId]);
|
|
5775
|
-
}
|
|
5776
|
-
return result;
|
|
5777
|
-
}
|
|
5778
5803
|
/**
|
|
5779
5804
|
* Array of all positions in the zone.
|
|
5780
5805
|
*/
|
|
@@ -14523,6 +14548,13 @@
|
|
|
14523
14548
|
.add("minute_number", nullHandlerDecorator(minuteNumberAdapter))
|
|
14524
14549
|
.add("second_number", nullHandlerDecorator(secondNumberAdapter));
|
|
14525
14550
|
|
|
14551
|
+
const DEFAULT_PIVOT_STYLE = {
|
|
14552
|
+
displayTotals: true,
|
|
14553
|
+
displayColumnHeaders: true,
|
|
14554
|
+
displayMeasuresRow: true,
|
|
14555
|
+
numberOfRows: Number.MAX_VALUE,
|
|
14556
|
+
numberOfColumns: Number.MAX_VALUE,
|
|
14557
|
+
};
|
|
14526
14558
|
const AGGREGATOR_NAMES = {
|
|
14527
14559
|
count: _t("Count"),
|
|
14528
14560
|
count_distinct: _t("Count Distinct"),
|
|
@@ -14818,6 +14850,25 @@
|
|
|
14818
14850
|
pivot: { ...definition, collapsedDomains: newDomains },
|
|
14819
14851
|
});
|
|
14820
14852
|
}
|
|
14853
|
+
function getPivotStyleFromFnArgs(definition, rowCountArg, includeTotalArg, includeColumnHeadersArg, columnCountArg, includeMeasuresRowArg, locale) {
|
|
14854
|
+
const style = definition.style;
|
|
14855
|
+
const numberOfRows = rowCountArg !== undefined
|
|
14856
|
+
? toNumber(rowCountArg, locale)
|
|
14857
|
+
: style?.numberOfRows ?? DEFAULT_PIVOT_STYLE.numberOfRows;
|
|
14858
|
+
const numberOfColumns = columnCountArg !== undefined
|
|
14859
|
+
? toNumber(columnCountArg, locale)
|
|
14860
|
+
: style?.numberOfColumns ?? DEFAULT_PIVOT_STYLE.numberOfColumns;
|
|
14861
|
+
const displayTotals = includeTotalArg !== undefined
|
|
14862
|
+
? toBoolean(includeTotalArg)
|
|
14863
|
+
: style?.displayTotals ?? DEFAULT_PIVOT_STYLE.displayTotals;
|
|
14864
|
+
const displayColumnHeaders = includeColumnHeadersArg !== undefined
|
|
14865
|
+
? toBoolean(includeColumnHeadersArg)
|
|
14866
|
+
: style?.displayColumnHeaders ?? DEFAULT_PIVOT_STYLE.displayColumnHeaders;
|
|
14867
|
+
const displayMeasuresRow = includeMeasuresRowArg !== undefined
|
|
14868
|
+
? toBoolean(includeMeasuresRowArg)
|
|
14869
|
+
: style?.displayMeasuresRow ?? DEFAULT_PIVOT_STYLE.displayMeasuresRow;
|
|
14870
|
+
return { numberOfRows, numberOfColumns, displayTotals, displayColumnHeaders, displayMeasuresRow };
|
|
14871
|
+
}
|
|
14821
14872
|
|
|
14822
14873
|
/**
|
|
14823
14874
|
* Get the pivot ID from the formula pivot ID.
|
|
@@ -15432,24 +15483,18 @@
|
|
|
15432
15483
|
arg("column_count (number, optional)", _t("number of columns")),
|
|
15433
15484
|
arg("include_measure_titles (boolean, default=TRUE)", _t("Whether to include the measure titles row or not.")),
|
|
15434
15485
|
],
|
|
15435
|
-
compute: function (pivotFormulaId, rowCount
|
|
15486
|
+
compute: function (pivotFormulaId, rowCount, includeTotal, includeColumnHeaders, columnCount, includeMeasureTitles) {
|
|
15436
15487
|
const _pivotFormulaId = toString(pivotFormulaId);
|
|
15437
|
-
const
|
|
15438
|
-
|
|
15488
|
+
const pivotId = getPivotId(_pivotFormulaId, this.getters);
|
|
15489
|
+
const pivot = this.getters.getPivot(pivotId);
|
|
15490
|
+
const coreDefinition = this.getters.getPivotCoreDefinition(pivotId);
|
|
15491
|
+
const pivotStyle = getPivotStyleFromFnArgs(coreDefinition, rowCount, includeTotal, includeColumnHeaders, columnCount, includeMeasureTitles, this.locale);
|
|
15492
|
+
if (pivotStyle.numberOfRows < 0) {
|
|
15439
15493
|
return new EvaluationError(_t("The number of rows must be positive."));
|
|
15440
15494
|
}
|
|
15441
|
-
|
|
15442
|
-
if (_columnCount < 0) {
|
|
15495
|
+
if (pivotStyle.numberOfColumns < 0) {
|
|
15443
15496
|
return new EvaluationError(_t("The number of columns must be positive."));
|
|
15444
15497
|
}
|
|
15445
|
-
const visibilityOptions = {
|
|
15446
|
-
displayColumnHeaders: toBoolean(includeColumnHeaders),
|
|
15447
|
-
displayTotals: toBoolean(includeTotal),
|
|
15448
|
-
displayMeasuresRow: toBoolean(includeMeasureTitles),
|
|
15449
|
-
};
|
|
15450
|
-
const pivotId = getPivotId(_pivotFormulaId, this.getters);
|
|
15451
|
-
const pivot = this.getters.getPivot(pivotId);
|
|
15452
|
-
const coreDefinition = this.getters.getPivotCoreDefinition(pivotId);
|
|
15453
15498
|
addPivotDependencies(this, coreDefinition, coreDefinition.measures);
|
|
15454
15499
|
pivot.init({ reload: pivot.needsReevaluation });
|
|
15455
15500
|
const error = pivot.assertIsValid({ throwOnError: false });
|
|
@@ -15460,20 +15505,20 @@
|
|
|
15460
15505
|
if (table.numberOfCells > PIVOT_MAX_NUMBER_OF_CELLS) {
|
|
15461
15506
|
return new EvaluationError(getPivotTooBigErrorMessage(table.numberOfCells, this.locale));
|
|
15462
15507
|
}
|
|
15463
|
-
const cells = table.getPivotCells(
|
|
15508
|
+
const cells = table.getPivotCells(pivotStyle);
|
|
15464
15509
|
let headerRows = 0;
|
|
15465
|
-
if (
|
|
15510
|
+
if (pivotStyle.displayColumnHeaders) {
|
|
15466
15511
|
headerRows = table.columns.length - 1;
|
|
15467
15512
|
}
|
|
15468
|
-
if (
|
|
15513
|
+
if (pivotStyle.displayMeasuresRow) {
|
|
15469
15514
|
headerRows++;
|
|
15470
15515
|
}
|
|
15471
15516
|
const pivotTitle = this.getters.getPivotName(pivotId);
|
|
15472
|
-
const tableHeight = Math.min(headerRows +
|
|
15517
|
+
const tableHeight = Math.min(headerRows + pivotStyle.numberOfRows, cells[0].length);
|
|
15473
15518
|
if (tableHeight === 0) {
|
|
15474
15519
|
return [[{ value: pivotTitle }]];
|
|
15475
15520
|
}
|
|
15476
|
-
const tableWidth = Math.min(1 +
|
|
15521
|
+
const tableWidth = Math.min(1 + pivotStyle.numberOfColumns, cells.length);
|
|
15477
15522
|
const result = [];
|
|
15478
15523
|
for (const col of range(0, tableWidth)) {
|
|
15479
15524
|
result[col] = [];
|
|
@@ -15496,7 +15541,7 @@
|
|
|
15496
15541
|
}
|
|
15497
15542
|
}
|
|
15498
15543
|
}
|
|
15499
|
-
if (
|
|
15544
|
+
if (pivotStyle.displayColumnHeaders || pivotStyle.displayMeasuresRow) {
|
|
15500
15545
|
result[0][0] = { value: pivotTitle };
|
|
15501
15546
|
}
|
|
15502
15547
|
return result;
|
|
@@ -17968,6 +18013,10 @@
|
|
|
17968
18013
|
}
|
|
17969
18014
|
return parts;
|
|
17970
18015
|
}
|
|
18016
|
+
function positionToBoundedRange(position) {
|
|
18017
|
+
const zone = { left: position.col, top: position.row, right: position.col, bottom: position.row };
|
|
18018
|
+
return { sheetId: position.sheetId, zone };
|
|
18019
|
+
}
|
|
17971
18020
|
/**
|
|
17972
18021
|
* Check that a zone is valid regarding the order of top-bottom and left-right.
|
|
17973
18022
|
* Left should be smaller than right, top should be smaller than bottom.
|
|
@@ -26656,6 +26705,7 @@
|
|
|
26656
26705
|
return data;
|
|
26657
26706
|
}
|
|
26658
26707
|
const figureIds = new Set();
|
|
26708
|
+
const chartIds = new Set();
|
|
26659
26709
|
const uuidGenerator = new UuidGenerator();
|
|
26660
26710
|
for (const sheet of data.sheets || []) {
|
|
26661
26711
|
for (const figure of sheet.figures || []) {
|
|
@@ -26663,6 +26713,12 @@
|
|
|
26663
26713
|
figure.id += uuidGenerator.smallUuid();
|
|
26664
26714
|
}
|
|
26665
26715
|
figureIds.add(figure.id);
|
|
26716
|
+
if (figure.tag === "chart") {
|
|
26717
|
+
if (chartIds.has(figure.data?.chartId)) {
|
|
26718
|
+
figure.data.chartId += uuidGenerator.smallUuid();
|
|
26719
|
+
}
|
|
26720
|
+
chartIds.add(figure.data?.chartId);
|
|
26721
|
+
}
|
|
26666
26722
|
}
|
|
26667
26723
|
}
|
|
26668
26724
|
data.uniqueFigureIds = true;
|
|
@@ -27030,11 +27086,6 @@
|
|
|
27030
27086
|
* @param sheetName couple of old and new sheet names to adapt ranges pointing to that sheet
|
|
27031
27087
|
*/
|
|
27032
27088
|
adaptRanges(applyChange, sheetId, sheetName) { }
|
|
27033
|
-
/**
|
|
27034
|
-
* Implement this method to clean unused external resources, such as images
|
|
27035
|
-
* stored on a server which have been deleted.
|
|
27036
|
-
*/
|
|
27037
|
-
garbageCollectExternalResources() { }
|
|
27038
27089
|
}
|
|
27039
27090
|
|
|
27040
27091
|
class BordersPlugin extends CorePlugin {
|
|
@@ -30878,17 +30929,6 @@
|
|
|
30878
30929
|
break;
|
|
30879
30930
|
}
|
|
30880
30931
|
}
|
|
30881
|
-
/**
|
|
30882
|
-
* Delete unused images from the file store
|
|
30883
|
-
*/
|
|
30884
|
-
garbageCollectExternalResources() {
|
|
30885
|
-
const images = new Set(this.getAllImages().map((image) => image.path));
|
|
30886
|
-
for (const path of this.syncedImages) {
|
|
30887
|
-
if (!images.has(path)) {
|
|
30888
|
-
this.fileStore?.delete(path);
|
|
30889
|
-
}
|
|
30890
|
-
}
|
|
30891
|
-
}
|
|
30892
30932
|
// ---------------------------------------------------------------------------
|
|
30893
30933
|
// Getters
|
|
30894
30934
|
// ---------------------------------------------------------------------------
|
|
@@ -30950,13 +30990,6 @@
|
|
|
30950
30990
|
sheet.images = [...sheet.images, ...images];
|
|
30951
30991
|
}
|
|
30952
30992
|
}
|
|
30953
|
-
getAllImages() {
|
|
30954
|
-
const images = [];
|
|
30955
|
-
for (const sheetId in this.images) {
|
|
30956
|
-
images.push(...Object.values(this.images[sheetId] || {}).filter(isDefined));
|
|
30957
|
-
}
|
|
30958
|
-
return images;
|
|
30959
|
-
}
|
|
30960
30993
|
}
|
|
30961
30994
|
|
|
30962
30995
|
class MergePlugin extends CorePlugin {
|
|
@@ -31732,26 +31765,22 @@
|
|
|
31732
31765
|
getNumberOfDataColumns() {
|
|
31733
31766
|
return this.columns.at(-1)?.length || 0;
|
|
31734
31767
|
}
|
|
31735
|
-
getSkippedRows(
|
|
31768
|
+
getSkippedRows(pivotStyle) {
|
|
31736
31769
|
const skippedRows = new Set();
|
|
31737
|
-
if (!
|
|
31770
|
+
if (!pivotStyle.displayColumnHeaders) {
|
|
31738
31771
|
for (let i = 0; i < this.columns.length - 1; i++) {
|
|
31739
31772
|
skippedRows.add(i);
|
|
31740
31773
|
}
|
|
31741
31774
|
}
|
|
31742
|
-
if (!
|
|
31775
|
+
if (!pivotStyle.displayMeasuresRow) {
|
|
31743
31776
|
skippedRows.add(this.columns.length - 1);
|
|
31744
31777
|
}
|
|
31745
31778
|
return skippedRows;
|
|
31746
31779
|
}
|
|
31747
|
-
getPivotCells(
|
|
31748
|
-
|
|
31749
|
-
displayTotals: true,
|
|
31750
|
-
displayMeasuresRow: true,
|
|
31751
|
-
}) {
|
|
31752
|
-
const key = JSON.stringify(visibilityOptions);
|
|
31780
|
+
getPivotCells(pivotStyle = DEFAULT_PIVOT_STYLE) {
|
|
31781
|
+
const key = JSON.stringify(pivotStyle);
|
|
31753
31782
|
if (!this.pivotCells[key]) {
|
|
31754
|
-
const { displayTotals } =
|
|
31783
|
+
const { displayTotals } = pivotStyle;
|
|
31755
31784
|
const numberOfDataRows = this.rows.length;
|
|
31756
31785
|
const numberOfDataColumns = this.getNumberOfDataColumns();
|
|
31757
31786
|
let pivotHeight = this.columns.length + numberOfDataRows;
|
|
@@ -31763,7 +31792,7 @@
|
|
|
31763
31792
|
pivotWidth -= this.measures.length;
|
|
31764
31793
|
}
|
|
31765
31794
|
const domainArray = [];
|
|
31766
|
-
const skippedRows = this.getSkippedRows(
|
|
31795
|
+
const skippedRows = this.getSkippedRows(pivotStyle);
|
|
31767
31796
|
for (let col = 0; col < pivotWidth; col++) {
|
|
31768
31797
|
domainArray.push([]);
|
|
31769
31798
|
for (let row = 0; row < pivotHeight; row++) {
|
|
@@ -34882,6 +34911,281 @@
|
|
|
34882
34911
|
}
|
|
34883
34912
|
}
|
|
34884
34913
|
|
|
34914
|
+
class ZoneSet {
|
|
34915
|
+
profilesStartingPosition = [0];
|
|
34916
|
+
profiles = new Map([[0, []]]);
|
|
34917
|
+
constructor(zones = []) {
|
|
34918
|
+
for (const zone of zones) {
|
|
34919
|
+
this.add(zone);
|
|
34920
|
+
}
|
|
34921
|
+
}
|
|
34922
|
+
isEmpty() {
|
|
34923
|
+
return this.profiles.size === 1 && this.profiles.get(0)?.length === 0;
|
|
34924
|
+
}
|
|
34925
|
+
add(zone) {
|
|
34926
|
+
modifyProfiles(this.profilesStartingPosition, this.profiles, [zone]);
|
|
34927
|
+
}
|
|
34928
|
+
delete(zone) {
|
|
34929
|
+
modifyProfiles(this.profilesStartingPosition, this.profiles, [zone], true);
|
|
34930
|
+
}
|
|
34931
|
+
has(zone) {
|
|
34932
|
+
return profilesContainsZone(this.profilesStartingPosition, this.profiles, zone);
|
|
34933
|
+
}
|
|
34934
|
+
difference(other) {
|
|
34935
|
+
const result = this.copy();
|
|
34936
|
+
for (const zone of other) {
|
|
34937
|
+
result.delete(zone);
|
|
34938
|
+
}
|
|
34939
|
+
return result;
|
|
34940
|
+
}
|
|
34941
|
+
copy() {
|
|
34942
|
+
const result = new ZoneSet();
|
|
34943
|
+
result.profilesStartingPosition = [...this.profilesStartingPosition];
|
|
34944
|
+
result.profiles = new Map();
|
|
34945
|
+
for (const [key, value] of this.profiles) {
|
|
34946
|
+
result.profiles.set(key, [...value]);
|
|
34947
|
+
}
|
|
34948
|
+
return result;
|
|
34949
|
+
}
|
|
34950
|
+
size() {
|
|
34951
|
+
let size = 0;
|
|
34952
|
+
for (const profile of this.profiles.values()) {
|
|
34953
|
+
size += profile.length;
|
|
34954
|
+
}
|
|
34955
|
+
return size / 2;
|
|
34956
|
+
}
|
|
34957
|
+
/**
|
|
34958
|
+
* iterator of all the zones in the ZoneSet
|
|
34959
|
+
*/
|
|
34960
|
+
[Symbol.iterator]() {
|
|
34961
|
+
return constructZonesFromProfiles(this.profilesStartingPosition, this.profiles)[Symbol.iterator]();
|
|
34962
|
+
}
|
|
34963
|
+
}
|
|
34964
|
+
|
|
34965
|
+
class RangeSet {
|
|
34966
|
+
setsBySheetId = {};
|
|
34967
|
+
constructor(ranges = []) {
|
|
34968
|
+
for (const range of ranges) {
|
|
34969
|
+
this.add(range);
|
|
34970
|
+
}
|
|
34971
|
+
}
|
|
34972
|
+
add(range) {
|
|
34973
|
+
if (!this.setsBySheetId[range.sheetId]) {
|
|
34974
|
+
this.setsBySheetId[range.sheetId] = new ZoneSet();
|
|
34975
|
+
}
|
|
34976
|
+
this.setsBySheetId[range.sheetId].add(range.zone);
|
|
34977
|
+
}
|
|
34978
|
+
addMany(ranges) {
|
|
34979
|
+
for (const range of ranges) {
|
|
34980
|
+
this.add(range);
|
|
34981
|
+
}
|
|
34982
|
+
}
|
|
34983
|
+
addPosition(position) {
|
|
34984
|
+
this.add(positionToBoundedRange(position));
|
|
34985
|
+
}
|
|
34986
|
+
addManyPositions(positions) {
|
|
34987
|
+
for (const position of positions) {
|
|
34988
|
+
this.addPosition(position);
|
|
34989
|
+
}
|
|
34990
|
+
}
|
|
34991
|
+
has(range) {
|
|
34992
|
+
if (!this.setsBySheetId[range.sheetId]) {
|
|
34993
|
+
return false;
|
|
34994
|
+
}
|
|
34995
|
+
return this.setsBySheetId[range.sheetId].has(range.zone);
|
|
34996
|
+
}
|
|
34997
|
+
hasPosition(position) {
|
|
34998
|
+
return this.has(positionToBoundedRange(position));
|
|
34999
|
+
}
|
|
35000
|
+
delete(range) {
|
|
35001
|
+
if (!this.setsBySheetId[range.sheetId]) {
|
|
35002
|
+
return;
|
|
35003
|
+
}
|
|
35004
|
+
this.setsBySheetId[range.sheetId].delete(range.zone);
|
|
35005
|
+
}
|
|
35006
|
+
deleteMany(ranges) {
|
|
35007
|
+
for (const range of ranges) {
|
|
35008
|
+
this.delete(range);
|
|
35009
|
+
}
|
|
35010
|
+
}
|
|
35011
|
+
deleteManyPositions(positions) {
|
|
35012
|
+
for (const position of positions) {
|
|
35013
|
+
this.delete(positionToBoundedRange(position));
|
|
35014
|
+
}
|
|
35015
|
+
}
|
|
35016
|
+
difference(other) {
|
|
35017
|
+
const result = new RangeSet();
|
|
35018
|
+
for (const sheetId in this.setsBySheetId) {
|
|
35019
|
+
result.setsBySheetId[sheetId] = this.setsBySheetId[sheetId];
|
|
35020
|
+
}
|
|
35021
|
+
for (const sheetId in other.setsBySheetId) {
|
|
35022
|
+
if (result.setsBySheetId[sheetId]) {
|
|
35023
|
+
result.setsBySheetId[sheetId] = result.setsBySheetId[sheetId].difference(other.setsBySheetId[sheetId]);
|
|
35024
|
+
}
|
|
35025
|
+
}
|
|
35026
|
+
return result;
|
|
35027
|
+
}
|
|
35028
|
+
copy() {
|
|
35029
|
+
const result = new RangeSet();
|
|
35030
|
+
for (const sheetId in this.setsBySheetId) {
|
|
35031
|
+
result.setsBySheetId[sheetId] = this.setsBySheetId[sheetId].copy();
|
|
35032
|
+
}
|
|
35033
|
+
return result;
|
|
35034
|
+
}
|
|
35035
|
+
clear() {
|
|
35036
|
+
this.setsBySheetId = {};
|
|
35037
|
+
}
|
|
35038
|
+
size() {
|
|
35039
|
+
let size = 0;
|
|
35040
|
+
for (const sheetId in this.setsBySheetId) {
|
|
35041
|
+
size += this.setsBySheetId[sheetId].size();
|
|
35042
|
+
}
|
|
35043
|
+
return size;
|
|
35044
|
+
}
|
|
35045
|
+
isEmpty() {
|
|
35046
|
+
for (const sheetId in this.setsBySheetId) {
|
|
35047
|
+
if (!this.setsBySheetId[sheetId].isEmpty()) {
|
|
35048
|
+
return false;
|
|
35049
|
+
}
|
|
35050
|
+
}
|
|
35051
|
+
return true;
|
|
35052
|
+
}
|
|
35053
|
+
/**
|
|
35054
|
+
* iterator of all the ranges in the RangeSet
|
|
35055
|
+
*/
|
|
35056
|
+
[Symbol.iterator]() {
|
|
35057
|
+
const result = [];
|
|
35058
|
+
for (const sheetId in this.setsBySheetId) {
|
|
35059
|
+
for (const zone of this.setsBySheetId[sheetId]) {
|
|
35060
|
+
result.push({ sheetId: sheetId, zone });
|
|
35061
|
+
}
|
|
35062
|
+
}
|
|
35063
|
+
return result[Symbol.iterator]();
|
|
35064
|
+
}
|
|
35065
|
+
}
|
|
35066
|
+
|
|
35067
|
+
/**
|
|
35068
|
+
* R-Tree of ranges, mapping zones (r-tree bounding boxes) to ranges (data of the r-tree item).
|
|
35069
|
+
* Ranges associated to the exact same bounding box are grouped together
|
|
35070
|
+
* to reduce the number of nodes in the R-tree.
|
|
35071
|
+
*/
|
|
35072
|
+
class DependenciesRTree {
|
|
35073
|
+
rTree;
|
|
35074
|
+
constructor(items = []) {
|
|
35075
|
+
const compactedBoxes = groupSameBoundingBoxes(items);
|
|
35076
|
+
this.rTree = new SpreadsheetRTree(compactedBoxes);
|
|
35077
|
+
}
|
|
35078
|
+
insert(item) {
|
|
35079
|
+
const data = this.rTree.search(item.boundingBox);
|
|
35080
|
+
const itemBoundingBox = item.boundingBox;
|
|
35081
|
+
const exactBoundingBox = data.find(({ boundingBox }) => boundingBox.sheetId === itemBoundingBox.sheetId &&
|
|
35082
|
+
boundingBox.zone.left === itemBoundingBox.zone.left &&
|
|
35083
|
+
boundingBox.zone.top === itemBoundingBox.zone.top &&
|
|
35084
|
+
boundingBox.zone.right === itemBoundingBox.zone.right &&
|
|
35085
|
+
boundingBox.zone.bottom === itemBoundingBox.zone.bottom);
|
|
35086
|
+
if (exactBoundingBox) {
|
|
35087
|
+
exactBoundingBox.data.add(item.data);
|
|
35088
|
+
}
|
|
35089
|
+
else {
|
|
35090
|
+
this.rTree.insert({ ...item, data: new RangeSet([item.data]) });
|
|
35091
|
+
}
|
|
35092
|
+
}
|
|
35093
|
+
search({ zone, sheetId }) {
|
|
35094
|
+
const results = new RangeSet();
|
|
35095
|
+
for (const { data } of this.rTree.search({ zone, sheetId })) {
|
|
35096
|
+
results.addMany(data);
|
|
35097
|
+
}
|
|
35098
|
+
return results;
|
|
35099
|
+
}
|
|
35100
|
+
remove(item) {
|
|
35101
|
+
const data = this.rTree.search(item.boundingBox);
|
|
35102
|
+
const itemBoundingBox = item.boundingBox;
|
|
35103
|
+
const exactBoundingBox = data.find(({ boundingBox }) => boundingBox.sheetId === itemBoundingBox.sheetId &&
|
|
35104
|
+
boundingBox.zone.left === itemBoundingBox.zone.left &&
|
|
35105
|
+
boundingBox.zone.top === itemBoundingBox.zone.top &&
|
|
35106
|
+
boundingBox.zone.right === itemBoundingBox.zone.right &&
|
|
35107
|
+
boundingBox.zone.bottom === itemBoundingBox.zone.bottom);
|
|
35108
|
+
if (exactBoundingBox) {
|
|
35109
|
+
exactBoundingBox.data.delete(item.data);
|
|
35110
|
+
}
|
|
35111
|
+
else {
|
|
35112
|
+
this.rTree.remove({ ...item, data: new RangeSet([item.data]) });
|
|
35113
|
+
}
|
|
35114
|
+
}
|
|
35115
|
+
}
|
|
35116
|
+
/**
|
|
35117
|
+
* Group together all formulas pointing to the exact same dependency (bounding box).
|
|
35118
|
+
* The goal is to optimize the following case:
|
|
35119
|
+
* - if any cell in B1:B1000 changes, C1 must be recomputed
|
|
35120
|
+
* - if any cell in B1:B1000 changes, C2 must be recomputed
|
|
35121
|
+
* - if any cell in B1:B1000 changes, C3 must be recomputed
|
|
35122
|
+
* ...
|
|
35123
|
+
* - if any cell in B1:B1000 changes, C1000 must be recomputed
|
|
35124
|
+
*
|
|
35125
|
+
* Instead of having 1000 entries in the R-tree, we want to have a single entry
|
|
35126
|
+
* with B1:B1000 (bounding box) pointing to C1:C1000 (formulas).
|
|
35127
|
+
*/
|
|
35128
|
+
function groupSameBoundingBoxes(items) {
|
|
35129
|
+
// Important: this function must be as fast as possible. It is on the evaluation hot path.
|
|
35130
|
+
let maxCol = 0;
|
|
35131
|
+
let maxRow = 0;
|
|
35132
|
+
for (let i = 0; i < items.length; i++) {
|
|
35133
|
+
const zone = items[i].boundingBox.zone;
|
|
35134
|
+
if (zone.right > maxCol) {
|
|
35135
|
+
maxCol = zone.right;
|
|
35136
|
+
}
|
|
35137
|
+
if (zone.bottom > maxRow) {
|
|
35138
|
+
maxRow = zone.bottom;
|
|
35139
|
+
}
|
|
35140
|
+
}
|
|
35141
|
+
maxCol += 1;
|
|
35142
|
+
maxRow += 1;
|
|
35143
|
+
// in most real-world cases, we can use a fast numeric key
|
|
35144
|
+
// but if the zones are too far right or bottom, we fallback to a slower string key
|
|
35145
|
+
const maxPossibleKey = (((maxRow + 1) * maxCol + 1) * maxRow + 1) * maxCol;
|
|
35146
|
+
const useFastKey = maxPossibleKey <= Number.MAX_SAFE_INTEGER;
|
|
35147
|
+
if (!useFastKey) {
|
|
35148
|
+
console.warn("Max col/row size exceeded, using slow zone key");
|
|
35149
|
+
}
|
|
35150
|
+
const groupedByBBox = {};
|
|
35151
|
+
for (const item of items) {
|
|
35152
|
+
const sheetId = item.boundingBox.sheetId;
|
|
35153
|
+
if (!groupedByBBox[sheetId]) {
|
|
35154
|
+
groupedByBBox[sheetId] = {};
|
|
35155
|
+
}
|
|
35156
|
+
const bBox = item.boundingBox.zone;
|
|
35157
|
+
let bBoxKey = 0;
|
|
35158
|
+
if (useFastKey) {
|
|
35159
|
+
bBoxKey =
|
|
35160
|
+
bBox.left +
|
|
35161
|
+
bBox.top * maxCol +
|
|
35162
|
+
bBox.right * maxCol * maxRow +
|
|
35163
|
+
bBox.bottom * maxCol * maxRow * maxCol;
|
|
35164
|
+
}
|
|
35165
|
+
else {
|
|
35166
|
+
bBoxKey = `${bBox.left},${bBox.top},${bBox.right},${bBox.bottom}`;
|
|
35167
|
+
}
|
|
35168
|
+
if (groupedByBBox[sheetId][bBoxKey]) {
|
|
35169
|
+
const ranges = groupedByBBox[sheetId][bBoxKey].data;
|
|
35170
|
+
ranges.add(item.data);
|
|
35171
|
+
}
|
|
35172
|
+
else {
|
|
35173
|
+
groupedByBBox[sheetId][bBoxKey] = {
|
|
35174
|
+
boundingBox: item.boundingBox,
|
|
35175
|
+
data: new RangeSet([item.data]),
|
|
35176
|
+
};
|
|
35177
|
+
}
|
|
35178
|
+
}
|
|
35179
|
+
const result = [];
|
|
35180
|
+
for (const sheetId in groupedByBBox) {
|
|
35181
|
+
const map = groupedByBBox[sheetId];
|
|
35182
|
+
for (const key in map) {
|
|
35183
|
+
result.push(map[key]);
|
|
35184
|
+
}
|
|
35185
|
+
}
|
|
35186
|
+
return result;
|
|
35187
|
+
}
|
|
35188
|
+
|
|
34885
35189
|
/**
|
|
34886
35190
|
* Implementation of a dependency Graph.
|
|
34887
35191
|
* The graph is used to evaluate the cells in the correct
|
|
@@ -34890,12 +35194,10 @@
|
|
|
34890
35194
|
* It uses an R-Tree data structure to efficiently find dependent cells.
|
|
34891
35195
|
*/
|
|
34892
35196
|
class FormulaDependencyGraph {
|
|
34893
|
-
createEmptyPositionSet;
|
|
34894
35197
|
dependencies = new PositionMap();
|
|
34895
35198
|
rTree;
|
|
34896
|
-
constructor(
|
|
34897
|
-
this.
|
|
34898
|
-
this.rTree = new SpreadsheetRTree(data);
|
|
35199
|
+
constructor(data = []) {
|
|
35200
|
+
this.rTree = new DependenciesRTree(data);
|
|
34899
35201
|
}
|
|
34900
35202
|
removeAllDependencies(formulaPosition) {
|
|
34901
35203
|
const ranges = this.dependencies.get(formulaPosition);
|
|
@@ -34909,7 +35211,10 @@
|
|
|
34909
35211
|
}
|
|
34910
35212
|
addDependencies(formulaPosition, dependencies) {
|
|
34911
35213
|
const rTreeItems = dependencies.map(({ sheetId, zone }) => ({
|
|
34912
|
-
data:
|
|
35214
|
+
data: {
|
|
35215
|
+
sheetId: formulaPosition.sheetId,
|
|
35216
|
+
zone: positionToZone(formulaPosition),
|
|
35217
|
+
},
|
|
34913
35218
|
boundingBox: {
|
|
34914
35219
|
zone,
|
|
34915
35220
|
sheetId,
|
|
@@ -34927,46 +35232,20 @@
|
|
|
34927
35232
|
}
|
|
34928
35233
|
}
|
|
34929
35234
|
/**
|
|
34930
|
-
* Return all the cells that depend on the provided ranges
|
|
34931
|
-
* in the correct order they should be evaluated.
|
|
34932
|
-
* This is called a topological ordering (excluding cycles)
|
|
35235
|
+
* Return all the cells that depend on the provided ranges.
|
|
34933
35236
|
*/
|
|
34934
|
-
getCellsDependingOn(ranges,
|
|
34935
|
-
|
|
35237
|
+
getCellsDependingOn(ranges, visited = new RangeSet()) {
|
|
35238
|
+
visited = visited.copy();
|
|
34936
35239
|
const queue = Array.from(ranges).reverse();
|
|
34937
35240
|
while (queue.length > 0) {
|
|
34938
35241
|
const range = queue.pop();
|
|
34939
|
-
|
|
34940
|
-
const
|
|
34941
|
-
|
|
34942
|
-
for (let row = zone.top; row <= zone.bottom; row++) {
|
|
34943
|
-
visited.add({ sheetId, col, row });
|
|
34944
|
-
}
|
|
34945
|
-
}
|
|
34946
|
-
const impactedPositions = this.rTree.search(range).map((dep) => dep.data);
|
|
34947
|
-
const nextInQueue = {};
|
|
34948
|
-
for (const position of impactedPositions) {
|
|
34949
|
-
if (!visited.has(position) && !ignore.has(position)) {
|
|
34950
|
-
if (!nextInQueue[position.sheetId]) {
|
|
34951
|
-
nextInQueue[position.sheetId] = [];
|
|
34952
|
-
}
|
|
34953
|
-
nextInQueue[position.sheetId].push(positionToZone(position));
|
|
34954
|
-
}
|
|
34955
|
-
}
|
|
34956
|
-
for (const sheetId in nextInQueue) {
|
|
34957
|
-
const zones = recomputeZones(nextInQueue[sheetId], []);
|
|
34958
|
-
queue.push(...zones.map((zone) => ({ sheetId, zone })));
|
|
34959
|
-
}
|
|
35242
|
+
visited.add(range);
|
|
35243
|
+
const impactedRanges = this.rTree.search(range);
|
|
35244
|
+
queue.push(...impactedRanges.difference(visited));
|
|
34960
35245
|
}
|
|
34961
35246
|
// remove initial ranges
|
|
34962
35247
|
for (const range of ranges) {
|
|
34963
|
-
|
|
34964
|
-
const sheetId = range.sheetId;
|
|
34965
|
-
for (let col = zone.left; col <= zone.right; col++) {
|
|
34966
|
-
for (let row = zone.top; row <= zone.bottom; row++) {
|
|
34967
|
-
visited.delete({ sheetId, col, row });
|
|
34968
|
-
}
|
|
34969
|
-
}
|
|
35248
|
+
visited.delete(range);
|
|
34970
35249
|
}
|
|
34971
35250
|
return visited;
|
|
34972
35251
|
}
|
|
@@ -35243,7 +35522,7 @@
|
|
|
35243
35522
|
getters;
|
|
35244
35523
|
compilationParams;
|
|
35245
35524
|
evaluatedCells = new PositionMap();
|
|
35246
|
-
formulaDependencies = lazy(new FormulaDependencyGraph(
|
|
35525
|
+
formulaDependencies = lazy(new FormulaDependencyGraph());
|
|
35247
35526
|
blockedArrayFormulas = new PositionSet({});
|
|
35248
35527
|
spreadingRelations = new SpreadingRelation();
|
|
35249
35528
|
constructor(context, getters) {
|
|
@@ -35278,7 +35557,7 @@
|
|
|
35278
35557
|
return undefined;
|
|
35279
35558
|
}
|
|
35280
35559
|
const arrayFormulas = this.spreadingRelations.searchFormulaPositionsSpreadingOn(position.sheetId, positionToZone(position));
|
|
35281
|
-
return
|
|
35560
|
+
return arrayFormulas.find((position) => !this.blockedArrayFormulas.has(position));
|
|
35282
35561
|
}
|
|
35283
35562
|
updateDependencies(position) {
|
|
35284
35563
|
// removing dependencies is slow because it requires
|
|
@@ -35322,57 +35601,72 @@
|
|
|
35322
35601
|
}
|
|
35323
35602
|
evaluateCells(positions) {
|
|
35324
35603
|
const start = performance.now();
|
|
35325
|
-
const
|
|
35326
|
-
|
|
35604
|
+
const rangesToCompute = new RangeSet();
|
|
35605
|
+
rangesToCompute.addManyPositions(positions);
|
|
35327
35606
|
const arrayFormulasPositions = this.getArrayFormulasImpactedByChangesOf(positions);
|
|
35328
|
-
|
|
35329
|
-
|
|
35330
|
-
|
|
35331
|
-
this.evaluate(
|
|
35607
|
+
rangesToCompute.addMany(this.getCellsDependingOn(rangesToCompute));
|
|
35608
|
+
rangesToCompute.addMany(arrayFormulasPositions);
|
|
35609
|
+
rangesToCompute.addMany(this.getCellsDependingOn(arrayFormulasPositions));
|
|
35610
|
+
this.evaluate(rangesToCompute);
|
|
35332
35611
|
console.debug("evaluate Cells", performance.now() - start, "ms");
|
|
35333
35612
|
}
|
|
35334
35613
|
getArrayFormulasImpactedByChangesOf(positions) {
|
|
35335
|
-
const
|
|
35614
|
+
const impactedRanges = new RangeSet();
|
|
35336
35615
|
for (const position of positions) {
|
|
35337
35616
|
const content = this.getters.getCell(position)?.content;
|
|
35338
35617
|
const arrayFormulaPosition = this.getArrayFormulaSpreadingOn(position);
|
|
35339
35618
|
if (arrayFormulaPosition !== undefined) {
|
|
35340
35619
|
// take into account new collisions.
|
|
35341
|
-
|
|
35620
|
+
impactedRanges.addPosition(arrayFormulaPosition);
|
|
35342
35621
|
}
|
|
35343
35622
|
if (!content) {
|
|
35344
35623
|
// The previous content could have blocked some array formulas
|
|
35345
|
-
|
|
35624
|
+
impactedRanges.addPosition(position);
|
|
35346
35625
|
}
|
|
35347
35626
|
}
|
|
35348
|
-
const
|
|
35349
|
-
|
|
35350
|
-
for (const zone of zonesBySheetIds[sheetId]) {
|
|
35351
|
-
impactedPositions.addMany(this.getArrayFormulasBlockedBy(sheetId, zone));
|
|
35352
|
-
}
|
|
35627
|
+
for (const range of [...impactedRanges]) {
|
|
35628
|
+
impactedRanges.addMany(this.getArrayFormulasBlockedBy(range.sheetId, range.zone));
|
|
35353
35629
|
}
|
|
35354
|
-
return
|
|
35630
|
+
return impactedRanges;
|
|
35355
35631
|
}
|
|
35356
35632
|
buildDependencyGraph() {
|
|
35357
35633
|
this.blockedArrayFormulas = this.createEmptyPositionSet();
|
|
35358
35634
|
this.spreadingRelations = new SpreadingRelation();
|
|
35359
35635
|
this.formulaDependencies = lazy(() => {
|
|
35360
|
-
const
|
|
35361
|
-
|
|
35362
|
-
.
|
|
35363
|
-
|
|
35364
|
-
|
|
35365
|
-
|
|
35366
|
-
|
|
35367
|
-
|
|
35368
|
-
|
|
35369
|
-
|
|
35636
|
+
const rTreeItems = [];
|
|
35637
|
+
for (const sheetId of this.getters.getSheetIds()) {
|
|
35638
|
+
const cells = this.getters.getCells(sheetId);
|
|
35639
|
+
for (const cellId in cells) {
|
|
35640
|
+
const cell = cells[cellId];
|
|
35641
|
+
if (cell.isFormula) {
|
|
35642
|
+
const directDependencies = cell.compiledFormula.dependencies;
|
|
35643
|
+
for (const range of directDependencies) {
|
|
35644
|
+
if (range.invalidSheetName || range.invalidXc) {
|
|
35645
|
+
continue;
|
|
35646
|
+
}
|
|
35647
|
+
rTreeItems.push({
|
|
35648
|
+
data: {
|
|
35649
|
+
sheetId,
|
|
35650
|
+
zone: positionToZone(this.getters.getCellPosition(cellId)),
|
|
35651
|
+
},
|
|
35652
|
+
boundingBox: { sheetId: range.sheetId, zone: range.zone },
|
|
35653
|
+
});
|
|
35654
|
+
}
|
|
35655
|
+
}
|
|
35656
|
+
}
|
|
35657
|
+
}
|
|
35658
|
+
return new FormulaDependencyGraph(rTreeItems);
|
|
35370
35659
|
});
|
|
35371
35660
|
}
|
|
35372
35661
|
evaluateAllCells() {
|
|
35373
35662
|
const start = performance.now();
|
|
35374
35663
|
this.evaluatedCells = new PositionMap();
|
|
35375
|
-
|
|
35664
|
+
const ranges = [];
|
|
35665
|
+
for (const sheetId of this.getters.getSheetIds()) {
|
|
35666
|
+
const zone = this.getters.getSheetZone(sheetId);
|
|
35667
|
+
ranges.push({ sheetId, zone });
|
|
35668
|
+
}
|
|
35669
|
+
this.evaluate(ranges);
|
|
35376
35670
|
console.debug("evaluate all cells", performance.now() - start, "ms");
|
|
35377
35671
|
}
|
|
35378
35672
|
evaluateFormulaResult(sheetId, formulaString) {
|
|
@@ -35396,48 +35690,47 @@
|
|
|
35396
35690
|
return handleError(error, "");
|
|
35397
35691
|
}
|
|
35398
35692
|
}
|
|
35399
|
-
getAllCells() {
|
|
35400
|
-
const positions = this.createEmptyPositionSet();
|
|
35401
|
-
positions.fillAllPositions();
|
|
35402
|
-
return positions;
|
|
35403
|
-
}
|
|
35404
35693
|
/**
|
|
35405
35694
|
* Return the position of formulas blocked by the given positions
|
|
35406
35695
|
* as well as all their dependencies.
|
|
35407
35696
|
*/
|
|
35408
35697
|
getArrayFormulasBlockedBy(sheetId, zone) {
|
|
35409
|
-
const arrayFormulaPositions =
|
|
35698
|
+
const arrayFormulaPositions = new RangeSet();
|
|
35410
35699
|
const arrayFormulas = this.spreadingRelations.searchFormulaPositionsSpreadingOn(sheetId, zone);
|
|
35411
|
-
arrayFormulaPositions.
|
|
35700
|
+
arrayFormulaPositions.addManyPositions(arrayFormulas);
|
|
35412
35701
|
const spilledPositions = [...arrayFormulas].filter((position) => !this.blockedArrayFormulas.has(position));
|
|
35413
35702
|
if (spilledPositions.length) {
|
|
35414
35703
|
// ignore the formula spreading on the position. Keep only the blocked ones
|
|
35415
|
-
arrayFormulaPositions.
|
|
35704
|
+
arrayFormulaPositions.deleteManyPositions(spilledPositions);
|
|
35416
35705
|
}
|
|
35417
35706
|
arrayFormulaPositions.addMany(this.getCellsDependingOn(arrayFormulaPositions));
|
|
35418
35707
|
return arrayFormulaPositions;
|
|
35419
35708
|
}
|
|
35420
|
-
|
|
35709
|
+
nextRangesToUpdate = new RangeSet();
|
|
35421
35710
|
cellsBeingComputed = new Set();
|
|
35422
35711
|
symbolsBeingComputed = new Set();
|
|
35423
|
-
evaluate(
|
|
35712
|
+
evaluate(ranges) {
|
|
35424
35713
|
this.cellsBeingComputed = new Set();
|
|
35425
|
-
this.
|
|
35714
|
+
this.nextRangesToUpdate = new RangeSet(ranges);
|
|
35426
35715
|
let currentIteration = 0;
|
|
35427
|
-
while (!this.
|
|
35716
|
+
while (!this.nextRangesToUpdate.isEmpty() && currentIteration++ < MAX_ITERATION) {
|
|
35428
35717
|
this.updateCompilationParameters();
|
|
35429
|
-
const
|
|
35430
|
-
|
|
35431
|
-
|
|
35432
|
-
|
|
35433
|
-
|
|
35434
|
-
|
|
35435
|
-
|
|
35436
|
-
|
|
35437
|
-
|
|
35438
|
-
|
|
35439
|
-
|
|
35440
|
-
|
|
35718
|
+
const ranges = [...this.nextRangesToUpdate];
|
|
35719
|
+
this.nextRangesToUpdate.clear();
|
|
35720
|
+
this.clearEvaluatedRanges(ranges);
|
|
35721
|
+
for (const range of ranges) {
|
|
35722
|
+
const { left, bottom, right, top } = range.zone;
|
|
35723
|
+
for (let col = left; col <= right; col++) {
|
|
35724
|
+
for (let row = top; row <= bottom; row++) {
|
|
35725
|
+
const position = { sheetId: range.sheetId, col, row };
|
|
35726
|
+
if (this.nextRangesToUpdate.hasPosition(position)) {
|
|
35727
|
+
continue;
|
|
35728
|
+
}
|
|
35729
|
+
const evaluatedCell = this.computeCell(position);
|
|
35730
|
+
if (evaluatedCell !== EMPTY_CELL) {
|
|
35731
|
+
this.evaluatedCells.set(position, evaluatedCell);
|
|
35732
|
+
}
|
|
35733
|
+
}
|
|
35441
35734
|
}
|
|
35442
35735
|
}
|
|
35443
35736
|
onIterationEndEvaluationRegistry.getAll().forEach((callback) => callback(this.getters));
|
|
@@ -35446,6 +35739,16 @@
|
|
|
35446
35739
|
console.warn("Maximum iteration reached while evaluating cells");
|
|
35447
35740
|
}
|
|
35448
35741
|
}
|
|
35742
|
+
clearEvaluatedRanges(ranges) {
|
|
35743
|
+
for (const range of ranges) {
|
|
35744
|
+
const { left, bottom, right, top } = range.zone;
|
|
35745
|
+
for (let col = left; col <= right; col++) {
|
|
35746
|
+
for (let row = top; row <= bottom; row++) {
|
|
35747
|
+
this.evaluatedCells.delete({ sheetId: range.sheetId, col, row });
|
|
35748
|
+
}
|
|
35749
|
+
}
|
|
35750
|
+
}
|
|
35751
|
+
}
|
|
35449
35752
|
computeCell(position) {
|
|
35450
35753
|
const evaluation = this.evaluatedCells.get(position);
|
|
35451
35754
|
if (evaluation) {
|
|
@@ -35518,9 +35821,9 @@
|
|
|
35518
35821
|
}
|
|
35519
35822
|
invalidatePositionsDependingOnSpread(sheetId, resultZone) {
|
|
35520
35823
|
// the result matrix is split in 2 zones to exclude the array formula position
|
|
35521
|
-
const invalidatedPositions = this.
|
|
35522
|
-
invalidatedPositions.delete({ sheetId,
|
|
35523
|
-
this.
|
|
35824
|
+
const invalidatedPositions = this.getCellsDependingOn(excludeTopLeft(resultZone).map((zone) => ({ sheetId, zone })));
|
|
35825
|
+
invalidatedPositions.delete({ sheetId, zone: resultZone });
|
|
35826
|
+
this.nextRangesToUpdate.addMany(invalidatedPositions);
|
|
35524
35827
|
}
|
|
35525
35828
|
assertSheetHasEnoughSpaceToSpreadFormulaResult({ sheetId, col, row }, matrixResult) {
|
|
35526
35829
|
const numberOfCols = this.getters.getNumberCols(sheetId);
|
|
@@ -35595,7 +35898,7 @@
|
|
|
35595
35898
|
}
|
|
35596
35899
|
const sheetId = position.sheetId;
|
|
35597
35900
|
this.invalidatePositionsDependingOnSpread(sheetId, zone);
|
|
35598
|
-
this.
|
|
35901
|
+
this.nextRangesToUpdate.addMany(this.getArrayFormulasBlockedBy(sheetId, zone));
|
|
35599
35902
|
}
|
|
35600
35903
|
/**
|
|
35601
35904
|
* Wraps a GetSymbolValue function to add cycle detection
|
|
@@ -35630,13 +35933,8 @@
|
|
|
35630
35933
|
}
|
|
35631
35934
|
return cell.compiledFormula.dependencies;
|
|
35632
35935
|
}
|
|
35633
|
-
getCellsDependingOn(
|
|
35634
|
-
|
|
35635
|
-
const zonesBySheetIds = aggregatePositionsToZones(positions);
|
|
35636
|
-
for (const sheetId in zonesBySheetIds) {
|
|
35637
|
-
ranges.push(...zonesBySheetIds[sheetId].map((zone) => ({ sheetId, zone })));
|
|
35638
|
-
}
|
|
35639
|
-
return this.formulaDependencies().getCellsDependingOn(ranges, this.nextPositionsToUpdate);
|
|
35936
|
+
getCellsDependingOn(ranges) {
|
|
35937
|
+
return this.formulaDependencies().getCellsDependingOn(ranges, this.nextRangesToUpdate);
|
|
35640
35938
|
}
|
|
35641
35939
|
}
|
|
35642
35940
|
function forEachSpreadPositionInMatrix(nbColumns, nbRows, callback) {
|
|
@@ -40136,18 +40434,8 @@
|
|
|
40136
40434
|
return EMPTY_PIVOT_CELL;
|
|
40137
40435
|
}
|
|
40138
40436
|
if (functionName === "PIVOT") {
|
|
40139
|
-
const
|
|
40140
|
-
const
|
|
40141
|
-
const includeColumnHeaders = toScalar(args[3]);
|
|
40142
|
-
const includeMeasures = toScalar(args[5]);
|
|
40143
|
-
const shouldIncludeMeasures = includeMeasures === undefined ? true : toBoolean(includeMeasures);
|
|
40144
|
-
const shouldIncludeColumnHeaders = includeColumnHeaders === undefined ? true : toBoolean(includeColumnHeaders);
|
|
40145
|
-
const visibilityOptions = {
|
|
40146
|
-
displayColumnHeaders: shouldIncludeColumnHeaders,
|
|
40147
|
-
displayTotals: shouldIncludeTotal,
|
|
40148
|
-
displayMeasuresRow: shouldIncludeMeasures,
|
|
40149
|
-
};
|
|
40150
|
-
const pivotCells = pivot.getCollapsedTableStructure().getPivotCells(visibilityOptions);
|
|
40437
|
+
const pivotStyle = getPivotStyleFromFnArgs(this.getters.getPivotCoreDefinition(pivotId), toScalar(args[1]), toScalar(args[2]), toScalar(args[3]), toScalar(args[4]), toScalar(args[5]), this.getters.getLocale());
|
|
40438
|
+
const pivotCells = pivot.getCollapsedTableStructure().getPivotCells(pivotStyle);
|
|
40151
40439
|
const pivotCol = position.col - mainPosition.col;
|
|
40152
40440
|
const pivotRow = position.row - mainPosition.row;
|
|
40153
40441
|
return pivotCells[pivotCol][pivotRow];
|
|
@@ -49877,7 +50165,6 @@
|
|
|
49877
50165
|
const startSnapshot = performance.now();
|
|
49878
50166
|
console.debug("Snapshot requested");
|
|
49879
50167
|
this.session.snapshot(this.exportData());
|
|
49880
|
-
this.garbageCollectExternalResources();
|
|
49881
50168
|
console.debug("Snapshot taken in", performance.now() - startSnapshot, "ms");
|
|
49882
50169
|
}
|
|
49883
50170
|
console.debug("Model created in", performance.now() - start, "ms");
|
|
@@ -50263,11 +50550,6 @@
|
|
|
50263
50550
|
data = deepCopy(data);
|
|
50264
50551
|
return getXLSX(data);
|
|
50265
50552
|
}
|
|
50266
|
-
garbageCollectExternalResources() {
|
|
50267
|
-
for (const plugin of this.corePlugins) {
|
|
50268
|
-
plugin.garbageCollectExternalResources();
|
|
50269
|
-
}
|
|
50270
|
-
}
|
|
50271
50553
|
}
|
|
50272
50554
|
function createCommand(type, payload = {}) {
|
|
50273
50555
|
const command = deepCopy(payload);
|
|
@@ -50411,8 +50693,8 @@
|
|
|
50411
50693
|
|
|
50412
50694
|
|
|
50413
50695
|
__info__.version = "19.1.0-alpha.3";
|
|
50414
|
-
__info__.date = "2025-10-
|
|
50415
|
-
__info__.hash = "
|
|
50696
|
+
__info__.date = "2025-10-23T08:19:27.355Z";
|
|
50697
|
+
__info__.hash = "78717d4";
|
|
50416
50698
|
|
|
50417
50699
|
|
|
50418
50700
|
})(this.o_spreadsheet_engine = this.o_spreadsheet_engine || {});
|