@odoo/o-spreadsheet 17.0.19 → 17.0.21
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 +422 -45
- package/dist/o-spreadsheet.d.ts +4 -1
- package/dist/o-spreadsheet.esm.js +422 -45
- package/dist/o-spreadsheet.iife.js +422 -45
- package/dist/o-spreadsheet.iife.min.js +102 -102
- package/dist/o_spreadsheet.xml +3 -3
- 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.0.
|
|
6
|
-
* @date 2024-
|
|
7
|
-
* @hash
|
|
5
|
+
* @version 17.0.21
|
|
6
|
+
* @date 2024-05-07T10:39:36.750Z
|
|
7
|
+
* @hash 998bea0
|
|
8
8
|
*/
|
|
9
9
|
|
|
10
10
|
(function (exports, owl) {
|
|
@@ -641,7 +641,7 @@
|
|
|
641
641
|
/**
|
|
642
642
|
* Compares two objects.
|
|
643
643
|
*/
|
|
644
|
-
function deepEquals(o1, o2) {
|
|
644
|
+
function deepEquals(o1, o2, ignoreFunctions) {
|
|
645
645
|
if (o1 === o2)
|
|
646
646
|
return true;
|
|
647
647
|
if ((o1 && !o2) || (o2 && !o1))
|
|
@@ -657,13 +657,16 @@
|
|
|
657
657
|
}
|
|
658
658
|
}
|
|
659
659
|
for (const key in o1) {
|
|
660
|
-
|
|
660
|
+
const typeOfO1Key = typeof o1[key];
|
|
661
|
+
if (typeOfO1Key !== typeof o2[key])
|
|
661
662
|
return false;
|
|
662
|
-
if (
|
|
663
|
-
if (!deepEquals(o1[key], o2[key]))
|
|
663
|
+
if (typeOfO1Key === "object") {
|
|
664
|
+
if (!deepEquals(o1[key], o2[key], ignoreFunctions))
|
|
664
665
|
return false;
|
|
665
666
|
}
|
|
666
667
|
else {
|
|
668
|
+
if (ignoreFunctions && typeOfO1Key === "function")
|
|
669
|
+
return true;
|
|
667
670
|
if (o1[key] !== o2[key])
|
|
668
671
|
return false;
|
|
669
672
|
}
|
|
@@ -3558,21 +3561,22 @@
|
|
|
3558
3561
|
xc = xc.split("!").at(-1);
|
|
3559
3562
|
}
|
|
3560
3563
|
if (xc.includes("$")) {
|
|
3561
|
-
xc = xc.
|
|
3564
|
+
xc = xc.replaceAll("$", "");
|
|
3562
3565
|
}
|
|
3563
|
-
let
|
|
3566
|
+
let firstRangePart = "";
|
|
3567
|
+
let secondRangePart;
|
|
3564
3568
|
if (xc.includes(":")) {
|
|
3565
|
-
|
|
3569
|
+
[firstRangePart, secondRangePart] = xc.split(":");
|
|
3570
|
+
firstRangePart = firstRangePart.trim();
|
|
3571
|
+
secondRangePart = secondRangePart.trim();
|
|
3566
3572
|
}
|
|
3567
3573
|
else {
|
|
3568
|
-
|
|
3574
|
+
firstRangePart = xc.trim();
|
|
3569
3575
|
}
|
|
3570
3576
|
let top, bottom, left, right;
|
|
3571
3577
|
let fullCol = false;
|
|
3572
3578
|
let fullRow = false;
|
|
3573
3579
|
let hasHeader = false;
|
|
3574
|
-
const firstRangePart = ranges[0];
|
|
3575
|
-
const secondRangePart = ranges[1] && ranges[1];
|
|
3576
3580
|
if (isColReference(firstRangePart)) {
|
|
3577
3581
|
left = right = lettersToNumber(firstRangePart);
|
|
3578
3582
|
top = bottom = 0;
|
|
@@ -3589,7 +3593,7 @@
|
|
|
3589
3593
|
top = bottom = c.row;
|
|
3590
3594
|
hasHeader = true;
|
|
3591
3595
|
}
|
|
3592
|
-
if (
|
|
3596
|
+
if (secondRangePart) {
|
|
3593
3597
|
if (isColReference(secondRangePart)) {
|
|
3594
3598
|
right = lettersToNumber(secondRangePart);
|
|
3595
3599
|
fullCol = true;
|
|
@@ -4758,6 +4762,7 @@
|
|
|
4758
4762
|
static template = "o-spreadsheet-ChartJsComponent";
|
|
4759
4763
|
canvas = owl.useRef("graphContainer");
|
|
4760
4764
|
chart;
|
|
4765
|
+
currentRuntime;
|
|
4761
4766
|
get background() {
|
|
4762
4767
|
return this.chartRuntime.background;
|
|
4763
4768
|
}
|
|
@@ -4774,9 +4779,17 @@
|
|
|
4774
4779
|
setup() {
|
|
4775
4780
|
owl.onMounted(() => {
|
|
4776
4781
|
const runtime = this.chartRuntime;
|
|
4777
|
-
this.
|
|
4782
|
+
this.currentRuntime = runtime;
|
|
4783
|
+
// Note: chartJS modify the runtime in place, so it's important to give it a copy
|
|
4784
|
+
this.createChart(deepCopy(runtime.chartJsConfig));
|
|
4785
|
+
});
|
|
4786
|
+
owl.useEffect(() => {
|
|
4787
|
+
const runtime = this.chartRuntime;
|
|
4788
|
+
if (!deepEquals(runtime, this.currentRuntime, "ignoreFunctions")) {
|
|
4789
|
+
this.currentRuntime = runtime;
|
|
4790
|
+
this.updateChartJs(deepCopy(runtime));
|
|
4791
|
+
}
|
|
4778
4792
|
});
|
|
4779
|
-
owl.useEffect(() => this.updateChartJs(this.chartRuntime), () => [this.chartRuntime]);
|
|
4780
4793
|
}
|
|
4781
4794
|
createChart(chartData) {
|
|
4782
4795
|
const canvas = this.canvas.el;
|
|
@@ -4804,8 +4817,7 @@
|
|
|
4804
4817
|
this.chart.config.options.plugins.tooltip = chartData.options.plugins.tooltip;
|
|
4805
4818
|
this.chart.config.options.plugins.legend = chartData.options.plugins.legend;
|
|
4806
4819
|
this.chart.config.options.scales = chartData.options?.scales;
|
|
4807
|
-
|
|
4808
|
-
this.chart.update("active");
|
|
4820
|
+
this.chart.update();
|
|
4809
4821
|
}
|
|
4810
4822
|
}
|
|
4811
4823
|
ChartJsComponent.props = {
|
|
@@ -8374,7 +8386,7 @@
|
|
|
8374
8386
|
/**
|
|
8375
8387
|
* Get a default chart js configuration
|
|
8376
8388
|
*/
|
|
8377
|
-
function getDefaultChartJsRuntime(chart, labels, fontColor, { format, locale }) {
|
|
8389
|
+
function getDefaultChartJsRuntime(chart, labels, fontColor, { format, locale, truncateLabels }) {
|
|
8378
8390
|
const options = {
|
|
8379
8391
|
// https://www.chartjs.org/docs/latest/general/responsive.html
|
|
8380
8392
|
responsive: true,
|
|
@@ -8421,7 +8433,7 @@
|
|
|
8421
8433
|
type: chart.type,
|
|
8422
8434
|
options,
|
|
8423
8435
|
data: {
|
|
8424
|
-
labels: labels.map(truncateLabel),
|
|
8436
|
+
labels: truncateLabels ? labels.map(truncateLabel) : labels,
|
|
8425
8437
|
datasets: [],
|
|
8426
8438
|
},
|
|
8427
8439
|
plugins: [],
|
|
@@ -9303,9 +9315,9 @@
|
|
|
9303
9315
|
}
|
|
9304
9316
|
return true;
|
|
9305
9317
|
}
|
|
9306
|
-
function getLineConfiguration(chart, labels,
|
|
9318
|
+
function getLineConfiguration(chart, labels, options) {
|
|
9307
9319
|
const fontColor = chartFontColor(chart.background);
|
|
9308
|
-
const config = getDefaultChartJsRuntime(chart, labels, fontColor,
|
|
9320
|
+
const config = getDefaultChartJsRuntime(chart, labels, fontColor, options);
|
|
9309
9321
|
const legend = {
|
|
9310
9322
|
labels: {
|
|
9311
9323
|
color: fontColor,
|
|
@@ -9348,7 +9360,7 @@
|
|
|
9348
9360
|
value = Number(value);
|
|
9349
9361
|
if (isNaN(value))
|
|
9350
9362
|
return value;
|
|
9351
|
-
const { locale, format } =
|
|
9363
|
+
const { locale, format } = options;
|
|
9352
9364
|
return formatValue(value, {
|
|
9353
9365
|
locale,
|
|
9354
9366
|
format: !format && Math.abs(value) >= 1000 ? "#,##" : format,
|
|
@@ -9381,9 +9393,10 @@
|
|
|
9381
9393
|
({ labels, dataSetsValues } = aggregateDataForLabels(labels, dataSetsValues));
|
|
9382
9394
|
}
|
|
9383
9395
|
const locale = getters.getLocale();
|
|
9396
|
+
const truncateLabels = axisType === "category";
|
|
9384
9397
|
const dataSetFormat = getChartDatasetFormat(getters, chart.dataSets);
|
|
9385
|
-
const
|
|
9386
|
-
const config = getLineConfiguration(chart, labels,
|
|
9398
|
+
const options = { format: dataSetFormat, locale, truncateLabels };
|
|
9399
|
+
const config = getLineConfiguration(chart, labels, options);
|
|
9387
9400
|
const labelFormat = getChartLabelFormat(getters, chart.labelRange);
|
|
9388
9401
|
if (axisType === "time") {
|
|
9389
9402
|
const axis = {
|
|
@@ -26667,17 +26680,13 @@
|
|
|
26667
26680
|
this.env.focusableElement.setFocusableElement(el);
|
|
26668
26681
|
}
|
|
26669
26682
|
this.contentHelper.updateEl(el);
|
|
26670
|
-
this.processTokenAtCursor();
|
|
26671
26683
|
});
|
|
26672
26684
|
owl.useEffect(() => {
|
|
26673
26685
|
this.processContent();
|
|
26674
26686
|
});
|
|
26675
|
-
owl.
|
|
26676
|
-
|
|
26677
|
-
|
|
26678
|
-
this.processTokenAtCursor();
|
|
26679
|
-
}
|
|
26680
|
-
});
|
|
26687
|
+
owl.useEffect(() => {
|
|
26688
|
+
this.processTokenAtCursor();
|
|
26689
|
+
}, () => [this.env.model.getters.getEditionMode() !== "inactive"]);
|
|
26681
26690
|
}
|
|
26682
26691
|
// ---------------------------------------------------------------------------
|
|
26683
26692
|
// Handlers
|
|
@@ -27108,6 +27117,7 @@
|
|
|
27108
27117
|
const dataValidationAutocompleteValues = this.env.model.getters.getAutoCompleteDataValidationValues();
|
|
27109
27118
|
if (!content.startsWith("=") && dataValidationAutocompleteValues.length) {
|
|
27110
27119
|
this.showDataValidationAutocomplete(dataValidationAutocompleteValues);
|
|
27120
|
+
return;
|
|
27111
27121
|
}
|
|
27112
27122
|
if (content.startsWith("=")) {
|
|
27113
27123
|
const token = this.env.model.getters.getTokenAtCursor();
|
|
@@ -27546,7 +27556,8 @@
|
|
|
27546
27556
|
get checkBoxCellPositions() {
|
|
27547
27557
|
return this.env.model.getters
|
|
27548
27558
|
.getVisibleCellPositions()
|
|
27549
|
-
.filter(this.env.model.getters.isCellValidCheckbox)
|
|
27559
|
+
.filter((position) => this.env.model.getters.isCellValidCheckbox(position) &&
|
|
27560
|
+
!this.env.model.getters.isFilterHeader(position));
|
|
27550
27561
|
}
|
|
27551
27562
|
get listIconsCellPositions() {
|
|
27552
27563
|
if (this.env.model.getters.isReadonly()) {
|
|
@@ -27554,7 +27565,8 @@
|
|
|
27554
27565
|
}
|
|
27555
27566
|
return this.env.model.getters
|
|
27556
27567
|
.getVisibleCellPositions()
|
|
27557
|
-
.filter(this.env.model.getters.cellHasListDataValidationIcon)
|
|
27568
|
+
.filter((position) => this.env.model.getters.cellHasListDataValidationIcon(position) &&
|
|
27569
|
+
!this.env.model.getters.isFilterHeader(position));
|
|
27558
27570
|
}
|
|
27559
27571
|
}
|
|
27560
27572
|
DataValidationOverlay.props = {};
|
|
@@ -34367,7 +34379,7 @@
|
|
|
34367
34379
|
this.clearBorders(cmd.sheetId, cmd.target);
|
|
34368
34380
|
break;
|
|
34369
34381
|
case "REMOVE_COLUMNS_ROWS":
|
|
34370
|
-
for (let el of cmd.elements) {
|
|
34382
|
+
for (let el of [...cmd.elements].sort((a, b) => b - a)) {
|
|
34371
34383
|
if (cmd.dimension === "COL") {
|
|
34372
34384
|
this.shiftBordersHorizontally(cmd.sheetId, el + 1, -1);
|
|
34373
34385
|
}
|
|
@@ -40147,6 +40159,353 @@
|
|
|
40147
40159
|
}
|
|
40148
40160
|
}
|
|
40149
40161
|
|
|
40162
|
+
/**
|
|
40163
|
+
* ####################################################
|
|
40164
|
+
* # INTRODUCTION
|
|
40165
|
+
* ####################################################
|
|
40166
|
+
*
|
|
40167
|
+
* This file contain the function recomputeZones.
|
|
40168
|
+
* This function try to recompute in a performant way
|
|
40169
|
+
* an ensemble of zones possibly overlapping to avoid
|
|
40170
|
+
* overlapping and to reduce the number of zones.
|
|
40171
|
+
*
|
|
40172
|
+
* It also allows to remove some zones from the ensemble.
|
|
40173
|
+
*
|
|
40174
|
+
* In the following example, 2 zones are overlapping.
|
|
40175
|
+
* Applying recomputeZones will return zones without
|
|
40176
|
+
* overlapping:
|
|
40177
|
+
*
|
|
40178
|
+
* ["B3:D4", "D2:E3"] ["B3:C4", "D2:D4", "E2:E3"]
|
|
40179
|
+
*
|
|
40180
|
+
* A B C D E A B C D E
|
|
40181
|
+
* 1 ___ 1 ___
|
|
40182
|
+
* 2 ___|_ | 2 ___| | |
|
|
40183
|
+
* 3 | |_|_| ---> 3 | | |_|
|
|
40184
|
+
* 4 |_____| 4 |___|_|
|
|
40185
|
+
* 6 6
|
|
40186
|
+
* 7 7
|
|
40187
|
+
*
|
|
40188
|
+
*
|
|
40189
|
+
* In the following example, 2 zones are contiguous.
|
|
40190
|
+
* Applying recomputeZones will return only one zone:
|
|
40191
|
+
*
|
|
40192
|
+
* ["B2:B3", "C2:D3"] ["B2:D3"]
|
|
40193
|
+
*
|
|
40194
|
+
* A B C D E A B C D E
|
|
40195
|
+
* 1 _ ___ 1 _____
|
|
40196
|
+
* 2 | | | ---> 2 | |
|
|
40197
|
+
* 3 |_|___| 3 |_____|
|
|
40198
|
+
* 4 4
|
|
40199
|
+
*
|
|
40200
|
+
*
|
|
40201
|
+
* In the following example, we want to remove a zone
|
|
40202
|
+
* from the ensemble. Applying recomputeZones will
|
|
40203
|
+
* return the ensemble without the zone to remove:
|
|
40204
|
+
*
|
|
40205
|
+
* remove ["C3:D3"] ["B2:B4", "C2:D2",
|
|
40206
|
+
* "C4:D4", "E2:E4"]
|
|
40207
|
+
*
|
|
40208
|
+
* A B C D E F A B C D E F
|
|
40209
|
+
* 1 _______ 1 _______
|
|
40210
|
+
* 2 | | ---> 2 | |___| |
|
|
40211
|
+
* 3 | xxx | 3 | |___| |
|
|
40212
|
+
* 4 |_______| 4 |_|___|_|
|
|
40213
|
+
* 5 5
|
|
40214
|
+
*
|
|
40215
|
+
*
|
|
40216
|
+
* The exercise seems simple when we have only 2 zones.
|
|
40217
|
+
* But with n zones and in a performant way, we want to
|
|
40218
|
+
* avoid comparing each zone with all the others.
|
|
40219
|
+
*
|
|
40220
|
+
*
|
|
40221
|
+
* ####################################################
|
|
40222
|
+
* # Methodological approach
|
|
40223
|
+
* ####################################################
|
|
40224
|
+
*
|
|
40225
|
+
* The methodological approach to avoid comparing each
|
|
40226
|
+
* zone with all the others is to use a data structure
|
|
40227
|
+
* that allow to quickly find which zones are
|
|
40228
|
+
* overlapping with any other given zone.
|
|
40229
|
+
*
|
|
40230
|
+
* Here the idea is to profile the zones at the columns level.
|
|
40231
|
+
*
|
|
40232
|
+
* To do that, we propose to use a data structure
|
|
40233
|
+
* composed of 2 parts:
|
|
40234
|
+
* - profilesStartingPosition: a sorted number array
|
|
40235
|
+
* indicating on which columns a new profile begins.
|
|
40236
|
+
* - profiles: a map where the key is a column
|
|
40237
|
+
* position (from profilesStartingPosition) and the
|
|
40238
|
+
* value is a sorted number array representing a
|
|
40239
|
+
* profile.
|
|
40240
|
+
*
|
|
40241
|
+
*
|
|
40242
|
+
* See the following example: here profileStartingPosition
|
|
40243
|
+
* corresponds to [A,C,E,G,K]
|
|
40244
|
+
* A B C D E F G H I J K so with number [0,2,4,6,10]
|
|
40245
|
+
* 1 ' ' ' '
|
|
40246
|
+
* 2 ' ' '_______' here profile correspond
|
|
40247
|
+
* 3 '___' |_______| for A to []
|
|
40248
|
+
* 4 | | for C to [3, 5]
|
|
40249
|
+
* 5 |___| for E to []
|
|
40250
|
+
* 6 for G to [2, 3]
|
|
40251
|
+
* 7 for K to []
|
|
40252
|
+
*
|
|
40253
|
+
*
|
|
40254
|
+
* Now we can easily find which zones are overlapping
|
|
40255
|
+
* with a given zone. Suppose we want to add a new zone
|
|
40256
|
+
* D5:H6 to the ensemble:
|
|
40257
|
+
*
|
|
40258
|
+
* With a binary search of left and right
|
|
40259
|
+
* A B C D E F G H I J K on profilesStartingPosition, we can
|
|
40260
|
+
* 1 ' ' ' ' find the indexes of the profiles on which
|
|
40261
|
+
* 2 ' ' '_______' to apply a modification.
|
|
40262
|
+
* 3 '___' |_______|
|
|
40263
|
+
* 4 | _|_______ Here we will:
|
|
40264
|
+
* 5 |_|_| | - add a new profile in D --> become [3, 6]
|
|
40265
|
+
* 6 |_________| - modify the profile in E --> become [4, 6]
|
|
40266
|
+
* 7 - modify the profile in G --> become [2, 3, 4, 6]
|
|
40267
|
+
* - add a new profile in I --> become [8, 10]
|
|
40268
|
+
*
|
|
40269
|
+
* See below the result:
|
|
40270
|
+
*
|
|
40271
|
+
* Note the particularity of the profile
|
|
40272
|
+
* A B C D E F G H I J K for G: it will correspond to [2, 3, 4, 6]
|
|
40273
|
+
* 1 ' ' ' ' ' '
|
|
40274
|
+
* 2 ' ' ' '___'___' To know how to modify the profile (add a
|
|
40275
|
+
* 3 '_'_' |___|___| zone or remove it) we do a binary
|
|
40276
|
+
* 4 | | |___ ___ search of the top and bottom value on the
|
|
40277
|
+
* 5 |_| | | | profile array. Depending on the result index
|
|
40278
|
+
* 6 |_|___|___| parity (odd or even), because zone boundaries
|
|
40279
|
+
* 7 go by pairs, we know if we are in a zone or
|
|
40280
|
+
* not and how operate.
|
|
40281
|
+
*/
|
|
40282
|
+
/**
|
|
40283
|
+
* Recompute the zone without the cells in toRemoveZones and avoid overlapping.
|
|
40284
|
+
* This compute is particularly useful because after this function:
|
|
40285
|
+
* - you will find coordinate of a cell only once among all the zones
|
|
40286
|
+
* - the number of zones will be reduced to the minimum
|
|
40287
|
+
*/
|
|
40288
|
+
function futureRecomputeZones(zones, zonesToRemove = []) {
|
|
40289
|
+
const profilesStartingPosition = [0];
|
|
40290
|
+
const profiles = new Map([[0, []]]);
|
|
40291
|
+
modifyProfiles(profilesStartingPosition, profiles, zones, false);
|
|
40292
|
+
modifyProfiles(profilesStartingPosition, profiles, zonesToRemove, true);
|
|
40293
|
+
return constructZonesFromProfiles(profilesStartingPosition, profiles);
|
|
40294
|
+
}
|
|
40295
|
+
function modifyProfiles(// export for testing only
|
|
40296
|
+
profilesStartingPosition, profiles, zones, toRemove = false) {
|
|
40297
|
+
for (const zone of zones) {
|
|
40298
|
+
const leftValue = zone.left;
|
|
40299
|
+
const rightValue = zone.right === undefined ? undefined : zone.right + 1;
|
|
40300
|
+
const leftIndex = findIndexAndCreateProfile(profilesStartingPosition, profiles, leftValue, true, 0);
|
|
40301
|
+
const rightIndex = findIndexAndCreateProfile(profilesStartingPosition, profiles, rightValue, false, leftIndex);
|
|
40302
|
+
for (let i = leftIndex; i <= rightIndex; i++) {
|
|
40303
|
+
const profile = profiles.get(profilesStartingPosition[i]);
|
|
40304
|
+
modifyProfile(profile, zone, toRemove);
|
|
40305
|
+
}
|
|
40306
|
+
// maybe this part cost in performance, and maybe it's not necessary (depending on the use case). To be checked
|
|
40307
|
+
removeContiguousProfiles(profilesStartingPosition, profiles, leftIndex, rightIndex);
|
|
40308
|
+
}
|
|
40309
|
+
}
|
|
40310
|
+
function findIndexAndCreateProfile(profilesStartingPosition, profiles, value, searchLeft, startIndex) {
|
|
40311
|
+
if (value === undefined) {
|
|
40312
|
+
// this is only the case when the value correspond to a bottom value that could be undefined
|
|
40313
|
+
return profilesStartingPosition.length - 1;
|
|
40314
|
+
}
|
|
40315
|
+
const predecessorIndex = binaryPredecessorSearch(profilesStartingPosition, value, startIndex);
|
|
40316
|
+
if (value != profilesStartingPosition[predecessorIndex]) {
|
|
40317
|
+
// mean that the value is not ending/starting at the same position as the previous/next profile
|
|
40318
|
+
// --> it's a new profile
|
|
40319
|
+
// --> we need to add it
|
|
40320
|
+
profilesStartingPosition.splice(predecessorIndex + 1, 0, value);
|
|
40321
|
+
// suppose the we want to add the for the left value
|
|
40322
|
+
// following profile following zone: 'C', the predecessor index
|
|
40323
|
+
// for B: [1, 3] "C3:D4" correspond to 'B'.
|
|
40324
|
+
// The next line code will
|
|
40325
|
+
// A B C D A B C D copy the profile of 'B'
|
|
40326
|
+
// 1 '___' 1 '___' to 'C'. In the rest of the
|
|
40327
|
+
// 2 | | ---> 2 | _|_ process the 'modifyProfile'
|
|
40328
|
+
// 3 |___| 3 |_|_| | function will adapt the waiting
|
|
40329
|
+
// 4 4 |___| 'C' profile [1, 3] to the
|
|
40330
|
+
// correct 'C' profile [1, 4]
|
|
40331
|
+
profiles.set(value, [...profiles.get(profilesStartingPosition[predecessorIndex])]);
|
|
40332
|
+
return searchLeft ? predecessorIndex + 1 : predecessorIndex;
|
|
40333
|
+
}
|
|
40334
|
+
return searchLeft ? predecessorIndex : predecessorIndex - 1;
|
|
40335
|
+
}
|
|
40336
|
+
/**
|
|
40337
|
+
* Suppose the following Suppose we want to add We want to have the
|
|
40338
|
+
* profile: the following zone: following profile:
|
|
40339
|
+
*
|
|
40340
|
+
* A B C D E F A B C D E F A B C D E F
|
|
40341
|
+
* 1 '___' 1 ' ' 1 '___'
|
|
40342
|
+
* 2 |___| 2 '___' 2 | |
|
|
40343
|
+
* 3 ' ' 3 | | 3 | |
|
|
40344
|
+
* 4 '___' --> 4 | | --> 4 | |
|
|
40345
|
+
* 6 | | 6 |___| 6 | |
|
|
40346
|
+
* 7 |___| 7 7 |___|
|
|
40347
|
+
* 8 8 8
|
|
40348
|
+
*
|
|
40349
|
+
* the profile for 'C' the top zone correspond Here [2, 3, 5, 8] with [3, 7]
|
|
40350
|
+
* corresponds to: to 3 and the bottom zone would be merged into [2, 8]
|
|
40351
|
+
* ____ ____ correspond to 6
|
|
40352
|
+
* [2, 3, 5, 8] would be the profile: The difficulty of modify profile
|
|
40353
|
+
* ____ is to know what must be deleted
|
|
40354
|
+
* Note that the 'filled [3, 7] and what must be added to the
|
|
40355
|
+
* zone' are always between existing profile.
|
|
40356
|
+
* an even index and its
|
|
40357
|
+
* next index
|
|
40358
|
+
*
|
|
40359
|
+
*/
|
|
40360
|
+
function modifyProfile(profile, zone, toRemove = false) {
|
|
40361
|
+
const topValue = zone.top;
|
|
40362
|
+
const bottomValue = zone.bottom === undefined ? undefined : zone.bottom + 1;
|
|
40363
|
+
const newPoints = [];
|
|
40364
|
+
// Case we want to add a zone to the profile:
|
|
40365
|
+
// - If the top predecessor index `topPredIndex` is even, it means the top of the zone is already positioned on a filled zone
|
|
40366
|
+
// so we don't need to add it to the profile. we can keep in reference the index of the predecessor.
|
|
40367
|
+
// - If it is odd, it means the top of the zone must be the beginning of a filled zone.
|
|
40368
|
+
// so we can keep the index of the top position
|
|
40369
|
+
// Case we want to remove a zone from the profile: it's the opposite of the previous case
|
|
40370
|
+
const topPredIndex = binaryPredecessorSearch(profile, topValue, 0, false);
|
|
40371
|
+
if ((topPredIndex % 2 !== 0 && !toRemove) || (topPredIndex % 2 === 0 && toRemove)) {
|
|
40372
|
+
newPoints.push(topValue);
|
|
40373
|
+
}
|
|
40374
|
+
if (bottomValue === undefined) {
|
|
40375
|
+
// The following two code lines will not impact the final result,
|
|
40376
|
+
// but they will impact the intermediate profile.
|
|
40377
|
+
// We keep them for performance reason
|
|
40378
|
+
profile.splice(topPredIndex + 1);
|
|
40379
|
+
profile.push(...newPoints);
|
|
40380
|
+
return;
|
|
40381
|
+
}
|
|
40382
|
+
// Case we want to add a zone to the profile:
|
|
40383
|
+
// - If the bottom successor index `bottomSuccIndex` is even, it means the bottom of the zone must be the ending of a filled zone
|
|
40384
|
+
// so we can keep the index of the bottom position.
|
|
40385
|
+
// - If it is odd, it means the bottom of the zone is already positioned on a filled zone
|
|
40386
|
+
// so we don't need to add it to the profile. we can keep in reference the index of the successor
|
|
40387
|
+
// Case we want to remove a zone from the profile: it's the opposite of the previous case
|
|
40388
|
+
const bottomSuccIndex = binarySuccessorSearch(profile, bottomValue, 0, false);
|
|
40389
|
+
if ((bottomSuccIndex % 2 === 0 && !toRemove) || (bottomSuccIndex % 2 !== 0 && toRemove)) {
|
|
40390
|
+
newPoints.push(bottomValue);
|
|
40391
|
+
}
|
|
40392
|
+
// add the top and bottom value to the profile and
|
|
40393
|
+
// remove all information between the top and bottom index
|
|
40394
|
+
profile.splice(topPredIndex + 1, bottomSuccIndex - topPredIndex - 1, ...newPoints);
|
|
40395
|
+
}
|
|
40396
|
+
function removeContiguousProfiles(profilesStartingPosition, profiles, leftIndex, rightIndex) {
|
|
40397
|
+
const start = leftIndex - 1 === -1 ? 0 : leftIndex - 1;
|
|
40398
|
+
const end = rightIndex === profilesStartingPosition.length - 1 ? rightIndex : rightIndex + 1;
|
|
40399
|
+
for (let i = end; i > start; i--) {
|
|
40400
|
+
if (deepEqualsArray(profiles.get(profilesStartingPosition[i]), profiles.get(profilesStartingPosition[i - 1]))) {
|
|
40401
|
+
profiles.delete(profilesStartingPosition[i]);
|
|
40402
|
+
profilesStartingPosition.splice(i, 1);
|
|
40403
|
+
}
|
|
40404
|
+
}
|
|
40405
|
+
}
|
|
40406
|
+
function constructZonesFromProfiles(profilesStartingPosition, profiles) {
|
|
40407
|
+
const mergedZone = [];
|
|
40408
|
+
let pendingZones = [];
|
|
40409
|
+
for (let colIndex = 0; colIndex < profilesStartingPosition.length; colIndex++) {
|
|
40410
|
+
const left = profilesStartingPosition[colIndex];
|
|
40411
|
+
const profile = profiles.get(left);
|
|
40412
|
+
if (!profile || profile.length === 0) {
|
|
40413
|
+
mergedZone.push(...pendingZones);
|
|
40414
|
+
pendingZones = [];
|
|
40415
|
+
continue;
|
|
40416
|
+
}
|
|
40417
|
+
let right = profilesStartingPosition[colIndex + 1];
|
|
40418
|
+
if (right !== undefined) {
|
|
40419
|
+
right--;
|
|
40420
|
+
}
|
|
40421
|
+
const nextPendingZones = [];
|
|
40422
|
+
for (let i = 0; i < profile.length; i += 2) {
|
|
40423
|
+
const top = profile[i];
|
|
40424
|
+
let bottom = profile[i + 1];
|
|
40425
|
+
if (bottom !== undefined) {
|
|
40426
|
+
bottom--;
|
|
40427
|
+
}
|
|
40428
|
+
const profileZone = {
|
|
40429
|
+
top,
|
|
40430
|
+
left,
|
|
40431
|
+
bottom,
|
|
40432
|
+
right,
|
|
40433
|
+
hasHeader: (bottom === undefined && top !== 0) || (right === undefined && left !== 0),
|
|
40434
|
+
};
|
|
40435
|
+
let findCorrespondingZone = false;
|
|
40436
|
+
for (let j = pendingZones.length - 1; j >= 0; j--) {
|
|
40437
|
+
const pendingZone = pendingZones[j];
|
|
40438
|
+
if (pendingZone.top === profileZone.top && pendingZone.bottom === profileZone.bottom) {
|
|
40439
|
+
pendingZone.right = profileZone.right;
|
|
40440
|
+
pendingZones.splice(j, 1);
|
|
40441
|
+
nextPendingZones.push(pendingZone);
|
|
40442
|
+
findCorrespondingZone = true;
|
|
40443
|
+
break;
|
|
40444
|
+
}
|
|
40445
|
+
}
|
|
40446
|
+
if (!findCorrespondingZone) {
|
|
40447
|
+
nextPendingZones.push(profileZone);
|
|
40448
|
+
}
|
|
40449
|
+
}
|
|
40450
|
+
mergedZone.push(...pendingZones);
|
|
40451
|
+
pendingZones = nextPendingZones;
|
|
40452
|
+
}
|
|
40453
|
+
mergedZone.push(...pendingZones);
|
|
40454
|
+
return mergedZone;
|
|
40455
|
+
}
|
|
40456
|
+
function binaryPredecessorSearch(arr, val, start = 0, matchEqual = true) {
|
|
40457
|
+
let end = arr.length - 1;
|
|
40458
|
+
let result = -1;
|
|
40459
|
+
while (start <= end) {
|
|
40460
|
+
const mid = Math.floor((start + end) / 2);
|
|
40461
|
+
if (arr[mid] === val && matchEqual) {
|
|
40462
|
+
return mid;
|
|
40463
|
+
}
|
|
40464
|
+
else if (arr[mid] < val) {
|
|
40465
|
+
result = mid;
|
|
40466
|
+
start = mid + 1;
|
|
40467
|
+
}
|
|
40468
|
+
else {
|
|
40469
|
+
end = mid - 1;
|
|
40470
|
+
}
|
|
40471
|
+
}
|
|
40472
|
+
return result;
|
|
40473
|
+
}
|
|
40474
|
+
function binarySuccessorSearch(arr, val, start = 0, matchEqual = true) {
|
|
40475
|
+
let end = arr.length - 1;
|
|
40476
|
+
let result = arr.length;
|
|
40477
|
+
while (start <= end) {
|
|
40478
|
+
const mid = Math.floor((start + end) / 2);
|
|
40479
|
+
if (arr[mid] === val && matchEqual) {
|
|
40480
|
+
return mid;
|
|
40481
|
+
}
|
|
40482
|
+
else if (arr[mid] > val) {
|
|
40483
|
+
result = mid;
|
|
40484
|
+
end = mid - 1;
|
|
40485
|
+
}
|
|
40486
|
+
else {
|
|
40487
|
+
start = mid + 1;
|
|
40488
|
+
}
|
|
40489
|
+
}
|
|
40490
|
+
return result;
|
|
40491
|
+
}
|
|
40492
|
+
/**
|
|
40493
|
+
* Compares two arrays.
|
|
40494
|
+
* For performance reasons, this function is to be preferred
|
|
40495
|
+
* to 'deepEquals' in the case we know that the inputs are arrays.
|
|
40496
|
+
*/
|
|
40497
|
+
function deepEqualsArray(arr1, arr2) {
|
|
40498
|
+
if (arr1.length !== arr2.length) {
|
|
40499
|
+
return false;
|
|
40500
|
+
}
|
|
40501
|
+
for (let i = 0; i < arr1.length; i++) {
|
|
40502
|
+
if (!deepEquals(arr1[i], arr2[i])) {
|
|
40503
|
+
return false;
|
|
40504
|
+
}
|
|
40505
|
+
}
|
|
40506
|
+
return true;
|
|
40507
|
+
}
|
|
40508
|
+
|
|
40150
40509
|
function quickselect(arr, k, left, right, compare) {
|
|
40151
40510
|
quickselectStep(arr, k, left || 0, right || (arr.length - 1), compare || defaultCompare);
|
|
40152
40511
|
}
|
|
@@ -40910,7 +41269,7 @@
|
|
|
40910
41269
|
}
|
|
40911
41270
|
}
|
|
40912
41271
|
/**
|
|
40913
|
-
* Return the
|
|
41272
|
+
* Return all the cells that depend on the provided ranges,
|
|
40914
41273
|
* in the correct order they should be evaluated.
|
|
40915
41274
|
* This is called a topological ordering (excluding cycles)
|
|
40916
41275
|
*/
|
|
@@ -40921,11 +41280,20 @@
|
|
|
40921
41280
|
const range = queue.pop();
|
|
40922
41281
|
visited.addMany(this.encoder.encodeBoundingBox(range));
|
|
40923
41282
|
const impactedPositionIds = this.rTree.search(range).map((dep) => dep.data);
|
|
41283
|
+
const nextInQueue = {};
|
|
40924
41284
|
for (const positionId of impactedPositionIds) {
|
|
40925
41285
|
if (!visited.has(positionId)) {
|
|
40926
|
-
|
|
41286
|
+
const { sheetId, zone } = this.encoder.decodeToBoundingBox(positionId);
|
|
41287
|
+
if (!nextInQueue[sheetId]) {
|
|
41288
|
+
nextInQueue[sheetId] = [];
|
|
41289
|
+
}
|
|
41290
|
+
nextInQueue[sheetId].push(zone);
|
|
40927
41291
|
}
|
|
40928
41292
|
}
|
|
41293
|
+
for (const sheetId in nextInQueue) {
|
|
41294
|
+
const zones = futureRecomputeZones(nextInQueue[sheetId]);
|
|
41295
|
+
queue.push(...zones.map((zone) => ({ sheetId, zone })));
|
|
41296
|
+
}
|
|
40929
41297
|
}
|
|
40930
41298
|
visited.deleteMany(ranges.flatMap((r) => this.encoder.encodeBoundingBox(r)));
|
|
40931
41299
|
return visited;
|
|
@@ -41081,7 +41449,7 @@
|
|
|
41081
41449
|
}
|
|
41082
41450
|
if (!content) {
|
|
41083
41451
|
// The previous content could have blocked some array formulas
|
|
41084
|
-
impactedPositionIds.addMany(this.
|
|
41452
|
+
impactedPositionIds.addMany(this.getArrayFormulasBlockedBy(positionId));
|
|
41085
41453
|
}
|
|
41086
41454
|
}
|
|
41087
41455
|
return impactedPositionIds;
|
|
@@ -41126,13 +41494,22 @@
|
|
|
41126
41494
|
}
|
|
41127
41495
|
return positionIds;
|
|
41128
41496
|
}
|
|
41129
|
-
|
|
41497
|
+
/**
|
|
41498
|
+
* Return the position of formulas blocked by the given position
|
|
41499
|
+
* as well as all their dependencies.
|
|
41500
|
+
*/
|
|
41501
|
+
getArrayFormulasBlockedBy(positionId) {
|
|
41130
41502
|
if (!this.spreadingRelations.hasArrayFormulaResult(positionId)) {
|
|
41131
41503
|
return [];
|
|
41132
41504
|
}
|
|
41133
41505
|
const arrayFormulas = this.spreadingRelations.getFormulaPositionsSpreadingOn(positionId);
|
|
41134
41506
|
const cells = new JetSet(arrayFormulas);
|
|
41135
|
-
|
|
41507
|
+
const arrayFormulaPositionId = this.getArrayFormulaSpreadingOnId(positionId);
|
|
41508
|
+
if (arrayFormulaPositionId) {
|
|
41509
|
+
// ignore the formula spreading on the position. Keep only the blocked ones
|
|
41510
|
+
cells.delete(arrayFormulaPositionId);
|
|
41511
|
+
}
|
|
41512
|
+
cells.addMany(this.getCellsDependingOn(cells));
|
|
41136
41513
|
return cells;
|
|
41137
41514
|
}
|
|
41138
41515
|
nextPositionsToUpdate = new JetSet();
|
|
@@ -41297,7 +41674,7 @@
|
|
|
41297
41674
|
}
|
|
41298
41675
|
this.evaluatedCells.delete(child);
|
|
41299
41676
|
this.nextPositionsToUpdate.addMany(this.getCellsDependingOn([child]));
|
|
41300
|
-
this.nextPositionsToUpdate.addMany(this.
|
|
41677
|
+
this.nextPositionsToUpdate.addMany(this.getArrayFormulasBlockedBy(child));
|
|
41301
41678
|
}
|
|
41302
41679
|
this.spreadingRelations.removeNode(positionId);
|
|
41303
41680
|
}
|
|
@@ -41656,7 +42033,7 @@
|
|
|
41656
42033
|
? getItemId(newFormat, data.formats)
|
|
41657
42034
|
: exportedCellData.format;
|
|
41658
42035
|
let content;
|
|
41659
|
-
if (isFormula && formulaCell
|
|
42036
|
+
if (isExported && isFormula && formulaCell?.compiledFormula.dependencies.length) {
|
|
41660
42037
|
content = this.getters.getFormulaCellContent(exportedSheetData.id, formulaCell.compiledFormula, formulaCell.compiledFormula.dependencies, true);
|
|
41661
42038
|
}
|
|
41662
42039
|
else {
|
|
@@ -57029,9 +57406,9 @@
|
|
|
57029
57406
|
exports.tokenize = tokenize;
|
|
57030
57407
|
|
|
57031
57408
|
|
|
57032
|
-
__info__.version = "17.0.
|
|
57033
|
-
__info__.date = "2024-
|
|
57034
|
-
__info__.hash = "
|
|
57409
|
+
__info__.version = "17.0.21";
|
|
57410
|
+
__info__.date = "2024-05-07T10:39:36.750Z";
|
|
57411
|
+
__info__.hash = "998bea0";
|
|
57035
57412
|
|
|
57036
57413
|
|
|
57037
57414
|
})(this.o_spreadsheet = this.o_spreadsheet || {}, owl);
|