@odoo/o-spreadsheet 19.1.0-alpha.7 → 19.1.0-alpha.9
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 +19 -22
- package/dist/o-spreadsheet-engine.esm.js +489 -198
- package/dist/o-spreadsheet-engine.iife.js +489 -198
- package/dist/o-spreadsheet-engine.min.iife.js +313 -313
- package/dist/o-spreadsheet.d.ts +259 -171
- package/dist/o_spreadsheet.esm.js +804 -424
- package/dist/o_spreadsheet.iife.js +804 -424
- package/dist/o_spreadsheet.min.iife.js +317 -317
- package/dist/o_spreadsheet.xml +147 -37
- 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-23T11:12:13.207Z
|
|
7
|
+
* @hash bd756dd
|
|
8
8
|
*/
|
|
9
9
|
|
|
10
10
|
(function (exports) {
|
|
@@ -911,8 +911,17 @@
|
|
|
911
911
|
/**
|
|
912
912
|
* Returns a function, that, as long as it continues to be invoked, will not
|
|
913
913
|
* be triggered. The function will be called after it stops being called for
|
|
914
|
-
* N milliseconds. If `immediate` is passed,
|
|
915
|
-
*
|
|
914
|
+
* N milliseconds. If `immediate` is passed, the function is called is called
|
|
915
|
+
* immediately on the first call and the debouncing is triggered starting the second
|
|
916
|
+
* call in the defined time window.
|
|
917
|
+
*
|
|
918
|
+
* Example:
|
|
919
|
+
* debouncedFunction = debounce(() => console.log('Hello!'), 250);
|
|
920
|
+
* debouncedFunction(); debouncedFunction(); // Will log 'Hello!' after 250ms
|
|
921
|
+
*
|
|
922
|
+
* debouncedFunction = debounce(() => console.log('Hello!'), 250, true);
|
|
923
|
+
* debouncedFunction(); debouncedFunction(); // Will log 'Hello!' and relog it after 250ms
|
|
924
|
+
*
|
|
916
925
|
*
|
|
917
926
|
* Also decorate the argument function with two methods: stopDebounce and isDebouncePending.
|
|
918
927
|
*
|
|
@@ -920,21 +929,21 @@
|
|
|
920
929
|
*/
|
|
921
930
|
function debounce(func, wait, immediate) {
|
|
922
931
|
let timeout = undefined;
|
|
932
|
+
let firstCalled = false;
|
|
923
933
|
const debounced = function () {
|
|
924
934
|
const context = this;
|
|
925
935
|
const args = Array.from(arguments);
|
|
936
|
+
if (!firstCalled && immediate) {
|
|
937
|
+
firstCalled = true;
|
|
938
|
+
return func.apply(context, args);
|
|
939
|
+
}
|
|
926
940
|
function later() {
|
|
927
941
|
timeout = undefined;
|
|
928
|
-
|
|
929
|
-
|
|
930
|
-
}
|
|
942
|
+
firstCalled = false;
|
|
943
|
+
func.apply(context, args);
|
|
931
944
|
}
|
|
932
|
-
const callNow = immediate && !timeout;
|
|
933
945
|
clearTimeout(timeout);
|
|
934
946
|
timeout = setTimeout(later, wait);
|
|
935
|
-
if (callNow) {
|
|
936
|
-
func.apply(context, args);
|
|
937
|
-
}
|
|
938
947
|
};
|
|
939
948
|
debounced.isDebouncePending = () => timeout !== undefined;
|
|
940
949
|
debounced.stopDebounce = () => {
|
|
@@ -5185,6 +5194,29 @@
|
|
|
5185
5194
|
removeContiguousProfiles(profilesStartingPosition, profiles, leftIndex, rightIndex);
|
|
5186
5195
|
}
|
|
5187
5196
|
}
|
|
5197
|
+
function profilesContainsZone(profilesStartingPosition, profiles, zone) {
|
|
5198
|
+
const leftValue = zone.left;
|
|
5199
|
+
const rightValue = zone.right;
|
|
5200
|
+
const topValue = zone.top;
|
|
5201
|
+
const bottomValue = zone.bottom + 1;
|
|
5202
|
+
const leftIndex = binaryPredecessorSearch(profilesStartingPosition, leftValue, 0);
|
|
5203
|
+
const rightIndex = binaryPredecessorSearch(profilesStartingPosition, rightValue, leftIndex);
|
|
5204
|
+
if (leftIndex === -1 || rightIndex === -1) {
|
|
5205
|
+
return false;
|
|
5206
|
+
}
|
|
5207
|
+
for (let i = leftIndex; i <= rightIndex; i++) {
|
|
5208
|
+
const profile = profiles.get(profilesStartingPosition[i]);
|
|
5209
|
+
const topPredIndex = binaryPredecessorSearch(profile, topValue, 0, true);
|
|
5210
|
+
const bottomSuccIndex = binarySuccessorSearch(profile, bottomValue, 0, true);
|
|
5211
|
+
if (topPredIndex === -1 || topPredIndex % 2 !== 0) {
|
|
5212
|
+
return false;
|
|
5213
|
+
}
|
|
5214
|
+
if (topValue < profile[topPredIndex] || bottomValue > profile[bottomSuccIndex]) {
|
|
5215
|
+
return false;
|
|
5216
|
+
}
|
|
5217
|
+
}
|
|
5218
|
+
return true;
|
|
5219
|
+
}
|
|
5188
5220
|
function findIndexAndCreateProfile(profilesStartingPosition, profiles, value, searchLeft, startIndex) {
|
|
5189
5221
|
if (value === undefined) {
|
|
5190
5222
|
// this is only the case when the value correspond to a bottom value that could be undefined
|
|
@@ -5269,7 +5301,18 @@
|
|
|
5269
5301
|
}
|
|
5270
5302
|
// add the top and bottom value to the profile and
|
|
5271
5303
|
// remove all information between the top and bottom index
|
|
5272
|
-
|
|
5304
|
+
const toDelete = bottomSuccIndex - topPredIndex - 1;
|
|
5305
|
+
const toInsert = newPoints.length;
|
|
5306
|
+
const start = topPredIndex + 1;
|
|
5307
|
+
// fast path and slow path
|
|
5308
|
+
if (start === profile.length - 1 && toDelete === 1 && toInsert === 1) {
|
|
5309
|
+
// fast path: we just need to replace the last element
|
|
5310
|
+
profile[start] = newPoints[0] ?? newPoints[1];
|
|
5311
|
+
}
|
|
5312
|
+
else {
|
|
5313
|
+
// equivalent but slower and with memory allocation
|
|
5314
|
+
profile.splice(start, toDelete, ...newPoints);
|
|
5315
|
+
}
|
|
5273
5316
|
}
|
|
5274
5317
|
function removeContiguousProfiles(profilesStartingPosition, profiles, leftIndex, rightIndex) {
|
|
5275
5318
|
const start = leftIndex - 1 === -1 ? 0 : leftIndex - 1;
|
|
@@ -5308,8 +5351,10 @@
|
|
|
5308
5351
|
left,
|
|
5309
5352
|
bottom,
|
|
5310
5353
|
right,
|
|
5311
|
-
hasHeader: (bottom === undefined && top !== 0) || (right === undefined && left !== 0),
|
|
5312
5354
|
};
|
|
5355
|
+
if ((bottom === undefined && top !== 0) || (right === undefined && left !== 0)) {
|
|
5356
|
+
profileZone.hasHeader = true;
|
|
5357
|
+
}
|
|
5313
5358
|
let findCorrespondingZone = false;
|
|
5314
5359
|
for (let j = pendingZones.length - 1; j >= 0; j--) {
|
|
5315
5360
|
const pendingZone = pendingZones[j];
|
|
@@ -5764,17 +5809,6 @@
|
|
|
5764
5809
|
}
|
|
5765
5810
|
return [leftColumnZone, rightPartZone];
|
|
5766
5811
|
}
|
|
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
5812
|
/**
|
|
5779
5813
|
* Array of all positions in the zone.
|
|
5780
5814
|
*/
|
|
@@ -14523,6 +14557,13 @@
|
|
|
14523
14557
|
.add("minute_number", nullHandlerDecorator(minuteNumberAdapter))
|
|
14524
14558
|
.add("second_number", nullHandlerDecorator(secondNumberAdapter));
|
|
14525
14559
|
|
|
14560
|
+
const DEFAULT_PIVOT_STYLE = {
|
|
14561
|
+
displayTotals: true,
|
|
14562
|
+
displayColumnHeaders: true,
|
|
14563
|
+
displayMeasuresRow: true,
|
|
14564
|
+
numberOfRows: Number.MAX_VALUE,
|
|
14565
|
+
numberOfColumns: Number.MAX_VALUE,
|
|
14566
|
+
};
|
|
14526
14567
|
const AGGREGATOR_NAMES = {
|
|
14527
14568
|
count: _t("Count"),
|
|
14528
14569
|
count_distinct: _t("Count Distinct"),
|
|
@@ -14818,6 +14859,25 @@
|
|
|
14818
14859
|
pivot: { ...definition, collapsedDomains: newDomains },
|
|
14819
14860
|
});
|
|
14820
14861
|
}
|
|
14862
|
+
function getPivotStyleFromFnArgs(definition, rowCountArg, includeTotalArg, includeColumnHeadersArg, columnCountArg, includeMeasuresRowArg, locale) {
|
|
14863
|
+
const style = definition.style;
|
|
14864
|
+
const numberOfRows = rowCountArg !== undefined
|
|
14865
|
+
? toNumber(rowCountArg, locale)
|
|
14866
|
+
: style?.numberOfRows ?? DEFAULT_PIVOT_STYLE.numberOfRows;
|
|
14867
|
+
const numberOfColumns = columnCountArg !== undefined
|
|
14868
|
+
? toNumber(columnCountArg, locale)
|
|
14869
|
+
: style?.numberOfColumns ?? DEFAULT_PIVOT_STYLE.numberOfColumns;
|
|
14870
|
+
const displayTotals = includeTotalArg !== undefined
|
|
14871
|
+
? toBoolean(includeTotalArg)
|
|
14872
|
+
: style?.displayTotals ?? DEFAULT_PIVOT_STYLE.displayTotals;
|
|
14873
|
+
const displayColumnHeaders = includeColumnHeadersArg !== undefined
|
|
14874
|
+
? toBoolean(includeColumnHeadersArg)
|
|
14875
|
+
: style?.displayColumnHeaders ?? DEFAULT_PIVOT_STYLE.displayColumnHeaders;
|
|
14876
|
+
const displayMeasuresRow = includeMeasuresRowArg !== undefined
|
|
14877
|
+
? toBoolean(includeMeasuresRowArg)
|
|
14878
|
+
: style?.displayMeasuresRow ?? DEFAULT_PIVOT_STYLE.displayMeasuresRow;
|
|
14879
|
+
return { numberOfRows, numberOfColumns, displayTotals, displayColumnHeaders, displayMeasuresRow };
|
|
14880
|
+
}
|
|
14821
14881
|
|
|
14822
14882
|
/**
|
|
14823
14883
|
* Get the pivot ID from the formula pivot ID.
|
|
@@ -15432,24 +15492,18 @@
|
|
|
15432
15492
|
arg("column_count (number, optional)", _t("number of columns")),
|
|
15433
15493
|
arg("include_measure_titles (boolean, default=TRUE)", _t("Whether to include the measure titles row or not.")),
|
|
15434
15494
|
],
|
|
15435
|
-
compute: function (pivotFormulaId, rowCount
|
|
15495
|
+
compute: function (pivotFormulaId, rowCount, includeTotal, includeColumnHeaders, columnCount, includeMeasureTitles) {
|
|
15436
15496
|
const _pivotFormulaId = toString(pivotFormulaId);
|
|
15437
|
-
const
|
|
15438
|
-
|
|
15497
|
+
const pivotId = getPivotId(_pivotFormulaId, this.getters);
|
|
15498
|
+
const pivot = this.getters.getPivot(pivotId);
|
|
15499
|
+
const coreDefinition = this.getters.getPivotCoreDefinition(pivotId);
|
|
15500
|
+
const pivotStyle = getPivotStyleFromFnArgs(coreDefinition, rowCount, includeTotal, includeColumnHeaders, columnCount, includeMeasureTitles, this.locale);
|
|
15501
|
+
if (pivotStyle.numberOfRows < 0) {
|
|
15439
15502
|
return new EvaluationError(_t("The number of rows must be positive."));
|
|
15440
15503
|
}
|
|
15441
|
-
|
|
15442
|
-
if (_columnCount < 0) {
|
|
15504
|
+
if (pivotStyle.numberOfColumns < 0) {
|
|
15443
15505
|
return new EvaluationError(_t("The number of columns must be positive."));
|
|
15444
15506
|
}
|
|
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
15507
|
addPivotDependencies(this, coreDefinition, coreDefinition.measures);
|
|
15454
15508
|
pivot.init({ reload: pivot.needsReevaluation });
|
|
15455
15509
|
const error = pivot.assertIsValid({ throwOnError: false });
|
|
@@ -15460,20 +15514,20 @@
|
|
|
15460
15514
|
if (table.numberOfCells > PIVOT_MAX_NUMBER_OF_CELLS) {
|
|
15461
15515
|
return new EvaluationError(getPivotTooBigErrorMessage(table.numberOfCells, this.locale));
|
|
15462
15516
|
}
|
|
15463
|
-
const cells = table.getPivotCells(
|
|
15517
|
+
const cells = table.getPivotCells(pivotStyle);
|
|
15464
15518
|
let headerRows = 0;
|
|
15465
|
-
if (
|
|
15519
|
+
if (pivotStyle.displayColumnHeaders) {
|
|
15466
15520
|
headerRows = table.columns.length - 1;
|
|
15467
15521
|
}
|
|
15468
|
-
if (
|
|
15522
|
+
if (pivotStyle.displayMeasuresRow) {
|
|
15469
15523
|
headerRows++;
|
|
15470
15524
|
}
|
|
15471
15525
|
const pivotTitle = this.getters.getPivotName(pivotId);
|
|
15472
|
-
const tableHeight = Math.min(headerRows +
|
|
15526
|
+
const tableHeight = Math.min(headerRows + pivotStyle.numberOfRows, cells[0].length);
|
|
15473
15527
|
if (tableHeight === 0) {
|
|
15474
15528
|
return [[{ value: pivotTitle }]];
|
|
15475
15529
|
}
|
|
15476
|
-
const tableWidth = Math.min(1 +
|
|
15530
|
+
const tableWidth = Math.min(1 + pivotStyle.numberOfColumns, cells.length);
|
|
15477
15531
|
const result = [];
|
|
15478
15532
|
for (const col of range(0, tableWidth)) {
|
|
15479
15533
|
result[col] = [];
|
|
@@ -15496,7 +15550,7 @@
|
|
|
15496
15550
|
}
|
|
15497
15551
|
}
|
|
15498
15552
|
}
|
|
15499
|
-
if (
|
|
15553
|
+
if (pivotStyle.displayColumnHeaders || pivotStyle.displayMeasuresRow) {
|
|
15500
15554
|
result[0][0] = { value: pivotTitle };
|
|
15501
15555
|
}
|
|
15502
15556
|
return result;
|
|
@@ -17968,6 +18022,10 @@
|
|
|
17968
18022
|
}
|
|
17969
18023
|
return parts;
|
|
17970
18024
|
}
|
|
18025
|
+
function positionToBoundedRange(position) {
|
|
18026
|
+
const zone = { left: position.col, top: position.row, right: position.col, bottom: position.row };
|
|
18027
|
+
return { sheetId: position.sheetId, zone };
|
|
18028
|
+
}
|
|
17971
18029
|
/**
|
|
17972
18030
|
* Check that a zone is valid regarding the order of top-bottom and left-right.
|
|
17973
18031
|
* Left should be smaller than right, top should be smaller than bottom.
|
|
@@ -26656,6 +26714,7 @@
|
|
|
26656
26714
|
return data;
|
|
26657
26715
|
}
|
|
26658
26716
|
const figureIds = new Set();
|
|
26717
|
+
const chartIds = new Set();
|
|
26659
26718
|
const uuidGenerator = new UuidGenerator();
|
|
26660
26719
|
for (const sheet of data.sheets || []) {
|
|
26661
26720
|
for (const figure of sheet.figures || []) {
|
|
@@ -26663,6 +26722,12 @@
|
|
|
26663
26722
|
figure.id += uuidGenerator.smallUuid();
|
|
26664
26723
|
}
|
|
26665
26724
|
figureIds.add(figure.id);
|
|
26725
|
+
if (figure.tag === "chart") {
|
|
26726
|
+
if (chartIds.has(figure.data?.chartId)) {
|
|
26727
|
+
figure.data.chartId += uuidGenerator.smallUuid();
|
|
26728
|
+
}
|
|
26729
|
+
chartIds.add(figure.data?.chartId);
|
|
26730
|
+
}
|
|
26666
26731
|
}
|
|
26667
26732
|
}
|
|
26668
26733
|
data.uniqueFigureIds = true;
|
|
@@ -27030,11 +27095,6 @@
|
|
|
27030
27095
|
* @param sheetName couple of old and new sheet names to adapt ranges pointing to that sheet
|
|
27031
27096
|
*/
|
|
27032
27097
|
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
27098
|
}
|
|
27039
27099
|
|
|
27040
27100
|
class BordersPlugin extends CorePlugin {
|
|
@@ -30878,17 +30938,6 @@
|
|
|
30878
30938
|
break;
|
|
30879
30939
|
}
|
|
30880
30940
|
}
|
|
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
30941
|
// ---------------------------------------------------------------------------
|
|
30893
30942
|
// Getters
|
|
30894
30943
|
// ---------------------------------------------------------------------------
|
|
@@ -30950,13 +30999,6 @@
|
|
|
30950
30999
|
sheet.images = [...sheet.images, ...images];
|
|
30951
31000
|
}
|
|
30952
31001
|
}
|
|
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
31002
|
}
|
|
30961
31003
|
|
|
30962
31004
|
class MergePlugin extends CorePlugin {
|
|
@@ -31732,26 +31774,22 @@
|
|
|
31732
31774
|
getNumberOfDataColumns() {
|
|
31733
31775
|
return this.columns.at(-1)?.length || 0;
|
|
31734
31776
|
}
|
|
31735
|
-
getSkippedRows(
|
|
31777
|
+
getSkippedRows(pivotStyle) {
|
|
31736
31778
|
const skippedRows = new Set();
|
|
31737
|
-
if (!
|
|
31779
|
+
if (!pivotStyle.displayColumnHeaders) {
|
|
31738
31780
|
for (let i = 0; i < this.columns.length - 1; i++) {
|
|
31739
31781
|
skippedRows.add(i);
|
|
31740
31782
|
}
|
|
31741
31783
|
}
|
|
31742
|
-
if (!
|
|
31784
|
+
if (!pivotStyle.displayMeasuresRow) {
|
|
31743
31785
|
skippedRows.add(this.columns.length - 1);
|
|
31744
31786
|
}
|
|
31745
31787
|
return skippedRows;
|
|
31746
31788
|
}
|
|
31747
|
-
getPivotCells(
|
|
31748
|
-
|
|
31749
|
-
displayTotals: true,
|
|
31750
|
-
displayMeasuresRow: true,
|
|
31751
|
-
}) {
|
|
31752
|
-
const key = JSON.stringify(visibilityOptions);
|
|
31789
|
+
getPivotCells(pivotStyle = DEFAULT_PIVOT_STYLE) {
|
|
31790
|
+
const key = JSON.stringify(pivotStyle);
|
|
31753
31791
|
if (!this.pivotCells[key]) {
|
|
31754
|
-
const { displayTotals } =
|
|
31792
|
+
const { displayTotals } = pivotStyle;
|
|
31755
31793
|
const numberOfDataRows = this.rows.length;
|
|
31756
31794
|
const numberOfDataColumns = this.getNumberOfDataColumns();
|
|
31757
31795
|
let pivotHeight = this.columns.length + numberOfDataRows;
|
|
@@ -31763,7 +31801,7 @@
|
|
|
31763
31801
|
pivotWidth -= this.measures.length;
|
|
31764
31802
|
}
|
|
31765
31803
|
const domainArray = [];
|
|
31766
|
-
const skippedRows = this.getSkippedRows(
|
|
31804
|
+
const skippedRows = this.getSkippedRows(pivotStyle);
|
|
31767
31805
|
for (let col = 0; col < pivotWidth; col++) {
|
|
31768
31806
|
domainArray.push([]);
|
|
31769
31807
|
for (let row = 0; row < pivotHeight; row++) {
|
|
@@ -34882,6 +34920,281 @@
|
|
|
34882
34920
|
}
|
|
34883
34921
|
}
|
|
34884
34922
|
|
|
34923
|
+
class ZoneSet {
|
|
34924
|
+
profilesStartingPosition = [0];
|
|
34925
|
+
profiles = new Map([[0, []]]);
|
|
34926
|
+
constructor(zones = []) {
|
|
34927
|
+
for (const zone of zones) {
|
|
34928
|
+
this.add(zone);
|
|
34929
|
+
}
|
|
34930
|
+
}
|
|
34931
|
+
isEmpty() {
|
|
34932
|
+
return this.profiles.size === 1 && this.profiles.get(0)?.length === 0;
|
|
34933
|
+
}
|
|
34934
|
+
add(zone) {
|
|
34935
|
+
modifyProfiles(this.profilesStartingPosition, this.profiles, [zone]);
|
|
34936
|
+
}
|
|
34937
|
+
delete(zone) {
|
|
34938
|
+
modifyProfiles(this.profilesStartingPosition, this.profiles, [zone], true);
|
|
34939
|
+
}
|
|
34940
|
+
has(zone) {
|
|
34941
|
+
return profilesContainsZone(this.profilesStartingPosition, this.profiles, zone);
|
|
34942
|
+
}
|
|
34943
|
+
difference(other) {
|
|
34944
|
+
const result = this.copy();
|
|
34945
|
+
for (const zone of other) {
|
|
34946
|
+
result.delete(zone);
|
|
34947
|
+
}
|
|
34948
|
+
return result;
|
|
34949
|
+
}
|
|
34950
|
+
copy() {
|
|
34951
|
+
const result = new ZoneSet();
|
|
34952
|
+
result.profilesStartingPosition = [...this.profilesStartingPosition];
|
|
34953
|
+
result.profiles = new Map();
|
|
34954
|
+
for (const [key, value] of this.profiles) {
|
|
34955
|
+
result.profiles.set(key, [...value]);
|
|
34956
|
+
}
|
|
34957
|
+
return result;
|
|
34958
|
+
}
|
|
34959
|
+
size() {
|
|
34960
|
+
let size = 0;
|
|
34961
|
+
for (const profile of this.profiles.values()) {
|
|
34962
|
+
size += profile.length;
|
|
34963
|
+
}
|
|
34964
|
+
return size / 2;
|
|
34965
|
+
}
|
|
34966
|
+
/**
|
|
34967
|
+
* iterator of all the zones in the ZoneSet
|
|
34968
|
+
*/
|
|
34969
|
+
[Symbol.iterator]() {
|
|
34970
|
+
return constructZonesFromProfiles(this.profilesStartingPosition, this.profiles)[Symbol.iterator]();
|
|
34971
|
+
}
|
|
34972
|
+
}
|
|
34973
|
+
|
|
34974
|
+
class RangeSet {
|
|
34975
|
+
setsBySheetId = {};
|
|
34976
|
+
constructor(ranges = []) {
|
|
34977
|
+
for (const range of ranges) {
|
|
34978
|
+
this.add(range);
|
|
34979
|
+
}
|
|
34980
|
+
}
|
|
34981
|
+
add(range) {
|
|
34982
|
+
if (!this.setsBySheetId[range.sheetId]) {
|
|
34983
|
+
this.setsBySheetId[range.sheetId] = new ZoneSet();
|
|
34984
|
+
}
|
|
34985
|
+
this.setsBySheetId[range.sheetId].add(range.zone);
|
|
34986
|
+
}
|
|
34987
|
+
addMany(ranges) {
|
|
34988
|
+
for (const range of ranges) {
|
|
34989
|
+
this.add(range);
|
|
34990
|
+
}
|
|
34991
|
+
}
|
|
34992
|
+
addPosition(position) {
|
|
34993
|
+
this.add(positionToBoundedRange(position));
|
|
34994
|
+
}
|
|
34995
|
+
addManyPositions(positions) {
|
|
34996
|
+
for (const position of positions) {
|
|
34997
|
+
this.addPosition(position);
|
|
34998
|
+
}
|
|
34999
|
+
}
|
|
35000
|
+
has(range) {
|
|
35001
|
+
if (!this.setsBySheetId[range.sheetId]) {
|
|
35002
|
+
return false;
|
|
35003
|
+
}
|
|
35004
|
+
return this.setsBySheetId[range.sheetId].has(range.zone);
|
|
35005
|
+
}
|
|
35006
|
+
hasPosition(position) {
|
|
35007
|
+
return this.has(positionToBoundedRange(position));
|
|
35008
|
+
}
|
|
35009
|
+
delete(range) {
|
|
35010
|
+
if (!this.setsBySheetId[range.sheetId]) {
|
|
35011
|
+
return;
|
|
35012
|
+
}
|
|
35013
|
+
this.setsBySheetId[range.sheetId].delete(range.zone);
|
|
35014
|
+
}
|
|
35015
|
+
deleteMany(ranges) {
|
|
35016
|
+
for (const range of ranges) {
|
|
35017
|
+
this.delete(range);
|
|
35018
|
+
}
|
|
35019
|
+
}
|
|
35020
|
+
deleteManyPositions(positions) {
|
|
35021
|
+
for (const position of positions) {
|
|
35022
|
+
this.delete(positionToBoundedRange(position));
|
|
35023
|
+
}
|
|
35024
|
+
}
|
|
35025
|
+
difference(other) {
|
|
35026
|
+
const result = new RangeSet();
|
|
35027
|
+
for (const sheetId in this.setsBySheetId) {
|
|
35028
|
+
result.setsBySheetId[sheetId] = this.setsBySheetId[sheetId];
|
|
35029
|
+
}
|
|
35030
|
+
for (const sheetId in other.setsBySheetId) {
|
|
35031
|
+
if (result.setsBySheetId[sheetId]) {
|
|
35032
|
+
result.setsBySheetId[sheetId] = result.setsBySheetId[sheetId].difference(other.setsBySheetId[sheetId]);
|
|
35033
|
+
}
|
|
35034
|
+
}
|
|
35035
|
+
return result;
|
|
35036
|
+
}
|
|
35037
|
+
copy() {
|
|
35038
|
+
const result = new RangeSet();
|
|
35039
|
+
for (const sheetId in this.setsBySheetId) {
|
|
35040
|
+
result.setsBySheetId[sheetId] = this.setsBySheetId[sheetId].copy();
|
|
35041
|
+
}
|
|
35042
|
+
return result;
|
|
35043
|
+
}
|
|
35044
|
+
clear() {
|
|
35045
|
+
this.setsBySheetId = {};
|
|
35046
|
+
}
|
|
35047
|
+
size() {
|
|
35048
|
+
let size = 0;
|
|
35049
|
+
for (const sheetId in this.setsBySheetId) {
|
|
35050
|
+
size += this.setsBySheetId[sheetId].size();
|
|
35051
|
+
}
|
|
35052
|
+
return size;
|
|
35053
|
+
}
|
|
35054
|
+
isEmpty() {
|
|
35055
|
+
for (const sheetId in this.setsBySheetId) {
|
|
35056
|
+
if (!this.setsBySheetId[sheetId].isEmpty()) {
|
|
35057
|
+
return false;
|
|
35058
|
+
}
|
|
35059
|
+
}
|
|
35060
|
+
return true;
|
|
35061
|
+
}
|
|
35062
|
+
/**
|
|
35063
|
+
* iterator of all the ranges in the RangeSet
|
|
35064
|
+
*/
|
|
35065
|
+
[Symbol.iterator]() {
|
|
35066
|
+
const result = [];
|
|
35067
|
+
for (const sheetId in this.setsBySheetId) {
|
|
35068
|
+
for (const zone of this.setsBySheetId[sheetId]) {
|
|
35069
|
+
result.push({ sheetId: sheetId, zone });
|
|
35070
|
+
}
|
|
35071
|
+
}
|
|
35072
|
+
return result[Symbol.iterator]();
|
|
35073
|
+
}
|
|
35074
|
+
}
|
|
35075
|
+
|
|
35076
|
+
/**
|
|
35077
|
+
* R-Tree of ranges, mapping zones (r-tree bounding boxes) to ranges (data of the r-tree item).
|
|
35078
|
+
* Ranges associated to the exact same bounding box are grouped together
|
|
35079
|
+
* to reduce the number of nodes in the R-tree.
|
|
35080
|
+
*/
|
|
35081
|
+
class DependenciesRTree {
|
|
35082
|
+
rTree;
|
|
35083
|
+
constructor(items = []) {
|
|
35084
|
+
const compactedBoxes = groupSameBoundingBoxes(items);
|
|
35085
|
+
this.rTree = new SpreadsheetRTree(compactedBoxes);
|
|
35086
|
+
}
|
|
35087
|
+
insert(item) {
|
|
35088
|
+
const data = this.rTree.search(item.boundingBox);
|
|
35089
|
+
const itemBoundingBox = item.boundingBox;
|
|
35090
|
+
const exactBoundingBox = data.find(({ boundingBox }) => boundingBox.sheetId === itemBoundingBox.sheetId &&
|
|
35091
|
+
boundingBox.zone.left === itemBoundingBox.zone.left &&
|
|
35092
|
+
boundingBox.zone.top === itemBoundingBox.zone.top &&
|
|
35093
|
+
boundingBox.zone.right === itemBoundingBox.zone.right &&
|
|
35094
|
+
boundingBox.zone.bottom === itemBoundingBox.zone.bottom);
|
|
35095
|
+
if (exactBoundingBox) {
|
|
35096
|
+
exactBoundingBox.data.add(item.data);
|
|
35097
|
+
}
|
|
35098
|
+
else {
|
|
35099
|
+
this.rTree.insert({ ...item, data: new RangeSet([item.data]) });
|
|
35100
|
+
}
|
|
35101
|
+
}
|
|
35102
|
+
search({ zone, sheetId }) {
|
|
35103
|
+
const results = new RangeSet();
|
|
35104
|
+
for (const { data } of this.rTree.search({ zone, sheetId })) {
|
|
35105
|
+
results.addMany(data);
|
|
35106
|
+
}
|
|
35107
|
+
return results;
|
|
35108
|
+
}
|
|
35109
|
+
remove(item) {
|
|
35110
|
+
const data = this.rTree.search(item.boundingBox);
|
|
35111
|
+
const itemBoundingBox = item.boundingBox;
|
|
35112
|
+
const exactBoundingBox = data.find(({ boundingBox }) => boundingBox.sheetId === itemBoundingBox.sheetId &&
|
|
35113
|
+
boundingBox.zone.left === itemBoundingBox.zone.left &&
|
|
35114
|
+
boundingBox.zone.top === itemBoundingBox.zone.top &&
|
|
35115
|
+
boundingBox.zone.right === itemBoundingBox.zone.right &&
|
|
35116
|
+
boundingBox.zone.bottom === itemBoundingBox.zone.bottom);
|
|
35117
|
+
if (exactBoundingBox) {
|
|
35118
|
+
exactBoundingBox.data.delete(item.data);
|
|
35119
|
+
}
|
|
35120
|
+
else {
|
|
35121
|
+
this.rTree.remove({ ...item, data: new RangeSet([item.data]) });
|
|
35122
|
+
}
|
|
35123
|
+
}
|
|
35124
|
+
}
|
|
35125
|
+
/**
|
|
35126
|
+
* Group together all formulas pointing to the exact same dependency (bounding box).
|
|
35127
|
+
* The goal is to optimize the following case:
|
|
35128
|
+
* - if any cell in B1:B1000 changes, C1 must be recomputed
|
|
35129
|
+
* - if any cell in B1:B1000 changes, C2 must be recomputed
|
|
35130
|
+
* - if any cell in B1:B1000 changes, C3 must be recomputed
|
|
35131
|
+
* ...
|
|
35132
|
+
* - if any cell in B1:B1000 changes, C1000 must be recomputed
|
|
35133
|
+
*
|
|
35134
|
+
* Instead of having 1000 entries in the R-tree, we want to have a single entry
|
|
35135
|
+
* with B1:B1000 (bounding box) pointing to C1:C1000 (formulas).
|
|
35136
|
+
*/
|
|
35137
|
+
function groupSameBoundingBoxes(items) {
|
|
35138
|
+
// Important: this function must be as fast as possible. It is on the evaluation hot path.
|
|
35139
|
+
let maxCol = 0;
|
|
35140
|
+
let maxRow = 0;
|
|
35141
|
+
for (let i = 0; i < items.length; i++) {
|
|
35142
|
+
const zone = items[i].boundingBox.zone;
|
|
35143
|
+
if (zone.right > maxCol) {
|
|
35144
|
+
maxCol = zone.right;
|
|
35145
|
+
}
|
|
35146
|
+
if (zone.bottom > maxRow) {
|
|
35147
|
+
maxRow = zone.bottom;
|
|
35148
|
+
}
|
|
35149
|
+
}
|
|
35150
|
+
maxCol += 1;
|
|
35151
|
+
maxRow += 1;
|
|
35152
|
+
// in most real-world cases, we can use a fast numeric key
|
|
35153
|
+
// but if the zones are too far right or bottom, we fallback to a slower string key
|
|
35154
|
+
const maxPossibleKey = (((maxRow + 1) * maxCol + 1) * maxRow + 1) * maxCol;
|
|
35155
|
+
const useFastKey = maxPossibleKey <= Number.MAX_SAFE_INTEGER;
|
|
35156
|
+
if (!useFastKey) {
|
|
35157
|
+
console.warn("Max col/row size exceeded, using slow zone key");
|
|
35158
|
+
}
|
|
35159
|
+
const groupedByBBox = {};
|
|
35160
|
+
for (const item of items) {
|
|
35161
|
+
const sheetId = item.boundingBox.sheetId;
|
|
35162
|
+
if (!groupedByBBox[sheetId]) {
|
|
35163
|
+
groupedByBBox[sheetId] = {};
|
|
35164
|
+
}
|
|
35165
|
+
const bBox = item.boundingBox.zone;
|
|
35166
|
+
let bBoxKey = 0;
|
|
35167
|
+
if (useFastKey) {
|
|
35168
|
+
bBoxKey =
|
|
35169
|
+
bBox.left +
|
|
35170
|
+
bBox.top * maxCol +
|
|
35171
|
+
bBox.right * maxCol * maxRow +
|
|
35172
|
+
bBox.bottom * maxCol * maxRow * maxCol;
|
|
35173
|
+
}
|
|
35174
|
+
else {
|
|
35175
|
+
bBoxKey = `${bBox.left},${bBox.top},${bBox.right},${bBox.bottom}`;
|
|
35176
|
+
}
|
|
35177
|
+
if (groupedByBBox[sheetId][bBoxKey]) {
|
|
35178
|
+
const ranges = groupedByBBox[sheetId][bBoxKey].data;
|
|
35179
|
+
ranges.add(item.data);
|
|
35180
|
+
}
|
|
35181
|
+
else {
|
|
35182
|
+
groupedByBBox[sheetId][bBoxKey] = {
|
|
35183
|
+
boundingBox: item.boundingBox,
|
|
35184
|
+
data: new RangeSet([item.data]),
|
|
35185
|
+
};
|
|
35186
|
+
}
|
|
35187
|
+
}
|
|
35188
|
+
const result = [];
|
|
35189
|
+
for (const sheetId in groupedByBBox) {
|
|
35190
|
+
const map = groupedByBBox[sheetId];
|
|
35191
|
+
for (const key in map) {
|
|
35192
|
+
result.push(map[key]);
|
|
35193
|
+
}
|
|
35194
|
+
}
|
|
35195
|
+
return result;
|
|
35196
|
+
}
|
|
35197
|
+
|
|
34885
35198
|
/**
|
|
34886
35199
|
* Implementation of a dependency Graph.
|
|
34887
35200
|
* The graph is used to evaluate the cells in the correct
|
|
@@ -34890,12 +35203,10 @@
|
|
|
34890
35203
|
* It uses an R-Tree data structure to efficiently find dependent cells.
|
|
34891
35204
|
*/
|
|
34892
35205
|
class FormulaDependencyGraph {
|
|
34893
|
-
createEmptyPositionSet;
|
|
34894
35206
|
dependencies = new PositionMap();
|
|
34895
35207
|
rTree;
|
|
34896
|
-
constructor(
|
|
34897
|
-
this.
|
|
34898
|
-
this.rTree = new SpreadsheetRTree(data);
|
|
35208
|
+
constructor(data = []) {
|
|
35209
|
+
this.rTree = new DependenciesRTree(data);
|
|
34899
35210
|
}
|
|
34900
35211
|
removeAllDependencies(formulaPosition) {
|
|
34901
35212
|
const ranges = this.dependencies.get(formulaPosition);
|
|
@@ -34909,7 +35220,10 @@
|
|
|
34909
35220
|
}
|
|
34910
35221
|
addDependencies(formulaPosition, dependencies) {
|
|
34911
35222
|
const rTreeItems = dependencies.map(({ sheetId, zone }) => ({
|
|
34912
|
-
data:
|
|
35223
|
+
data: {
|
|
35224
|
+
sheetId: formulaPosition.sheetId,
|
|
35225
|
+
zone: positionToZone(formulaPosition),
|
|
35226
|
+
},
|
|
34913
35227
|
boundingBox: {
|
|
34914
35228
|
zone,
|
|
34915
35229
|
sheetId,
|
|
@@ -34927,46 +35241,20 @@
|
|
|
34927
35241
|
}
|
|
34928
35242
|
}
|
|
34929
35243
|
/**
|
|
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)
|
|
35244
|
+
* Return all the cells that depend on the provided ranges.
|
|
34933
35245
|
*/
|
|
34934
|
-
getCellsDependingOn(ranges,
|
|
34935
|
-
|
|
35246
|
+
getCellsDependingOn(ranges, visited = new RangeSet()) {
|
|
35247
|
+
visited = visited.copy();
|
|
34936
35248
|
const queue = Array.from(ranges).reverse();
|
|
34937
35249
|
while (queue.length > 0) {
|
|
34938
35250
|
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
|
-
}
|
|
35251
|
+
visited.add(range);
|
|
35252
|
+
const impactedRanges = this.rTree.search(range);
|
|
35253
|
+
queue.push(...impactedRanges.difference(visited));
|
|
34960
35254
|
}
|
|
34961
35255
|
// remove initial ranges
|
|
34962
35256
|
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
|
-
}
|
|
35257
|
+
visited.delete(range);
|
|
34970
35258
|
}
|
|
34971
35259
|
return visited;
|
|
34972
35260
|
}
|
|
@@ -35243,7 +35531,7 @@
|
|
|
35243
35531
|
getters;
|
|
35244
35532
|
compilationParams;
|
|
35245
35533
|
evaluatedCells = new PositionMap();
|
|
35246
|
-
formulaDependencies = lazy(new FormulaDependencyGraph(
|
|
35534
|
+
formulaDependencies = lazy(new FormulaDependencyGraph());
|
|
35247
35535
|
blockedArrayFormulas = new PositionSet({});
|
|
35248
35536
|
spreadingRelations = new SpreadingRelation();
|
|
35249
35537
|
constructor(context, getters) {
|
|
@@ -35278,7 +35566,7 @@
|
|
|
35278
35566
|
return undefined;
|
|
35279
35567
|
}
|
|
35280
35568
|
const arrayFormulas = this.spreadingRelations.searchFormulaPositionsSpreadingOn(position.sheetId, positionToZone(position));
|
|
35281
|
-
return
|
|
35569
|
+
return arrayFormulas.find((position) => !this.blockedArrayFormulas.has(position));
|
|
35282
35570
|
}
|
|
35283
35571
|
updateDependencies(position) {
|
|
35284
35572
|
// removing dependencies is slow because it requires
|
|
@@ -35322,57 +35610,72 @@
|
|
|
35322
35610
|
}
|
|
35323
35611
|
evaluateCells(positions) {
|
|
35324
35612
|
const start = performance.now();
|
|
35325
|
-
const
|
|
35326
|
-
|
|
35613
|
+
const rangesToCompute = new RangeSet();
|
|
35614
|
+
rangesToCompute.addManyPositions(positions);
|
|
35327
35615
|
const arrayFormulasPositions = this.getArrayFormulasImpactedByChangesOf(positions);
|
|
35328
|
-
|
|
35329
|
-
|
|
35330
|
-
|
|
35331
|
-
this.evaluate(
|
|
35616
|
+
rangesToCompute.addMany(this.getCellsDependingOn(rangesToCompute));
|
|
35617
|
+
rangesToCompute.addMany(arrayFormulasPositions);
|
|
35618
|
+
rangesToCompute.addMany(this.getCellsDependingOn(arrayFormulasPositions));
|
|
35619
|
+
this.evaluate(rangesToCompute);
|
|
35332
35620
|
console.debug("evaluate Cells", performance.now() - start, "ms");
|
|
35333
35621
|
}
|
|
35334
35622
|
getArrayFormulasImpactedByChangesOf(positions) {
|
|
35335
|
-
const
|
|
35623
|
+
const impactedRanges = new RangeSet();
|
|
35336
35624
|
for (const position of positions) {
|
|
35337
35625
|
const content = this.getters.getCell(position)?.content;
|
|
35338
35626
|
const arrayFormulaPosition = this.getArrayFormulaSpreadingOn(position);
|
|
35339
35627
|
if (arrayFormulaPosition !== undefined) {
|
|
35340
35628
|
// take into account new collisions.
|
|
35341
|
-
|
|
35629
|
+
impactedRanges.addPosition(arrayFormulaPosition);
|
|
35342
35630
|
}
|
|
35343
35631
|
if (!content) {
|
|
35344
35632
|
// The previous content could have blocked some array formulas
|
|
35345
|
-
|
|
35633
|
+
impactedRanges.addPosition(position);
|
|
35346
35634
|
}
|
|
35347
35635
|
}
|
|
35348
|
-
const
|
|
35349
|
-
|
|
35350
|
-
for (const zone of zonesBySheetIds[sheetId]) {
|
|
35351
|
-
impactedPositions.addMany(this.getArrayFormulasBlockedBy(sheetId, zone));
|
|
35352
|
-
}
|
|
35636
|
+
for (const range of [...impactedRanges]) {
|
|
35637
|
+
impactedRanges.addMany(this.getArrayFormulasBlockedBy(range.sheetId, range.zone));
|
|
35353
35638
|
}
|
|
35354
|
-
return
|
|
35639
|
+
return impactedRanges;
|
|
35355
35640
|
}
|
|
35356
35641
|
buildDependencyGraph() {
|
|
35357
35642
|
this.blockedArrayFormulas = this.createEmptyPositionSet();
|
|
35358
35643
|
this.spreadingRelations = new SpreadingRelation();
|
|
35359
35644
|
this.formulaDependencies = lazy(() => {
|
|
35360
|
-
const
|
|
35361
|
-
|
|
35362
|
-
.
|
|
35363
|
-
|
|
35364
|
-
|
|
35365
|
-
|
|
35366
|
-
|
|
35367
|
-
|
|
35368
|
-
|
|
35369
|
-
|
|
35645
|
+
const rTreeItems = [];
|
|
35646
|
+
for (const sheetId of this.getters.getSheetIds()) {
|
|
35647
|
+
const cells = this.getters.getCells(sheetId);
|
|
35648
|
+
for (const cellId in cells) {
|
|
35649
|
+
const cell = cells[cellId];
|
|
35650
|
+
if (cell.isFormula) {
|
|
35651
|
+
const directDependencies = cell.compiledFormula.dependencies;
|
|
35652
|
+
for (const range of directDependencies) {
|
|
35653
|
+
if (range.invalidSheetName || range.invalidXc) {
|
|
35654
|
+
continue;
|
|
35655
|
+
}
|
|
35656
|
+
rTreeItems.push({
|
|
35657
|
+
data: {
|
|
35658
|
+
sheetId,
|
|
35659
|
+
zone: positionToZone(this.getters.getCellPosition(cellId)),
|
|
35660
|
+
},
|
|
35661
|
+
boundingBox: { sheetId: range.sheetId, zone: range.zone },
|
|
35662
|
+
});
|
|
35663
|
+
}
|
|
35664
|
+
}
|
|
35665
|
+
}
|
|
35666
|
+
}
|
|
35667
|
+
return new FormulaDependencyGraph(rTreeItems);
|
|
35370
35668
|
});
|
|
35371
35669
|
}
|
|
35372
35670
|
evaluateAllCells() {
|
|
35373
35671
|
const start = performance.now();
|
|
35374
35672
|
this.evaluatedCells = new PositionMap();
|
|
35375
|
-
|
|
35673
|
+
const ranges = [];
|
|
35674
|
+
for (const sheetId of this.getters.getSheetIds()) {
|
|
35675
|
+
const zone = this.getters.getSheetZone(sheetId);
|
|
35676
|
+
ranges.push({ sheetId, zone });
|
|
35677
|
+
}
|
|
35678
|
+
this.evaluate(ranges);
|
|
35376
35679
|
console.debug("evaluate all cells", performance.now() - start, "ms");
|
|
35377
35680
|
}
|
|
35378
35681
|
evaluateFormulaResult(sheetId, formulaString) {
|
|
@@ -35396,48 +35699,47 @@
|
|
|
35396
35699
|
return handleError(error, "");
|
|
35397
35700
|
}
|
|
35398
35701
|
}
|
|
35399
|
-
getAllCells() {
|
|
35400
|
-
const positions = this.createEmptyPositionSet();
|
|
35401
|
-
positions.fillAllPositions();
|
|
35402
|
-
return positions;
|
|
35403
|
-
}
|
|
35404
35702
|
/**
|
|
35405
35703
|
* Return the position of formulas blocked by the given positions
|
|
35406
35704
|
* as well as all their dependencies.
|
|
35407
35705
|
*/
|
|
35408
35706
|
getArrayFormulasBlockedBy(sheetId, zone) {
|
|
35409
|
-
const arrayFormulaPositions =
|
|
35707
|
+
const arrayFormulaPositions = new RangeSet();
|
|
35410
35708
|
const arrayFormulas = this.spreadingRelations.searchFormulaPositionsSpreadingOn(sheetId, zone);
|
|
35411
|
-
arrayFormulaPositions.
|
|
35709
|
+
arrayFormulaPositions.addManyPositions(arrayFormulas);
|
|
35412
35710
|
const spilledPositions = [...arrayFormulas].filter((position) => !this.blockedArrayFormulas.has(position));
|
|
35413
35711
|
if (spilledPositions.length) {
|
|
35414
35712
|
// ignore the formula spreading on the position. Keep only the blocked ones
|
|
35415
|
-
arrayFormulaPositions.
|
|
35713
|
+
arrayFormulaPositions.deleteManyPositions(spilledPositions);
|
|
35416
35714
|
}
|
|
35417
35715
|
arrayFormulaPositions.addMany(this.getCellsDependingOn(arrayFormulaPositions));
|
|
35418
35716
|
return arrayFormulaPositions;
|
|
35419
35717
|
}
|
|
35420
|
-
|
|
35718
|
+
nextRangesToUpdate = new RangeSet();
|
|
35421
35719
|
cellsBeingComputed = new Set();
|
|
35422
35720
|
symbolsBeingComputed = new Set();
|
|
35423
|
-
evaluate(
|
|
35721
|
+
evaluate(ranges) {
|
|
35424
35722
|
this.cellsBeingComputed = new Set();
|
|
35425
|
-
this.
|
|
35723
|
+
this.nextRangesToUpdate = new RangeSet(ranges);
|
|
35426
35724
|
let currentIteration = 0;
|
|
35427
|
-
while (!this.
|
|
35725
|
+
while (!this.nextRangesToUpdate.isEmpty() && currentIteration++ < MAX_ITERATION) {
|
|
35428
35726
|
this.updateCompilationParameters();
|
|
35429
|
-
const
|
|
35430
|
-
|
|
35431
|
-
|
|
35432
|
-
|
|
35433
|
-
|
|
35434
|
-
|
|
35435
|
-
|
|
35436
|
-
|
|
35437
|
-
|
|
35438
|
-
|
|
35439
|
-
|
|
35440
|
-
|
|
35727
|
+
const ranges = [...this.nextRangesToUpdate];
|
|
35728
|
+
this.nextRangesToUpdate.clear();
|
|
35729
|
+
this.clearEvaluatedRanges(ranges);
|
|
35730
|
+
for (const range of ranges) {
|
|
35731
|
+
const { left, bottom, right, top } = range.zone;
|
|
35732
|
+
for (let col = left; col <= right; col++) {
|
|
35733
|
+
for (let row = top; row <= bottom; row++) {
|
|
35734
|
+
const position = { sheetId: range.sheetId, col, row };
|
|
35735
|
+
if (this.nextRangesToUpdate.hasPosition(position)) {
|
|
35736
|
+
continue;
|
|
35737
|
+
}
|
|
35738
|
+
const evaluatedCell = this.computeCell(position);
|
|
35739
|
+
if (evaluatedCell !== EMPTY_CELL) {
|
|
35740
|
+
this.evaluatedCells.set(position, evaluatedCell);
|
|
35741
|
+
}
|
|
35742
|
+
}
|
|
35441
35743
|
}
|
|
35442
35744
|
}
|
|
35443
35745
|
onIterationEndEvaluationRegistry.getAll().forEach((callback) => callback(this.getters));
|
|
@@ -35446,6 +35748,16 @@
|
|
|
35446
35748
|
console.warn("Maximum iteration reached while evaluating cells");
|
|
35447
35749
|
}
|
|
35448
35750
|
}
|
|
35751
|
+
clearEvaluatedRanges(ranges) {
|
|
35752
|
+
for (const range of ranges) {
|
|
35753
|
+
const { left, bottom, right, top } = range.zone;
|
|
35754
|
+
for (let col = left; col <= right; col++) {
|
|
35755
|
+
for (let row = top; row <= bottom; row++) {
|
|
35756
|
+
this.evaluatedCells.delete({ sheetId: range.sheetId, col, row });
|
|
35757
|
+
}
|
|
35758
|
+
}
|
|
35759
|
+
}
|
|
35760
|
+
}
|
|
35449
35761
|
computeCell(position) {
|
|
35450
35762
|
const evaluation = this.evaluatedCells.get(position);
|
|
35451
35763
|
if (evaluation) {
|
|
@@ -35518,9 +35830,9 @@
|
|
|
35518
35830
|
}
|
|
35519
35831
|
invalidatePositionsDependingOnSpread(sheetId, resultZone) {
|
|
35520
35832
|
// the result matrix is split in 2 zones to exclude the array formula position
|
|
35521
|
-
const invalidatedPositions = this.
|
|
35522
|
-
invalidatedPositions.delete({ sheetId,
|
|
35523
|
-
this.
|
|
35833
|
+
const invalidatedPositions = this.getCellsDependingOn(excludeTopLeft(resultZone).map((zone) => ({ sheetId, zone })));
|
|
35834
|
+
invalidatedPositions.delete({ sheetId, zone: resultZone });
|
|
35835
|
+
this.nextRangesToUpdate.addMany(invalidatedPositions);
|
|
35524
35836
|
}
|
|
35525
35837
|
assertSheetHasEnoughSpaceToSpreadFormulaResult({ sheetId, col, row }, matrixResult) {
|
|
35526
35838
|
const numberOfCols = this.getters.getNumberCols(sheetId);
|
|
@@ -35595,7 +35907,7 @@
|
|
|
35595
35907
|
}
|
|
35596
35908
|
const sheetId = position.sheetId;
|
|
35597
35909
|
this.invalidatePositionsDependingOnSpread(sheetId, zone);
|
|
35598
|
-
this.
|
|
35910
|
+
this.nextRangesToUpdate.addMany(this.getArrayFormulasBlockedBy(sheetId, zone));
|
|
35599
35911
|
}
|
|
35600
35912
|
/**
|
|
35601
35913
|
* Wraps a GetSymbolValue function to add cycle detection
|
|
@@ -35630,13 +35942,8 @@
|
|
|
35630
35942
|
}
|
|
35631
35943
|
return cell.compiledFormula.dependencies;
|
|
35632
35944
|
}
|
|
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);
|
|
35945
|
+
getCellsDependingOn(ranges) {
|
|
35946
|
+
return this.formulaDependencies().getCellsDependingOn(ranges, this.nextRangesToUpdate);
|
|
35640
35947
|
}
|
|
35641
35948
|
}
|
|
35642
35949
|
function forEachSpreadPositionInMatrix(nbColumns, nbRows, callback) {
|
|
@@ -40136,18 +40443,8 @@
|
|
|
40136
40443
|
return EMPTY_PIVOT_CELL;
|
|
40137
40444
|
}
|
|
40138
40445
|
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);
|
|
40446
|
+
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());
|
|
40447
|
+
const pivotCells = pivot.getCollapsedTableStructure().getPivotCells(pivotStyle);
|
|
40151
40448
|
const pivotCol = position.col - mainPosition.col;
|
|
40152
40449
|
const pivotRow = position.row - mainPosition.row;
|
|
40153
40450
|
return pivotCells[pivotCol][pivotRow];
|
|
@@ -49877,7 +50174,6 @@
|
|
|
49877
50174
|
const startSnapshot = performance.now();
|
|
49878
50175
|
console.debug("Snapshot requested");
|
|
49879
50176
|
this.session.snapshot(this.exportData());
|
|
49880
|
-
this.garbageCollectExternalResources();
|
|
49881
50177
|
console.debug("Snapshot taken in", performance.now() - startSnapshot, "ms");
|
|
49882
50178
|
}
|
|
49883
50179
|
console.debug("Model created in", performance.now() - start, "ms");
|
|
@@ -50263,11 +50559,6 @@
|
|
|
50263
50559
|
data = deepCopy(data);
|
|
50264
50560
|
return getXLSX(data);
|
|
50265
50561
|
}
|
|
50266
|
-
garbageCollectExternalResources() {
|
|
50267
|
-
for (const plugin of this.corePlugins) {
|
|
50268
|
-
plugin.garbageCollectExternalResources();
|
|
50269
|
-
}
|
|
50270
|
-
}
|
|
50271
50562
|
}
|
|
50272
50563
|
function createCommand(type, payload = {}) {
|
|
50273
50564
|
const command = deepCopy(payload);
|
|
@@ -50411,8 +50702,8 @@
|
|
|
50411
50702
|
|
|
50412
50703
|
|
|
50413
50704
|
__info__.version = "19.1.0-alpha.3";
|
|
50414
|
-
__info__.date = "2025-10-
|
|
50415
|
-
__info__.hash = "
|
|
50705
|
+
__info__.date = "2025-10-23T11:12:13.207Z";
|
|
50706
|
+
__info__.hash = "bd756dd";
|
|
50416
50707
|
|
|
50417
50708
|
|
|
50418
50709
|
})(this.o_spreadsheet_engine = this.o_spreadsheet_engine || {});
|