@odoo/o-spreadsheet 17.0.5 → 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 +877 -167
- package/dist/o-spreadsheet.d.ts +17 -17
- package/dist/o-spreadsheet.esm.js +877 -167
- package/dist/o-spreadsheet.iife.js +877 -167
- package/dist/o-spreadsheet.iife.min.js +206 -206
- package/dist/o_spreadsheet.xml +3 -3
- 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
|
}
|
|
@@ -5287,7 +5280,7 @@
|
|
|
5287
5280
|
};
|
|
5288
5281
|
baselineCell = getters.getEvaluatedCell(baselinePosition);
|
|
5289
5282
|
}
|
|
5290
|
-
const background = getters.
|
|
5283
|
+
const { background, fontColor } = getters.getStyleOfSingleCellChart(chart.background, chart.keyValue);
|
|
5291
5284
|
const locale = getters.getLocale();
|
|
5292
5285
|
return {
|
|
5293
5286
|
title: _t(chart.title),
|
|
@@ -5296,7 +5289,7 @@
|
|
|
5296
5289
|
baselineArrow: getBaselineArrowDirection(baselineCell, keyValueCell, chart.baselineMode),
|
|
5297
5290
|
baselineColor: getBaselineColor(baselineCell, chart.baselineMode, keyValueCell, chart.baselineColorUp, chart.baselineColorDown),
|
|
5298
5291
|
baselineDescr: chart.baselineDescr ? _t(chart.baselineDescr) : "",
|
|
5299
|
-
fontColor
|
|
5292
|
+
fontColor,
|
|
5300
5293
|
background,
|
|
5301
5294
|
baselineStyle: chart.baselineMode !== "percentage" && baseline
|
|
5302
5295
|
? getters.getCellStyle({
|
|
@@ -6591,6 +6584,7 @@
|
|
|
6591
6584
|
},
|
|
6592
6585
|
};
|
|
6593
6586
|
|
|
6587
|
+
const macRegex = /Mac/i;
|
|
6594
6588
|
/**
|
|
6595
6589
|
* Return true if the event was triggered from
|
|
6596
6590
|
* a child element.
|
|
@@ -6630,7 +6624,15 @@
|
|
|
6630
6624
|
return Array.from(document.querySelectorAll(".o-spreadsheet .o-menu"));
|
|
6631
6625
|
}
|
|
6632
6626
|
function isMacOS() {
|
|
6633
|
-
return navigator.userAgent
|
|
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;
|
|
6634
6636
|
}
|
|
6635
6637
|
|
|
6636
6638
|
/**
|
|
@@ -8879,7 +8881,7 @@
|
|
|
8879
8881
|
});
|
|
8880
8882
|
return {
|
|
8881
8883
|
chartJsConfig: config,
|
|
8882
|
-
background: getters.
|
|
8884
|
+
background: getters.getStyleOfSingleCellChart(chart.background, dataRange).background,
|
|
8883
8885
|
};
|
|
8884
8886
|
}
|
|
8885
8887
|
|
|
@@ -8960,7 +8962,7 @@
|
|
|
8960
8962
|
else if (format.includes("h") || format.includes("H")) {
|
|
8961
8963
|
return "hour";
|
|
8962
8964
|
}
|
|
8963
|
-
else if (format.includes("
|
|
8965
|
+
else if (format.includes("d")) {
|
|
8964
8966
|
return "day";
|
|
8965
8967
|
}
|
|
8966
8968
|
else if (format.includes("M")) {
|
|
@@ -21213,10 +21215,10 @@
|
|
|
21213
21215
|
function updateSelectionWithArrowKeys(ev, selection) {
|
|
21214
21216
|
const direction = arrowMap[ev.key];
|
|
21215
21217
|
if (ev.shiftKey) {
|
|
21216
|
-
selection.resizeAnchorZone(direction, ev
|
|
21218
|
+
selection.resizeAnchorZone(direction, isCtrlKey(ev) ? "end" : 1);
|
|
21217
21219
|
}
|
|
21218
21220
|
else {
|
|
21219
|
-
selection.moveAnchorCell(direction, ev
|
|
21221
|
+
selection.moveAnchorCell(direction, isCtrlKey(ev) ? "end" : 1);
|
|
21220
21222
|
}
|
|
21221
21223
|
}
|
|
21222
21224
|
|
|
@@ -28201,7 +28203,10 @@
|
|
|
28201
28203
|
return;
|
|
28202
28204
|
}
|
|
28203
28205
|
const [col, row] = this.getCartesianCoordinates(ev);
|
|
28204
|
-
this.props.onCellClicked(col, row, {
|
|
28206
|
+
this.props.onCellClicked(col, row, {
|
|
28207
|
+
expandZone: ev.shiftKey,
|
|
28208
|
+
addZone: isCtrlKey(ev),
|
|
28209
|
+
});
|
|
28205
28210
|
}
|
|
28206
28211
|
onDoubleClick(ev) {
|
|
28207
28212
|
const [col, row] = this.getCartesianCoordinates(ev);
|
|
@@ -28431,7 +28436,7 @@
|
|
|
28431
28436
|
this._increaseSelection(index);
|
|
28432
28437
|
}
|
|
28433
28438
|
else {
|
|
28434
|
-
this._selectElement(index, ev
|
|
28439
|
+
this._selectElement(index, isCtrlKey(ev));
|
|
28435
28440
|
}
|
|
28436
28441
|
this.lastSelectedElementIndex = index;
|
|
28437
28442
|
const mouseMoveSelect = (col, row) => {
|
|
@@ -28595,8 +28600,8 @@
|
|
|
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");
|
|
@@ -28769,8 +28774,8 @@
|
|
|
28769
28774
|
this.env.raiseError(MergeErrorMessage);
|
|
28770
28775
|
}
|
|
28771
28776
|
}
|
|
28772
|
-
_selectElement(index,
|
|
28773
|
-
this.env.model.selection.selectRow(index,
|
|
28777
|
+
_selectElement(index, addDistinctHeader) {
|
|
28778
|
+
this.env.model.selection.selectRow(index, addDistinctHeader ? "newAnchor" : "overrideSelection");
|
|
28774
28779
|
}
|
|
28775
28780
|
_increaseSelection(index) {
|
|
28776
28781
|
this.env.model.selection.selectRow(index, "updateAnchor");
|
|
@@ -29572,17 +29577,17 @@
|
|
|
29572
29577
|
// ---------------------------------------------------------------------------
|
|
29573
29578
|
// Zone selection with mouse
|
|
29574
29579
|
// ---------------------------------------------------------------------------
|
|
29575
|
-
onCellClicked(col, row, {
|
|
29580
|
+
onCellClicked(col, row, { addZone, expandZone }) {
|
|
29576
29581
|
if (this.env.model.getters.hasOpenedPopover()) {
|
|
29577
29582
|
this.closeOpenedPopover();
|
|
29578
29583
|
}
|
|
29579
29584
|
if (this.env.model.getters.getEditionMode() === "editing") {
|
|
29580
29585
|
interactiveStopEdition(this.env);
|
|
29581
29586
|
}
|
|
29582
|
-
if (
|
|
29587
|
+
if (expandZone) {
|
|
29583
29588
|
this.env.model.selection.setAnchorCorner(col, row);
|
|
29584
29589
|
}
|
|
29585
|
-
else if (
|
|
29590
|
+
else if (addZone) {
|
|
29586
29591
|
this.env.model.selection.addCellToSelection(col, row);
|
|
29587
29592
|
}
|
|
29588
29593
|
else {
|
|
@@ -29643,7 +29648,7 @@
|
|
|
29643
29648
|
onKeydown(ev) {
|
|
29644
29649
|
const eventKey = ev.key.toUpperCase();
|
|
29645
29650
|
let keyDownString = "";
|
|
29646
|
-
if (ev
|
|
29651
|
+
if (isCtrlKey(ev) && eventKey !== "CTRL")
|
|
29647
29652
|
keyDownString += "CTRL+";
|
|
29648
29653
|
if (ev.metaKey)
|
|
29649
29654
|
keyDownString += "CTRL+";
|
|
@@ -34534,7 +34539,7 @@
|
|
|
34534
34539
|
*/
|
|
34535
34540
|
function getBorderId(border) {
|
|
34536
34541
|
for (let [key, value] of Object.entries(borders)) {
|
|
34537
|
-
if (
|
|
34542
|
+
if (deepEquals(value, border)) {
|
|
34538
34543
|
return parseInt(key, 10);
|
|
34539
34544
|
}
|
|
34540
34545
|
}
|
|
@@ -39853,56 +39858,758 @@
|
|
|
39853
39858
|
}
|
|
39854
39859
|
}
|
|
39855
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
|
+
|
|
39856
40426
|
/**
|
|
39857
|
-
*
|
|
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
|
+
|
|
40571
|
+
/**
|
|
40572
|
+
* Implementation of a dependency Graph.
|
|
39858
40573
|
* The graph is used to evaluate the cells in the correct
|
|
39859
40574
|
* order, and should be updated each time a cell's content is modified
|
|
39860
40575
|
*
|
|
40576
|
+
* It uses an R-Tree data structure to efficiently find dependent cells.
|
|
39861
40577
|
*/
|
|
39862
40578
|
class FormulaDependencyGraph {
|
|
39863
|
-
|
|
39864
|
-
* Internal structure:
|
|
39865
|
-
* - key: a cell position (encoded as an integer)
|
|
39866
|
-
* - value: a set of cell positions that depends on the key
|
|
39867
|
-
*
|
|
39868
|
-
* Given
|
|
39869
|
-
* - A1:"= B1 + SQRT(B2)"
|
|
39870
|
-
* - C1:"= B1";
|
|
39871
|
-
* - C2:"= C1"
|
|
39872
|
-
*
|
|
39873
|
-
* we will have something like:
|
|
39874
|
-
* - B1 ---> (A1, C1) meaning A1 and C1 depends on B1
|
|
39875
|
-
* - B2 ---> (A1) meaning A1 depends on B2
|
|
39876
|
-
* - C1 ---> (C2) meaning C2 depends on C1
|
|
39877
|
-
*/
|
|
39878
|
-
inverseDependencies = new Map();
|
|
40579
|
+
encoder;
|
|
39879
40580
|
dependencies = new Map();
|
|
40581
|
+
rTree;
|
|
40582
|
+
constructor(encoder, data = []) {
|
|
40583
|
+
this.encoder = encoder;
|
|
40584
|
+
this.rTree = new SpreadsheetRTree(data);
|
|
40585
|
+
}
|
|
39880
40586
|
removeAllDependencies(formulaPositionId) {
|
|
39881
|
-
const
|
|
39882
|
-
if (!
|
|
40587
|
+
const ranges = this.dependencies.get(formulaPositionId);
|
|
40588
|
+
if (!ranges) {
|
|
39883
40589
|
return;
|
|
39884
40590
|
}
|
|
39885
|
-
for (const
|
|
39886
|
-
this.
|
|
40591
|
+
for (const range of ranges) {
|
|
40592
|
+
this.rTree.remove(range);
|
|
39887
40593
|
}
|
|
39888
40594
|
this.dependencies.delete(formulaPositionId);
|
|
39889
40595
|
}
|
|
39890
40596
|
addDependencies(formulaPositionId, dependencies) {
|
|
39891
|
-
|
|
39892
|
-
|
|
39893
|
-
|
|
39894
|
-
|
|
39895
|
-
|
|
39896
|
-
|
|
39897
|
-
|
|
39898
|
-
|
|
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);
|
|
39899
40606
|
}
|
|
39900
40607
|
const existingDependencies = this.dependencies.get(formulaPositionId);
|
|
39901
40608
|
if (existingDependencies) {
|
|
39902
|
-
existingDependencies.push(...
|
|
40609
|
+
existingDependencies.push(...rTreeItems);
|
|
39903
40610
|
}
|
|
39904
40611
|
else {
|
|
39905
|
-
this.dependencies.set(formulaPositionId,
|
|
40612
|
+
this.dependencies.set(formulaPositionId, rTreeItems);
|
|
39906
40613
|
}
|
|
39907
40614
|
}
|
|
39908
40615
|
/**
|
|
@@ -39910,20 +40617,20 @@
|
|
|
39910
40617
|
* in the correct order they should be evaluated.
|
|
39911
40618
|
* This is called a topological ordering (excluding cycles)
|
|
39912
40619
|
*/
|
|
39913
|
-
getCellsDependingOn(
|
|
40620
|
+
getCellsDependingOn(ranges) {
|
|
39914
40621
|
const visited = new JetSet();
|
|
39915
|
-
const queue = Array.from(
|
|
40622
|
+
const queue = Array.from(ranges).reverse();
|
|
39916
40623
|
while (queue.length > 0) {
|
|
39917
|
-
const
|
|
39918
|
-
visited.add(
|
|
39919
|
-
const
|
|
39920
|
-
for (const
|
|
39921
|
-
if (!visited.has(
|
|
39922
|
-
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));
|
|
39923
40630
|
}
|
|
39924
40631
|
}
|
|
39925
40632
|
}
|
|
39926
|
-
visited.delete(...
|
|
40633
|
+
visited.delete(...ranges.flatMap((r) => this.encoder.encodeBoundingBox(r)));
|
|
39927
40634
|
return visited;
|
|
39928
40635
|
}
|
|
39929
40636
|
}
|
|
@@ -40011,9 +40718,9 @@
|
|
|
40011
40718
|
context;
|
|
40012
40719
|
getters;
|
|
40013
40720
|
compilationParams;
|
|
40014
|
-
|
|
40721
|
+
encoder = new PositionBitsEncoder();
|
|
40015
40722
|
evaluatedCells = new Map();
|
|
40016
|
-
formulaDependencies = lazy(new FormulaDependencyGraph());
|
|
40723
|
+
formulaDependencies = lazy(new FormulaDependencyGraph(this.encoder));
|
|
40017
40724
|
blockedArrayFormulas = new Set();
|
|
40018
40725
|
spreadingRelations = new SpreadingRelation();
|
|
40019
40726
|
constructor(context, getters) {
|
|
@@ -40022,23 +40729,23 @@
|
|
|
40022
40729
|
this.compilationParams = buildCompilationParameters(this.context, this.getters, this.computeAndSave.bind(this));
|
|
40023
40730
|
}
|
|
40024
40731
|
getEvaluatedCell(position) {
|
|
40025
|
-
return (this.evaluatedCells.get(this.
|
|
40732
|
+
return (this.evaluatedCells.get(this.encoder.encode(position)) ||
|
|
40026
40733
|
createEvaluatedCell("", { locale: this.getters.getLocale() }));
|
|
40027
40734
|
}
|
|
40028
40735
|
getSpreadPositionsOf(position) {
|
|
40029
|
-
const positionId = this.
|
|
40736
|
+
const positionId = this.encoder.encode(position);
|
|
40030
40737
|
if (!this.spreadingRelations.isArrayFormula(positionId)) {
|
|
40031
40738
|
return [];
|
|
40032
40739
|
}
|
|
40033
|
-
return Array.from(this.spreadingRelations.getArrayResultPositionIds(positionId)).map(this.
|
|
40740
|
+
return Array.from(this.spreadingRelations.getArrayResultPositionIds(positionId)).map((positionId) => this.encoder.decode(positionId));
|
|
40034
40741
|
}
|
|
40035
40742
|
getArrayFormulaSpreadingOn(position) {
|
|
40036
|
-
const positionId = this.
|
|
40743
|
+
const positionId = this.encoder.encode(position);
|
|
40037
40744
|
const formulaPosition = this.getArrayFormulaSpreadingOnId(positionId);
|
|
40038
|
-
return formulaPosition !== undefined ? this.
|
|
40745
|
+
return formulaPosition !== undefined ? this.encoder.decode(formulaPosition) : undefined;
|
|
40039
40746
|
}
|
|
40040
40747
|
getEvaluatedPositions() {
|
|
40041
|
-
return [...this.evaluatedCells.keys()].map(this.
|
|
40748
|
+
return [...this.evaluatedCells.keys()].map((p) => this.encoder.decode(p));
|
|
40042
40749
|
}
|
|
40043
40750
|
getArrayFormulaSpreadingOnId(positionId) {
|
|
40044
40751
|
if (!this.spreadingRelations.hasArrayFormulaResult(positionId)) {
|
|
@@ -40048,7 +40755,7 @@
|
|
|
40048
40755
|
return Array.from(arrayFormulas).find((positionId) => !this.blockedArrayFormulas.has(positionId));
|
|
40049
40756
|
}
|
|
40050
40757
|
updateDependencies(position) {
|
|
40051
|
-
const positionId = this.
|
|
40758
|
+
const positionId = this.encoder.encode(position);
|
|
40052
40759
|
this.formulaDependencies().removeAllDependencies(positionId);
|
|
40053
40760
|
const dependencies = this.getDirectDependencies(positionId);
|
|
40054
40761
|
this.formulaDependencies().addDependencies(positionId, dependencies);
|
|
@@ -40058,12 +40765,12 @@
|
|
|
40058
40765
|
this.compilationParams = buildCompilationParameters(this.context, this.getters, this.computeAndSave.bind(this));
|
|
40059
40766
|
}
|
|
40060
40767
|
evaluateCells(positions) {
|
|
40061
|
-
const cells = positions.map(this.
|
|
40768
|
+
const cells = positions.map((p) => this.encoder.encode(p));
|
|
40062
40769
|
const cellsToCompute = new JetSet(cells);
|
|
40063
|
-
const
|
|
40770
|
+
const arrayFormulasPositionIds = this.getArrayFormulasImpactedByChangesOf(cells);
|
|
40064
40771
|
cellsToCompute.add(...this.getCellsDependingOn(cells));
|
|
40065
|
-
cellsToCompute.add(...
|
|
40066
|
-
cellsToCompute.add(...this.getCellsDependingOn(
|
|
40772
|
+
cellsToCompute.add(...arrayFormulasPositionIds);
|
|
40773
|
+
cellsToCompute.add(...this.getCellsDependingOn(arrayFormulasPositionIds));
|
|
40067
40774
|
this.evaluate(cellsToCompute);
|
|
40068
40775
|
}
|
|
40069
40776
|
getArrayFormulasImpactedByChangesOf(positionIds) {
|
|
@@ -40086,12 +40793,14 @@
|
|
|
40086
40793
|
this.blockedArrayFormulas = new Set();
|
|
40087
40794
|
this.spreadingRelations = new SpreadingRelation();
|
|
40088
40795
|
this.formulaDependencies = lazy(() => {
|
|
40089
|
-
const
|
|
40090
|
-
|
|
40091
|
-
|
|
40092
|
-
|
|
40093
|
-
|
|
40094
|
-
|
|
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);
|
|
40095
40804
|
});
|
|
40096
40805
|
}
|
|
40097
40806
|
evaluateAllCells() {
|
|
@@ -40113,7 +40822,7 @@
|
|
|
40113
40822
|
for (const sheetId of this.getters.getSheetIds()) {
|
|
40114
40823
|
const cellIds = this.getters.getCells(sheetId);
|
|
40115
40824
|
for (const cellId in cellIds) {
|
|
40116
|
-
positionIds.add(this.
|
|
40825
|
+
positionIds.add(this.encoder.encode(this.getters.getCellPosition(cellId)));
|
|
40117
40826
|
}
|
|
40118
40827
|
}
|
|
40119
40828
|
return positionIds;
|
|
@@ -40180,7 +40889,7 @@
|
|
|
40180
40889
|
}
|
|
40181
40890
|
}
|
|
40182
40891
|
computeAndSave(position) {
|
|
40183
|
-
const positionId = this.
|
|
40892
|
+
const positionId = this.encoder.encode(position);
|
|
40184
40893
|
const evaluatedCell = this.computeCell(positionId);
|
|
40185
40894
|
if (!this.evaluatedCells.has(positionId)) {
|
|
40186
40895
|
this.setEvaluatedCell(positionId, evaluatedCell);
|
|
@@ -40240,15 +40949,15 @@
|
|
|
40240
40949
|
throw new Error(_t("Result couldn't be automatically expanded. Please insert more columns and rows."));
|
|
40241
40950
|
}
|
|
40242
40951
|
updateSpreadRelation({ sheetId, col, row, }) {
|
|
40243
|
-
const arrayFormulaPositionId = this.
|
|
40952
|
+
const arrayFormulaPositionId = this.encoder.encode({ sheetId, col, row });
|
|
40244
40953
|
return (i, j) => {
|
|
40245
40954
|
const position = { sheetId, col: i + col, row: j + row };
|
|
40246
|
-
const resultPositionId = this.
|
|
40955
|
+
const resultPositionId = this.encoder.encode(position);
|
|
40247
40956
|
this.spreadingRelations.addRelation({ resultPositionId, arrayFormulaPositionId });
|
|
40248
40957
|
};
|
|
40249
40958
|
}
|
|
40250
40959
|
checkCollision({ sheetId, col, row }) {
|
|
40251
|
-
const formulaPositionId = this.
|
|
40960
|
+
const formulaPositionId = this.encoder.encode({ sheetId, col, row });
|
|
40252
40961
|
return (i, j) => {
|
|
40253
40962
|
const position = { sheetId: sheetId, col: i + col, row: j + row };
|
|
40254
40963
|
const rawCell = this.getters.getCell(position);
|
|
@@ -40269,7 +40978,7 @@
|
|
|
40269
40978
|
format: format || matrixResult[i][j]?.format,
|
|
40270
40979
|
locale: this.getters.getLocale(),
|
|
40271
40980
|
});
|
|
40272
|
-
const positionId = this.
|
|
40981
|
+
const positionId = this.encoder.encode(position);
|
|
40273
40982
|
this.setEvaluatedCell(positionId, evaluatedCell);
|
|
40274
40983
|
// check if formula dependencies present in the spread zone
|
|
40275
40984
|
// if so, they need to be recomputed
|
|
@@ -40301,29 +41010,17 @@
|
|
|
40301
41010
|
if (!cell?.isFormula) {
|
|
40302
41011
|
return [];
|
|
40303
41012
|
}
|
|
40304
|
-
|
|
40305
|
-
for (const range of cell.compiledFormula.dependencies) {
|
|
40306
|
-
if (range.invalidSheetName || range.invalidXc) {
|
|
40307
|
-
continue;
|
|
40308
|
-
}
|
|
40309
|
-
const sheetId = range.sheetId;
|
|
40310
|
-
forEachPositionsInZone(range.zone, (col, row) => {
|
|
40311
|
-
dependencies.push(this.encodePosition({ sheetId, col, row }));
|
|
40312
|
-
});
|
|
40313
|
-
}
|
|
40314
|
-
return dependencies;
|
|
41013
|
+
return cell.compiledFormula.dependencies;
|
|
40315
41014
|
}
|
|
40316
41015
|
getCellsDependingOn(positionIds) {
|
|
40317
|
-
|
|
41016
|
+
const ranges = [];
|
|
41017
|
+
for (const positionId of positionIds) {
|
|
41018
|
+
ranges.push(this.encoder.decodeToBoundingBox(positionId));
|
|
41019
|
+
}
|
|
41020
|
+
return this.formulaDependencies().getCellsDependingOn(ranges);
|
|
40318
41021
|
}
|
|
40319
41022
|
getCell(positionId) {
|
|
40320
|
-
return this.getters.getCell(this.
|
|
40321
|
-
}
|
|
40322
|
-
encodePosition(position) {
|
|
40323
|
-
return this.positionEncoder.encode(position);
|
|
40324
|
-
}
|
|
40325
|
-
decodePosition(positionId) {
|
|
40326
|
-
return this.positionEncoder.decode(positionId);
|
|
41023
|
+
return this.getters.getCell(this.encoder.decode(positionId));
|
|
40327
41024
|
}
|
|
40328
41025
|
}
|
|
40329
41026
|
function forEachSpreadPositionInMatrix(nbColumns, nbRows, callback) {
|
|
@@ -40379,6 +41076,13 @@
|
|
|
40379
41076
|
encode({ sheetId, col, row }) {
|
|
40380
41077
|
return (this.encodeSheet(sheetId) << 42n) | (BigInt(col) << 21n) | BigInt(row);
|
|
40381
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
|
+
}
|
|
40382
41086
|
decode(id) {
|
|
40383
41087
|
// keep only the last 21 bits by AND-ing the bit sequence with 21 ones
|
|
40384
41088
|
const row = Number(id & 2097151n);
|
|
@@ -40386,6 +41090,10 @@
|
|
|
40386
41090
|
const sheetId = this.decodeSheet(id >> 42n);
|
|
40387
41091
|
return { sheetId, col, row };
|
|
40388
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
|
+
}
|
|
40389
41097
|
encodeSheet(sheetId) {
|
|
40390
41098
|
const sheetKey = this.sheetMapping[sheetId];
|
|
40391
41099
|
if (sheetKey === undefined) {
|
|
@@ -40556,7 +41264,12 @@
|
|
|
40556
41264
|
// Getters
|
|
40557
41265
|
// ---------------------------------------------------------------------------
|
|
40558
41266
|
evaluateFormula(sheetId, formulaString) {
|
|
40559
|
-
|
|
41267
|
+
try {
|
|
41268
|
+
return this.evaluator.evaluateFormula(sheetId, formulaString);
|
|
41269
|
+
}
|
|
41270
|
+
catch (error) {
|
|
41271
|
+
return error instanceof EvaluationError ? error.errorType : CellErrorType.GenericError;
|
|
41272
|
+
}
|
|
40560
41273
|
}
|
|
40561
41274
|
/**
|
|
40562
41275
|
* Return the value of each cell in the range as they are displayed in the grid.
|
|
@@ -40825,7 +41538,7 @@
|
|
|
40825
41538
|
}
|
|
40826
41539
|
|
|
40827
41540
|
class EvaluationChartPlugin extends UIPlugin {
|
|
40828
|
-
static getters = ["getChartRuntime", "
|
|
41541
|
+
static getters = ["getChartRuntime", "getStyleOfSingleCellChart"];
|
|
40829
41542
|
charts = {};
|
|
40830
41543
|
createRuntimeChart = chartRuntimeFactory(this.getters);
|
|
40831
41544
|
handle(cmd) {
|
|
@@ -40863,25 +41576,26 @@
|
|
|
40863
41576
|
return this.charts[figureId];
|
|
40864
41577
|
}
|
|
40865
41578
|
/**
|
|
40866
|
-
* Get the background
|
|
40867
|
-
* of the chart. In order of priority, it will return :
|
|
40868
|
-
*
|
|
40869
|
-
* - the chart background color if one is defined
|
|
40870
|
-
* - the fill color of the cell if one is defined
|
|
40871
|
-
* - the fill color of the cell from conditional formats if one is defined
|
|
40872
|
-
* - 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.
|
|
40873
41580
|
*/
|
|
40874
|
-
|
|
41581
|
+
getStyleOfSingleCellChart(chartBackground, mainRange) {
|
|
40875
41582
|
if (chartBackground)
|
|
40876
|
-
return chartBackground;
|
|
41583
|
+
return { background: chartBackground, fontColor: chartFontColor(chartBackground) };
|
|
40877
41584
|
if (!mainRange) {
|
|
40878
|
-
return
|
|
41585
|
+
return {
|
|
41586
|
+
background: BACKGROUND_CHART_COLOR,
|
|
41587
|
+
fontColor: chartFontColor(BACKGROUND_CHART_COLOR),
|
|
41588
|
+
};
|
|
40879
41589
|
}
|
|
40880
41590
|
const col = mainRange.zone.left;
|
|
40881
41591
|
const row = mainRange.zone.top;
|
|
40882
41592
|
const sheetId = mainRange.sheetId;
|
|
40883
41593
|
const style = this.getters.getCellComputedStyle({ sheetId, col, row });
|
|
40884
|
-
|
|
41594
|
+
const background = style.fillColor || BACKGROUND_CHART_COLOR;
|
|
41595
|
+
return {
|
|
41596
|
+
background,
|
|
41597
|
+
fontColor: style.textColor || chartFontColor(background),
|
|
41598
|
+
};
|
|
40885
41599
|
}
|
|
40886
41600
|
exportForExcel(data) {
|
|
40887
41601
|
for (const sheet of data.sheets) {
|
|
@@ -40992,45 +41706,40 @@
|
|
|
40992
41706
|
getComputedStyles(sheetId) {
|
|
40993
41707
|
const computedStyle = {};
|
|
40994
41708
|
for (let cf of this.getters.getConditionalFormats(sheetId).reverse()) {
|
|
40995
|
-
|
|
40996
|
-
|
|
40997
|
-
|
|
40998
|
-
|
|
40999
|
-
|
|
41000
|
-
|
|
41001
|
-
|
|
41002
|
-
|
|
41003
|
-
|
|
41004
|
-
|
|
41005
|
-
|
|
41006
|
-
for (let
|
|
41007
|
-
|
|
41008
|
-
|
|
41009
|
-
|
|
41010
|
-
const
|
|
41011
|
-
|
|
41012
|
-
|
|
41013
|
-
|
|
41014
|
-
|
|
41015
|
-
|
|
41016
|
-
});
|
|
41017
|
-
}
|
|
41018
|
-
return value;
|
|
41019
|
-
});
|
|
41020
|
-
if (predicate && predicate(target, { ...cf.rule, values })) {
|
|
41021
|
-
if (!computedStyle[col])
|
|
41022
|
-
computedStyle[col] = [];
|
|
41023
|
-
// we must combine all the properties of all the CF rules applied to the given cell
|
|
41024
|
-
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
|
+
});
|
|
41025
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);
|
|
41026
41738
|
}
|
|
41027
41739
|
}
|
|
41028
41740
|
}
|
|
41029
|
-
|
|
41030
|
-
|
|
41031
|
-
}
|
|
41032
|
-
catch (_) {
|
|
41033
|
-
// we don't care about the errors within the evaluation of a rule
|
|
41741
|
+
}
|
|
41742
|
+
break;
|
|
41034
41743
|
}
|
|
41035
41744
|
}
|
|
41036
41745
|
return computedStyle;
|
|
@@ -41802,7 +42511,7 @@
|
|
|
41802
42511
|
let row = zone.bottom;
|
|
41803
42512
|
if (col > 0) {
|
|
41804
42513
|
let leftPosition = { sheetId, col: col - 1, row };
|
|
41805
|
-
while (this.getters.
|
|
42514
|
+
while (this.getters.getCorrespondingFormulaCell(leftPosition) ||
|
|
41806
42515
|
this.getters.getCell(leftPosition)?.content) {
|
|
41807
42516
|
row += 1;
|
|
41808
42517
|
leftPosition = { sheetId, col: col - 1, row };
|
|
@@ -41812,7 +42521,7 @@
|
|
|
41812
42521
|
col = zone.right;
|
|
41813
42522
|
if (col <= this.getters.getNumberCols(sheetId)) {
|
|
41814
42523
|
let rightPosition = { sheetId, col: col + 1, row };
|
|
41815
|
-
while (this.getters.
|
|
42524
|
+
while (this.getters.getCorrespondingFormulaCell(rightPosition) ||
|
|
41816
42525
|
this.getters.getCell(rightPosition)?.content) {
|
|
41817
42526
|
row += 1;
|
|
41818
42527
|
rightPosition = { sheetId, col: col + 1, row };
|
|
@@ -52193,7 +52902,7 @@
|
|
|
52193
52902
|
}
|
|
52194
52903
|
onKeydown(ev) {
|
|
52195
52904
|
let keyDownString = "";
|
|
52196
|
-
if (ev
|
|
52905
|
+
if (isCtrlKey(ev)) {
|
|
52197
52906
|
keyDownString += "CTRL+";
|
|
52198
52907
|
}
|
|
52199
52908
|
keyDownString += ev.key.toUpperCase();
|
|
@@ -55905,6 +56614,7 @@
|
|
|
55905
56614
|
colorToRGBA,
|
|
55906
56615
|
positionToZone,
|
|
55907
56616
|
isDefined: isDefined$1,
|
|
56617
|
+
isMatrix,
|
|
55908
56618
|
lazy,
|
|
55909
56619
|
genericRepeat,
|
|
55910
56620
|
createAction,
|
|
@@ -55988,9 +56698,9 @@
|
|
|
55988
56698
|
exports.tokenize = tokenize;
|
|
55989
56699
|
|
|
55990
56700
|
|
|
55991
|
-
__info__.version = "17.0.
|
|
55992
|
-
__info__.date = "
|
|
55993
|
-
__info__.hash = "
|
|
56701
|
+
__info__.version = "17.0.6";
|
|
56702
|
+
__info__.date = "2024-01-04T12:23:10.357Z";
|
|
56703
|
+
__info__.hash = "310ca57";
|
|
55994
56704
|
|
|
55995
56705
|
|
|
55996
56706
|
})(this.o_spreadsheet = this.o_spreadsheet || {}, owl);
|