@odoo/o-spreadsheet 18.0.28 → 18.0.30
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 +354 -243
- package/dist/o-spreadsheet.d.ts +33 -32
- package/dist/o-spreadsheet.esm.js +354 -243
- package/dist/o-spreadsheet.iife.js +354 -243
- package/dist/o-spreadsheet.iife.min.js +364 -364
- package/dist/o_spreadsheet.xml +6 -5
- package/package.json +1 -1
|
@@ -2,9 +2,9 @@
|
|
|
2
2
|
/**
|
|
3
3
|
* This file is generated by o-spreadsheet build tools. Do not edit it.
|
|
4
4
|
* @see https://github.com/odoo/o-spreadsheet
|
|
5
|
-
* @version 18.0.
|
|
6
|
-
* @date 2025-05-
|
|
7
|
-
* @hash
|
|
5
|
+
* @version 18.0.30
|
|
6
|
+
* @date 2025-05-26T12:35:05.184Z
|
|
7
|
+
* @hash 838c4f7
|
|
8
8
|
*/
|
|
9
9
|
|
|
10
10
|
'use strict';
|
|
@@ -1454,18 +1454,53 @@ function lettersToNumber(letters) {
|
|
|
1454
1454
|
let result = 0;
|
|
1455
1455
|
const l = letters.length;
|
|
1456
1456
|
for (let i = 0; i < l; i++) {
|
|
1457
|
-
const
|
|
1458
|
-
const colIndex = charCode >= 65 && charCode <= 90 ? charCode - 64 : charCode - 96;
|
|
1457
|
+
const colIndex = charToNumber(letters[i]);
|
|
1459
1458
|
result = result * 26 + colIndex;
|
|
1460
1459
|
}
|
|
1461
1460
|
return result - 1;
|
|
1462
1461
|
}
|
|
1462
|
+
function charToNumber(char) {
|
|
1463
|
+
const charCode = char.charCodeAt(0);
|
|
1464
|
+
return charCode >= 65 && charCode <= 90 ? charCode - 64 : charCode - 96;
|
|
1465
|
+
}
|
|
1463
1466
|
function isCharALetter(char) {
|
|
1464
1467
|
return (char >= "A" && char <= "Z") || (char >= "a" && char <= "z");
|
|
1465
1468
|
}
|
|
1466
1469
|
function isCharADigit(char) {
|
|
1467
1470
|
return char >= "0" && char <= "9";
|
|
1468
1471
|
}
|
|
1472
|
+
// we limit the max column to 3 letters and max row to 7 digits for performance reasons
|
|
1473
|
+
const MAX_COL = lettersToNumber("ZZZ");
|
|
1474
|
+
const MAX_ROW = 9999998;
|
|
1475
|
+
function consumeSpaces(chars) {
|
|
1476
|
+
while (chars.current === " ") {
|
|
1477
|
+
chars.advanceBy(1);
|
|
1478
|
+
}
|
|
1479
|
+
}
|
|
1480
|
+
function consumeLetters(chars) {
|
|
1481
|
+
if (chars.current === "$")
|
|
1482
|
+
chars.advanceBy(1);
|
|
1483
|
+
if (!chars.current || !isCharALetter(chars.current)) {
|
|
1484
|
+
return -1;
|
|
1485
|
+
}
|
|
1486
|
+
let colCoordinate = 0;
|
|
1487
|
+
while (chars.current && isCharALetter(chars.current)) {
|
|
1488
|
+
colCoordinate = colCoordinate * 26 + charToNumber(chars.shift());
|
|
1489
|
+
}
|
|
1490
|
+
return colCoordinate;
|
|
1491
|
+
}
|
|
1492
|
+
function consumeDigits(chars) {
|
|
1493
|
+
if (chars.current === "$")
|
|
1494
|
+
chars.advanceBy(1);
|
|
1495
|
+
if (!chars.current || !isCharADigit(chars.current)) {
|
|
1496
|
+
return -1;
|
|
1497
|
+
}
|
|
1498
|
+
let num = 0;
|
|
1499
|
+
while (chars.current && isCharADigit(chars.current)) {
|
|
1500
|
+
num = num * 10 + Number(chars.shift());
|
|
1501
|
+
}
|
|
1502
|
+
return num;
|
|
1503
|
+
}
|
|
1469
1504
|
/**
|
|
1470
1505
|
* Convert a "XC" coordinate to cartesian coordinates.
|
|
1471
1506
|
*
|
|
@@ -1476,33 +1511,17 @@ function isCharADigit(char) {
|
|
|
1476
1511
|
* Note: it also accepts lowercase coordinates, but not fixed references
|
|
1477
1512
|
*/
|
|
1478
1513
|
function toCartesian(xc) {
|
|
1479
|
-
|
|
1480
|
-
|
|
1481
|
-
|
|
1482
|
-
|
|
1483
|
-
// Process letter part
|
|
1484
|
-
if (xc[i] === "$")
|
|
1485
|
-
i++;
|
|
1486
|
-
while (i < xc.length && isCharALetter(xc[i])) {
|
|
1487
|
-
letterPart += xc[i++];
|
|
1488
|
-
}
|
|
1489
|
-
if (letterPart.length === 0 || letterPart.length > 3) {
|
|
1490
|
-
// limit to max 3 letters for performance reasons
|
|
1514
|
+
const chars = new TokenizingChars(xc);
|
|
1515
|
+
consumeSpaces(chars);
|
|
1516
|
+
const letterPart = consumeLetters(chars);
|
|
1517
|
+
if (letterPart === -1 || !chars.current) {
|
|
1491
1518
|
throw new Error(`Invalid cell description: ${xc}`);
|
|
1492
1519
|
}
|
|
1493
|
-
|
|
1494
|
-
|
|
1495
|
-
|
|
1496
|
-
|
|
1497
|
-
|
|
1498
|
-
}
|
|
1499
|
-
if (i !== xc.length || numberPart.length === 0 || numberPart.length > 7) {
|
|
1500
|
-
// limit to max 7 numbers for performance reasons
|
|
1501
|
-
throw new Error(`Invalid cell description: ${xc}`);
|
|
1502
|
-
}
|
|
1503
|
-
const col = lettersToNumber(letterPart);
|
|
1504
|
-
const row = Number(numberPart) - 1;
|
|
1505
|
-
if (isNaN(row)) {
|
|
1520
|
+
const num = consumeDigits(chars);
|
|
1521
|
+
consumeSpaces(chars);
|
|
1522
|
+
const col = letterPart - 1;
|
|
1523
|
+
const row = num - 1;
|
|
1524
|
+
if (!chars.isOver() || col > MAX_COL || row > MAX_ROW) {
|
|
1506
1525
|
throw new Error(`Invalid cell description: ${xc}`);
|
|
1507
1526
|
}
|
|
1508
1527
|
return { col, row };
|
|
@@ -1914,67 +1933,6 @@ class LazyTranslatedString extends String {
|
|
|
1914
1933
|
}
|
|
1915
1934
|
}
|
|
1916
1935
|
|
|
1917
|
-
/** Reference of a cell (eg. A1, $B$5) */
|
|
1918
|
-
const cellReference = new RegExp(/\$?([A-Z]{1,3})\$?([0-9]{1,7})/, "i");
|
|
1919
|
-
// Same as above, but matches the exact string (nothing before or after)
|
|
1920
|
-
const singleCellReference = new RegExp(/^\$?([A-Z]{1,3})\$?([0-9]{1,7})$/, "i");
|
|
1921
|
-
/** Reference of a column header (eg. A, AB, $A) */
|
|
1922
|
-
const colHeader = new RegExp(/^\$?([A-Z]{1,3})+$/, "i");
|
|
1923
|
-
/** Reference of a row header (eg. 1, $1) */
|
|
1924
|
-
const rowHeader = new RegExp(/^\$?([0-9]{1,7})+$/, "i");
|
|
1925
|
-
/** Reference of a column (eg. A, $CA, Sheet1!B) */
|
|
1926
|
-
const colReference = new RegExp(/^\s*('.+'!|[^']+!)?\$?([A-Z]{1,3})$/, "i");
|
|
1927
|
-
/** Reference of a row (eg. 1, 59, Sheet1!9) */
|
|
1928
|
-
const rowReference = new RegExp(/^\s*('.+'!|[^']+!)?\$?([0-9]{1,7})$/, "i");
|
|
1929
|
-
/** Reference of a normal range or a full row range (eg. A1:B1, 1:$5, $A2:5) */
|
|
1930
|
-
const fullRowXc = /(\$?[A-Z]{1,3})?\$?[0-9]{1,7}\s*:\s*(\$?[A-Z]{1,3})?\$?[0-9]{1,7}\s*/i;
|
|
1931
|
-
/** Reference of a normal range or a column row range (eg. A1:B1, A:$B, $A1:C) */
|
|
1932
|
-
const fullColXc = /\$?[A-Z]{1,3}(\$?[0-9]{1,7})?\s*:\s*\$?[A-Z]{1,3}(\$?[0-9]{1,7})?\s*/i;
|
|
1933
|
-
/** Reference of a cell or a range, it can be a bounded range, a full row or a full column */
|
|
1934
|
-
const rangeReference = new RegExp(/^\s*('.+'!|[^']+!)?/.source +
|
|
1935
|
-
"(" +
|
|
1936
|
-
[cellReference.source, fullRowXc.source, fullColXc.source].join("|") +
|
|
1937
|
-
")" +
|
|
1938
|
-
/$/.source, "i");
|
|
1939
|
-
/**
|
|
1940
|
-
* Return true if the given xc is the reference of a column (e.g. A or AC or Sheet1!A)
|
|
1941
|
-
*/
|
|
1942
|
-
function isColReference(xc) {
|
|
1943
|
-
return colReference.test(xc);
|
|
1944
|
-
}
|
|
1945
|
-
/**
|
|
1946
|
-
* Return true if the given xc is the reference of a column (e.g. 1 or Sheet1!1)
|
|
1947
|
-
*/
|
|
1948
|
-
function isRowReference(xc) {
|
|
1949
|
-
return rowReference.test(xc);
|
|
1950
|
-
}
|
|
1951
|
-
function isColHeader(str) {
|
|
1952
|
-
return colHeader.test(str);
|
|
1953
|
-
}
|
|
1954
|
-
function isRowHeader(str) {
|
|
1955
|
-
return rowHeader.test(str);
|
|
1956
|
-
}
|
|
1957
|
-
/**
|
|
1958
|
-
* Return true if the given xc is the reference of a single cell,
|
|
1959
|
-
* without any specified sheet (e.g. A1)
|
|
1960
|
-
*/
|
|
1961
|
-
function isSingleCellReference(xc) {
|
|
1962
|
-
return singleCellReference.test(xc);
|
|
1963
|
-
}
|
|
1964
|
-
function splitReference(ref) {
|
|
1965
|
-
if (!ref.includes("!")) {
|
|
1966
|
-
return { xc: ref };
|
|
1967
|
-
}
|
|
1968
|
-
const parts = ref.split("!");
|
|
1969
|
-
const xc = parts.pop();
|
|
1970
|
-
const sheetName = getUnquotedSheetName(parts.join("!")) || undefined;
|
|
1971
|
-
return { sheetName, xc };
|
|
1972
|
-
}
|
|
1973
|
-
/** Return a reference SheetName!xc from the given arguments */
|
|
1974
|
-
function getFullReference(sheetName, xc) {
|
|
1975
|
-
return sheetName !== undefined ? `${getCanonicalSymbolName(sheetName)}!${xc}` : xc;
|
|
1976
|
-
}
|
|
1977
|
-
|
|
1978
1936
|
/**
|
|
1979
1937
|
* Convert from a cartesian reference to a Zone
|
|
1980
1938
|
* The range boundaries will be kept in the same order as the
|
|
@@ -1992,63 +1950,55 @@ function getFullReference(sheetName, xc) {
|
|
|
1992
1950
|
*
|
|
1993
1951
|
*/
|
|
1994
1952
|
function toZoneWithoutBoundaryChanges(xc) {
|
|
1995
|
-
|
|
1996
|
-
|
|
1997
|
-
|
|
1998
|
-
if (
|
|
1999
|
-
|
|
2000
|
-
}
|
|
2001
|
-
|
|
2002
|
-
|
|
2003
|
-
if (xc.includes(":")) {
|
|
2004
|
-
[firstRangePart, secondRangePart] = xc.split(":");
|
|
2005
|
-
firstRangePart = firstRangePart.trim();
|
|
2006
|
-
secondRangePart = secondRangePart.trim();
|
|
2007
|
-
}
|
|
2008
|
-
else {
|
|
2009
|
-
firstRangePart = xc.trim();
|
|
2010
|
-
}
|
|
1953
|
+
const chars = new TokenizingChars(xc);
|
|
1954
|
+
consumeSpaces(chars);
|
|
1955
|
+
const sheetSeparatorIndex = xc.indexOf("!");
|
|
1956
|
+
if (sheetSeparatorIndex !== -1) {
|
|
1957
|
+
chars.advanceBy(sheetSeparatorIndex + 1);
|
|
1958
|
+
}
|
|
1959
|
+
const leftLetters = consumeLetters(chars);
|
|
1960
|
+
const leftNumbers = consumeDigits(chars);
|
|
2011
1961
|
let top, bottom, left, right;
|
|
2012
1962
|
let fullCol = false;
|
|
2013
1963
|
let fullRow = false;
|
|
2014
1964
|
let hasHeader = false;
|
|
2015
|
-
if (
|
|
2016
|
-
left = right =
|
|
1965
|
+
if (leftNumbers === -1) {
|
|
1966
|
+
left = right = leftLetters - 1;
|
|
2017
1967
|
top = bottom = 0;
|
|
2018
1968
|
fullCol = true;
|
|
2019
1969
|
}
|
|
2020
|
-
else if (
|
|
2021
|
-
top = bottom =
|
|
1970
|
+
else if (leftLetters === -1) {
|
|
1971
|
+
top = bottom = leftNumbers - 1;
|
|
2022
1972
|
left = right = 0;
|
|
2023
1973
|
fullRow = true;
|
|
2024
1974
|
}
|
|
2025
1975
|
else {
|
|
2026
|
-
|
|
2027
|
-
|
|
2028
|
-
top = bottom = c.row;
|
|
1976
|
+
left = right = leftLetters - 1;
|
|
1977
|
+
top = bottom = leftNumbers - 1;
|
|
2029
1978
|
hasHeader = true;
|
|
2030
1979
|
}
|
|
2031
|
-
|
|
2032
|
-
|
|
2033
|
-
|
|
1980
|
+
consumeSpaces(chars);
|
|
1981
|
+
if (chars.current === ":") {
|
|
1982
|
+
chars.advanceBy(1);
|
|
1983
|
+
consumeSpaces(chars);
|
|
1984
|
+
const rightLetters = consumeLetters(chars);
|
|
1985
|
+
const rightNumbers = consumeDigits(chars);
|
|
1986
|
+
if (rightNumbers === -1) {
|
|
1987
|
+
right = rightLetters - 1;
|
|
2034
1988
|
fullCol = true;
|
|
2035
1989
|
}
|
|
2036
|
-
else if (
|
|
2037
|
-
bottom =
|
|
1990
|
+
else if (rightLetters === -1) {
|
|
1991
|
+
bottom = rightNumbers - 1;
|
|
2038
1992
|
fullRow = true;
|
|
2039
1993
|
}
|
|
2040
1994
|
else {
|
|
2041
|
-
|
|
2042
|
-
|
|
2043
|
-
bottom = c.row;
|
|
1995
|
+
right = rightLetters - 1;
|
|
1996
|
+
bottom = rightNumbers - 1;
|
|
2044
1997
|
top = fullCol ? bottom : top;
|
|
2045
1998
|
left = fullRow ? right : left;
|
|
2046
1999
|
hasHeader = true;
|
|
2047
2000
|
}
|
|
2048
2001
|
}
|
|
2049
|
-
if (fullCol && fullRow) {
|
|
2050
|
-
throw new Error("Wrong zone xc. The zone cannot be at the same time a full column and a full row");
|
|
2051
|
-
}
|
|
2052
2002
|
const zone = {
|
|
2053
2003
|
top,
|
|
2054
2004
|
left,
|
|
@@ -2077,7 +2027,16 @@ function toZoneWithoutBoundaryChanges(xc) {
|
|
|
2077
2027
|
*/
|
|
2078
2028
|
function toUnboundedZone(xc) {
|
|
2079
2029
|
const zone = toZoneWithoutBoundaryChanges(xc);
|
|
2080
|
-
|
|
2030
|
+
const orderedZone = reorderZone(zone);
|
|
2031
|
+
const bottom = orderedZone.bottom;
|
|
2032
|
+
const right = orderedZone.right;
|
|
2033
|
+
if ((bottom !== undefined && bottom > MAX_ROW) || (right !== undefined && right > MAX_COL)) {
|
|
2034
|
+
throw new Error(`Range string out of bounds: ${xc}`); // limit the size of the zone for performance
|
|
2035
|
+
}
|
|
2036
|
+
if (bottom === undefined && right === undefined) {
|
|
2037
|
+
throw new Error("Wrong zone xc. The zone cannot be at the same time a full column and a full row");
|
|
2038
|
+
}
|
|
2039
|
+
return orderedZone;
|
|
2081
2040
|
}
|
|
2082
2041
|
/**
|
|
2083
2042
|
* Convert from a cartesian reference to a Zone.
|
|
@@ -4014,6 +3973,113 @@ function transposeMatrix(matrix) {
|
|
|
4014
3973
|
}
|
|
4015
3974
|
return generateMatrix(matrix[0].length, matrix.length, (i, j) => matrix[j][i]);
|
|
4016
3975
|
}
|
|
3976
|
+
/**
|
|
3977
|
+
* Enables a formula function to accept matrix or vector inputs instead of simple value, computing results across multiple dimensions.
|
|
3978
|
+
*
|
|
3979
|
+
* ```
|
|
3980
|
+
* / |‾ ‾| \ |‾ ‾|
|
|
3981
|
+
* | | [A] | | | compute(A, D, E), compute(A, D, F), compute(A, D, G) |
|
|
3982
|
+
* applyVectorization| compute, | [B], D, [E, F, G] | | <=> | compute(B, D, E), compute(B, D, F), compute(B, D, G) |
|
|
3983
|
+
* | | [C] | | | compute(C, D, E), compute(C, D, F), compute(C, D, G) |
|
|
3984
|
+
* \ |_ _| / |_ _|
|
|
3985
|
+
* ```
|
|
3986
|
+
*
|
|
3987
|
+
* By default, all arguments are vectorized. To control which arguments are vectorized,
|
|
3988
|
+
* pass an `acceptToVectorize` boolean array of the same length as `args`:
|
|
3989
|
+
* - `true` enables vectorization for that argument
|
|
3990
|
+
* - `false` disables vectorization for that argument
|
|
3991
|
+
*
|
|
3992
|
+
* For example, with `[true, true, false]` on previous example you get:
|
|
3993
|
+
*
|
|
3994
|
+
* ```
|
|
3995
|
+
* |‾ ‾|
|
|
3996
|
+
* | compute(A, D, [E, F, G]) |
|
|
3997
|
+
* | compute(B, D, [E, F, G]) |
|
|
3998
|
+
* | compute(C, D, [E, F, G]) |
|
|
3999
|
+
* |_ _|
|
|
4000
|
+
* ```
|
|
4001
|
+
*
|
|
4002
|
+
* @remarks
|
|
4003
|
+
* This helper is automatically applied (by default) to **all** `compute` functions
|
|
4004
|
+
* across the various spreadsheet formula modules:
|
|
4005
|
+
* - If an argument is declared **scalar** (not `"range"`), it is vectorized.
|
|
4006
|
+
* - If **all** arguments are declared **ranges**, no vectorization occurs.
|
|
4007
|
+
* - e.g. `SUM(A1:B2)` returns a 1×1 sum over the range.
|
|
4008
|
+
* - e.g. `COS(A1:B2)` over `A1:B2` returns a 2×2 element-wise result.
|
|
4009
|
+
* - For special behaviors (e.g. the `IF` function), you may declare all arguments
|
|
4010
|
+
* as ranges and invoke this helper directly within your `compute` implementation.
|
|
4011
|
+
*/
|
|
4012
|
+
function applyVectorization(formula, args, acceptToVectorize = undefined) {
|
|
4013
|
+
let countVectorizedCol = 1;
|
|
4014
|
+
let countVectorizedRow = 1;
|
|
4015
|
+
let vectorizedColLimit = Infinity;
|
|
4016
|
+
let vectorizedRowLimit = Infinity;
|
|
4017
|
+
let vectorArgsType = undefined;
|
|
4018
|
+
for (let i = 0; i < args.length; i++) {
|
|
4019
|
+
const arg = args[i];
|
|
4020
|
+
if (isMatrix(arg) && (acceptToVectorize === undefined || acceptToVectorize[i])) {
|
|
4021
|
+
const nColumns = arg.length;
|
|
4022
|
+
const nRows = arg[0].length;
|
|
4023
|
+
if (nColumns !== 1 || nRows !== 1) {
|
|
4024
|
+
vectorArgsType ??= new Array(args.length);
|
|
4025
|
+
if (nColumns !== 1 && nRows !== 1) {
|
|
4026
|
+
vectorArgsType[i] = "matrix";
|
|
4027
|
+
countVectorizedCol = Math.max(countVectorizedCol, nColumns);
|
|
4028
|
+
countVectorizedRow = Math.max(countVectorizedRow, nRows);
|
|
4029
|
+
vectorizedColLimit = Math.min(vectorizedColLimit, nColumns);
|
|
4030
|
+
vectorizedRowLimit = Math.min(vectorizedRowLimit, nRows);
|
|
4031
|
+
}
|
|
4032
|
+
else if (nColumns !== 1) {
|
|
4033
|
+
vectorArgsType[i] = "horizontal";
|
|
4034
|
+
countVectorizedCol = Math.max(countVectorizedCol, nColumns);
|
|
4035
|
+
vectorizedColLimit = Math.min(vectorizedColLimit, nColumns);
|
|
4036
|
+
}
|
|
4037
|
+
else if (nRows !== 1) {
|
|
4038
|
+
vectorArgsType[i] = "vertical";
|
|
4039
|
+
countVectorizedRow = Math.max(countVectorizedRow, nRows);
|
|
4040
|
+
vectorizedRowLimit = Math.min(vectorizedRowLimit, nRows);
|
|
4041
|
+
}
|
|
4042
|
+
}
|
|
4043
|
+
else {
|
|
4044
|
+
args[i] = arg[0][0];
|
|
4045
|
+
}
|
|
4046
|
+
}
|
|
4047
|
+
}
|
|
4048
|
+
if (countVectorizedCol === 1 && countVectorizedRow === 1) {
|
|
4049
|
+
// either this function is not vectorized or it ends up with a 1x1 dimension
|
|
4050
|
+
return formula(...args);
|
|
4051
|
+
}
|
|
4052
|
+
const getArgOffset = (i, j) => args.map((arg, index) => {
|
|
4053
|
+
switch (vectorArgsType?.[index]) {
|
|
4054
|
+
case "matrix":
|
|
4055
|
+
return arg[i][j];
|
|
4056
|
+
case "horizontal":
|
|
4057
|
+
return arg[i][0];
|
|
4058
|
+
case "vertical":
|
|
4059
|
+
return arg[0][j];
|
|
4060
|
+
case undefined:
|
|
4061
|
+
return arg;
|
|
4062
|
+
}
|
|
4063
|
+
});
|
|
4064
|
+
return generateMatrix(countVectorizedCol, countVectorizedRow, (col, row) => {
|
|
4065
|
+
if (col > vectorizedColLimit - 1 || row > vectorizedRowLimit - 1) {
|
|
4066
|
+
return new NotAvailableError(_t("Array arguments to [[FUNCTION_NAME]] are of different size."));
|
|
4067
|
+
}
|
|
4068
|
+
const singleCellComputeResult = formula(...getArgOffset(col, row));
|
|
4069
|
+
// In the case where the user tries to vectorize arguments of an array formula, we will get an
|
|
4070
|
+
// array for every combination of the vectorized arguments, which will lead to a 3D matrix and
|
|
4071
|
+
// we won't be able to return the values.
|
|
4072
|
+
// In this case, we keep the first element of each spreading part, just as Excel does, and
|
|
4073
|
+
// create an array with these parts.
|
|
4074
|
+
// For exemple, we have MUNIT(x) that return an unitary matrix of x*x. If we use it with a
|
|
4075
|
+
// range, like MUNIT(A1:A2), we will get two unitary matrices (one for the value in A1 and one
|
|
4076
|
+
// for the value in A2). In this case, we will simply take the first value of each matrix and
|
|
4077
|
+
// return the array [First value of MUNIT(A1), First value of MUNIT(A2)].
|
|
4078
|
+
return isMatrix(singleCellComputeResult)
|
|
4079
|
+
? singleCellComputeResult[0][0]
|
|
4080
|
+
: singleCellComputeResult;
|
|
4081
|
+
});
|
|
4082
|
+
}
|
|
4017
4083
|
// -----------------------------------------------------------------------------
|
|
4018
4084
|
// CONDITIONAL EXPLORE FUNCTIONS
|
|
4019
4085
|
// -----------------------------------------------------------------------------
|
|
@@ -5798,6 +5864,67 @@ function scrollDelay(value) {
|
|
|
5798
5864
|
return MIN_DELAY + (MAX_DELAY - MIN_DELAY) * Math.exp(-ACCELERATION * (value - 1));
|
|
5799
5865
|
}
|
|
5800
5866
|
|
|
5867
|
+
/** Reference of a cell (eg. A1, $B$5) */
|
|
5868
|
+
const cellReference = new RegExp(/\$?([A-Z]{1,3})\$?([0-9]{1,7})/, "i");
|
|
5869
|
+
// Same as above, but matches the exact string (nothing before or after)
|
|
5870
|
+
const singleCellReference = new RegExp(/^\$?([A-Z]{1,3})\$?([0-9]{1,7})$/, "i");
|
|
5871
|
+
/** Reference of a column header (eg. A, AB, $A) */
|
|
5872
|
+
const colHeader = new RegExp(/^\$?([A-Z]{1,3})+$/, "i");
|
|
5873
|
+
/** Reference of a row header (eg. 1, $1) */
|
|
5874
|
+
const rowHeader = new RegExp(/^\$?([0-9]{1,7})+$/, "i");
|
|
5875
|
+
/** Reference of a column (eg. A, $CA, Sheet1!B) */
|
|
5876
|
+
const colReference = new RegExp(/^\s*('.+'!|[^']+!)?\$?([A-Z]{1,3})$/, "i");
|
|
5877
|
+
/** Reference of a row (eg. 1, 59, Sheet1!9) */
|
|
5878
|
+
const rowReference = new RegExp(/^\s*('.+'!|[^']+!)?\$?([0-9]{1,7})$/, "i");
|
|
5879
|
+
/** Reference of a normal range or a full row range (eg. A1:B1, 1:$5, $A2:5) */
|
|
5880
|
+
const fullRowXc = /(\$?[A-Z]{1,3})?\$?[0-9]{1,7}\s*:\s*(\$?[A-Z]{1,3})?\$?[0-9]{1,7}\s*/i;
|
|
5881
|
+
/** Reference of a normal range or a column row range (eg. A1:B1, A:$B, $A1:C) */
|
|
5882
|
+
const fullColXc = /\$?[A-Z]{1,3}(\$?[0-9]{1,7})?\s*:\s*\$?[A-Z]{1,3}(\$?[0-9]{1,7})?\s*/i;
|
|
5883
|
+
/** Reference of a cell or a range, it can be a bounded range, a full row or a full column */
|
|
5884
|
+
const rangeReference = new RegExp(/^\s*('.+'!|[^']+!)?/.source +
|
|
5885
|
+
"(" +
|
|
5886
|
+
[cellReference.source, fullRowXc.source, fullColXc.source].join("|") +
|
|
5887
|
+
")" +
|
|
5888
|
+
/$/.source, "i");
|
|
5889
|
+
/**
|
|
5890
|
+
* Return true if the given xc is the reference of a column (e.g. A or AC or Sheet1!A)
|
|
5891
|
+
*/
|
|
5892
|
+
function isColReference(xc) {
|
|
5893
|
+
return colReference.test(xc);
|
|
5894
|
+
}
|
|
5895
|
+
/**
|
|
5896
|
+
* Return true if the given xc is the reference of a column (e.g. 1 or Sheet1!1)
|
|
5897
|
+
*/
|
|
5898
|
+
function isRowReference(xc) {
|
|
5899
|
+
return rowReference.test(xc);
|
|
5900
|
+
}
|
|
5901
|
+
function isColHeader(str) {
|
|
5902
|
+
return colHeader.test(str);
|
|
5903
|
+
}
|
|
5904
|
+
function isRowHeader(str) {
|
|
5905
|
+
return rowHeader.test(str);
|
|
5906
|
+
}
|
|
5907
|
+
/**
|
|
5908
|
+
* Return true if the given xc is the reference of a single cell,
|
|
5909
|
+
* without any specified sheet (e.g. A1)
|
|
5910
|
+
*/
|
|
5911
|
+
function isSingleCellReference(xc) {
|
|
5912
|
+
return singleCellReference.test(xc);
|
|
5913
|
+
}
|
|
5914
|
+
function splitReference(ref) {
|
|
5915
|
+
if (!ref.includes("!")) {
|
|
5916
|
+
return { xc: ref };
|
|
5917
|
+
}
|
|
5918
|
+
const parts = ref.split("!");
|
|
5919
|
+
const xc = parts.pop();
|
|
5920
|
+
const sheetName = getUnquotedSheetName(parts.join("!")) || undefined;
|
|
5921
|
+
return { sheetName, xc };
|
|
5922
|
+
}
|
|
5923
|
+
/** Return a reference SheetName!xc from the given arguments */
|
|
5924
|
+
function getFullReference(sheetName, xc) {
|
|
5925
|
+
return sheetName !== undefined ? `${getCanonicalSymbolName(sheetName)}!${xc}` : xc;
|
|
5926
|
+
}
|
|
5927
|
+
|
|
5801
5928
|
class RangeImpl {
|
|
5802
5929
|
getSheetSize;
|
|
5803
5930
|
_zone;
|
|
@@ -7259,14 +7386,20 @@ function multiplyMatrices(matrix1, matrix2) {
|
|
|
7259
7386
|
/**
|
|
7260
7387
|
* Return the input if it's a scalar or the first element of the input if it's a matrix.
|
|
7261
7388
|
*/
|
|
7262
|
-
function toScalar(
|
|
7263
|
-
if (!isMatrix(
|
|
7264
|
-
return
|
|
7389
|
+
function toScalar(arg) {
|
|
7390
|
+
if (!isMatrix(arg)) {
|
|
7391
|
+
return arg;
|
|
7265
7392
|
}
|
|
7266
|
-
if (
|
|
7393
|
+
if (!isSingleElementMatrix(arg)) {
|
|
7267
7394
|
throw new EvaluationError(_t("The value should be a scalar or a 1x1 matrix"));
|
|
7268
7395
|
}
|
|
7269
|
-
return
|
|
7396
|
+
return arg[0][0];
|
|
7397
|
+
}
|
|
7398
|
+
function isSingleElementMatrix(matrix) {
|
|
7399
|
+
return matrix.length === 1 && matrix[0].length === 1;
|
|
7400
|
+
}
|
|
7401
|
+
function isMultipleElementMatrix(arg) {
|
|
7402
|
+
return isMatrix(arg) && !isSingleElementMatrix(arg);
|
|
7270
7403
|
}
|
|
7271
7404
|
|
|
7272
7405
|
function assertSameNumberOfElements(...args) {
|
|
@@ -21467,7 +21600,7 @@ const FILTER = {
|
|
|
21467
21600
|
}
|
|
21468
21601
|
return mode === "row" ? transposeMatrix(result) : result;
|
|
21469
21602
|
},
|
|
21470
|
-
isExported:
|
|
21603
|
+
isExported: false,
|
|
21471
21604
|
};
|
|
21472
21605
|
// -----------------------------------------------------------------------------
|
|
21473
21606
|
// SORT
|
|
@@ -24435,16 +24568,23 @@ const FALSE = {
|
|
|
24435
24568
|
const IF = {
|
|
24436
24569
|
description: _t("Returns value depending on logical expression."),
|
|
24437
24570
|
args: [
|
|
24438
|
-
arg("logical_expression (boolean)", _t("An expression or reference to a cell containing an expression that represents some logical value, i.e. TRUE or FALSE.")),
|
|
24439
|
-
arg("value_if_true (any)", _t("The value the function returns if logical_expression is TRUE.")),
|
|
24440
|
-
arg("value_if_false (any, default=FALSE)", _t("The value the function returns if logical_expression is FALSE.")),
|
|
24571
|
+
arg("logical_expression (boolean, range<boolean>)", _t("An expression or reference to a cell containing an expression that represents some logical value, i.e. TRUE or FALSE.")),
|
|
24572
|
+
arg("value_if_true (any, range)", _t("The value the function returns if logical_expression is TRUE.")),
|
|
24573
|
+
arg("value_if_false (any, range, default=FALSE)", _t("The value the function returns if logical_expression is FALSE.")),
|
|
24441
24574
|
],
|
|
24442
24575
|
compute: function (logicalExpression, valueIfTrue, valueIfFalse) {
|
|
24443
|
-
|
|
24576
|
+
if (isMultipleElementMatrix(logicalExpression)) {
|
|
24577
|
+
return applyVectorization(IF.compute, [logicalExpression, valueIfTrue, valueIfFalse]);
|
|
24578
|
+
}
|
|
24579
|
+
let result = toBoolean(toScalar(logicalExpression)) ? valueIfTrue : valueIfFalse;
|
|
24580
|
+
// useful for interpreting empty cell references as empty strings. But must be removed to make empty cell references equal to zero
|
|
24581
|
+
if (!isMultipleElementMatrix(result)) {
|
|
24582
|
+
result = toScalar(result);
|
|
24583
|
+
}
|
|
24444
24584
|
if (result === undefined) {
|
|
24445
24585
|
return { value: "" };
|
|
24446
24586
|
}
|
|
24447
|
-
if (result.value === null) {
|
|
24587
|
+
if (!isMatrix(result) && result.value === null) {
|
|
24448
24588
|
return { ...result, value: "" };
|
|
24449
24589
|
}
|
|
24450
24590
|
return result;
|
|
@@ -24457,15 +24597,22 @@ const IF = {
|
|
|
24457
24597
|
const IFERROR = {
|
|
24458
24598
|
description: _t("Value if it is not an error, otherwise 2nd argument."),
|
|
24459
24599
|
args: [
|
|
24460
|
-
arg("value (any)", _t("The value to return if value itself is not an error.")),
|
|
24461
|
-
arg(`value_if_error (any, default="empty")`, _t("The value the function returns if value is an error.")),
|
|
24600
|
+
arg("value (any, range)", _t("The value to return if value itself is not an error.")),
|
|
24601
|
+
arg(`value_if_error (any, range, default="empty")`, _t("The value the function returns if value is an error.")),
|
|
24462
24602
|
],
|
|
24463
|
-
compute: function (value, valueIfError
|
|
24464
|
-
|
|
24603
|
+
compute: function (value, valueIfError) {
|
|
24604
|
+
if (isMultipleElementMatrix(value)) {
|
|
24605
|
+
return applyVectorization(IFERROR.compute, [value, valueIfError]);
|
|
24606
|
+
}
|
|
24607
|
+
let result = isEvaluationError(toScalar(value)?.value) ? valueIfError : value;
|
|
24608
|
+
// useful for interpreting empty cell references as empty strings. But must be removed to make empty cell references equal to zero
|
|
24609
|
+
if (!isMultipleElementMatrix(result)) {
|
|
24610
|
+
result = toScalar(result);
|
|
24611
|
+
}
|
|
24465
24612
|
if (result === undefined) {
|
|
24466
24613
|
return { value: "" };
|
|
24467
24614
|
}
|
|
24468
|
-
if (result.value === null) {
|
|
24615
|
+
if (!isMatrix(result) && result.value === null) {
|
|
24469
24616
|
return { ...result, value: "" };
|
|
24470
24617
|
}
|
|
24471
24618
|
return result;
|
|
@@ -24478,15 +24625,22 @@ const IFERROR = {
|
|
|
24478
24625
|
const IFNA = {
|
|
24479
24626
|
description: _t("Value if it is not an #N/A error, otherwise 2nd argument."),
|
|
24480
24627
|
args: [
|
|
24481
|
-
arg("value (any)", _t("The value to return if value itself is not #N/A an error.")),
|
|
24482
|
-
arg(`value_if_error (any, default="empty")`, _t("The value the function returns if value is an #N/A error.")),
|
|
24628
|
+
arg("value (any, range)", _t("The value to return if value itself is not #N/A an error.")),
|
|
24629
|
+
arg(`value_if_error (any, range, default="empty")`, _t("The value the function returns if value is an #N/A error.")),
|
|
24483
24630
|
],
|
|
24484
|
-
compute: function (value, valueIfError
|
|
24485
|
-
|
|
24631
|
+
compute: function (value, valueIfError) {
|
|
24632
|
+
if (isMultipleElementMatrix(value)) {
|
|
24633
|
+
return applyVectorization(IFNA.compute, [value, valueIfError]);
|
|
24634
|
+
}
|
|
24635
|
+
let result = toScalar(value)?.value === CellErrorType.NotAvailable ? valueIfError : value;
|
|
24636
|
+
// useful for interpreting empty cell references as empty strings. But must be removed to make empty cell references equal to zero
|
|
24637
|
+
if (!isMultipleElementMatrix(result)) {
|
|
24638
|
+
result = toScalar(result);
|
|
24639
|
+
}
|
|
24486
24640
|
if (result === undefined) {
|
|
24487
24641
|
return { value: "" };
|
|
24488
24642
|
}
|
|
24489
|
-
if (result.value === null) {
|
|
24643
|
+
if (!isMatrix(result) && result.value === null) {
|
|
24490
24644
|
return { ...result, value: "" };
|
|
24491
24645
|
}
|
|
24492
24646
|
return result;
|
|
@@ -24499,23 +24653,31 @@ const IFNA = {
|
|
|
24499
24653
|
const IFS = {
|
|
24500
24654
|
description: _t("Returns a value depending on multiple logical expressions."),
|
|
24501
24655
|
args: [
|
|
24502
|
-
arg("condition1 (boolean)", _t("The first condition to be evaluated. This can be a boolean, a number, an array, or a reference to any of those.")),
|
|
24503
|
-
arg("value1 (any)", _t("The returned value if condition1 is TRUE.")),
|
|
24504
|
-
arg("condition2 (boolean, any, repeating)", _t("Additional conditions to be evaluated if the previous ones are FALSE.")),
|
|
24505
|
-
arg("value2 (any, repeating)", _t("Additional values to be returned if their corresponding conditions are TRUE.")),
|
|
24656
|
+
arg("condition1 (boolean, range<boolean>)", _t("The first condition to be evaluated. This can be a boolean, a number, an array, or a reference to any of those.")),
|
|
24657
|
+
arg("value1 (any, range)", _t("The returned value if condition1 is TRUE.")),
|
|
24658
|
+
arg("condition2 (boolean, any, range, repeating)", _t("Additional conditions to be evaluated if the previous ones are FALSE.")),
|
|
24659
|
+
arg("value2 (any, range, repeating)", _t("Additional values to be returned if their corresponding conditions are TRUE.")),
|
|
24506
24660
|
],
|
|
24507
24661
|
compute: function (...values) {
|
|
24508
24662
|
assert(() => values.length % 2 === 0, _t("Wrong number of arguments. Expected an even number of arguments."));
|
|
24509
|
-
|
|
24510
|
-
if (
|
|
24511
|
-
|
|
24512
|
-
|
|
24663
|
+
while (values.length > 0) {
|
|
24664
|
+
if (isMultipleElementMatrix(values[0])) {
|
|
24665
|
+
return applyVectorization(IFS.compute, values);
|
|
24666
|
+
}
|
|
24667
|
+
const condition = toBoolean(toScalar(values.shift()));
|
|
24668
|
+
let valueIfTrue = values.shift();
|
|
24669
|
+
if (condition) {
|
|
24670
|
+
// useful for interpreting empty cell references as empty strings. But must be removed to make empty cell references equal to zero
|
|
24671
|
+
if (!isMultipleElementMatrix(valueIfTrue)) {
|
|
24672
|
+
valueIfTrue = toScalar(valueIfTrue);
|
|
24673
|
+
}
|
|
24674
|
+
if (valueIfTrue === undefined) {
|
|
24513
24675
|
return { value: "" };
|
|
24514
24676
|
}
|
|
24515
|
-
if (
|
|
24516
|
-
return { ...
|
|
24677
|
+
if (!isMatrix(valueIfTrue) && valueIfTrue.value === null) {
|
|
24678
|
+
return { ...valueIfTrue, value: "" };
|
|
24517
24679
|
}
|
|
24518
|
-
return
|
|
24680
|
+
return valueIfTrue;
|
|
24519
24681
|
}
|
|
24520
24682
|
}
|
|
24521
24683
|
return new EvaluationError(_t("No match."));
|
|
@@ -26159,7 +26321,7 @@ const SPLIT = {
|
|
|
26159
26321
|
}
|
|
26160
26322
|
return transposeMatrix([result]);
|
|
26161
26323
|
},
|
|
26162
|
-
isExported:
|
|
26324
|
+
isExported: false,
|
|
26163
26325
|
};
|
|
26164
26326
|
// -----------------------------------------------------------------------------
|
|
26165
26327
|
// SUBSTITUTE
|
|
@@ -26352,86 +26514,21 @@ for (let category of categories) {
|
|
|
26352
26514
|
functionRegistry.add(name, { isExported: false, ...addDescr });
|
|
26353
26515
|
}
|
|
26354
26516
|
}
|
|
26355
|
-
|
|
26517
|
+
//------------------------------------------------------------------------------
|
|
26518
|
+
// CREATE COMPUTE FUNCTION
|
|
26519
|
+
//------------------------------------------------------------------------------
|
|
26356
26520
|
function createComputeFunction(descr, functionName) {
|
|
26357
26521
|
function vectorizedCompute(...args) {
|
|
26358
|
-
|
|
26359
|
-
let countVectorizableRow = 1;
|
|
26360
|
-
let vectorizableColLimit = Infinity;
|
|
26361
|
-
let vectorizableRowLimit = Infinity;
|
|
26362
|
-
let vectorArgsType = undefined;
|
|
26363
|
-
//#region Compute vectorisation limits
|
|
26522
|
+
const acceptToVectorize = [];
|
|
26364
26523
|
for (let i = 0; i < args.length; i++) {
|
|
26365
26524
|
const argDefinition = descr.args[descr.getArgToFocus(i + 1) - 1];
|
|
26366
26525
|
const arg = args[i];
|
|
26367
|
-
if (isMatrix(arg) && !argDefinition.acceptMatrix) {
|
|
26368
|
-
// if argDefinition does not accept a matrix but arg is still a matrix
|
|
26369
|
-
// --> triggers the arguments vectorization
|
|
26370
|
-
const nColumns = arg.length;
|
|
26371
|
-
const nRows = arg[0].length;
|
|
26372
|
-
if (nColumns !== 1 || nRows !== 1) {
|
|
26373
|
-
vectorArgsType ??= new Array(args.length);
|
|
26374
|
-
if (nColumns !== 1 && nRows !== 1) {
|
|
26375
|
-
vectorArgsType[i] = "matrix";
|
|
26376
|
-
countVectorizableCol = Math.max(countVectorizableCol, nColumns);
|
|
26377
|
-
countVectorizableRow = Math.max(countVectorizableRow, nRows);
|
|
26378
|
-
vectorizableColLimit = Math.min(vectorizableColLimit, nColumns);
|
|
26379
|
-
vectorizableRowLimit = Math.min(vectorizableRowLimit, nRows);
|
|
26380
|
-
}
|
|
26381
|
-
else if (nColumns !== 1) {
|
|
26382
|
-
vectorArgsType[i] = "horizontal";
|
|
26383
|
-
countVectorizableCol = Math.max(countVectorizableCol, nColumns);
|
|
26384
|
-
vectorizableColLimit = Math.min(vectorizableColLimit, nColumns);
|
|
26385
|
-
}
|
|
26386
|
-
else if (nRows !== 1) {
|
|
26387
|
-
vectorArgsType[i] = "vertical";
|
|
26388
|
-
countVectorizableRow = Math.max(countVectorizableRow, nRows);
|
|
26389
|
-
vectorizableRowLimit = Math.min(vectorizableRowLimit, nRows);
|
|
26390
|
-
}
|
|
26391
|
-
}
|
|
26392
|
-
else {
|
|
26393
|
-
args[i] = arg[0][0];
|
|
26394
|
-
}
|
|
26395
|
-
}
|
|
26396
26526
|
if (!isMatrix(arg) && argDefinition.acceptMatrixOnly) {
|
|
26397
26527
|
throw new BadExpressionError(_t("Function %s expects the parameter '%s' to be reference to a cell or range.", functionName, (i + 1).toString()));
|
|
26398
26528
|
}
|
|
26529
|
+
acceptToVectorize.push(!argDefinition.acceptMatrix);
|
|
26399
26530
|
}
|
|
26400
|
-
|
|
26401
|
-
if (countVectorizableCol === 1 && countVectorizableRow === 1) {
|
|
26402
|
-
// either this function is not vectorized or it ends up with a 1x1 dimension
|
|
26403
|
-
return errorHandlingCompute.apply(this, args);
|
|
26404
|
-
}
|
|
26405
|
-
const getArgOffset = (i, j) => args.map((arg, index) => {
|
|
26406
|
-
switch (vectorArgsType?.[index]) {
|
|
26407
|
-
case "matrix":
|
|
26408
|
-
return arg[i][j];
|
|
26409
|
-
case "horizontal":
|
|
26410
|
-
return arg[i][0];
|
|
26411
|
-
case "vertical":
|
|
26412
|
-
return arg[0][j];
|
|
26413
|
-
case undefined:
|
|
26414
|
-
return arg;
|
|
26415
|
-
}
|
|
26416
|
-
});
|
|
26417
|
-
return generateMatrix(countVectorizableCol, countVectorizableRow, (col, row) => {
|
|
26418
|
-
if (col > vectorizableColLimit - 1 || row > vectorizableRowLimit - 1) {
|
|
26419
|
-
return notAvailableError;
|
|
26420
|
-
}
|
|
26421
|
-
const singleCellComputeResult = errorHandlingCompute.apply(this, getArgOffset(col, row));
|
|
26422
|
-
// In the case where the user tries to vectorize arguments of an array formula, we will get an
|
|
26423
|
-
// array for every combination of the vectorized arguments, which will lead to a 3D matrix and
|
|
26424
|
-
// we won't be able to return the values.
|
|
26425
|
-
// In this case, we keep the first element of each spreading part, just as Excel does, and
|
|
26426
|
-
// create an array with these parts.
|
|
26427
|
-
// For exemple, we have MUNIT(x) that return an unitary matrix of x*x. If we use it with a
|
|
26428
|
-
// range, like MUNIT(A1:A2), we will get two unitary matrices (one for the value in A1 and one
|
|
26429
|
-
// for the value in A2). In this case, we will simply take the first value of each matrix and
|
|
26430
|
-
// return the array [First value of MUNIT(A1), First value of MUNIT(A2)].
|
|
26431
|
-
return isMatrix(singleCellComputeResult)
|
|
26432
|
-
? singleCellComputeResult[0][0]
|
|
26433
|
-
: singleCellComputeResult;
|
|
26434
|
-
});
|
|
26531
|
+
return applyVectorization(errorHandlingCompute.bind(this), args, acceptToVectorize);
|
|
26435
26532
|
}
|
|
26436
26533
|
function errorHandlingCompute(...args) {
|
|
26437
26534
|
for (let i = 0; i < args.length; i++) {
|
|
@@ -39420,7 +39517,7 @@ class AbstractComposerStore extends SpreadsheetStore {
|
|
|
39420
39517
|
proposals &&
|
|
39421
39518
|
!["ARG_SEPARATOR", "LEFT_PAREN", "OPERATOR"].includes(tokenAtCursor.type)) {
|
|
39422
39519
|
const filteredProposals = fuzzyLookup(searchTerm, proposals, (p) => p.fuzzySearchKey || p.text);
|
|
39423
|
-
if (!exactMatch || filteredProposals.length
|
|
39520
|
+
if (!exactMatch || filteredProposals.length) {
|
|
39424
39521
|
proposals = filteredProposals;
|
|
39425
39522
|
}
|
|
39426
39523
|
}
|
|
@@ -39495,12 +39592,13 @@ class StandaloneComposerStore extends AbstractComposerStore {
|
|
|
39495
39592
|
return providersDefinitions;
|
|
39496
39593
|
}
|
|
39497
39594
|
getComposerContent() {
|
|
39595
|
+
let content = this._currentContent;
|
|
39498
39596
|
if (this.editionMode === "inactive") {
|
|
39499
39597
|
// References in the content might not be linked to the current active sheet
|
|
39500
39598
|
// We here force the sheet name prefix for all references that are not in
|
|
39501
39599
|
// the current active sheet
|
|
39502
39600
|
const defaultRangeSheetId = this.args().defaultRangeSheetId;
|
|
39503
|
-
|
|
39601
|
+
content = rangeTokenize(this.args().content)
|
|
39504
39602
|
.map((token) => {
|
|
39505
39603
|
if (token.type === "REFERENCE") {
|
|
39506
39604
|
const range = this.getters.getRangeFromSheetXC(defaultRangeSheetId, token.value);
|
|
@@ -39510,7 +39608,7 @@ class StandaloneComposerStore extends AbstractComposerStore {
|
|
|
39510
39608
|
})
|
|
39511
39609
|
.join("");
|
|
39512
39610
|
}
|
|
39513
|
-
return this.
|
|
39611
|
+
return localizeContent(content, this.getters.getLocale());
|
|
39514
39612
|
}
|
|
39515
39613
|
stopEdition() {
|
|
39516
39614
|
this._stopEdition();
|
|
@@ -43113,6 +43211,9 @@ class PivotMeasureEditor extends owl.Component {
|
|
|
43113
43211
|
measure: this.props.measure,
|
|
43114
43212
|
});
|
|
43115
43213
|
}
|
|
43214
|
+
get isCalculatedMeasureInvalid() {
|
|
43215
|
+
return this.env.model.getters.getMeasureCompiledFormula(this.props.measure).isBadExpression;
|
|
43216
|
+
}
|
|
43116
43217
|
}
|
|
43117
43218
|
|
|
43118
43219
|
css /* scss */ `
|
|
@@ -44025,7 +44126,9 @@ function compareDimensionValues(dimension, a, b) {
|
|
|
44025
44126
|
return dimension.order === "asc" ? -1 : 1;
|
|
44026
44127
|
}
|
|
44027
44128
|
if (dimension.type === "integer" || dimension.type === "datetime") {
|
|
44028
|
-
return dimension.order === "asc"
|
|
44129
|
+
return dimension.order === "asc"
|
|
44130
|
+
? toNumber(a, DEFAULT_LOCALE) - toNumber(b, DEFAULT_LOCALE)
|
|
44131
|
+
: toNumber(b, DEFAULT_LOCALE) - toNumber(a, DEFAULT_LOCALE);
|
|
44029
44132
|
}
|
|
44030
44133
|
return dimension.order === "asc" ? a.localeCompare(b) : b.localeCompare(a);
|
|
44031
44134
|
}
|
|
@@ -46766,8 +46869,7 @@ class CellComposerStore extends AbstractComposerStore {
|
|
|
46766
46869
|
if (!spreader) {
|
|
46767
46870
|
return undefined;
|
|
46768
46871
|
}
|
|
46769
|
-
|
|
46770
|
-
return cell?.content;
|
|
46872
|
+
return this.getters.getCellText(spreader, { showFormula: true });
|
|
46771
46873
|
}
|
|
46772
46874
|
get currentEditedCell() {
|
|
46773
46875
|
return {
|
|
@@ -49570,6 +49672,7 @@ function useGridDrawing(refName, model, canvasSize) {
|
|
|
49570
49672
|
const friction = 0.95;
|
|
49571
49673
|
const verticalScrollFactor = 1;
|
|
49572
49674
|
const horizontalScrollFactor = 1;
|
|
49675
|
+
const resetTimeoutDuration = 100;
|
|
49573
49676
|
function useTouchScroll(ref, updateScroll, canMoveUp) {
|
|
49574
49677
|
let lastX = 0;
|
|
49575
49678
|
let lastY = 0;
|
|
@@ -49577,6 +49680,7 @@ function useTouchScroll(ref, updateScroll, canMoveUp) {
|
|
|
49577
49680
|
let velocityY = 0;
|
|
49578
49681
|
let isMouseDown = false;
|
|
49579
49682
|
let lastTime = 0;
|
|
49683
|
+
let resetTimeout = null;
|
|
49580
49684
|
useRefListener(ref, "touchstart", onTouchStart, { capture: false });
|
|
49581
49685
|
useRefListener(ref, "touchmove", onTouchMove, { capture: false });
|
|
49582
49686
|
useRefListener(ref, "touchend", onTouchEnd, { capture: false });
|
|
@@ -49589,6 +49693,10 @@ function useTouchScroll(ref, updateScroll, canMoveUp) {
|
|
|
49589
49693
|
function onTouchMove(event) {
|
|
49590
49694
|
if (!isMouseDown)
|
|
49591
49695
|
return;
|
|
49696
|
+
if (resetTimeout) {
|
|
49697
|
+
clearTimeout(resetTimeout);
|
|
49698
|
+
resetTimeout = null;
|
|
49699
|
+
}
|
|
49592
49700
|
const currentTime = Date.now();
|
|
49593
49701
|
const { clientX, clientY } = event.touches[0];
|
|
49594
49702
|
let deltaX = lastX - clientX;
|
|
@@ -49605,6 +49713,10 @@ function useTouchScroll(ref, updateScroll, canMoveUp) {
|
|
|
49605
49713
|
}
|
|
49606
49714
|
event.stopPropagation();
|
|
49607
49715
|
}
|
|
49716
|
+
resetTimeout = setTimeout(() => {
|
|
49717
|
+
velocityX = 0;
|
|
49718
|
+
velocityY = 0;
|
|
49719
|
+
}, resetTimeoutDuration);
|
|
49608
49720
|
updateScroll(deltaX * horizontalScrollFactor, deltaY * verticalScrollFactor);
|
|
49609
49721
|
}
|
|
49610
49722
|
function onTouchEnd(ev) {
|
|
@@ -58447,10 +58559,9 @@ class Evaluator {
|
|
|
58447
58559
|
return this.evaluatedCells.keysForSheet(sheetId);
|
|
58448
58560
|
}
|
|
58449
58561
|
getArrayFormulaSpreadingOn(position) {
|
|
58450
|
-
const
|
|
58451
|
-
|
|
58452
|
-
|
|
58453
|
-
return this.spreadingRelations.isArrayFormula(position) ? position : undefined;
|
|
58562
|
+
const isEmpty = this.getEvaluatedCell(position).type === CellValueType.empty;
|
|
58563
|
+
if (isEmpty) {
|
|
58564
|
+
return undefined;
|
|
58454
58565
|
}
|
|
58455
58566
|
const arrayFormulas = this.spreadingRelations.searchFormulaPositionsSpreadingOn(position.sheetId, positionToZone(position));
|
|
58456
58567
|
return Array.from(arrayFormulas).find((position) => !this.blockedArrayFormulas.has(position));
|
|
@@ -74293,6 +74404,6 @@ exports.tokenColors = tokenColors;
|
|
|
74293
74404
|
exports.tokenize = tokenize;
|
|
74294
74405
|
|
|
74295
74406
|
|
|
74296
|
-
__info__.version = "18.0.
|
|
74297
|
-
__info__.date = "2025-05-
|
|
74298
|
-
__info__.hash = "
|
|
74407
|
+
__info__.version = "18.0.30";
|
|
74408
|
+
__info__.date = "2025-05-26T12:35:05.184Z";
|
|
74409
|
+
__info__.hash = "838c4f7";
|