@odoo/o-spreadsheet 17.0.4 → 17.0.6
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 +970 -220
- package/dist/o-spreadsheet.d.ts +24 -21
- package/dist/o-spreadsheet.esm.js +970 -220
- package/dist/o-spreadsheet.iife.js +970 -220
- package/dist/o-spreadsheet.iife.min.js +274 -276
- package/dist/o_spreadsheet.xml +4 -4
- package/package.json +6 -2
|
@@ -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
|
|
7
|
-
* @hash
|
|
5
|
+
* @version 17.0.6
|
|
6
|
+
* @date 2024-01-04T12:23:10.357Z
|
|
7
|
+
* @hash 310ca57
|
|
8
8
|
*/
|
|
9
9
|
|
|
10
10
|
(function (exports, owl) {
|
|
@@ -330,13 +330,6 @@
|
|
|
330
330
|
//------------------------------------------------------------------------------
|
|
331
331
|
// Miscellaneous
|
|
332
332
|
//------------------------------------------------------------------------------
|
|
333
|
-
/**
|
|
334
|
-
* Stringify an object, like JSON.stringify, except that the first level of keys
|
|
335
|
-
* is ordered.
|
|
336
|
-
*/
|
|
337
|
-
function stringify(obj) {
|
|
338
|
-
return JSON.stringify(obj, Object.keys(obj).sort());
|
|
339
|
-
}
|
|
340
333
|
/**
|
|
341
334
|
* Remove quotes from a quoted string
|
|
342
335
|
* ```js
|
|
@@ -559,7 +552,7 @@
|
|
|
559
552
|
*/
|
|
560
553
|
function getItemId(item, itemsDic) {
|
|
561
554
|
for (let [key, value] of Object.entries(itemsDic)) {
|
|
562
|
-
if (
|
|
555
|
+
if (deepEquals(value, item)) {
|
|
563
556
|
return parseInt(key, 10);
|
|
564
557
|
}
|
|
565
558
|
}
|
|
@@ -3388,8 +3381,10 @@
|
|
|
3388
3381
|
const cellReference = new RegExp(/\$?([A-Z]{1,3})\$?([0-9]{1,7})/, "i");
|
|
3389
3382
|
// Same as above, but matches the exact string (nothing before or after)
|
|
3390
3383
|
const singleCellReference = new RegExp(/^\$?([A-Z]{1,3})\$?([0-9]{1,7})$/, "i");
|
|
3391
|
-
/** Reference of a column header (eg. A, AB) */
|
|
3392
|
-
const colHeader = new RegExp(
|
|
3384
|
+
/** Reference of a column header (eg. A, AB, $A) */
|
|
3385
|
+
const colHeader = new RegExp(/^\$?([A-Z]{1,3})+$/, "i");
|
|
3386
|
+
/** Reference of a row header (eg. 1, $1) */
|
|
3387
|
+
const rowHeader = new RegExp(/^\$?([0-9]{1,7})+$/, "i");
|
|
3393
3388
|
/** Reference of a column (eg. A, $CA, Sheet1!B) */
|
|
3394
3389
|
const colReference = new RegExp(/^\s*('.+'!|[^']+!)?\$?([A-Z]{1,3})$/, "i");
|
|
3395
3390
|
/** Reference of a row (eg. 1, 59, Sheet1!9) */
|
|
@@ -3419,6 +3414,9 @@
|
|
|
3419
3414
|
function isColHeader(str) {
|
|
3420
3415
|
return colHeader.test(str);
|
|
3421
3416
|
}
|
|
3417
|
+
function isRowHeader(str) {
|
|
3418
|
+
return rowHeader.test(str);
|
|
3419
|
+
}
|
|
3422
3420
|
/**
|
|
3423
3421
|
* Return true if the given xc is the reference of a single cell,
|
|
3424
3422
|
* without any specified sheet (e.g. A1)
|
|
@@ -4102,18 +4100,10 @@
|
|
|
4102
4100
|
if (isFullCol) {
|
|
4103
4101
|
parts[0].rowFixed = parts[0].rowFixed || parts[1].rowFixed;
|
|
4104
4102
|
parts[1].rowFixed = parts[0].rowFixed || parts[1].rowFixed;
|
|
4105
|
-
if (zone.left === zone.right) {
|
|
4106
|
-
parts[0].colFixed = parts[0].colFixed || parts[1].colFixed;
|
|
4107
|
-
parts[1].colFixed = parts[0].colFixed || parts[1].colFixed;
|
|
4108
|
-
}
|
|
4109
4103
|
}
|
|
4110
4104
|
if (isFullRow) {
|
|
4111
4105
|
parts[0].colFixed = parts[0].colFixed || parts[1].colFixed;
|
|
4112
4106
|
parts[1].colFixed = parts[0].colFixed || parts[1].colFixed;
|
|
4113
|
-
if (zone.top === zone.bottom) {
|
|
4114
|
-
parts[0].rowFixed = parts[0].rowFixed || parts[1].rowFixed;
|
|
4115
|
-
parts[1].rowFixed = parts[0].rowFixed || parts[1].rowFixed;
|
|
4116
|
-
}
|
|
4117
4107
|
}
|
|
4118
4108
|
return parts;
|
|
4119
4109
|
}
|
|
@@ -4355,7 +4345,7 @@
|
|
|
4355
4345
|
function getDefaultCellHeight(ctx, cell, colSize) {
|
|
4356
4346
|
if (!cell || !cell.content)
|
|
4357
4347
|
return DEFAULT_CELL_HEIGHT;
|
|
4358
|
-
const maxWidth = cell.style?.wrapping ? colSize - 2 * MIN_CELL_TEXT_MARGIN : undefined;
|
|
4348
|
+
const maxWidth = cell.style?.wrapping === "wrap" ? colSize - 2 * MIN_CELL_TEXT_MARGIN : undefined;
|
|
4359
4349
|
const numberOfLines = cell.isFormula
|
|
4360
4350
|
? 1
|
|
4361
4351
|
: splitTextToWidth(ctx, cell.content, cell.style, maxWidth).length;
|
|
@@ -5290,7 +5280,7 @@
|
|
|
5290
5280
|
};
|
|
5291
5281
|
baselineCell = getters.getEvaluatedCell(baselinePosition);
|
|
5292
5282
|
}
|
|
5293
|
-
const background = getters.
|
|
5283
|
+
const { background, fontColor } = getters.getStyleOfSingleCellChart(chart.background, chart.keyValue);
|
|
5294
5284
|
const locale = getters.getLocale();
|
|
5295
5285
|
return {
|
|
5296
5286
|
title: _t(chart.title),
|
|
@@ -5299,7 +5289,7 @@
|
|
|
5299
5289
|
baselineArrow: getBaselineArrowDirection(baselineCell, keyValueCell, chart.baselineMode),
|
|
5300
5290
|
baselineColor: getBaselineColor(baselineCell, chart.baselineMode, keyValueCell, chart.baselineColorUp, chart.baselineColorDown),
|
|
5301
5291
|
baselineDescr: chart.baselineDescr ? _t(chart.baselineDescr) : "",
|
|
5302
|
-
fontColor
|
|
5292
|
+
fontColor,
|
|
5303
5293
|
background,
|
|
5304
5294
|
baselineStyle: chart.baselineMode !== "percentage" && baseline
|
|
5305
5295
|
? getters.getCellStyle({
|
|
@@ -6250,10 +6240,6 @@
|
|
|
6250
6240
|
|
|
6251
6241
|
.o-error-tooltip-message {
|
|
6252
6242
|
overflow: hidden;
|
|
6253
|
-
display: -webkit-box; /* Limit to 3 lines */
|
|
6254
|
-
-webkit-line-clamp: 3;
|
|
6255
|
-
line-clamp: 3;
|
|
6256
|
-
-webkit-box-orient: vertical;
|
|
6257
6243
|
}
|
|
6258
6244
|
}
|
|
6259
6245
|
`;
|
|
@@ -6598,6 +6584,7 @@
|
|
|
6598
6584
|
},
|
|
6599
6585
|
};
|
|
6600
6586
|
|
|
6587
|
+
const macRegex = /Mac/i;
|
|
6601
6588
|
/**
|
|
6602
6589
|
* Return true if the event was triggered from
|
|
6603
6590
|
* a child element.
|
|
@@ -6636,6 +6623,17 @@
|
|
|
6636
6623
|
function getOpenedMenus() {
|
|
6637
6624
|
return Array.from(document.querySelectorAll(".o-spreadsheet .o-menu"));
|
|
6638
6625
|
}
|
|
6626
|
+
function isMacOS() {
|
|
6627
|
+
return Boolean(macRegex.test(navigator.userAgent));
|
|
6628
|
+
}
|
|
6629
|
+
/**
|
|
6630
|
+
* @param {KeyboardEvent | MouseEvent} ev
|
|
6631
|
+
* @returns Returns true if the event was triggered with the "ctrl" modifier pressed.
|
|
6632
|
+
* On Mac, this is the "meta" or "command" key.
|
|
6633
|
+
*/
|
|
6634
|
+
function isCtrlKey(ev) {
|
|
6635
|
+
return isMacOS() ? ev.metaKey : ev.ctrlKey;
|
|
6636
|
+
}
|
|
6639
6637
|
|
|
6640
6638
|
/**
|
|
6641
6639
|
* Return the o-spreadsheet element position relative
|
|
@@ -8883,7 +8881,7 @@
|
|
|
8883
8881
|
});
|
|
8884
8882
|
return {
|
|
8885
8883
|
chartJsConfig: config,
|
|
8886
|
-
background: getters.
|
|
8884
|
+
background: getters.getStyleOfSingleCellChart(chart.background, dataRange).background,
|
|
8887
8885
|
};
|
|
8888
8886
|
}
|
|
8889
8887
|
|
|
@@ -8964,7 +8962,7 @@
|
|
|
8964
8962
|
else if (format.includes("h") || format.includes("H")) {
|
|
8965
8963
|
return "hour";
|
|
8966
8964
|
}
|
|
8967
|
-
else if (format.includes("
|
|
8965
|
+
else if (format.includes("d")) {
|
|
8968
8966
|
return "day";
|
|
8969
8967
|
}
|
|
8970
8968
|
else if (format.includes("M")) {
|
|
@@ -21217,10 +21215,10 @@
|
|
|
21217
21215
|
function updateSelectionWithArrowKeys(ev, selection) {
|
|
21218
21216
|
const direction = arrowMap[ev.key];
|
|
21219
21217
|
if (ev.shiftKey) {
|
|
21220
|
-
selection.resizeAnchorZone(direction, ev
|
|
21218
|
+
selection.resizeAnchorZone(direction, isCtrlKey(ev) ? "end" : 1);
|
|
21221
21219
|
}
|
|
21222
21220
|
else {
|
|
21223
|
-
selection.moveAnchorCell(direction, ev
|
|
21221
|
+
selection.moveAnchorCell(direction, isCtrlKey(ev) ? "end" : 1);
|
|
21224
21222
|
}
|
|
21225
21223
|
}
|
|
21226
21224
|
|
|
@@ -28205,7 +28203,10 @@
|
|
|
28205
28203
|
return;
|
|
28206
28204
|
}
|
|
28207
28205
|
const [col, row] = this.getCartesianCoordinates(ev);
|
|
28208
|
-
this.props.onCellClicked(col, row, {
|
|
28206
|
+
this.props.onCellClicked(col, row, {
|
|
28207
|
+
expandZone: ev.shiftKey,
|
|
28208
|
+
addZone: isCtrlKey(ev),
|
|
28209
|
+
});
|
|
28209
28210
|
}
|
|
28210
28211
|
onDoubleClick(ev) {
|
|
28211
28212
|
const [col, row] = this.getCartesianCoordinates(ev);
|
|
@@ -28280,6 +28281,7 @@
|
|
|
28280
28281
|
draggerShadowThickness: 0,
|
|
28281
28282
|
delta: 0,
|
|
28282
28283
|
base: 0,
|
|
28284
|
+
position: "before",
|
|
28283
28285
|
});
|
|
28284
28286
|
_computeHandleDisplay(ev) {
|
|
28285
28287
|
const position = this._getEvOffset(ev);
|
|
@@ -28404,11 +28406,13 @@
|
|
|
28404
28406
|
this.state.draggerLinePosition = dimensions.start;
|
|
28405
28407
|
this.state.draggerShadowPosition = dimensions.start;
|
|
28406
28408
|
this.state.base = elementIndex;
|
|
28409
|
+
this.state.position = "before";
|
|
28407
28410
|
}
|
|
28408
28411
|
else if (this._getSelectedZoneEnd() < elementIndex) {
|
|
28409
28412
|
this.state.draggerLinePosition = dimensions.end;
|
|
28410
28413
|
this.state.draggerShadowPosition = dimensions.end - this.state.draggerShadowThickness;
|
|
28411
|
-
this.state.base = elementIndex
|
|
28414
|
+
this.state.base = elementIndex;
|
|
28415
|
+
this.state.position = "after";
|
|
28412
28416
|
}
|
|
28413
28417
|
else {
|
|
28414
28418
|
this.state.draggerLinePosition = startDimensions.start;
|
|
@@ -28432,7 +28436,7 @@
|
|
|
28432
28436
|
this._increaseSelection(index);
|
|
28433
28437
|
}
|
|
28434
28438
|
else {
|
|
28435
|
-
this._selectElement(index, ev
|
|
28439
|
+
this._selectElement(index, isCtrlKey(ev));
|
|
28436
28440
|
}
|
|
28437
28441
|
this.lastSelectedElementIndex = index;
|
|
28438
28442
|
const mouseMoveSelect = (col, row) => {
|
|
@@ -28590,13 +28594,14 @@
|
|
|
28590
28594
|
dimension: "COL",
|
|
28591
28595
|
base: this.state.base,
|
|
28592
28596
|
elements,
|
|
28597
|
+
position: this.state.position,
|
|
28593
28598
|
});
|
|
28594
28599
|
if (!result.isSuccessful && result.reasons.includes("WillRemoveExistingMerge" /* CommandResult.WillRemoveExistingMerge */)) {
|
|
28595
28600
|
this.env.raiseError(MergeErrorMessage);
|
|
28596
28601
|
}
|
|
28597
28602
|
}
|
|
28598
|
-
_selectElement(index,
|
|
28599
|
-
this.env.model.selection.selectColumn(index,
|
|
28603
|
+
_selectElement(index, addDistinctHeader) {
|
|
28604
|
+
this.env.model.selection.selectColumn(index, addDistinctHeader ? "newAnchor" : "overrideSelection");
|
|
28600
28605
|
}
|
|
28601
28606
|
_increaseSelection(index) {
|
|
28602
28607
|
this.env.model.selection.selectColumn(index, "updateAnchor");
|
|
@@ -28763,13 +28768,14 @@
|
|
|
28763
28768
|
dimension: "ROW",
|
|
28764
28769
|
base: this.state.base,
|
|
28765
28770
|
elements,
|
|
28771
|
+
position: this.state.position,
|
|
28766
28772
|
});
|
|
28767
28773
|
if (!result.isSuccessful && result.reasons.includes("WillRemoveExistingMerge" /* CommandResult.WillRemoveExistingMerge */)) {
|
|
28768
28774
|
this.env.raiseError(MergeErrorMessage);
|
|
28769
28775
|
}
|
|
28770
28776
|
}
|
|
28771
|
-
_selectElement(index,
|
|
28772
|
-
this.env.model.selection.selectRow(index,
|
|
28777
|
+
_selectElement(index, addDistinctHeader) {
|
|
28778
|
+
this.env.model.selection.selectRow(index, addDistinctHeader ? "newAnchor" : "overrideSelection");
|
|
28773
28779
|
}
|
|
28774
28780
|
_increaseSelection(index) {
|
|
28775
28781
|
this.env.model.selection.selectRow(index, "updateAnchor");
|
|
@@ -28870,8 +28876,8 @@
|
|
|
28870
28876
|
return val * (deltaMode === 0 ? 1 : DEFAULT_CELL_HEIGHT);
|
|
28871
28877
|
}
|
|
28872
28878
|
const onMouseWheel = (ev) => {
|
|
28873
|
-
const deltaX = normalize(ev.shiftKey ? ev.deltaY : ev.deltaX, ev.deltaMode);
|
|
28874
|
-
const deltaY = normalize(ev.shiftKey ? ev.deltaX : ev.deltaY, ev.deltaMode);
|
|
28879
|
+
const deltaX = normalize(ev.shiftKey && !isMacOS() ? ev.deltaY : ev.deltaX, ev.deltaMode);
|
|
28880
|
+
const deltaY = normalize(ev.shiftKey && !isMacOS() ? ev.deltaX : ev.deltaY, ev.deltaMode);
|
|
28875
28881
|
handler(deltaX, deltaY);
|
|
28876
28882
|
};
|
|
28877
28883
|
return onMouseWheel;
|
|
@@ -29571,17 +29577,17 @@
|
|
|
29571
29577
|
// ---------------------------------------------------------------------------
|
|
29572
29578
|
// Zone selection with mouse
|
|
29573
29579
|
// ---------------------------------------------------------------------------
|
|
29574
|
-
onCellClicked(col, row, {
|
|
29580
|
+
onCellClicked(col, row, { addZone, expandZone }) {
|
|
29575
29581
|
if (this.env.model.getters.hasOpenedPopover()) {
|
|
29576
29582
|
this.closeOpenedPopover();
|
|
29577
29583
|
}
|
|
29578
29584
|
if (this.env.model.getters.getEditionMode() === "editing") {
|
|
29579
29585
|
interactiveStopEdition(this.env);
|
|
29580
29586
|
}
|
|
29581
|
-
if (
|
|
29587
|
+
if (expandZone) {
|
|
29582
29588
|
this.env.model.selection.setAnchorCorner(col, row);
|
|
29583
29589
|
}
|
|
29584
|
-
else if (
|
|
29590
|
+
else if (addZone) {
|
|
29585
29591
|
this.env.model.selection.addCellToSelection(col, row);
|
|
29586
29592
|
}
|
|
29587
29593
|
else {
|
|
@@ -29642,7 +29648,7 @@
|
|
|
29642
29648
|
onKeydown(ev) {
|
|
29643
29649
|
const eventKey = ev.key.toUpperCase();
|
|
29644
29650
|
let keyDownString = "";
|
|
29645
|
-
if (ev
|
|
29651
|
+
if (isCtrlKey(ev) && eventKey !== "CTRL")
|
|
29646
29652
|
keyDownString += "CTRL+";
|
|
29647
29653
|
if (ev.metaKey)
|
|
29648
29654
|
keyDownString += "CTRL+";
|
|
@@ -33325,7 +33331,7 @@
|
|
|
33325
33331
|
SPACE: goTo(State.RightRef),
|
|
33326
33332
|
NUMBER: goTo(State.Found),
|
|
33327
33333
|
REFERENCE: goTo(State.Found, (token) => isSingleCellReference(token.value)),
|
|
33328
|
-
SYMBOL: goTo(State.Found, (token) => isColHeader(token.value)),
|
|
33334
|
+
SYMBOL: goTo(State.Found, (token) => isColHeader(token.value) || isRowHeader(token.value)),
|
|
33329
33335
|
},
|
|
33330
33336
|
[State.RightColumnRef]: {
|
|
33331
33337
|
SPACE: goTo(State.RightColumnRef),
|
|
@@ -33336,6 +33342,7 @@
|
|
|
33336
33342
|
SPACE: goTo(State.RightRowRef),
|
|
33337
33343
|
NUMBER: goTo(State.Found),
|
|
33338
33344
|
REFERENCE: goTo(State.Found, (token) => isSingleCellReference(token.value)),
|
|
33345
|
+
SYMBOL: goTo(State.Found, (token) => isRowHeader(token.value)),
|
|
33339
33346
|
},
|
|
33340
33347
|
[State.Found]: {},
|
|
33341
33348
|
};
|
|
@@ -34532,7 +34539,7 @@
|
|
|
34532
34539
|
*/
|
|
34533
34540
|
function getBorderId(border) {
|
|
34534
34541
|
for (let [key, value] of Object.entries(borders)) {
|
|
34535
|
-
if (
|
|
34542
|
+
if (deepEquals(value, border)) {
|
|
34536
34543
|
return parseInt(key, 10);
|
|
34537
34544
|
}
|
|
34538
34545
|
}
|
|
@@ -37317,7 +37324,16 @@
|
|
|
37317
37324
|
return consecutiveIndexes;
|
|
37318
37325
|
}
|
|
37319
37326
|
getAllVisibleHeaders(sheetId, dimension) {
|
|
37320
|
-
|
|
37327
|
+
const headers = range(0, this.getters.getNumberHeaders(sheetId, dimension));
|
|
37328
|
+
const foldedHeaders = [];
|
|
37329
|
+
this.getters.getHeaderGroups(sheetId, dimension).forEach((group) => {
|
|
37330
|
+
if (group.isFolded) {
|
|
37331
|
+
foldedHeaders.push(...range(group.start, group.end + 1));
|
|
37332
|
+
}
|
|
37333
|
+
});
|
|
37334
|
+
return headers.filter((i) => {
|
|
37335
|
+
return !this.hiddenHeaders[sheetId][dimension][i] && !foldedHeaders.includes(i);
|
|
37336
|
+
});
|
|
37321
37337
|
}
|
|
37322
37338
|
import(data) {
|
|
37323
37339
|
for (let sheet of data.sheets) {
|
|
@@ -39777,10 +39793,13 @@
|
|
|
39777
39793
|
if (evaluatedCell === undefined) {
|
|
39778
39794
|
return { value: null, format: this.getters.getCell(position)?.format };
|
|
39779
39795
|
}
|
|
39796
|
+
if (evaluatedCell.type === CellValueType.error) {
|
|
39797
|
+
throw evaluatedCell.error;
|
|
39798
|
+
}
|
|
39780
39799
|
return evaluatedCell;
|
|
39781
39800
|
}
|
|
39782
39801
|
getEvaluatedCellIfNotEmpty(position) {
|
|
39783
|
-
const evaluatedCell = this.
|
|
39802
|
+
const evaluatedCell = this.computeCell(position);
|
|
39784
39803
|
if (evaluatedCell.type === CellValueType.empty) {
|
|
39785
39804
|
const cell = this.getters.getCell(position);
|
|
39786
39805
|
if (!cell || (!cell.isFormula && cell.content === "")) {
|
|
@@ -39789,13 +39808,6 @@
|
|
|
39789
39808
|
}
|
|
39790
39809
|
return evaluatedCell;
|
|
39791
39810
|
}
|
|
39792
|
-
getEvaluatedCell(position) {
|
|
39793
|
-
const evaluatedCell = this.computeCell(position);
|
|
39794
|
-
if (evaluatedCell.type === CellValueType.error) {
|
|
39795
|
-
throw evaluatedCell.error;
|
|
39796
|
-
}
|
|
39797
|
-
return evaluatedCell;
|
|
39798
|
-
}
|
|
39799
39811
|
/**
|
|
39800
39812
|
* Return the values of the cell(s) used in reference, but always in the format of a range even
|
|
39801
39813
|
* if a single cell is referenced. It is a list of col values. This is useful for the formulas that describe parameters as
|
|
@@ -39818,7 +39830,11 @@
|
|
|
39818
39830
|
const { top, left, bottom, right } = zone;
|
|
39819
39831
|
const cacheKey = `${sheetId}-${top}-${left}-${bottom}-${right}`;
|
|
39820
39832
|
if (cacheKey in this.rangeCache) {
|
|
39821
|
-
|
|
39833
|
+
const result = this.rangeCache[cacheKey];
|
|
39834
|
+
if (result instanceof EvaluationError) {
|
|
39835
|
+
throw result;
|
|
39836
|
+
}
|
|
39837
|
+
return result;
|
|
39822
39838
|
}
|
|
39823
39839
|
const height = _zone.bottom - _zone.top + 1;
|
|
39824
39840
|
const width = _zone.right - _zone.left + 1;
|
|
@@ -39828,6 +39844,11 @@
|
|
|
39828
39844
|
const colIndex = col - _zone.left;
|
|
39829
39845
|
matrix[colIndex] = new Array(height);
|
|
39830
39846
|
for (let row = _zone.top; row <= _zone.bottom; row++) {
|
|
39847
|
+
const evaluatedCell = this.getEvaluatedCellIfNotEmpty({ sheetId, col, row });
|
|
39848
|
+
if (evaluatedCell?.type === CellValueType.error) {
|
|
39849
|
+
this.rangeCache[cacheKey] = evaluatedCell.error;
|
|
39850
|
+
throw evaluatedCell.error;
|
|
39851
|
+
}
|
|
39831
39852
|
const rowIndex = row - _zone.top;
|
|
39832
39853
|
matrix[colIndex][rowIndex] = this.readCell({ sheetId, col, row });
|
|
39833
39854
|
}
|
|
@@ -39837,56 +39858,758 @@
|
|
|
39837
39858
|
}
|
|
39838
39859
|
}
|
|
39839
39860
|
|
|
39861
|
+
function quickselect(arr, k, left, right, compare) {
|
|
39862
|
+
quickselectStep(arr, k, left || 0, right || (arr.length - 1), compare || defaultCompare);
|
|
39863
|
+
}
|
|
39864
|
+
|
|
39865
|
+
function quickselectStep(arr, k, left, right, compare) {
|
|
39866
|
+
|
|
39867
|
+
while (right > left) {
|
|
39868
|
+
if (right - left > 600) {
|
|
39869
|
+
var n = right - left + 1;
|
|
39870
|
+
var m = k - left + 1;
|
|
39871
|
+
var z = Math.log(n);
|
|
39872
|
+
var s = 0.5 * Math.exp(2 * z / 3);
|
|
39873
|
+
var sd = 0.5 * Math.sqrt(z * s * (n - s) / n) * (m - n / 2 < 0 ? -1 : 1);
|
|
39874
|
+
var newLeft = Math.max(left, Math.floor(k - m * s / n + sd));
|
|
39875
|
+
var newRight = Math.min(right, Math.floor(k + (n - m) * s / n + sd));
|
|
39876
|
+
quickselectStep(arr, k, newLeft, newRight, compare);
|
|
39877
|
+
}
|
|
39878
|
+
|
|
39879
|
+
var t = arr[k];
|
|
39880
|
+
var i = left;
|
|
39881
|
+
var j = right;
|
|
39882
|
+
|
|
39883
|
+
swap(arr, left, k);
|
|
39884
|
+
if (compare(arr[right], t) > 0) swap(arr, left, right);
|
|
39885
|
+
|
|
39886
|
+
while (i < j) {
|
|
39887
|
+
swap(arr, i, j);
|
|
39888
|
+
i++;
|
|
39889
|
+
j--;
|
|
39890
|
+
while (compare(arr[i], t) < 0) i++;
|
|
39891
|
+
while (compare(arr[j], t) > 0) j--;
|
|
39892
|
+
}
|
|
39893
|
+
|
|
39894
|
+
if (compare(arr[left], t) === 0) swap(arr, left, j);
|
|
39895
|
+
else {
|
|
39896
|
+
j++;
|
|
39897
|
+
swap(arr, j, right);
|
|
39898
|
+
}
|
|
39899
|
+
|
|
39900
|
+
if (j <= k) left = j + 1;
|
|
39901
|
+
if (k <= j) right = j - 1;
|
|
39902
|
+
}
|
|
39903
|
+
}
|
|
39904
|
+
|
|
39905
|
+
function swap(arr, i, j) {
|
|
39906
|
+
var tmp = arr[i];
|
|
39907
|
+
arr[i] = arr[j];
|
|
39908
|
+
arr[j] = tmp;
|
|
39909
|
+
}
|
|
39910
|
+
|
|
39911
|
+
function defaultCompare(a, b) {
|
|
39912
|
+
return a < b ? -1 : a > b ? 1 : 0;
|
|
39913
|
+
}
|
|
39914
|
+
|
|
39915
|
+
class RBush {
|
|
39916
|
+
constructor(maxEntries = 9) {
|
|
39917
|
+
// max entries in a node is 9 by default; min node fill is 40% for best performance
|
|
39918
|
+
this._maxEntries = Math.max(4, maxEntries);
|
|
39919
|
+
this._minEntries = Math.max(2, Math.ceil(this._maxEntries * 0.4));
|
|
39920
|
+
this.clear();
|
|
39921
|
+
}
|
|
39922
|
+
|
|
39923
|
+
all() {
|
|
39924
|
+
return this._all(this.data, []);
|
|
39925
|
+
}
|
|
39926
|
+
|
|
39927
|
+
search(bbox) {
|
|
39928
|
+
let node = this.data;
|
|
39929
|
+
const result = [];
|
|
39930
|
+
|
|
39931
|
+
if (!intersects(bbox, node)) return result;
|
|
39932
|
+
|
|
39933
|
+
const toBBox = this.toBBox;
|
|
39934
|
+
const nodesToSearch = [];
|
|
39935
|
+
|
|
39936
|
+
while (node) {
|
|
39937
|
+
for (let i = 0; i < node.children.length; i++) {
|
|
39938
|
+
const child = node.children[i];
|
|
39939
|
+
const childBBox = node.leaf ? toBBox(child) : child;
|
|
39940
|
+
|
|
39941
|
+
if (intersects(bbox, childBBox)) {
|
|
39942
|
+
if (node.leaf) result.push(child);
|
|
39943
|
+
else if (contains(bbox, childBBox)) this._all(child, result);
|
|
39944
|
+
else nodesToSearch.push(child);
|
|
39945
|
+
}
|
|
39946
|
+
}
|
|
39947
|
+
node = nodesToSearch.pop();
|
|
39948
|
+
}
|
|
39949
|
+
|
|
39950
|
+
return result;
|
|
39951
|
+
}
|
|
39952
|
+
|
|
39953
|
+
collides(bbox) {
|
|
39954
|
+
let node = this.data;
|
|
39955
|
+
|
|
39956
|
+
if (!intersects(bbox, node)) return false;
|
|
39957
|
+
|
|
39958
|
+
const nodesToSearch = [];
|
|
39959
|
+
while (node) {
|
|
39960
|
+
for (let i = 0; i < node.children.length; i++) {
|
|
39961
|
+
const child = node.children[i];
|
|
39962
|
+
const childBBox = node.leaf ? this.toBBox(child) : child;
|
|
39963
|
+
|
|
39964
|
+
if (intersects(bbox, childBBox)) {
|
|
39965
|
+
if (node.leaf || contains(bbox, childBBox)) return true;
|
|
39966
|
+
nodesToSearch.push(child);
|
|
39967
|
+
}
|
|
39968
|
+
}
|
|
39969
|
+
node = nodesToSearch.pop();
|
|
39970
|
+
}
|
|
39971
|
+
|
|
39972
|
+
return false;
|
|
39973
|
+
}
|
|
39974
|
+
|
|
39975
|
+
load(data) {
|
|
39976
|
+
if (!(data && data.length)) return this;
|
|
39977
|
+
|
|
39978
|
+
if (data.length < this._minEntries) {
|
|
39979
|
+
for (let i = 0; i < data.length; i++) {
|
|
39980
|
+
this.insert(data[i]);
|
|
39981
|
+
}
|
|
39982
|
+
return this;
|
|
39983
|
+
}
|
|
39984
|
+
|
|
39985
|
+
// recursively build the tree with the given data from scratch using OMT algorithm
|
|
39986
|
+
let node = this._build(data.slice(), 0, data.length - 1, 0);
|
|
39987
|
+
|
|
39988
|
+
if (!this.data.children.length) {
|
|
39989
|
+
// save as is if tree is empty
|
|
39990
|
+
this.data = node;
|
|
39991
|
+
|
|
39992
|
+
} else if (this.data.height === node.height) {
|
|
39993
|
+
// split root if trees have the same height
|
|
39994
|
+
this._splitRoot(this.data, node);
|
|
39995
|
+
|
|
39996
|
+
} else {
|
|
39997
|
+
if (this.data.height < node.height) {
|
|
39998
|
+
// swap trees if inserted one is bigger
|
|
39999
|
+
const tmpNode = this.data;
|
|
40000
|
+
this.data = node;
|
|
40001
|
+
node = tmpNode;
|
|
40002
|
+
}
|
|
40003
|
+
|
|
40004
|
+
// insert the small tree into the large tree at appropriate level
|
|
40005
|
+
this._insert(node, this.data.height - node.height - 1, true);
|
|
40006
|
+
}
|
|
40007
|
+
|
|
40008
|
+
return this;
|
|
40009
|
+
}
|
|
40010
|
+
|
|
40011
|
+
insert(item) {
|
|
40012
|
+
if (item) this._insert(item, this.data.height - 1);
|
|
40013
|
+
return this;
|
|
40014
|
+
}
|
|
40015
|
+
|
|
40016
|
+
clear() {
|
|
40017
|
+
this.data = createNode([]);
|
|
40018
|
+
return this;
|
|
40019
|
+
}
|
|
40020
|
+
|
|
40021
|
+
remove(item, equalsFn) {
|
|
40022
|
+
if (!item) return this;
|
|
40023
|
+
|
|
40024
|
+
let node = this.data;
|
|
40025
|
+
const bbox = this.toBBox(item);
|
|
40026
|
+
const path = [];
|
|
40027
|
+
const indexes = [];
|
|
40028
|
+
let i, parent, goingUp;
|
|
40029
|
+
|
|
40030
|
+
// depth-first iterative tree traversal
|
|
40031
|
+
while (node || path.length) {
|
|
40032
|
+
|
|
40033
|
+
if (!node) { // go up
|
|
40034
|
+
node = path.pop();
|
|
40035
|
+
parent = path[path.length - 1];
|
|
40036
|
+
i = indexes.pop();
|
|
40037
|
+
goingUp = true;
|
|
40038
|
+
}
|
|
40039
|
+
|
|
40040
|
+
if (node.leaf) { // check current node
|
|
40041
|
+
const index = findItem(item, node.children, equalsFn);
|
|
40042
|
+
|
|
40043
|
+
if (index !== -1) {
|
|
40044
|
+
// item found, remove the item and condense tree upwards
|
|
40045
|
+
node.children.splice(index, 1);
|
|
40046
|
+
path.push(node);
|
|
40047
|
+
this._condense(path);
|
|
40048
|
+
return this;
|
|
40049
|
+
}
|
|
40050
|
+
}
|
|
40051
|
+
|
|
40052
|
+
if (!goingUp && !node.leaf && contains(node, bbox)) { // go down
|
|
40053
|
+
path.push(node);
|
|
40054
|
+
indexes.push(i);
|
|
40055
|
+
i = 0;
|
|
40056
|
+
parent = node;
|
|
40057
|
+
node = node.children[0];
|
|
40058
|
+
|
|
40059
|
+
} else if (parent) { // go right
|
|
40060
|
+
i++;
|
|
40061
|
+
node = parent.children[i];
|
|
40062
|
+
goingUp = false;
|
|
40063
|
+
|
|
40064
|
+
} else node = null; // nothing found
|
|
40065
|
+
}
|
|
40066
|
+
|
|
40067
|
+
return this;
|
|
40068
|
+
}
|
|
40069
|
+
|
|
40070
|
+
toBBox(item) { return item; }
|
|
40071
|
+
|
|
40072
|
+
compareMinX(a, b) { return a.minX - b.minX; }
|
|
40073
|
+
compareMinY(a, b) { return a.minY - b.minY; }
|
|
40074
|
+
|
|
40075
|
+
toJSON() { return this.data; }
|
|
40076
|
+
|
|
40077
|
+
fromJSON(data) {
|
|
40078
|
+
this.data = data;
|
|
40079
|
+
return this;
|
|
40080
|
+
}
|
|
40081
|
+
|
|
40082
|
+
_all(node, result) {
|
|
40083
|
+
const nodesToSearch = [];
|
|
40084
|
+
while (node) {
|
|
40085
|
+
if (node.leaf) result.push(...node.children);
|
|
40086
|
+
else nodesToSearch.push(...node.children);
|
|
40087
|
+
|
|
40088
|
+
node = nodesToSearch.pop();
|
|
40089
|
+
}
|
|
40090
|
+
return result;
|
|
40091
|
+
}
|
|
40092
|
+
|
|
40093
|
+
_build(items, left, right, height) {
|
|
40094
|
+
|
|
40095
|
+
const N = right - left + 1;
|
|
40096
|
+
let M = this._maxEntries;
|
|
40097
|
+
let node;
|
|
40098
|
+
|
|
40099
|
+
if (N <= M) {
|
|
40100
|
+
// reached leaf level; return leaf
|
|
40101
|
+
node = createNode(items.slice(left, right + 1));
|
|
40102
|
+
calcBBox(node, this.toBBox);
|
|
40103
|
+
return node;
|
|
40104
|
+
}
|
|
40105
|
+
|
|
40106
|
+
if (!height) {
|
|
40107
|
+
// target height of the bulk-loaded tree
|
|
40108
|
+
height = Math.ceil(Math.log(N) / Math.log(M));
|
|
40109
|
+
|
|
40110
|
+
// target number of root entries to maximize storage utilization
|
|
40111
|
+
M = Math.ceil(N / Math.pow(M, height - 1));
|
|
40112
|
+
}
|
|
40113
|
+
|
|
40114
|
+
node = createNode([]);
|
|
40115
|
+
node.leaf = false;
|
|
40116
|
+
node.height = height;
|
|
40117
|
+
|
|
40118
|
+
// split the items into M mostly square tiles
|
|
40119
|
+
|
|
40120
|
+
const N2 = Math.ceil(N / M);
|
|
40121
|
+
const N1 = N2 * Math.ceil(Math.sqrt(M));
|
|
40122
|
+
|
|
40123
|
+
multiSelect(items, left, right, N1, this.compareMinX);
|
|
40124
|
+
|
|
40125
|
+
for (let i = left; i <= right; i += N1) {
|
|
40126
|
+
|
|
40127
|
+
const right2 = Math.min(i + N1 - 1, right);
|
|
40128
|
+
|
|
40129
|
+
multiSelect(items, i, right2, N2, this.compareMinY);
|
|
40130
|
+
|
|
40131
|
+
for (let j = i; j <= right2; j += N2) {
|
|
40132
|
+
|
|
40133
|
+
const right3 = Math.min(j + N2 - 1, right2);
|
|
40134
|
+
|
|
40135
|
+
// pack each entry recursively
|
|
40136
|
+
node.children.push(this._build(items, j, right3, height - 1));
|
|
40137
|
+
}
|
|
40138
|
+
}
|
|
40139
|
+
|
|
40140
|
+
calcBBox(node, this.toBBox);
|
|
40141
|
+
|
|
40142
|
+
return node;
|
|
40143
|
+
}
|
|
40144
|
+
|
|
40145
|
+
_chooseSubtree(bbox, node, level, path) {
|
|
40146
|
+
while (true) {
|
|
40147
|
+
path.push(node);
|
|
40148
|
+
|
|
40149
|
+
if (node.leaf || path.length - 1 === level) break;
|
|
40150
|
+
|
|
40151
|
+
let minArea = Infinity;
|
|
40152
|
+
let minEnlargement = Infinity;
|
|
40153
|
+
let targetNode;
|
|
40154
|
+
|
|
40155
|
+
for (let i = 0; i < node.children.length; i++) {
|
|
40156
|
+
const child = node.children[i];
|
|
40157
|
+
const area = bboxArea(child);
|
|
40158
|
+
const enlargement = enlargedArea(bbox, child) - area;
|
|
40159
|
+
|
|
40160
|
+
// choose entry with the least area enlargement
|
|
40161
|
+
if (enlargement < minEnlargement) {
|
|
40162
|
+
minEnlargement = enlargement;
|
|
40163
|
+
minArea = area < minArea ? area : minArea;
|
|
40164
|
+
targetNode = child;
|
|
40165
|
+
|
|
40166
|
+
} else if (enlargement === minEnlargement) {
|
|
40167
|
+
// otherwise choose one with the smallest area
|
|
40168
|
+
if (area < minArea) {
|
|
40169
|
+
minArea = area;
|
|
40170
|
+
targetNode = child;
|
|
40171
|
+
}
|
|
40172
|
+
}
|
|
40173
|
+
}
|
|
40174
|
+
|
|
40175
|
+
node = targetNode || node.children[0];
|
|
40176
|
+
}
|
|
40177
|
+
|
|
40178
|
+
return node;
|
|
40179
|
+
}
|
|
40180
|
+
|
|
40181
|
+
_insert(item, level, isNode) {
|
|
40182
|
+
const bbox = isNode ? item : this.toBBox(item);
|
|
40183
|
+
const insertPath = [];
|
|
40184
|
+
|
|
40185
|
+
// find the best node for accommodating the item, saving all nodes along the path too
|
|
40186
|
+
const node = this._chooseSubtree(bbox, this.data, level, insertPath);
|
|
40187
|
+
|
|
40188
|
+
// put the item into the node
|
|
40189
|
+
node.children.push(item);
|
|
40190
|
+
extend(node, bbox);
|
|
40191
|
+
|
|
40192
|
+
// split on node overflow; propagate upwards if necessary
|
|
40193
|
+
while (level >= 0) {
|
|
40194
|
+
if (insertPath[level].children.length > this._maxEntries) {
|
|
40195
|
+
this._split(insertPath, level);
|
|
40196
|
+
level--;
|
|
40197
|
+
} else break;
|
|
40198
|
+
}
|
|
40199
|
+
|
|
40200
|
+
// adjust bboxes along the insertion path
|
|
40201
|
+
this._adjustParentBBoxes(bbox, insertPath, level);
|
|
40202
|
+
}
|
|
40203
|
+
|
|
40204
|
+
// split overflowed node into two
|
|
40205
|
+
_split(insertPath, level) {
|
|
40206
|
+
const node = insertPath[level];
|
|
40207
|
+
const M = node.children.length;
|
|
40208
|
+
const m = this._minEntries;
|
|
40209
|
+
|
|
40210
|
+
this._chooseSplitAxis(node, m, M);
|
|
40211
|
+
|
|
40212
|
+
const splitIndex = this._chooseSplitIndex(node, m, M);
|
|
40213
|
+
|
|
40214
|
+
const newNode = createNode(node.children.splice(splitIndex, node.children.length - splitIndex));
|
|
40215
|
+
newNode.height = node.height;
|
|
40216
|
+
newNode.leaf = node.leaf;
|
|
40217
|
+
|
|
40218
|
+
calcBBox(node, this.toBBox);
|
|
40219
|
+
calcBBox(newNode, this.toBBox);
|
|
40220
|
+
|
|
40221
|
+
if (level) insertPath[level - 1].children.push(newNode);
|
|
40222
|
+
else this._splitRoot(node, newNode);
|
|
40223
|
+
}
|
|
40224
|
+
|
|
40225
|
+
_splitRoot(node, newNode) {
|
|
40226
|
+
// split root node
|
|
40227
|
+
this.data = createNode([node, newNode]);
|
|
40228
|
+
this.data.height = node.height + 1;
|
|
40229
|
+
this.data.leaf = false;
|
|
40230
|
+
calcBBox(this.data, this.toBBox);
|
|
40231
|
+
}
|
|
40232
|
+
|
|
40233
|
+
_chooseSplitIndex(node, m, M) {
|
|
40234
|
+
let index;
|
|
40235
|
+
let minOverlap = Infinity;
|
|
40236
|
+
let minArea = Infinity;
|
|
40237
|
+
|
|
40238
|
+
for (let i = m; i <= M - m; i++) {
|
|
40239
|
+
const bbox1 = distBBox(node, 0, i, this.toBBox);
|
|
40240
|
+
const bbox2 = distBBox(node, i, M, this.toBBox);
|
|
40241
|
+
|
|
40242
|
+
const overlap = intersectionArea(bbox1, bbox2);
|
|
40243
|
+
const area = bboxArea(bbox1) + bboxArea(bbox2);
|
|
40244
|
+
|
|
40245
|
+
// choose distribution with minimum overlap
|
|
40246
|
+
if (overlap < minOverlap) {
|
|
40247
|
+
minOverlap = overlap;
|
|
40248
|
+
index = i;
|
|
40249
|
+
|
|
40250
|
+
minArea = area < minArea ? area : minArea;
|
|
40251
|
+
|
|
40252
|
+
} else if (overlap === minOverlap) {
|
|
40253
|
+
// otherwise choose distribution with minimum area
|
|
40254
|
+
if (area < minArea) {
|
|
40255
|
+
minArea = area;
|
|
40256
|
+
index = i;
|
|
40257
|
+
}
|
|
40258
|
+
}
|
|
40259
|
+
}
|
|
40260
|
+
|
|
40261
|
+
return index || M - m;
|
|
40262
|
+
}
|
|
40263
|
+
|
|
40264
|
+
// sorts node children by the best axis for split
|
|
40265
|
+
_chooseSplitAxis(node, m, M) {
|
|
40266
|
+
const compareMinX = node.leaf ? this.compareMinX : compareNodeMinX;
|
|
40267
|
+
const compareMinY = node.leaf ? this.compareMinY : compareNodeMinY;
|
|
40268
|
+
const xMargin = this._allDistMargin(node, m, M, compareMinX);
|
|
40269
|
+
const yMargin = this._allDistMargin(node, m, M, compareMinY);
|
|
40270
|
+
|
|
40271
|
+
// if total distributions margin value is minimal for x, sort by minX,
|
|
40272
|
+
// otherwise it's already sorted by minY
|
|
40273
|
+
if (xMargin < yMargin) node.children.sort(compareMinX);
|
|
40274
|
+
}
|
|
40275
|
+
|
|
40276
|
+
// total margin of all possible split distributions where each node is at least m full
|
|
40277
|
+
_allDistMargin(node, m, M, compare) {
|
|
40278
|
+
node.children.sort(compare);
|
|
40279
|
+
|
|
40280
|
+
const toBBox = this.toBBox;
|
|
40281
|
+
const leftBBox = distBBox(node, 0, m, toBBox);
|
|
40282
|
+
const rightBBox = distBBox(node, M - m, M, toBBox);
|
|
40283
|
+
let margin = bboxMargin(leftBBox) + bboxMargin(rightBBox);
|
|
40284
|
+
|
|
40285
|
+
for (let i = m; i < M - m; i++) {
|
|
40286
|
+
const child = node.children[i];
|
|
40287
|
+
extend(leftBBox, node.leaf ? toBBox(child) : child);
|
|
40288
|
+
margin += bboxMargin(leftBBox);
|
|
40289
|
+
}
|
|
40290
|
+
|
|
40291
|
+
for (let i = M - m - 1; i >= m; i--) {
|
|
40292
|
+
const child = node.children[i];
|
|
40293
|
+
extend(rightBBox, node.leaf ? toBBox(child) : child);
|
|
40294
|
+
margin += bboxMargin(rightBBox);
|
|
40295
|
+
}
|
|
40296
|
+
|
|
40297
|
+
return margin;
|
|
40298
|
+
}
|
|
40299
|
+
|
|
40300
|
+
_adjustParentBBoxes(bbox, path, level) {
|
|
40301
|
+
// adjust bboxes along the given tree path
|
|
40302
|
+
for (let i = level; i >= 0; i--) {
|
|
40303
|
+
extend(path[i], bbox);
|
|
40304
|
+
}
|
|
40305
|
+
}
|
|
40306
|
+
|
|
40307
|
+
_condense(path) {
|
|
40308
|
+
// go through the path, removing empty nodes and updating bboxes
|
|
40309
|
+
for (let i = path.length - 1, siblings; i >= 0; i--) {
|
|
40310
|
+
if (path[i].children.length === 0) {
|
|
40311
|
+
if (i > 0) {
|
|
40312
|
+
siblings = path[i - 1].children;
|
|
40313
|
+
siblings.splice(siblings.indexOf(path[i]), 1);
|
|
40314
|
+
|
|
40315
|
+
} else this.clear();
|
|
40316
|
+
|
|
40317
|
+
} else calcBBox(path[i], this.toBBox);
|
|
40318
|
+
}
|
|
40319
|
+
}
|
|
40320
|
+
}
|
|
40321
|
+
|
|
40322
|
+
function findItem(item, items, equalsFn) {
|
|
40323
|
+
if (!equalsFn) return items.indexOf(item);
|
|
40324
|
+
|
|
40325
|
+
for (let i = 0; i < items.length; i++) {
|
|
40326
|
+
if (equalsFn(item, items[i])) return i;
|
|
40327
|
+
}
|
|
40328
|
+
return -1;
|
|
40329
|
+
}
|
|
40330
|
+
|
|
40331
|
+
// calculate node's bbox from bboxes of its children
|
|
40332
|
+
function calcBBox(node, toBBox) {
|
|
40333
|
+
distBBox(node, 0, node.children.length, toBBox, node);
|
|
40334
|
+
}
|
|
40335
|
+
|
|
40336
|
+
// min bounding rectangle of node children from k to p-1
|
|
40337
|
+
function distBBox(node, k, p, toBBox, destNode) {
|
|
40338
|
+
if (!destNode) destNode = createNode(null);
|
|
40339
|
+
destNode.minX = Infinity;
|
|
40340
|
+
destNode.minY = Infinity;
|
|
40341
|
+
destNode.maxX = -Infinity;
|
|
40342
|
+
destNode.maxY = -Infinity;
|
|
40343
|
+
|
|
40344
|
+
for (let i = k; i < p; i++) {
|
|
40345
|
+
const child = node.children[i];
|
|
40346
|
+
extend(destNode, node.leaf ? toBBox(child) : child);
|
|
40347
|
+
}
|
|
40348
|
+
|
|
40349
|
+
return destNode;
|
|
40350
|
+
}
|
|
40351
|
+
|
|
40352
|
+
function extend(a, b) {
|
|
40353
|
+
a.minX = Math.min(a.minX, b.minX);
|
|
40354
|
+
a.minY = Math.min(a.minY, b.minY);
|
|
40355
|
+
a.maxX = Math.max(a.maxX, b.maxX);
|
|
40356
|
+
a.maxY = Math.max(a.maxY, b.maxY);
|
|
40357
|
+
return a;
|
|
40358
|
+
}
|
|
40359
|
+
|
|
40360
|
+
function compareNodeMinX(a, b) { return a.minX - b.minX; }
|
|
40361
|
+
function compareNodeMinY(a, b) { return a.minY - b.minY; }
|
|
40362
|
+
|
|
40363
|
+
function bboxArea(a) { return (a.maxX - a.minX) * (a.maxY - a.minY); }
|
|
40364
|
+
function bboxMargin(a) { return (a.maxX - a.minX) + (a.maxY - a.minY); }
|
|
40365
|
+
|
|
40366
|
+
function enlargedArea(a, b) {
|
|
40367
|
+
return (Math.max(b.maxX, a.maxX) - Math.min(b.minX, a.minX)) *
|
|
40368
|
+
(Math.max(b.maxY, a.maxY) - Math.min(b.minY, a.minY));
|
|
40369
|
+
}
|
|
40370
|
+
|
|
40371
|
+
function intersectionArea(a, b) {
|
|
40372
|
+
const minX = Math.max(a.minX, b.minX);
|
|
40373
|
+
const minY = Math.max(a.minY, b.minY);
|
|
40374
|
+
const maxX = Math.min(a.maxX, b.maxX);
|
|
40375
|
+
const maxY = Math.min(a.maxY, b.maxY);
|
|
40376
|
+
|
|
40377
|
+
return Math.max(0, maxX - minX) *
|
|
40378
|
+
Math.max(0, maxY - minY);
|
|
40379
|
+
}
|
|
40380
|
+
|
|
40381
|
+
function contains(a, b) {
|
|
40382
|
+
return a.minX <= b.minX &&
|
|
40383
|
+
a.minY <= b.minY &&
|
|
40384
|
+
b.maxX <= a.maxX &&
|
|
40385
|
+
b.maxY <= a.maxY;
|
|
40386
|
+
}
|
|
40387
|
+
|
|
40388
|
+
function intersects(a, b) {
|
|
40389
|
+
return b.minX <= a.maxX &&
|
|
40390
|
+
b.minY <= a.maxY &&
|
|
40391
|
+
b.maxX >= a.minX &&
|
|
40392
|
+
b.maxY >= a.minY;
|
|
40393
|
+
}
|
|
40394
|
+
|
|
40395
|
+
function createNode(children) {
|
|
40396
|
+
return {
|
|
40397
|
+
children,
|
|
40398
|
+
height: 1,
|
|
40399
|
+
leaf: true,
|
|
40400
|
+
minX: Infinity,
|
|
40401
|
+
minY: Infinity,
|
|
40402
|
+
maxX: -Infinity,
|
|
40403
|
+
maxY: -Infinity
|
|
40404
|
+
};
|
|
40405
|
+
}
|
|
40406
|
+
|
|
40407
|
+
// sort an array so that items come in groups of n unsorted items, with groups sorted between each other;
|
|
40408
|
+
// combines selection algorithm with binary divide & conquer approach
|
|
40409
|
+
|
|
40410
|
+
function multiSelect(arr, left, right, n, compare) {
|
|
40411
|
+
const stack = [left, right];
|
|
40412
|
+
|
|
40413
|
+
while (stack.length) {
|
|
40414
|
+
right = stack.pop();
|
|
40415
|
+
left = stack.pop();
|
|
40416
|
+
|
|
40417
|
+
if (right - left <= n) continue;
|
|
40418
|
+
|
|
40419
|
+
const mid = left + Math.ceil((right - left) / n / 2) * n;
|
|
40420
|
+
quickselect(arr, mid, left, right, compare);
|
|
40421
|
+
|
|
40422
|
+
stack.push(left, mid, mid, right);
|
|
40423
|
+
}
|
|
40424
|
+
}
|
|
40425
|
+
|
|
40426
|
+
/**
|
|
40427
|
+
* R-Tree Data Structure
|
|
40428
|
+
*
|
|
40429
|
+
* R-Tree is a spatial data structure used for efficient indexing and querying
|
|
40430
|
+
* of multi-dimensional objects, particularly in geometric and spatial applications.
|
|
40431
|
+
*
|
|
40432
|
+
* It organizes objects into a tree hierarchy, grouping nearby objects together
|
|
40433
|
+
* in bounding boxes. Each node in the tree represents a bounding box that
|
|
40434
|
+
* contains its child nodes or leaf objects. This hierarchical structure allows
|
|
40435
|
+
* for faster spatial queries.
|
|
40436
|
+
*
|
|
40437
|
+
* @see https://en.wikipedia.org/wiki/R-tree
|
|
40438
|
+
*
|
|
40439
|
+
* Consider a 2D Space with four zones: A, B, C, D
|
|
40440
|
+
* +--------------------------+
|
|
40441
|
+
* | |
|
|
40442
|
+
* | +---+ +-------+ |
|
|
40443
|
+
* | | A | | B | |
|
|
40444
|
+
* | +---+ +-------+ |
|
|
40445
|
+
* | |
|
|
40446
|
+
* | |
|
|
40447
|
+
* | +---+ |
|
|
40448
|
+
* | | C | |
|
|
40449
|
+
* | +---+ |
|
|
40450
|
+
* | +-----------+ |
|
|
40451
|
+
* | | D | |
|
|
40452
|
+
* | +-----------+ |
|
|
40453
|
+
* | |
|
|
40454
|
+
* +--------------------------+
|
|
40455
|
+
*
|
|
40456
|
+
* It groups together zones that are spatially close into a minimum bounding box.
|
|
40457
|
+
* For example, A and B are grouped together in rectangle R1, and C and D are grouped
|
|
40458
|
+
* in R2.
|
|
40459
|
+
*
|
|
40460
|
+
* R0
|
|
40461
|
+
* +--------------------------+
|
|
40462
|
+
* | R1 |
|
|
40463
|
+
* | +-----------------+ |
|
|
40464
|
+
* | | A | | B | |
|
|
40465
|
+
* | +-----------------+ |
|
|
40466
|
+
* | |
|
|
40467
|
+
* | R2 |
|
|
40468
|
+
* | +---+---+---+ |
|
|
40469
|
+
* | | | C | | |
|
|
40470
|
+
* | | +---+ | |
|
|
40471
|
+
* | +-----------+ |
|
|
40472
|
+
* | | D | |
|
|
40473
|
+
* | +-----------+ |
|
|
40474
|
+
* | |
|
|
40475
|
+
* +--------------------------+
|
|
40476
|
+
*
|
|
40477
|
+
* The tree would look like this:
|
|
40478
|
+
* R0
|
|
40479
|
+
* / \
|
|
40480
|
+
* / \
|
|
40481
|
+
* R1 R2
|
|
40482
|
+
* | |
|
|
40483
|
+
* A,B C,D
|
|
40484
|
+
|
|
40485
|
+
* Choosing how to group the zones is crucial for the performance of the tree.
|
|
40486
|
+
* Key considerations include avoiding excessive empty space coverage and minimizing overlap
|
|
40487
|
+
* to reduce the number of subtrees processed during searches.
|
|
40488
|
+
*
|
|
40489
|
+
* Various heuristics exist for determining the optimal grouping strategy, such as "least enlargement"
|
|
40490
|
+
* which prioritizes grouping nodes resulting in the smallest increase in bounding box size. In cases where
|
|
40491
|
+
* the choice cannot be made based on this criterion due to the same enlargement for different groupings,
|
|
40492
|
+
* we then evaluate "least area," aiming to minimize the overall area of bounding boxes.
|
|
40493
|
+
*
|
|
40494
|
+
* This implementation is tailored for spreadsheet use, indexing objects associated
|
|
40495
|
+
* with a zone and a sheet.
|
|
40496
|
+
*
|
|
40497
|
+
* It uses the RBush library under the hood. One 2D RBush R-tree per sheet.
|
|
40498
|
+
* @see https://github.com/mourner/rbush
|
|
40499
|
+
*/
|
|
40500
|
+
class SpreadsheetRTree {
|
|
40501
|
+
/**
|
|
40502
|
+
* One 2D R-tree per sheet
|
|
40503
|
+
*/
|
|
40504
|
+
rTrees = {};
|
|
40505
|
+
/**
|
|
40506
|
+
* Bulk-inserts the given items into the tree. Bulk insertion is usually ~2-3 times
|
|
40507
|
+
* faster than inserting items one by one. After bulk loading (bulk insertion into
|
|
40508
|
+
* an empty tree), subsequent query performance is also ~20-30% better.
|
|
40509
|
+
*/
|
|
40510
|
+
constructor(items = []) {
|
|
40511
|
+
const rangesPerSheet = {};
|
|
40512
|
+
for (const item of items) {
|
|
40513
|
+
const sheetId = item.boundingBox.sheetId;
|
|
40514
|
+
if (!rangesPerSheet[sheetId]) {
|
|
40515
|
+
rangesPerSheet[sheetId] = [];
|
|
40516
|
+
}
|
|
40517
|
+
rangesPerSheet[sheetId].push(item);
|
|
40518
|
+
}
|
|
40519
|
+
for (const sheetId in rangesPerSheet) {
|
|
40520
|
+
this.rTrees[sheetId] = new ZoneRBush();
|
|
40521
|
+
this.rTrees[sheetId].load(rangesPerSheet[sheetId]); // bulk-insert
|
|
40522
|
+
}
|
|
40523
|
+
}
|
|
40524
|
+
insert(item) {
|
|
40525
|
+
const sheetId = item.boundingBox.sheetId;
|
|
40526
|
+
if (!this.rTrees[sheetId]) {
|
|
40527
|
+
this.rTrees[sheetId] = new ZoneRBush();
|
|
40528
|
+
}
|
|
40529
|
+
this.rTrees[sheetId].insert(item);
|
|
40530
|
+
}
|
|
40531
|
+
search({ zone, sheetId }) {
|
|
40532
|
+
if (!this.rTrees[sheetId]) {
|
|
40533
|
+
return [];
|
|
40534
|
+
}
|
|
40535
|
+
return this.rTrees[sheetId].search({
|
|
40536
|
+
minX: zone.left,
|
|
40537
|
+
minY: zone.top,
|
|
40538
|
+
maxX: zone.right,
|
|
40539
|
+
maxY: zone.bottom,
|
|
40540
|
+
});
|
|
40541
|
+
}
|
|
40542
|
+
remove(item) {
|
|
40543
|
+
const sheetId = item.boundingBox.sheetId;
|
|
40544
|
+
if (!this.rTrees[sheetId]) {
|
|
40545
|
+
return;
|
|
40546
|
+
}
|
|
40547
|
+
this.rTrees[sheetId].remove(item, deepEquals);
|
|
40548
|
+
}
|
|
40549
|
+
}
|
|
40550
|
+
/**
|
|
40551
|
+
* RBush extension to use zones as bounding boxes
|
|
40552
|
+
*/
|
|
40553
|
+
class ZoneRBush extends RBush {
|
|
40554
|
+
toBBox({ boundingBox }) {
|
|
40555
|
+
const zone = boundingBox.zone;
|
|
40556
|
+
return {
|
|
40557
|
+
minX: zone.left,
|
|
40558
|
+
minY: zone.top,
|
|
40559
|
+
maxX: zone.right,
|
|
40560
|
+
maxY: zone.bottom,
|
|
40561
|
+
};
|
|
40562
|
+
}
|
|
40563
|
+
compareMinX(a, b) {
|
|
40564
|
+
return a.boundingBox.zone.left - b.boundingBox.zone.left;
|
|
40565
|
+
}
|
|
40566
|
+
compareMinY(a, b) {
|
|
40567
|
+
return a.boundingBox.zone.top - b.boundingBox.zone.top;
|
|
40568
|
+
}
|
|
40569
|
+
}
|
|
40570
|
+
|
|
39840
40571
|
/**
|
|
39841
|
-
*
|
|
40572
|
+
* Implementation of a dependency Graph.
|
|
39842
40573
|
* The graph is used to evaluate the cells in the correct
|
|
39843
40574
|
* order, and should be updated each time a cell's content is modified
|
|
39844
40575
|
*
|
|
40576
|
+
* It uses an R-Tree data structure to efficiently find dependent cells.
|
|
39845
40577
|
*/
|
|
39846
40578
|
class FormulaDependencyGraph {
|
|
39847
|
-
|
|
39848
|
-
* Internal structure:
|
|
39849
|
-
* - key: a cell position (encoded as an integer)
|
|
39850
|
-
* - value: a set of cell positions that depends on the key
|
|
39851
|
-
*
|
|
39852
|
-
* Given
|
|
39853
|
-
* - A1:"= B1 + SQRT(B2)"
|
|
39854
|
-
* - C1:"= B1";
|
|
39855
|
-
* - C2:"= C1"
|
|
39856
|
-
*
|
|
39857
|
-
* we will have something like:
|
|
39858
|
-
* - B1 ---> (A1, C1) meaning A1 and C1 depends on B1
|
|
39859
|
-
* - B2 ---> (A1) meaning A1 depends on B2
|
|
39860
|
-
* - C1 ---> (C2) meaning C2 depends on C1
|
|
39861
|
-
*/
|
|
39862
|
-
inverseDependencies = new Map();
|
|
40579
|
+
encoder;
|
|
39863
40580
|
dependencies = new Map();
|
|
40581
|
+
rTree;
|
|
40582
|
+
constructor(encoder, data = []) {
|
|
40583
|
+
this.encoder = encoder;
|
|
40584
|
+
this.rTree = new SpreadsheetRTree(data);
|
|
40585
|
+
}
|
|
39864
40586
|
removeAllDependencies(formulaPositionId) {
|
|
39865
|
-
const
|
|
39866
|
-
if (!
|
|
40587
|
+
const ranges = this.dependencies.get(formulaPositionId);
|
|
40588
|
+
if (!ranges) {
|
|
39867
40589
|
return;
|
|
39868
40590
|
}
|
|
39869
|
-
for (const
|
|
39870
|
-
this.
|
|
40591
|
+
for (const range of ranges) {
|
|
40592
|
+
this.rTree.remove(range);
|
|
39871
40593
|
}
|
|
39872
40594
|
this.dependencies.delete(formulaPositionId);
|
|
39873
40595
|
}
|
|
39874
40596
|
addDependencies(formulaPositionId, dependencies) {
|
|
39875
|
-
|
|
39876
|
-
|
|
39877
|
-
|
|
39878
|
-
|
|
39879
|
-
|
|
39880
|
-
|
|
39881
|
-
|
|
39882
|
-
|
|
40597
|
+
const rTreeItems = dependencies.map(({ sheetId, zone }) => ({
|
|
40598
|
+
data: formulaPositionId,
|
|
40599
|
+
boundingBox: {
|
|
40600
|
+
zone,
|
|
40601
|
+
sheetId,
|
|
40602
|
+
},
|
|
40603
|
+
}));
|
|
40604
|
+
for (const item of rTreeItems) {
|
|
40605
|
+
this.rTree.insert(item);
|
|
39883
40606
|
}
|
|
39884
40607
|
const existingDependencies = this.dependencies.get(formulaPositionId);
|
|
39885
40608
|
if (existingDependencies) {
|
|
39886
|
-
existingDependencies.push(...
|
|
40609
|
+
existingDependencies.push(...rTreeItems);
|
|
39887
40610
|
}
|
|
39888
40611
|
else {
|
|
39889
|
-
this.dependencies.set(formulaPositionId,
|
|
40612
|
+
this.dependencies.set(formulaPositionId, rTreeItems);
|
|
39890
40613
|
}
|
|
39891
40614
|
}
|
|
39892
40615
|
/**
|
|
@@ -39894,20 +40617,20 @@
|
|
|
39894
40617
|
* in the correct order they should be evaluated.
|
|
39895
40618
|
* This is called a topological ordering (excluding cycles)
|
|
39896
40619
|
*/
|
|
39897
|
-
getCellsDependingOn(
|
|
40620
|
+
getCellsDependingOn(ranges) {
|
|
39898
40621
|
const visited = new JetSet();
|
|
39899
|
-
const queue = Array.from(
|
|
40622
|
+
const queue = Array.from(ranges).reverse();
|
|
39900
40623
|
while (queue.length > 0) {
|
|
39901
|
-
const
|
|
39902
|
-
visited.add(
|
|
39903
|
-
const
|
|
39904
|
-
for (const
|
|
39905
|
-
if (!visited.has(
|
|
39906
|
-
queue.push(
|
|
40624
|
+
const range = queue.pop();
|
|
40625
|
+
visited.add(...this.encoder.encodeBoundingBox(range));
|
|
40626
|
+
const impactedPositionIds = this.rTree.search(range).map((dep) => dep.data);
|
|
40627
|
+
for (const positionId of impactedPositionIds) {
|
|
40628
|
+
if (!visited.has(positionId)) {
|
|
40629
|
+
queue.push(this.encoder.decodeToBoundingBox(positionId));
|
|
39907
40630
|
}
|
|
39908
40631
|
}
|
|
39909
40632
|
}
|
|
39910
|
-
visited.delete(...
|
|
40633
|
+
visited.delete(...ranges.flatMap((r) => this.encoder.encodeBoundingBox(r)));
|
|
39911
40634
|
return visited;
|
|
39912
40635
|
}
|
|
39913
40636
|
}
|
|
@@ -39995,9 +40718,9 @@
|
|
|
39995
40718
|
context;
|
|
39996
40719
|
getters;
|
|
39997
40720
|
compilationParams;
|
|
39998
|
-
|
|
40721
|
+
encoder = new PositionBitsEncoder();
|
|
39999
40722
|
evaluatedCells = new Map();
|
|
40000
|
-
formulaDependencies = lazy(new FormulaDependencyGraph());
|
|
40723
|
+
formulaDependencies = lazy(new FormulaDependencyGraph(this.encoder));
|
|
40001
40724
|
blockedArrayFormulas = new Set();
|
|
40002
40725
|
spreadingRelations = new SpreadingRelation();
|
|
40003
40726
|
constructor(context, getters) {
|
|
@@ -40006,23 +40729,23 @@
|
|
|
40006
40729
|
this.compilationParams = buildCompilationParameters(this.context, this.getters, this.computeAndSave.bind(this));
|
|
40007
40730
|
}
|
|
40008
40731
|
getEvaluatedCell(position) {
|
|
40009
|
-
return (this.evaluatedCells.get(this.
|
|
40732
|
+
return (this.evaluatedCells.get(this.encoder.encode(position)) ||
|
|
40010
40733
|
createEvaluatedCell("", { locale: this.getters.getLocale() }));
|
|
40011
40734
|
}
|
|
40012
40735
|
getSpreadPositionsOf(position) {
|
|
40013
|
-
const positionId = this.
|
|
40736
|
+
const positionId = this.encoder.encode(position);
|
|
40014
40737
|
if (!this.spreadingRelations.isArrayFormula(positionId)) {
|
|
40015
40738
|
return [];
|
|
40016
40739
|
}
|
|
40017
|
-
return Array.from(this.spreadingRelations.getArrayResultPositionIds(positionId)).map(this.
|
|
40740
|
+
return Array.from(this.spreadingRelations.getArrayResultPositionIds(positionId)).map((positionId) => this.encoder.decode(positionId));
|
|
40018
40741
|
}
|
|
40019
40742
|
getArrayFormulaSpreadingOn(position) {
|
|
40020
|
-
const positionId = this.
|
|
40743
|
+
const positionId = this.encoder.encode(position);
|
|
40021
40744
|
const formulaPosition = this.getArrayFormulaSpreadingOnId(positionId);
|
|
40022
|
-
return formulaPosition !== undefined ? this.
|
|
40745
|
+
return formulaPosition !== undefined ? this.encoder.decode(formulaPosition) : undefined;
|
|
40023
40746
|
}
|
|
40024
40747
|
getEvaluatedPositions() {
|
|
40025
|
-
return [...this.evaluatedCells.keys()].map(this.
|
|
40748
|
+
return [...this.evaluatedCells.keys()].map((p) => this.encoder.decode(p));
|
|
40026
40749
|
}
|
|
40027
40750
|
getArrayFormulaSpreadingOnId(positionId) {
|
|
40028
40751
|
if (!this.spreadingRelations.hasArrayFormulaResult(positionId)) {
|
|
@@ -40032,7 +40755,7 @@
|
|
|
40032
40755
|
return Array.from(arrayFormulas).find((positionId) => !this.blockedArrayFormulas.has(positionId));
|
|
40033
40756
|
}
|
|
40034
40757
|
updateDependencies(position) {
|
|
40035
|
-
const positionId = this.
|
|
40758
|
+
const positionId = this.encoder.encode(position);
|
|
40036
40759
|
this.formulaDependencies().removeAllDependencies(positionId);
|
|
40037
40760
|
const dependencies = this.getDirectDependencies(positionId);
|
|
40038
40761
|
this.formulaDependencies().addDependencies(positionId, dependencies);
|
|
@@ -40042,12 +40765,12 @@
|
|
|
40042
40765
|
this.compilationParams = buildCompilationParameters(this.context, this.getters, this.computeAndSave.bind(this));
|
|
40043
40766
|
}
|
|
40044
40767
|
evaluateCells(positions) {
|
|
40045
|
-
const cells = positions.map(this.
|
|
40768
|
+
const cells = positions.map((p) => this.encoder.encode(p));
|
|
40046
40769
|
const cellsToCompute = new JetSet(cells);
|
|
40047
|
-
const
|
|
40770
|
+
const arrayFormulasPositionIds = this.getArrayFormulasImpactedByChangesOf(cells);
|
|
40048
40771
|
cellsToCompute.add(...this.getCellsDependingOn(cells));
|
|
40049
|
-
cellsToCompute.add(...
|
|
40050
|
-
cellsToCompute.add(...this.getCellsDependingOn(
|
|
40772
|
+
cellsToCompute.add(...arrayFormulasPositionIds);
|
|
40773
|
+
cellsToCompute.add(...this.getCellsDependingOn(arrayFormulasPositionIds));
|
|
40051
40774
|
this.evaluate(cellsToCompute);
|
|
40052
40775
|
}
|
|
40053
40776
|
getArrayFormulasImpactedByChangesOf(positionIds) {
|
|
@@ -40070,12 +40793,14 @@
|
|
|
40070
40793
|
this.blockedArrayFormulas = new Set();
|
|
40071
40794
|
this.spreadingRelations = new SpreadingRelation();
|
|
40072
40795
|
this.formulaDependencies = lazy(() => {
|
|
40073
|
-
const
|
|
40074
|
-
|
|
40075
|
-
|
|
40076
|
-
|
|
40077
|
-
|
|
40078
|
-
|
|
40796
|
+
const dependencies = [...this.getAllCells()].flatMap((positionId) => this.getDirectDependencies(positionId).map((range) => ({
|
|
40797
|
+
data: positionId,
|
|
40798
|
+
boundingBox: {
|
|
40799
|
+
zone: range.zone,
|
|
40800
|
+
sheetId: range.sheetId,
|
|
40801
|
+
},
|
|
40802
|
+
})));
|
|
40803
|
+
return new FormulaDependencyGraph(this.encoder, dependencies);
|
|
40079
40804
|
});
|
|
40080
40805
|
}
|
|
40081
40806
|
evaluateAllCells() {
|
|
@@ -40097,7 +40822,7 @@
|
|
|
40097
40822
|
for (const sheetId of this.getters.getSheetIds()) {
|
|
40098
40823
|
const cellIds = this.getters.getCells(sheetId);
|
|
40099
40824
|
for (const cellId in cellIds) {
|
|
40100
|
-
positionIds.add(this.
|
|
40825
|
+
positionIds.add(this.encoder.encode(this.getters.getCellPosition(cellId)));
|
|
40101
40826
|
}
|
|
40102
40827
|
}
|
|
40103
40828
|
return positionIds;
|
|
@@ -40164,7 +40889,7 @@
|
|
|
40164
40889
|
}
|
|
40165
40890
|
}
|
|
40166
40891
|
computeAndSave(position) {
|
|
40167
|
-
const positionId = this.
|
|
40892
|
+
const positionId = this.encoder.encode(position);
|
|
40168
40893
|
const evaluatedCell = this.computeCell(positionId);
|
|
40169
40894
|
if (!this.evaluatedCells.has(positionId)) {
|
|
40170
40895
|
this.setEvaluatedCell(positionId, evaluatedCell);
|
|
@@ -40224,15 +40949,15 @@
|
|
|
40224
40949
|
throw new Error(_t("Result couldn't be automatically expanded. Please insert more columns and rows."));
|
|
40225
40950
|
}
|
|
40226
40951
|
updateSpreadRelation({ sheetId, col, row, }) {
|
|
40227
|
-
const arrayFormulaPositionId = this.
|
|
40952
|
+
const arrayFormulaPositionId = this.encoder.encode({ sheetId, col, row });
|
|
40228
40953
|
return (i, j) => {
|
|
40229
40954
|
const position = { sheetId, col: i + col, row: j + row };
|
|
40230
|
-
const resultPositionId = this.
|
|
40955
|
+
const resultPositionId = this.encoder.encode(position);
|
|
40231
40956
|
this.spreadingRelations.addRelation({ resultPositionId, arrayFormulaPositionId });
|
|
40232
40957
|
};
|
|
40233
40958
|
}
|
|
40234
40959
|
checkCollision({ sheetId, col, row }) {
|
|
40235
|
-
const formulaPositionId = this.
|
|
40960
|
+
const formulaPositionId = this.encoder.encode({ sheetId, col, row });
|
|
40236
40961
|
return (i, j) => {
|
|
40237
40962
|
const position = { sheetId: sheetId, col: i + col, row: j + row };
|
|
40238
40963
|
const rawCell = this.getters.getCell(position);
|
|
@@ -40253,7 +40978,7 @@
|
|
|
40253
40978
|
format: format || matrixResult[i][j]?.format,
|
|
40254
40979
|
locale: this.getters.getLocale(),
|
|
40255
40980
|
});
|
|
40256
|
-
const positionId = this.
|
|
40981
|
+
const positionId = this.encoder.encode(position);
|
|
40257
40982
|
this.setEvaluatedCell(positionId, evaluatedCell);
|
|
40258
40983
|
// check if formula dependencies present in the spread zone
|
|
40259
40984
|
// if so, they need to be recomputed
|
|
@@ -40285,29 +41010,17 @@
|
|
|
40285
41010
|
if (!cell?.isFormula) {
|
|
40286
41011
|
return [];
|
|
40287
41012
|
}
|
|
40288
|
-
|
|
40289
|
-
for (const range of cell.compiledFormula.dependencies) {
|
|
40290
|
-
if (range.invalidSheetName || range.invalidXc) {
|
|
40291
|
-
continue;
|
|
40292
|
-
}
|
|
40293
|
-
const sheetId = range.sheetId;
|
|
40294
|
-
forEachPositionsInZone(range.zone, (col, row) => {
|
|
40295
|
-
dependencies.push(this.encodePosition({ sheetId, col, row }));
|
|
40296
|
-
});
|
|
40297
|
-
}
|
|
40298
|
-
return dependencies;
|
|
41013
|
+
return cell.compiledFormula.dependencies;
|
|
40299
41014
|
}
|
|
40300
41015
|
getCellsDependingOn(positionIds) {
|
|
40301
|
-
|
|
41016
|
+
const ranges = [];
|
|
41017
|
+
for (const positionId of positionIds) {
|
|
41018
|
+
ranges.push(this.encoder.decodeToBoundingBox(positionId));
|
|
41019
|
+
}
|
|
41020
|
+
return this.formulaDependencies().getCellsDependingOn(ranges);
|
|
40302
41021
|
}
|
|
40303
41022
|
getCell(positionId) {
|
|
40304
|
-
return this.getters.getCell(this.
|
|
40305
|
-
}
|
|
40306
|
-
encodePosition(position) {
|
|
40307
|
-
return this.positionEncoder.encode(position);
|
|
40308
|
-
}
|
|
40309
|
-
decodePosition(positionId) {
|
|
40310
|
-
return this.positionEncoder.decode(positionId);
|
|
41023
|
+
return this.getters.getCell(this.encoder.decode(positionId));
|
|
40311
41024
|
}
|
|
40312
41025
|
}
|
|
40313
41026
|
function forEachSpreadPositionInMatrix(nbColumns, nbRows, callback) {
|
|
@@ -40363,6 +41076,13 @@
|
|
|
40363
41076
|
encode({ sheetId, col, row }) {
|
|
40364
41077
|
return (this.encodeSheet(sheetId) << 42n) | (BigInt(col) << 21n) | BigInt(row);
|
|
40365
41078
|
}
|
|
41079
|
+
encodeBoundingBox({ sheetId, zone }) {
|
|
41080
|
+
const positions = [];
|
|
41081
|
+
forEachPositionsInZone(zone, (col, row) => {
|
|
41082
|
+
positions.push(this.encode({ sheetId, col, row }));
|
|
41083
|
+
});
|
|
41084
|
+
return positions;
|
|
41085
|
+
}
|
|
40366
41086
|
decode(id) {
|
|
40367
41087
|
// keep only the last 21 bits by AND-ing the bit sequence with 21 ones
|
|
40368
41088
|
const row = Number(id & 2097151n);
|
|
@@ -40370,6 +41090,10 @@
|
|
|
40370
41090
|
const sheetId = this.decodeSheet(id >> 42n);
|
|
40371
41091
|
return { sheetId, col, row };
|
|
40372
41092
|
}
|
|
41093
|
+
decodeToBoundingBox(id) {
|
|
41094
|
+
const { sheetId, col, row } = this.decode(id);
|
|
41095
|
+
return { sheetId, zone: { left: col, top: row, right: col, bottom: row } };
|
|
41096
|
+
}
|
|
40373
41097
|
encodeSheet(sheetId) {
|
|
40374
41098
|
const sheetKey = this.sheetMapping[sheetId];
|
|
40375
41099
|
if (sheetKey === undefined) {
|
|
@@ -40540,7 +41264,12 @@
|
|
|
40540
41264
|
// Getters
|
|
40541
41265
|
// ---------------------------------------------------------------------------
|
|
40542
41266
|
evaluateFormula(sheetId, formulaString) {
|
|
40543
|
-
|
|
41267
|
+
try {
|
|
41268
|
+
return this.evaluator.evaluateFormula(sheetId, formulaString);
|
|
41269
|
+
}
|
|
41270
|
+
catch (error) {
|
|
41271
|
+
return error instanceof EvaluationError ? error.errorType : CellErrorType.GenericError;
|
|
41272
|
+
}
|
|
40544
41273
|
}
|
|
40545
41274
|
/**
|
|
40546
41275
|
* Return the value of each cell in the range as they are displayed in the grid.
|
|
@@ -40809,7 +41538,7 @@
|
|
|
40809
41538
|
}
|
|
40810
41539
|
|
|
40811
41540
|
class EvaluationChartPlugin extends UIPlugin {
|
|
40812
|
-
static getters = ["getChartRuntime", "
|
|
41541
|
+
static getters = ["getChartRuntime", "getStyleOfSingleCellChart"];
|
|
40813
41542
|
charts = {};
|
|
40814
41543
|
createRuntimeChart = chartRuntimeFactory(this.getters);
|
|
40815
41544
|
handle(cmd) {
|
|
@@ -40847,25 +41576,26 @@
|
|
|
40847
41576
|
return this.charts[figureId];
|
|
40848
41577
|
}
|
|
40849
41578
|
/**
|
|
40850
|
-
* Get the background
|
|
40851
|
-
* of the chart. In order of priority, it will return :
|
|
40852
|
-
*
|
|
40853
|
-
* - the chart background color if one is defined
|
|
40854
|
-
* - the fill color of the cell if one is defined
|
|
40855
|
-
* - the fill color of the cell from conditional formats if one is defined
|
|
40856
|
-
* - the default chart color if no other color is defined
|
|
41579
|
+
* Get the background and textColor of a chart based on the color of the first cell of the main range of the chart.
|
|
40857
41580
|
*/
|
|
40858
|
-
|
|
41581
|
+
getStyleOfSingleCellChart(chartBackground, mainRange) {
|
|
40859
41582
|
if (chartBackground)
|
|
40860
|
-
return chartBackground;
|
|
41583
|
+
return { background: chartBackground, fontColor: chartFontColor(chartBackground) };
|
|
40861
41584
|
if (!mainRange) {
|
|
40862
|
-
return
|
|
41585
|
+
return {
|
|
41586
|
+
background: BACKGROUND_CHART_COLOR,
|
|
41587
|
+
fontColor: chartFontColor(BACKGROUND_CHART_COLOR),
|
|
41588
|
+
};
|
|
40863
41589
|
}
|
|
40864
41590
|
const col = mainRange.zone.left;
|
|
40865
41591
|
const row = mainRange.zone.top;
|
|
40866
41592
|
const sheetId = mainRange.sheetId;
|
|
40867
41593
|
const style = this.getters.getCellComputedStyle({ sheetId, col, row });
|
|
40868
|
-
|
|
41594
|
+
const background = style.fillColor || BACKGROUND_CHART_COLOR;
|
|
41595
|
+
return {
|
|
41596
|
+
background,
|
|
41597
|
+
fontColor: style.textColor || chartFontColor(background),
|
|
41598
|
+
};
|
|
40869
41599
|
}
|
|
40870
41600
|
exportForExcel(data) {
|
|
40871
41601
|
for (const sheet of data.sheets) {
|
|
@@ -40976,45 +41706,40 @@
|
|
|
40976
41706
|
getComputedStyles(sheetId) {
|
|
40977
41707
|
const computedStyle = {};
|
|
40978
41708
|
for (let cf of this.getters.getConditionalFormats(sheetId).reverse()) {
|
|
40979
|
-
|
|
40980
|
-
|
|
40981
|
-
|
|
40982
|
-
|
|
40983
|
-
|
|
40984
|
-
|
|
40985
|
-
|
|
40986
|
-
|
|
40987
|
-
|
|
40988
|
-
|
|
40989
|
-
|
|
40990
|
-
for (let
|
|
40991
|
-
|
|
40992
|
-
|
|
40993
|
-
|
|
40994
|
-
const
|
|
40995
|
-
|
|
40996
|
-
|
|
40997
|
-
|
|
40998
|
-
|
|
40999
|
-
|
|
41000
|
-
});
|
|
41001
|
-
}
|
|
41002
|
-
return value;
|
|
41003
|
-
});
|
|
41004
|
-
if (predicate && predicate(target, { ...cf.rule, values })) {
|
|
41005
|
-
if (!computedStyle[col])
|
|
41006
|
-
computedStyle[col] = [];
|
|
41007
|
-
// we must combine all the properties of all the CF rules applied to the given cell
|
|
41008
|
-
computedStyle[col][row] = Object.assign(computedStyle[col]?.[row] || {}, cf.rule.style);
|
|
41709
|
+
switch (cf.rule.type) {
|
|
41710
|
+
case "ColorScaleRule":
|
|
41711
|
+
for (let range of cf.ranges) {
|
|
41712
|
+
this.applyColorScale(sheetId, range, cf.rule, computedStyle);
|
|
41713
|
+
}
|
|
41714
|
+
break;
|
|
41715
|
+
case "CellIsRule":
|
|
41716
|
+
const formulas = cf.rule.values.map((value) => value.startsWith("=") ? compile(value) : undefined);
|
|
41717
|
+
for (let ref of cf.ranges) {
|
|
41718
|
+
const zone = this.getters.getRangeFromSheetXC(sheetId, ref).zone;
|
|
41719
|
+
for (let row = zone.top; row <= zone.bottom; row++) {
|
|
41720
|
+
for (let col = zone.left; col <= zone.right; col++) {
|
|
41721
|
+
const predicate = this.rulePredicate[cf.rule.type];
|
|
41722
|
+
const target = { sheetId, col, row };
|
|
41723
|
+
const values = cf.rule.values.map((value, i) => {
|
|
41724
|
+
const compiledFormula = formulas[i];
|
|
41725
|
+
if (compiledFormula) {
|
|
41726
|
+
return this.getters.getTranslatedCellFormula(sheetId, col - zone.left, row - zone.top, {
|
|
41727
|
+
...compiledFormula,
|
|
41728
|
+
dependencies: compiledFormula.dependencies.map((d) => this.getters.getRangeFromSheetXC(sheetId, d)),
|
|
41729
|
+
});
|
|
41009
41730
|
}
|
|
41731
|
+
return value;
|
|
41732
|
+
});
|
|
41733
|
+
if (predicate && predicate(target, { ...cf.rule, values })) {
|
|
41734
|
+
if (!computedStyle[col])
|
|
41735
|
+
computedStyle[col] = [];
|
|
41736
|
+
// we must combine all the properties of all the CF rules applied to the given cell
|
|
41737
|
+
computedStyle[col][row] = Object.assign(computedStyle[col]?.[row] || {}, cf.rule.style);
|
|
41010
41738
|
}
|
|
41011
41739
|
}
|
|
41012
41740
|
}
|
|
41013
|
-
|
|
41014
|
-
|
|
41015
|
-
}
|
|
41016
|
-
catch (_) {
|
|
41017
|
-
// we don't care about the errors within the evaluation of a rule
|
|
41741
|
+
}
|
|
41742
|
+
break;
|
|
41018
41743
|
}
|
|
41019
41744
|
}
|
|
41020
41745
|
return computedStyle;
|
|
@@ -41785,19 +42510,21 @@
|
|
|
41785
42510
|
let col = zone.left;
|
|
41786
42511
|
let row = zone.bottom;
|
|
41787
42512
|
if (col > 0) {
|
|
41788
|
-
let
|
|
41789
|
-
while (
|
|
42513
|
+
let leftPosition = { sheetId, col: col - 1, row };
|
|
42514
|
+
while (this.getters.getCorrespondingFormulaCell(leftPosition) ||
|
|
42515
|
+
this.getters.getCell(leftPosition)?.content) {
|
|
41790
42516
|
row += 1;
|
|
41791
|
-
|
|
42517
|
+
leftPosition = { sheetId, col: col - 1, row };
|
|
41792
42518
|
}
|
|
41793
42519
|
}
|
|
41794
42520
|
if (row === zone.bottom) {
|
|
41795
42521
|
col = zone.right;
|
|
41796
42522
|
if (col <= this.getters.getNumberCols(sheetId)) {
|
|
41797
|
-
let
|
|
41798
|
-
while (
|
|
42523
|
+
let rightPosition = { sheetId, col: col + 1, row };
|
|
42524
|
+
while (this.getters.getCorrespondingFormulaCell(rightPosition) ||
|
|
42525
|
+
this.getters.getCell(rightPosition)?.content) {
|
|
41799
42526
|
row += 1;
|
|
41800
|
-
|
|
42527
|
+
rightPosition = { sheetId, col: col + 1, row };
|
|
41801
42528
|
}
|
|
41802
42529
|
}
|
|
41803
42530
|
}
|
|
@@ -42789,6 +43516,7 @@
|
|
|
42789
43516
|
isReplayingInitialRevisions = false;
|
|
42790
43517
|
processedRevisions = new Set();
|
|
42791
43518
|
uuidGenerator = new UuidGenerator();
|
|
43519
|
+
lastLocalOperation;
|
|
42792
43520
|
/**
|
|
42793
43521
|
* Manages the collaboration between multiple users on the same spreadsheet.
|
|
42794
43522
|
* It can forward local state changes to other users to ensure they all eventually
|
|
@@ -42821,6 +43549,11 @@
|
|
|
42821
43549
|
return;
|
|
42822
43550
|
const revision = new Revision(this.uuidGenerator.uuidv4(), this.clientId, commands, rootCommand, changes, Date.now());
|
|
42823
43551
|
this.revisions.append(revision.id, revision);
|
|
43552
|
+
// REQUEST_REDO just repeats the last operation, the
|
|
43553
|
+
// last operation is still the same and should not change.
|
|
43554
|
+
if (rootCommand.type !== "REQUEST_REDO") {
|
|
43555
|
+
this.lastLocalOperation = revision;
|
|
43556
|
+
}
|
|
42824
43557
|
this.trigger("new-local-state-update", { id: revision.id });
|
|
42825
43558
|
this.sendUpdateMessage({
|
|
42826
43559
|
type: "REMOTE_REVISION",
|
|
@@ -42917,19 +43650,10 @@
|
|
|
42917
43650
|
return this.pendingMessages.length === 0;
|
|
42918
43651
|
}
|
|
42919
43652
|
/**
|
|
42920
|
-
* Get the last local revision
|
|
43653
|
+
* Get the last local revision
|
|
42921
43654
|
* */
|
|
42922
|
-
getLastLocalNonEmptyRevision(
|
|
42923
|
-
|
|
42924
|
-
for (const rev of revisions) {
|
|
42925
|
-
if (rev.rootCommand === "SNAPSHOT")
|
|
42926
|
-
return undefined;
|
|
42927
|
-
if (!rev.rootCommand || rev.rootCommand === "REMOTE")
|
|
42928
|
-
continue;
|
|
42929
|
-
if (!ignoredRootCommands.includes(rev.rootCommand?.type) && rev.commands.length)
|
|
42930
|
-
return rev;
|
|
42931
|
-
}
|
|
42932
|
-
return undefined;
|
|
43655
|
+
getLastLocalNonEmptyRevision() {
|
|
43656
|
+
return this.lastLocalOperation;
|
|
42933
43657
|
}
|
|
42934
43658
|
_move(position) {
|
|
42935
43659
|
// this method is debounced and might be called after the client
|
|
@@ -42989,7 +43713,7 @@
|
|
|
42989
43713
|
break;
|
|
42990
43714
|
case "REMOTE_REVISION":
|
|
42991
43715
|
const { clientId, commands, timestamp } = message;
|
|
42992
|
-
const revision = new Revision(message.nextRevisionId, clientId, commands,
|
|
43716
|
+
const revision = new Revision(message.nextRevisionId, clientId, commands, undefined, undefined, timestamp);
|
|
42993
43717
|
if (revision.clientId !== this.clientId) {
|
|
42994
43718
|
this.revisions.insert(revision.id, revision, message.serverRevisionId);
|
|
42995
43719
|
const pendingCommands = this.pendingMessages
|
|
@@ -43002,10 +43726,11 @@
|
|
|
43002
43726
|
}
|
|
43003
43727
|
break;
|
|
43004
43728
|
case "SNAPSHOT_CREATED": {
|
|
43005
|
-
const revision = new Revision(message.nextRevisionId, "server", [],
|
|
43729
|
+
const revision = new Revision(message.nextRevisionId, "server", [], undefined, undefined, Date.now());
|
|
43006
43730
|
this.revisions.insert(revision.id, revision, message.serverRevisionId);
|
|
43007
43731
|
this.dropPendingHistoryMessages();
|
|
43008
43732
|
this.trigger("snapshot");
|
|
43733
|
+
this.lastLocalOperation = undefined;
|
|
43009
43734
|
break;
|
|
43010
43735
|
}
|
|
43011
43736
|
}
|
|
@@ -45560,6 +46285,9 @@
|
|
|
45560
46285
|
// Other
|
|
45561
46286
|
// ---------------------------------------------------------------------------
|
|
45562
46287
|
initInput(id, initialRanges, inputHasSingleRange = false) {
|
|
46288
|
+
if (this.inputs[id]) {
|
|
46289
|
+
this.unfocus();
|
|
46290
|
+
}
|
|
45563
46291
|
this.inputs[id] = new SelectionInputPlugin(this.config, initialRanges, inputHasSingleRange);
|
|
45564
46292
|
if (initialRanges.length === 0) {
|
|
45565
46293
|
const input = this.inputs[id];
|
|
@@ -46358,7 +47086,7 @@
|
|
|
46358
47086
|
* Ignore standard undo/redo revisions (that are empty)
|
|
46359
47087
|
*/
|
|
46360
47088
|
getPossibleRevisionToRepeat() {
|
|
46361
|
-
return this.session.getLastLocalNonEmptyRevision(
|
|
47089
|
+
return this.session.getLastLocalNonEmptyRevision();
|
|
46362
47090
|
}
|
|
46363
47091
|
}
|
|
46364
47092
|
|
|
@@ -47143,6 +47871,10 @@
|
|
|
47143
47871
|
return xc.slice(0, indexOfNumber) + "$" + xc.slice(indexOfNumber);
|
|
47144
47872
|
case "colrow":
|
|
47145
47873
|
indexOfNumber = xc.search(/[0-9]/);
|
|
47874
|
+
if (indexOfNumber === -1 || indexOfNumber === 0) {
|
|
47875
|
+
// no row number (eg. A) or no column (eg. 1)
|
|
47876
|
+
return "$" + xc;
|
|
47877
|
+
}
|
|
47146
47878
|
xc = xc.slice(0, indexOfNumber) + "$" + xc.slice(indexOfNumber);
|
|
47147
47879
|
return "$" + xc;
|
|
47148
47880
|
case "none":
|
|
@@ -47913,6 +48645,12 @@
|
|
|
47913
48645
|
break;
|
|
47914
48646
|
case "HIDE_COLUMNS_ROWS":
|
|
47915
48647
|
case "UNHIDE_COLUMNS_ROWS":
|
|
48648
|
+
case "GROUP_HEADERS":
|
|
48649
|
+
case "UNGROUP_HEADERS":
|
|
48650
|
+
case "FOLD_HEADER_GROUP":
|
|
48651
|
+
case "UNFOLD_HEADER_GROUP":
|
|
48652
|
+
case "FOLD_ALL_HEADER_GROUPS":
|
|
48653
|
+
case "UNFOLD_ALL_HEADER_GROUPS":
|
|
47916
48654
|
this.updateHiddenRows();
|
|
47917
48655
|
break;
|
|
47918
48656
|
case "UPDATE_FILTER":
|
|
@@ -48614,7 +49352,7 @@
|
|
|
48614
49352
|
sheetId: cmd.sheetId,
|
|
48615
49353
|
base: cmd.base,
|
|
48616
49354
|
quantity: thickness,
|
|
48617
|
-
position:
|
|
49355
|
+
position: cmd.position,
|
|
48618
49356
|
});
|
|
48619
49357
|
const isCol = cmd.dimension === "COL";
|
|
48620
49358
|
const start = cmd.elements[0];
|
|
@@ -48631,12 +49369,13 @@
|
|
|
48631
49369
|
},
|
|
48632
49370
|
];
|
|
48633
49371
|
const state = new ClipboardCellsState(target, "CUT", this.getters, this.dispatch, this.selection);
|
|
49372
|
+
const base = isBasedBefore ? cmd.base : cmd.base + 1;
|
|
48634
49373
|
const pasteTarget = [
|
|
48635
49374
|
{
|
|
48636
|
-
left: isCol ?
|
|
48637
|
-
right: isCol ?
|
|
48638
|
-
top: !isCol ?
|
|
48639
|
-
bottom: !isCol ?
|
|
49375
|
+
left: isCol ? base : 0,
|
|
49376
|
+
right: isCol ? base + thickness - 1 : this.getters.getNumberCols(cmd.sheetId) - 1,
|
|
49377
|
+
top: !isCol ? base : 0,
|
|
49378
|
+
bottom: !isCol ? base + thickness - 1 : this.getters.getNumberRows(cmd.sheetId) - 1,
|
|
48640
49379
|
},
|
|
48641
49380
|
];
|
|
48642
49381
|
state.paste(pasteTarget, { selectTarget: true });
|
|
@@ -48671,6 +49410,11 @@
|
|
|
48671
49410
|
doesElementsHaveCommonMerges(id, cmd.base - 1, cmd.base)) {
|
|
48672
49411
|
return "WillRemoveExistingMerge" /* CommandResult.WillRemoveExistingMerge */;
|
|
48673
49412
|
}
|
|
49413
|
+
const headers = [cmd.base, ...cmd.elements];
|
|
49414
|
+
const maxHeaderValue = isCol ? this.getters.getNumberCols(id) : this.getters.getNumberRows(id);
|
|
49415
|
+
if (headers.some((h) => h < 0 || h >= maxHeaderValue)) {
|
|
49416
|
+
return "InvalidHeaderIndex" /* CommandResult.InvalidHeaderIndex */;
|
|
49417
|
+
}
|
|
48674
49418
|
return "Success" /* CommandResult.Success */;
|
|
48675
49419
|
}
|
|
48676
49420
|
//-------------------------------------------
|
|
@@ -49716,10 +50460,13 @@
|
|
|
49716
50460
|
}
|
|
49717
50461
|
break;
|
|
49718
50462
|
case "UPDATE_CELL":
|
|
49719
|
-
if ("content" in cmd || "format" in cmd
|
|
50463
|
+
if ("content" in cmd || "format" in cmd) {
|
|
49720
50464
|
this.headerPositions = {};
|
|
49721
50465
|
this.isDirty = true;
|
|
49722
50466
|
}
|
|
50467
|
+
else {
|
|
50468
|
+
this.headerPositions[cmd.sheetId] = this.computeHeaderPositionsOfSheet(cmd.sheetId);
|
|
50469
|
+
}
|
|
49723
50470
|
break;
|
|
49724
50471
|
case "UPDATE_FILTER":
|
|
49725
50472
|
case "REMOVE_FILTER_TABLE":
|
|
@@ -51876,6 +52623,8 @@
|
|
|
51876
52623
|
*:before,
|
|
51877
52624
|
*:after {
|
|
51878
52625
|
box-sizing: content-box;
|
|
52626
|
+
/** rtl not supported ATM */
|
|
52627
|
+
direction: ltr;
|
|
51879
52628
|
}
|
|
51880
52629
|
.o-separator {
|
|
51881
52630
|
border-bottom: ${MENU_SEPARATOR_BORDER_WIDTH}px solid ${SEPARATOR_COLOR};
|
|
@@ -52153,7 +52902,7 @@
|
|
|
52153
52902
|
}
|
|
52154
52903
|
onKeydown(ev) {
|
|
52155
52904
|
let keyDownString = "";
|
|
52156
|
-
if (ev
|
|
52905
|
+
if (isCtrlKey(ev)) {
|
|
52157
52906
|
keyDownString += "CTRL+";
|
|
52158
52907
|
}
|
|
52159
52908
|
keyDownString += ev.key.toUpperCase();
|
|
@@ -55865,6 +56614,7 @@
|
|
|
55865
56614
|
colorToRGBA,
|
|
55866
56615
|
positionToZone,
|
|
55867
56616
|
isDefined: isDefined$1,
|
|
56617
|
+
isMatrix,
|
|
55868
56618
|
lazy,
|
|
55869
56619
|
genericRepeat,
|
|
55870
56620
|
createAction,
|
|
@@ -55948,9 +56698,9 @@
|
|
|
55948
56698
|
exports.tokenize = tokenize;
|
|
55949
56699
|
|
|
55950
56700
|
|
|
55951
|
-
__info__.version = "17.0.
|
|
55952
|
-
__info__.date = "
|
|
55953
|
-
__info__.hash = "
|
|
56701
|
+
__info__.version = "17.0.6";
|
|
56702
|
+
__info__.date = "2024-01-04T12:23:10.357Z";
|
|
56703
|
+
__info__.hash = "310ca57";
|
|
55954
56704
|
|
|
55955
56705
|
|
|
55956
56706
|
})(this.o_spreadsheet = this.o_spreadsheet || {}, owl);
|