@odoo/o-spreadsheet 18.1.20 → 18.1.22
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 +41 -40
- package/dist/o-spreadsheet.esm.js +354 -243
- package/dist/o-spreadsheet.iife.js +354 -243
- package/dist/o-spreadsheet.iife.min.js +380 -380
- 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.1.
|
|
6
|
-
* @date 2025-05-
|
|
7
|
-
* @hash
|
|
5
|
+
* @version 18.1.22
|
|
6
|
+
* @date 2025-05-26T12:35:56.145Z
|
|
7
|
+
* @hash ff4b0ba
|
|
8
8
|
*/
|
|
9
9
|
|
|
10
10
|
'use strict';
|
|
@@ -1608,18 +1608,53 @@ function lettersToNumber(letters) {
|
|
|
1608
1608
|
let result = 0;
|
|
1609
1609
|
const l = letters.length;
|
|
1610
1610
|
for (let i = 0; i < l; i++) {
|
|
1611
|
-
const
|
|
1612
|
-
const colIndex = charCode >= 65 && charCode <= 90 ? charCode - 64 : charCode - 96;
|
|
1611
|
+
const colIndex = charToNumber(letters[i]);
|
|
1613
1612
|
result = result * 26 + colIndex;
|
|
1614
1613
|
}
|
|
1615
1614
|
return result - 1;
|
|
1616
1615
|
}
|
|
1616
|
+
function charToNumber(char) {
|
|
1617
|
+
const charCode = char.charCodeAt(0);
|
|
1618
|
+
return charCode >= 65 && charCode <= 90 ? charCode - 64 : charCode - 96;
|
|
1619
|
+
}
|
|
1617
1620
|
function isCharALetter(char) {
|
|
1618
1621
|
return (char >= "A" && char <= "Z") || (char >= "a" && char <= "z");
|
|
1619
1622
|
}
|
|
1620
1623
|
function isCharADigit(char) {
|
|
1621
1624
|
return char >= "0" && char <= "9";
|
|
1622
1625
|
}
|
|
1626
|
+
// we limit the max column to 3 letters and max row to 7 digits for performance reasons
|
|
1627
|
+
const MAX_COL = lettersToNumber("ZZZ");
|
|
1628
|
+
const MAX_ROW = 9999998;
|
|
1629
|
+
function consumeSpaces(chars) {
|
|
1630
|
+
while (chars.current === " ") {
|
|
1631
|
+
chars.advanceBy(1);
|
|
1632
|
+
}
|
|
1633
|
+
}
|
|
1634
|
+
function consumeLetters(chars) {
|
|
1635
|
+
if (chars.current === "$")
|
|
1636
|
+
chars.advanceBy(1);
|
|
1637
|
+
if (!chars.current || !isCharALetter(chars.current)) {
|
|
1638
|
+
return -1;
|
|
1639
|
+
}
|
|
1640
|
+
let colCoordinate = 0;
|
|
1641
|
+
while (chars.current && isCharALetter(chars.current)) {
|
|
1642
|
+
colCoordinate = colCoordinate * 26 + charToNumber(chars.shift());
|
|
1643
|
+
}
|
|
1644
|
+
return colCoordinate;
|
|
1645
|
+
}
|
|
1646
|
+
function consumeDigits(chars) {
|
|
1647
|
+
if (chars.current === "$")
|
|
1648
|
+
chars.advanceBy(1);
|
|
1649
|
+
if (!chars.current || !isCharADigit(chars.current)) {
|
|
1650
|
+
return -1;
|
|
1651
|
+
}
|
|
1652
|
+
let num = 0;
|
|
1653
|
+
while (chars.current && isCharADigit(chars.current)) {
|
|
1654
|
+
num = num * 10 + Number(chars.shift());
|
|
1655
|
+
}
|
|
1656
|
+
return num;
|
|
1657
|
+
}
|
|
1623
1658
|
/**
|
|
1624
1659
|
* Convert a "XC" coordinate to cartesian coordinates.
|
|
1625
1660
|
*
|
|
@@ -1630,33 +1665,17 @@ function isCharADigit(char) {
|
|
|
1630
1665
|
* Note: it also accepts lowercase coordinates, but not fixed references
|
|
1631
1666
|
*/
|
|
1632
1667
|
function toCartesian(xc) {
|
|
1633
|
-
|
|
1634
|
-
|
|
1635
|
-
|
|
1636
|
-
|
|
1637
|
-
// Process letter part
|
|
1638
|
-
if (xc[i] === "$")
|
|
1639
|
-
i++;
|
|
1640
|
-
while (i < xc.length && isCharALetter(xc[i])) {
|
|
1641
|
-
letterPart += xc[i++];
|
|
1642
|
-
}
|
|
1643
|
-
if (letterPart.length === 0 || letterPart.length > 3) {
|
|
1644
|
-
// limit to max 3 letters for performance reasons
|
|
1668
|
+
const chars = new TokenizingChars(xc);
|
|
1669
|
+
consumeSpaces(chars);
|
|
1670
|
+
const letterPart = consumeLetters(chars);
|
|
1671
|
+
if (letterPart === -1 || !chars.current) {
|
|
1645
1672
|
throw new Error(`Invalid cell description: ${xc}`);
|
|
1646
1673
|
}
|
|
1647
|
-
|
|
1648
|
-
|
|
1649
|
-
|
|
1650
|
-
|
|
1651
|
-
|
|
1652
|
-
}
|
|
1653
|
-
if (i !== xc.length || numberPart.length === 0 || numberPart.length > 7) {
|
|
1654
|
-
// limit to max 7 numbers for performance reasons
|
|
1655
|
-
throw new Error(`Invalid cell description: ${xc}`);
|
|
1656
|
-
}
|
|
1657
|
-
const col = lettersToNumber(letterPart);
|
|
1658
|
-
const row = Number(numberPart) - 1;
|
|
1659
|
-
if (isNaN(row)) {
|
|
1674
|
+
const num = consumeDigits(chars);
|
|
1675
|
+
consumeSpaces(chars);
|
|
1676
|
+
const col = letterPart - 1;
|
|
1677
|
+
const row = num - 1;
|
|
1678
|
+
if (!chars.isOver() || col > MAX_COL || row > MAX_ROW) {
|
|
1660
1679
|
throw new Error(`Invalid cell description: ${xc}`);
|
|
1661
1680
|
}
|
|
1662
1681
|
return { col, row };
|
|
@@ -2068,67 +2087,6 @@ class LazyTranslatedString extends String {
|
|
|
2068
2087
|
}
|
|
2069
2088
|
}
|
|
2070
2089
|
|
|
2071
|
-
/** Reference of a cell (eg. A1, $B$5) */
|
|
2072
|
-
const cellReference = new RegExp(/\$?([A-Z]{1,3})\$?([0-9]{1,7})/, "i");
|
|
2073
|
-
// Same as above, but matches the exact string (nothing before or after)
|
|
2074
|
-
const singleCellReference = new RegExp(/^\$?([A-Z]{1,3})\$?([0-9]{1,7})$/, "i");
|
|
2075
|
-
/** Reference of a column header (eg. A, AB, $A) */
|
|
2076
|
-
const colHeader = new RegExp(/^\$?([A-Z]{1,3})+$/, "i");
|
|
2077
|
-
/** Reference of a row header (eg. 1, $1) */
|
|
2078
|
-
const rowHeader = new RegExp(/^\$?([0-9]{1,7})+$/, "i");
|
|
2079
|
-
/** Reference of a column (eg. A, $CA, Sheet1!B) */
|
|
2080
|
-
const colReference = new RegExp(/^\s*('.+'!|[^']+!)?\$?([A-Z]{1,3})$/, "i");
|
|
2081
|
-
/** Reference of a row (eg. 1, 59, Sheet1!9) */
|
|
2082
|
-
const rowReference = new RegExp(/^\s*('.+'!|[^']+!)?\$?([0-9]{1,7})$/, "i");
|
|
2083
|
-
/** Reference of a normal range or a full row range (eg. A1:B1, 1:$5, $A2:5) */
|
|
2084
|
-
const fullRowXc = /(\$?[A-Z]{1,3})?\$?[0-9]{1,7}\s*:\s*(\$?[A-Z]{1,3})?\$?[0-9]{1,7}\s*/i;
|
|
2085
|
-
/** Reference of a normal range or a column row range (eg. A1:B1, A:$B, $A1:C) */
|
|
2086
|
-
const fullColXc = /\$?[A-Z]{1,3}(\$?[0-9]{1,7})?\s*:\s*\$?[A-Z]{1,3}(\$?[0-9]{1,7})?\s*/i;
|
|
2087
|
-
/** Reference of a cell or a range, it can be a bounded range, a full row or a full column */
|
|
2088
|
-
const rangeReference = new RegExp(/^\s*('.+'!|[^']+!)?/.source +
|
|
2089
|
-
"(" +
|
|
2090
|
-
[cellReference.source, fullRowXc.source, fullColXc.source].join("|") +
|
|
2091
|
-
")" +
|
|
2092
|
-
/$/.source, "i");
|
|
2093
|
-
/**
|
|
2094
|
-
* Return true if the given xc is the reference of a column (e.g. A or AC or Sheet1!A)
|
|
2095
|
-
*/
|
|
2096
|
-
function isColReference(xc) {
|
|
2097
|
-
return colReference.test(xc);
|
|
2098
|
-
}
|
|
2099
|
-
/**
|
|
2100
|
-
* Return true if the given xc is the reference of a column (e.g. 1 or Sheet1!1)
|
|
2101
|
-
*/
|
|
2102
|
-
function isRowReference(xc) {
|
|
2103
|
-
return rowReference.test(xc);
|
|
2104
|
-
}
|
|
2105
|
-
function isColHeader(str) {
|
|
2106
|
-
return colHeader.test(str);
|
|
2107
|
-
}
|
|
2108
|
-
function isRowHeader(str) {
|
|
2109
|
-
return rowHeader.test(str);
|
|
2110
|
-
}
|
|
2111
|
-
/**
|
|
2112
|
-
* Return true if the given xc is the reference of a single cell,
|
|
2113
|
-
* without any specified sheet (e.g. A1)
|
|
2114
|
-
*/
|
|
2115
|
-
function isSingleCellReference(xc) {
|
|
2116
|
-
return singleCellReference.test(xc);
|
|
2117
|
-
}
|
|
2118
|
-
function splitReference(ref) {
|
|
2119
|
-
if (!ref.includes("!")) {
|
|
2120
|
-
return { xc: ref };
|
|
2121
|
-
}
|
|
2122
|
-
const parts = ref.split("!");
|
|
2123
|
-
const xc = parts.pop();
|
|
2124
|
-
const sheetName = getUnquotedSheetName(parts.join("!")) || undefined;
|
|
2125
|
-
return { sheetName, xc };
|
|
2126
|
-
}
|
|
2127
|
-
/** Return a reference SheetName!xc from the given arguments */
|
|
2128
|
-
function getFullReference(sheetName, xc) {
|
|
2129
|
-
return sheetName !== undefined ? `${getCanonicalSymbolName(sheetName)}!${xc}` : xc;
|
|
2130
|
-
}
|
|
2131
|
-
|
|
2132
2090
|
/**
|
|
2133
2091
|
* Convert from a cartesian reference to a Zone
|
|
2134
2092
|
* The range boundaries will be kept in the same order as the
|
|
@@ -2146,63 +2104,55 @@ function getFullReference(sheetName, xc) {
|
|
|
2146
2104
|
*
|
|
2147
2105
|
*/
|
|
2148
2106
|
function toZoneWithoutBoundaryChanges(xc) {
|
|
2149
|
-
|
|
2150
|
-
|
|
2151
|
-
|
|
2152
|
-
if (
|
|
2153
|
-
|
|
2154
|
-
}
|
|
2155
|
-
|
|
2156
|
-
|
|
2157
|
-
if (xc.includes(":")) {
|
|
2158
|
-
[firstRangePart, secondRangePart] = xc.split(":");
|
|
2159
|
-
firstRangePart = firstRangePart.trim();
|
|
2160
|
-
secondRangePart = secondRangePart.trim();
|
|
2161
|
-
}
|
|
2162
|
-
else {
|
|
2163
|
-
firstRangePart = xc.trim();
|
|
2164
|
-
}
|
|
2107
|
+
const chars = new TokenizingChars(xc);
|
|
2108
|
+
consumeSpaces(chars);
|
|
2109
|
+
const sheetSeparatorIndex = xc.indexOf("!");
|
|
2110
|
+
if (sheetSeparatorIndex !== -1) {
|
|
2111
|
+
chars.advanceBy(sheetSeparatorIndex + 1);
|
|
2112
|
+
}
|
|
2113
|
+
const leftLetters = consumeLetters(chars);
|
|
2114
|
+
const leftNumbers = consumeDigits(chars);
|
|
2165
2115
|
let top, bottom, left, right;
|
|
2166
2116
|
let fullCol = false;
|
|
2167
2117
|
let fullRow = false;
|
|
2168
2118
|
let hasHeader = false;
|
|
2169
|
-
if (
|
|
2170
|
-
left = right =
|
|
2119
|
+
if (leftNumbers === -1) {
|
|
2120
|
+
left = right = leftLetters - 1;
|
|
2171
2121
|
top = bottom = 0;
|
|
2172
2122
|
fullCol = true;
|
|
2173
2123
|
}
|
|
2174
|
-
else if (
|
|
2175
|
-
top = bottom =
|
|
2124
|
+
else if (leftLetters === -1) {
|
|
2125
|
+
top = bottom = leftNumbers - 1;
|
|
2176
2126
|
left = right = 0;
|
|
2177
2127
|
fullRow = true;
|
|
2178
2128
|
}
|
|
2179
2129
|
else {
|
|
2180
|
-
|
|
2181
|
-
|
|
2182
|
-
top = bottom = c.row;
|
|
2130
|
+
left = right = leftLetters - 1;
|
|
2131
|
+
top = bottom = leftNumbers - 1;
|
|
2183
2132
|
hasHeader = true;
|
|
2184
2133
|
}
|
|
2185
|
-
|
|
2186
|
-
|
|
2187
|
-
|
|
2134
|
+
consumeSpaces(chars);
|
|
2135
|
+
if (chars.current === ":") {
|
|
2136
|
+
chars.advanceBy(1);
|
|
2137
|
+
consumeSpaces(chars);
|
|
2138
|
+
const rightLetters = consumeLetters(chars);
|
|
2139
|
+
const rightNumbers = consumeDigits(chars);
|
|
2140
|
+
if (rightNumbers === -1) {
|
|
2141
|
+
right = rightLetters - 1;
|
|
2188
2142
|
fullCol = true;
|
|
2189
2143
|
}
|
|
2190
|
-
else if (
|
|
2191
|
-
bottom =
|
|
2144
|
+
else if (rightLetters === -1) {
|
|
2145
|
+
bottom = rightNumbers - 1;
|
|
2192
2146
|
fullRow = true;
|
|
2193
2147
|
}
|
|
2194
2148
|
else {
|
|
2195
|
-
|
|
2196
|
-
|
|
2197
|
-
bottom = c.row;
|
|
2149
|
+
right = rightLetters - 1;
|
|
2150
|
+
bottom = rightNumbers - 1;
|
|
2198
2151
|
top = fullCol ? bottom : top;
|
|
2199
2152
|
left = fullRow ? right : left;
|
|
2200
2153
|
hasHeader = true;
|
|
2201
2154
|
}
|
|
2202
2155
|
}
|
|
2203
|
-
if (fullCol && fullRow) {
|
|
2204
|
-
throw new Error("Wrong zone xc. The zone cannot be at the same time a full column and a full row");
|
|
2205
|
-
}
|
|
2206
2156
|
const zone = {
|
|
2207
2157
|
top,
|
|
2208
2158
|
left,
|
|
@@ -2231,7 +2181,16 @@ function toZoneWithoutBoundaryChanges(xc) {
|
|
|
2231
2181
|
*/
|
|
2232
2182
|
function toUnboundedZone(xc) {
|
|
2233
2183
|
const zone = toZoneWithoutBoundaryChanges(xc);
|
|
2234
|
-
|
|
2184
|
+
const orderedZone = reorderZone(zone);
|
|
2185
|
+
const bottom = orderedZone.bottom;
|
|
2186
|
+
const right = orderedZone.right;
|
|
2187
|
+
if ((bottom !== undefined && bottom > MAX_ROW) || (right !== undefined && right > MAX_COL)) {
|
|
2188
|
+
throw new Error(`Range string out of bounds: ${xc}`); // limit the size of the zone for performance
|
|
2189
|
+
}
|
|
2190
|
+
if (bottom === undefined && right === undefined) {
|
|
2191
|
+
throw new Error("Wrong zone xc. The zone cannot be at the same time a full column and a full row");
|
|
2192
|
+
}
|
|
2193
|
+
return orderedZone;
|
|
2235
2194
|
}
|
|
2236
2195
|
/**
|
|
2237
2196
|
* Convert from a cartesian reference to a Zone.
|
|
@@ -4183,6 +4142,113 @@ function transposeMatrix(matrix) {
|
|
|
4183
4142
|
}
|
|
4184
4143
|
return generateMatrix(matrix[0].length, matrix.length, (i, j) => matrix[j][i]);
|
|
4185
4144
|
}
|
|
4145
|
+
/**
|
|
4146
|
+
* Enables a formula function to accept matrix or vector inputs instead of simple value, computing results across multiple dimensions.
|
|
4147
|
+
*
|
|
4148
|
+
* ```
|
|
4149
|
+
* / |‾ ‾| \ |‾ ‾|
|
|
4150
|
+
* | | [A] | | | compute(A, D, E), compute(A, D, F), compute(A, D, G) |
|
|
4151
|
+
* applyVectorization| compute, | [B], D, [E, F, G] | | <=> | compute(B, D, E), compute(B, D, F), compute(B, D, G) |
|
|
4152
|
+
* | | [C] | | | compute(C, D, E), compute(C, D, F), compute(C, D, G) |
|
|
4153
|
+
* \ |_ _| / |_ _|
|
|
4154
|
+
* ```
|
|
4155
|
+
*
|
|
4156
|
+
* By default, all arguments are vectorized. To control which arguments are vectorized,
|
|
4157
|
+
* pass an `acceptToVectorize` boolean array of the same length as `args`:
|
|
4158
|
+
* - `true` enables vectorization for that argument
|
|
4159
|
+
* - `false` disables vectorization for that argument
|
|
4160
|
+
*
|
|
4161
|
+
* For example, with `[true, true, false]` on previous example you get:
|
|
4162
|
+
*
|
|
4163
|
+
* ```
|
|
4164
|
+
* |‾ ‾|
|
|
4165
|
+
* | compute(A, D, [E, F, G]) |
|
|
4166
|
+
* | compute(B, D, [E, F, G]) |
|
|
4167
|
+
* | compute(C, D, [E, F, G]) |
|
|
4168
|
+
* |_ _|
|
|
4169
|
+
* ```
|
|
4170
|
+
*
|
|
4171
|
+
* @remarks
|
|
4172
|
+
* This helper is automatically applied (by default) to **all** `compute` functions
|
|
4173
|
+
* across the various spreadsheet formula modules:
|
|
4174
|
+
* - If an argument is declared **scalar** (not `"range"`), it is vectorized.
|
|
4175
|
+
* - If **all** arguments are declared **ranges**, no vectorization occurs.
|
|
4176
|
+
* - e.g. `SUM(A1:B2)` returns a 1×1 sum over the range.
|
|
4177
|
+
* - e.g. `COS(A1:B2)` over `A1:B2` returns a 2×2 element-wise result.
|
|
4178
|
+
* - For special behaviors (e.g. the `IF` function), you may declare all arguments
|
|
4179
|
+
* as ranges and invoke this helper directly within your `compute` implementation.
|
|
4180
|
+
*/
|
|
4181
|
+
function applyVectorization(formula, args, acceptToVectorize = undefined) {
|
|
4182
|
+
let countVectorizedCol = 1;
|
|
4183
|
+
let countVectorizedRow = 1;
|
|
4184
|
+
let vectorizedColLimit = Infinity;
|
|
4185
|
+
let vectorizedRowLimit = Infinity;
|
|
4186
|
+
let vectorArgsType = undefined;
|
|
4187
|
+
for (let i = 0; i < args.length; i++) {
|
|
4188
|
+
const arg = args[i];
|
|
4189
|
+
if (isMatrix(arg) && (acceptToVectorize === undefined || acceptToVectorize[i])) {
|
|
4190
|
+
const nColumns = arg.length;
|
|
4191
|
+
const nRows = arg[0].length;
|
|
4192
|
+
if (nColumns !== 1 || nRows !== 1) {
|
|
4193
|
+
vectorArgsType ??= new Array(args.length);
|
|
4194
|
+
if (nColumns !== 1 && nRows !== 1) {
|
|
4195
|
+
vectorArgsType[i] = "matrix";
|
|
4196
|
+
countVectorizedCol = Math.max(countVectorizedCol, nColumns);
|
|
4197
|
+
countVectorizedRow = Math.max(countVectorizedRow, nRows);
|
|
4198
|
+
vectorizedColLimit = Math.min(vectorizedColLimit, nColumns);
|
|
4199
|
+
vectorizedRowLimit = Math.min(vectorizedRowLimit, nRows);
|
|
4200
|
+
}
|
|
4201
|
+
else if (nColumns !== 1) {
|
|
4202
|
+
vectorArgsType[i] = "horizontal";
|
|
4203
|
+
countVectorizedCol = Math.max(countVectorizedCol, nColumns);
|
|
4204
|
+
vectorizedColLimit = Math.min(vectorizedColLimit, nColumns);
|
|
4205
|
+
}
|
|
4206
|
+
else if (nRows !== 1) {
|
|
4207
|
+
vectorArgsType[i] = "vertical";
|
|
4208
|
+
countVectorizedRow = Math.max(countVectorizedRow, nRows);
|
|
4209
|
+
vectorizedRowLimit = Math.min(vectorizedRowLimit, nRows);
|
|
4210
|
+
}
|
|
4211
|
+
}
|
|
4212
|
+
else {
|
|
4213
|
+
args[i] = arg[0][0];
|
|
4214
|
+
}
|
|
4215
|
+
}
|
|
4216
|
+
}
|
|
4217
|
+
if (countVectorizedCol === 1 && countVectorizedRow === 1) {
|
|
4218
|
+
// either this function is not vectorized or it ends up with a 1x1 dimension
|
|
4219
|
+
return formula(...args);
|
|
4220
|
+
}
|
|
4221
|
+
const getArgOffset = (i, j) => args.map((arg, index) => {
|
|
4222
|
+
switch (vectorArgsType?.[index]) {
|
|
4223
|
+
case "matrix":
|
|
4224
|
+
return arg[i][j];
|
|
4225
|
+
case "horizontal":
|
|
4226
|
+
return arg[i][0];
|
|
4227
|
+
case "vertical":
|
|
4228
|
+
return arg[0][j];
|
|
4229
|
+
case undefined:
|
|
4230
|
+
return arg;
|
|
4231
|
+
}
|
|
4232
|
+
});
|
|
4233
|
+
return generateMatrix(countVectorizedCol, countVectorizedRow, (col, row) => {
|
|
4234
|
+
if (col > vectorizedColLimit - 1 || row > vectorizedRowLimit - 1) {
|
|
4235
|
+
return new NotAvailableError(_t("Array arguments to [[FUNCTION_NAME]] are of different size."));
|
|
4236
|
+
}
|
|
4237
|
+
const singleCellComputeResult = formula(...getArgOffset(col, row));
|
|
4238
|
+
// In the case where the user tries to vectorize arguments of an array formula, we will get an
|
|
4239
|
+
// array for every combination of the vectorized arguments, which will lead to a 3D matrix and
|
|
4240
|
+
// we won't be able to return the values.
|
|
4241
|
+
// In this case, we keep the first element of each spreading part, just as Excel does, and
|
|
4242
|
+
// create an array with these parts.
|
|
4243
|
+
// For exemple, we have MUNIT(x) that return an unitary matrix of x*x. If we use it with a
|
|
4244
|
+
// range, like MUNIT(A1:A2), we will get two unitary matrices (one for the value in A1 and one
|
|
4245
|
+
// for the value in A2). In this case, we will simply take the first value of each matrix and
|
|
4246
|
+
// return the array [First value of MUNIT(A1), First value of MUNIT(A2)].
|
|
4247
|
+
return isMatrix(singleCellComputeResult)
|
|
4248
|
+
? singleCellComputeResult[0][0]
|
|
4249
|
+
: singleCellComputeResult;
|
|
4250
|
+
});
|
|
4251
|
+
}
|
|
4186
4252
|
// -----------------------------------------------------------------------------
|
|
4187
4253
|
// CONDITIONAL EXPLORE FUNCTIONS
|
|
4188
4254
|
// -----------------------------------------------------------------------------
|
|
@@ -5967,6 +6033,67 @@ function scrollDelay(value) {
|
|
|
5967
6033
|
return MIN_DELAY + (MAX_DELAY - MIN_DELAY) * Math.exp(-ACCELERATION * (value - 1));
|
|
5968
6034
|
}
|
|
5969
6035
|
|
|
6036
|
+
/** Reference of a cell (eg. A1, $B$5) */
|
|
6037
|
+
const cellReference = new RegExp(/\$?([A-Z]{1,3})\$?([0-9]{1,7})/, "i");
|
|
6038
|
+
// Same as above, but matches the exact string (nothing before or after)
|
|
6039
|
+
const singleCellReference = new RegExp(/^\$?([A-Z]{1,3})\$?([0-9]{1,7})$/, "i");
|
|
6040
|
+
/** Reference of a column header (eg. A, AB, $A) */
|
|
6041
|
+
const colHeader = new RegExp(/^\$?([A-Z]{1,3})+$/, "i");
|
|
6042
|
+
/** Reference of a row header (eg. 1, $1) */
|
|
6043
|
+
const rowHeader = new RegExp(/^\$?([0-9]{1,7})+$/, "i");
|
|
6044
|
+
/** Reference of a column (eg. A, $CA, Sheet1!B) */
|
|
6045
|
+
const colReference = new RegExp(/^\s*('.+'!|[^']+!)?\$?([A-Z]{1,3})$/, "i");
|
|
6046
|
+
/** Reference of a row (eg. 1, 59, Sheet1!9) */
|
|
6047
|
+
const rowReference = new RegExp(/^\s*('.+'!|[^']+!)?\$?([0-9]{1,7})$/, "i");
|
|
6048
|
+
/** Reference of a normal range or a full row range (eg. A1:B1, 1:$5, $A2:5) */
|
|
6049
|
+
const fullRowXc = /(\$?[A-Z]{1,3})?\$?[0-9]{1,7}\s*:\s*(\$?[A-Z]{1,3})?\$?[0-9]{1,7}\s*/i;
|
|
6050
|
+
/** Reference of a normal range or a column row range (eg. A1:B1, A:$B, $A1:C) */
|
|
6051
|
+
const fullColXc = /\$?[A-Z]{1,3}(\$?[0-9]{1,7})?\s*:\s*\$?[A-Z]{1,3}(\$?[0-9]{1,7})?\s*/i;
|
|
6052
|
+
/** Reference of a cell or a range, it can be a bounded range, a full row or a full column */
|
|
6053
|
+
const rangeReference = new RegExp(/^\s*('.+'!|[^']+!)?/.source +
|
|
6054
|
+
"(" +
|
|
6055
|
+
[cellReference.source, fullRowXc.source, fullColXc.source].join("|") +
|
|
6056
|
+
")" +
|
|
6057
|
+
/$/.source, "i");
|
|
6058
|
+
/**
|
|
6059
|
+
* Return true if the given xc is the reference of a column (e.g. A or AC or Sheet1!A)
|
|
6060
|
+
*/
|
|
6061
|
+
function isColReference(xc) {
|
|
6062
|
+
return colReference.test(xc);
|
|
6063
|
+
}
|
|
6064
|
+
/**
|
|
6065
|
+
* Return true if the given xc is the reference of a column (e.g. 1 or Sheet1!1)
|
|
6066
|
+
*/
|
|
6067
|
+
function isRowReference(xc) {
|
|
6068
|
+
return rowReference.test(xc);
|
|
6069
|
+
}
|
|
6070
|
+
function isColHeader(str) {
|
|
6071
|
+
return colHeader.test(str);
|
|
6072
|
+
}
|
|
6073
|
+
function isRowHeader(str) {
|
|
6074
|
+
return rowHeader.test(str);
|
|
6075
|
+
}
|
|
6076
|
+
/**
|
|
6077
|
+
* Return true if the given xc is the reference of a single cell,
|
|
6078
|
+
* without any specified sheet (e.g. A1)
|
|
6079
|
+
*/
|
|
6080
|
+
function isSingleCellReference(xc) {
|
|
6081
|
+
return singleCellReference.test(xc);
|
|
6082
|
+
}
|
|
6083
|
+
function splitReference(ref) {
|
|
6084
|
+
if (!ref.includes("!")) {
|
|
6085
|
+
return { xc: ref };
|
|
6086
|
+
}
|
|
6087
|
+
const parts = ref.split("!");
|
|
6088
|
+
const xc = parts.pop();
|
|
6089
|
+
const sheetName = getUnquotedSheetName(parts.join("!")) || undefined;
|
|
6090
|
+
return { sheetName, xc };
|
|
6091
|
+
}
|
|
6092
|
+
/** Return a reference SheetName!xc from the given arguments */
|
|
6093
|
+
function getFullReference(sheetName, xc) {
|
|
6094
|
+
return sheetName !== undefined ? `${getCanonicalSymbolName(sheetName)}!${xc}` : xc;
|
|
6095
|
+
}
|
|
6096
|
+
|
|
5970
6097
|
class RangeImpl {
|
|
5971
6098
|
getSheetSize;
|
|
5972
6099
|
_zone;
|
|
@@ -7413,14 +7540,20 @@ function multiplyMatrices(matrix1, matrix2) {
|
|
|
7413
7540
|
/**
|
|
7414
7541
|
* Return the input if it's a scalar or the first element of the input if it's a matrix.
|
|
7415
7542
|
*/
|
|
7416
|
-
function toScalar(
|
|
7417
|
-
if (!isMatrix(
|
|
7418
|
-
return
|
|
7543
|
+
function toScalar(arg) {
|
|
7544
|
+
if (!isMatrix(arg)) {
|
|
7545
|
+
return arg;
|
|
7419
7546
|
}
|
|
7420
|
-
if (
|
|
7547
|
+
if (!isSingleElementMatrix(arg)) {
|
|
7421
7548
|
throw new EvaluationError(_t("The value should be a scalar or a 1x1 matrix"));
|
|
7422
7549
|
}
|
|
7423
|
-
return
|
|
7550
|
+
return arg[0][0];
|
|
7551
|
+
}
|
|
7552
|
+
function isSingleElementMatrix(matrix) {
|
|
7553
|
+
return matrix.length === 1 && matrix[0].length === 1;
|
|
7554
|
+
}
|
|
7555
|
+
function isMultipleElementMatrix(arg) {
|
|
7556
|
+
return isMatrix(arg) && !isSingleElementMatrix(arg);
|
|
7424
7557
|
}
|
|
7425
7558
|
|
|
7426
7559
|
function assertSameNumberOfElements(...args) {
|
|
@@ -15264,7 +15397,7 @@ const FILTER = {
|
|
|
15264
15397
|
}
|
|
15265
15398
|
return mode === "row" ? transposeMatrix(result) : result;
|
|
15266
15399
|
},
|
|
15267
|
-
isExported:
|
|
15400
|
+
isExported: false,
|
|
15268
15401
|
};
|
|
15269
15402
|
// -----------------------------------------------------------------------------
|
|
15270
15403
|
// SORT
|
|
@@ -18395,16 +18528,23 @@ const FALSE = {
|
|
|
18395
18528
|
const IF = {
|
|
18396
18529
|
description: _t("Returns value depending on logical expression."),
|
|
18397
18530
|
args: [
|
|
18398
|
-
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.")),
|
|
18399
|
-
arg("value_if_true (any)", _t("The value the function returns if logical_expression is TRUE.")),
|
|
18400
|
-
arg("value_if_false (any, default=FALSE)", _t("The value the function returns if logical_expression is FALSE.")),
|
|
18531
|
+
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.")),
|
|
18532
|
+
arg("value_if_true (any, range)", _t("The value the function returns if logical_expression is TRUE.")),
|
|
18533
|
+
arg("value_if_false (any, range, default=FALSE)", _t("The value the function returns if logical_expression is FALSE.")),
|
|
18401
18534
|
],
|
|
18402
18535
|
compute: function (logicalExpression, valueIfTrue, valueIfFalse) {
|
|
18403
|
-
|
|
18536
|
+
if (isMultipleElementMatrix(logicalExpression)) {
|
|
18537
|
+
return applyVectorization(IF.compute, [logicalExpression, valueIfTrue, valueIfFalse]);
|
|
18538
|
+
}
|
|
18539
|
+
let result = toBoolean(toScalar(logicalExpression)) ? valueIfTrue : valueIfFalse;
|
|
18540
|
+
// useful for interpreting empty cell references as empty strings. But must be removed to make empty cell references equal to zero
|
|
18541
|
+
if (!isMultipleElementMatrix(result)) {
|
|
18542
|
+
result = toScalar(result);
|
|
18543
|
+
}
|
|
18404
18544
|
if (result === undefined) {
|
|
18405
18545
|
return { value: "" };
|
|
18406
18546
|
}
|
|
18407
|
-
if (result.value === null) {
|
|
18547
|
+
if (!isMatrix(result) && result.value === null) {
|
|
18408
18548
|
return { ...result, value: "" };
|
|
18409
18549
|
}
|
|
18410
18550
|
return result;
|
|
@@ -18417,15 +18557,22 @@ const IF = {
|
|
|
18417
18557
|
const IFERROR = {
|
|
18418
18558
|
description: _t("Value if it is not an error, otherwise 2nd argument."),
|
|
18419
18559
|
args: [
|
|
18420
|
-
arg("value (any)", _t("The value to return if value itself is not an error.")),
|
|
18421
|
-
arg(`value_if_error (any, default="empty")`, _t("The value the function returns if value is an error.")),
|
|
18560
|
+
arg("value (any, range)", _t("The value to return if value itself is not an error.")),
|
|
18561
|
+
arg(`value_if_error (any, range, default="empty")`, _t("The value the function returns if value is an error.")),
|
|
18422
18562
|
],
|
|
18423
|
-
compute: function (value, valueIfError
|
|
18424
|
-
|
|
18563
|
+
compute: function (value, valueIfError) {
|
|
18564
|
+
if (isMultipleElementMatrix(value)) {
|
|
18565
|
+
return applyVectorization(IFERROR.compute, [value, valueIfError]);
|
|
18566
|
+
}
|
|
18567
|
+
let result = isEvaluationError(toScalar(value)?.value) ? valueIfError : value;
|
|
18568
|
+
// useful for interpreting empty cell references as empty strings. But must be removed to make empty cell references equal to zero
|
|
18569
|
+
if (!isMultipleElementMatrix(result)) {
|
|
18570
|
+
result = toScalar(result);
|
|
18571
|
+
}
|
|
18425
18572
|
if (result === undefined) {
|
|
18426
18573
|
return { value: "" };
|
|
18427
18574
|
}
|
|
18428
|
-
if (result.value === null) {
|
|
18575
|
+
if (!isMatrix(result) && result.value === null) {
|
|
18429
18576
|
return { ...result, value: "" };
|
|
18430
18577
|
}
|
|
18431
18578
|
return result;
|
|
@@ -18438,15 +18585,22 @@ const IFERROR = {
|
|
|
18438
18585
|
const IFNA = {
|
|
18439
18586
|
description: _t("Value if it is not an #N/A error, otherwise 2nd argument."),
|
|
18440
18587
|
args: [
|
|
18441
|
-
arg("value (any)", _t("The value to return if value itself is not #N/A an error.")),
|
|
18442
|
-
arg(`value_if_error (any, default="empty")`, _t("The value the function returns if value is an #N/A error.")),
|
|
18588
|
+
arg("value (any, range)", _t("The value to return if value itself is not #N/A an error.")),
|
|
18589
|
+
arg(`value_if_error (any, range, default="empty")`, _t("The value the function returns if value is an #N/A error.")),
|
|
18443
18590
|
],
|
|
18444
|
-
compute: function (value, valueIfError
|
|
18445
|
-
|
|
18591
|
+
compute: function (value, valueIfError) {
|
|
18592
|
+
if (isMultipleElementMatrix(value)) {
|
|
18593
|
+
return applyVectorization(IFNA.compute, [value, valueIfError]);
|
|
18594
|
+
}
|
|
18595
|
+
let result = toScalar(value)?.value === CellErrorType.NotAvailable ? valueIfError : value;
|
|
18596
|
+
// useful for interpreting empty cell references as empty strings. But must be removed to make empty cell references equal to zero
|
|
18597
|
+
if (!isMultipleElementMatrix(result)) {
|
|
18598
|
+
result = toScalar(result);
|
|
18599
|
+
}
|
|
18446
18600
|
if (result === undefined) {
|
|
18447
18601
|
return { value: "" };
|
|
18448
18602
|
}
|
|
18449
|
-
if (result.value === null) {
|
|
18603
|
+
if (!isMatrix(result) && result.value === null) {
|
|
18450
18604
|
return { ...result, value: "" };
|
|
18451
18605
|
}
|
|
18452
18606
|
return result;
|
|
@@ -18459,23 +18613,31 @@ const IFNA = {
|
|
|
18459
18613
|
const IFS = {
|
|
18460
18614
|
description: _t("Returns a value depending on multiple logical expressions."),
|
|
18461
18615
|
args: [
|
|
18462
|
-
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.")),
|
|
18463
|
-
arg("value1 (any)", _t("The returned value if condition1 is TRUE.")),
|
|
18464
|
-
arg("condition2 (boolean, any, repeating)", _t("Additional conditions to be evaluated if the previous ones are FALSE.")),
|
|
18465
|
-
arg("value2 (any, repeating)", _t("Additional values to be returned if their corresponding conditions are TRUE.")),
|
|
18616
|
+
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.")),
|
|
18617
|
+
arg("value1 (any, range)", _t("The returned value if condition1 is TRUE.")),
|
|
18618
|
+
arg("condition2 (boolean, any, range, repeating)", _t("Additional conditions to be evaluated if the previous ones are FALSE.")),
|
|
18619
|
+
arg("value2 (any, range, repeating)", _t("Additional values to be returned if their corresponding conditions are TRUE.")),
|
|
18466
18620
|
],
|
|
18467
18621
|
compute: function (...values) {
|
|
18468
18622
|
assert(() => values.length % 2 === 0, _t("Wrong number of arguments. Expected an even number of arguments."));
|
|
18469
|
-
|
|
18470
|
-
if (
|
|
18471
|
-
|
|
18472
|
-
|
|
18623
|
+
while (values.length > 0) {
|
|
18624
|
+
if (isMultipleElementMatrix(values[0])) {
|
|
18625
|
+
return applyVectorization(IFS.compute, values);
|
|
18626
|
+
}
|
|
18627
|
+
const condition = toBoolean(toScalar(values.shift()));
|
|
18628
|
+
let valueIfTrue = values.shift();
|
|
18629
|
+
if (condition) {
|
|
18630
|
+
// useful for interpreting empty cell references as empty strings. But must be removed to make empty cell references equal to zero
|
|
18631
|
+
if (!isMultipleElementMatrix(valueIfTrue)) {
|
|
18632
|
+
valueIfTrue = toScalar(valueIfTrue);
|
|
18633
|
+
}
|
|
18634
|
+
if (valueIfTrue === undefined) {
|
|
18473
18635
|
return { value: "" };
|
|
18474
18636
|
}
|
|
18475
|
-
if (
|
|
18476
|
-
return { ...
|
|
18637
|
+
if (!isMatrix(valueIfTrue) && valueIfTrue.value === null) {
|
|
18638
|
+
return { ...valueIfTrue, value: "" };
|
|
18477
18639
|
}
|
|
18478
|
-
return
|
|
18640
|
+
return valueIfTrue;
|
|
18479
18641
|
}
|
|
18480
18642
|
}
|
|
18481
18643
|
return new EvaluationError(_t("No match."));
|
|
@@ -20137,7 +20299,7 @@ const SPLIT = {
|
|
|
20137
20299
|
}
|
|
20138
20300
|
return transposeMatrix([result]);
|
|
20139
20301
|
},
|
|
20140
|
-
isExported:
|
|
20302
|
+
isExported: false,
|
|
20141
20303
|
};
|
|
20142
20304
|
// -----------------------------------------------------------------------------
|
|
20143
20305
|
// SUBSTITUTE
|
|
@@ -20330,86 +20492,21 @@ for (let category of categories) {
|
|
|
20330
20492
|
functionRegistry.add(name, { isExported: false, ...addDescr });
|
|
20331
20493
|
}
|
|
20332
20494
|
}
|
|
20333
|
-
|
|
20495
|
+
//------------------------------------------------------------------------------
|
|
20496
|
+
// CREATE COMPUTE FUNCTION
|
|
20497
|
+
//------------------------------------------------------------------------------
|
|
20334
20498
|
function createComputeFunction(descr, functionName) {
|
|
20335
20499
|
function vectorizedCompute(...args) {
|
|
20336
|
-
|
|
20337
|
-
let countVectorizableRow = 1;
|
|
20338
|
-
let vectorizableColLimit = Infinity;
|
|
20339
|
-
let vectorizableRowLimit = Infinity;
|
|
20340
|
-
let vectorArgsType = undefined;
|
|
20341
|
-
//#region Compute vectorisation limits
|
|
20500
|
+
const acceptToVectorize = [];
|
|
20342
20501
|
for (let i = 0; i < args.length; i++) {
|
|
20343
20502
|
const argDefinition = descr.args[descr.getArgToFocus(i + 1) - 1];
|
|
20344
20503
|
const arg = args[i];
|
|
20345
|
-
if (isMatrix(arg) && !argDefinition.acceptMatrix) {
|
|
20346
|
-
// if argDefinition does not accept a matrix but arg is still a matrix
|
|
20347
|
-
// --> triggers the arguments vectorization
|
|
20348
|
-
const nColumns = arg.length;
|
|
20349
|
-
const nRows = arg[0].length;
|
|
20350
|
-
if (nColumns !== 1 || nRows !== 1) {
|
|
20351
|
-
vectorArgsType ??= new Array(args.length);
|
|
20352
|
-
if (nColumns !== 1 && nRows !== 1) {
|
|
20353
|
-
vectorArgsType[i] = "matrix";
|
|
20354
|
-
countVectorizableCol = Math.max(countVectorizableCol, nColumns);
|
|
20355
|
-
countVectorizableRow = Math.max(countVectorizableRow, nRows);
|
|
20356
|
-
vectorizableColLimit = Math.min(vectorizableColLimit, nColumns);
|
|
20357
|
-
vectorizableRowLimit = Math.min(vectorizableRowLimit, nRows);
|
|
20358
|
-
}
|
|
20359
|
-
else if (nColumns !== 1) {
|
|
20360
|
-
vectorArgsType[i] = "horizontal";
|
|
20361
|
-
countVectorizableCol = Math.max(countVectorizableCol, nColumns);
|
|
20362
|
-
vectorizableColLimit = Math.min(vectorizableColLimit, nColumns);
|
|
20363
|
-
}
|
|
20364
|
-
else if (nRows !== 1) {
|
|
20365
|
-
vectorArgsType[i] = "vertical";
|
|
20366
|
-
countVectorizableRow = Math.max(countVectorizableRow, nRows);
|
|
20367
|
-
vectorizableRowLimit = Math.min(vectorizableRowLimit, nRows);
|
|
20368
|
-
}
|
|
20369
|
-
}
|
|
20370
|
-
else {
|
|
20371
|
-
args[i] = arg[0][0];
|
|
20372
|
-
}
|
|
20373
|
-
}
|
|
20374
20504
|
if (!isMatrix(arg) && argDefinition.acceptMatrixOnly) {
|
|
20375
20505
|
throw new BadExpressionError(_t("Function %s expects the parameter '%s' to be reference to a cell or range.", functionName, (i + 1).toString()));
|
|
20376
20506
|
}
|
|
20507
|
+
acceptToVectorize.push(!argDefinition.acceptMatrix);
|
|
20377
20508
|
}
|
|
20378
|
-
|
|
20379
|
-
if (countVectorizableCol === 1 && countVectorizableRow === 1) {
|
|
20380
|
-
// either this function is not vectorized or it ends up with a 1x1 dimension
|
|
20381
|
-
return errorHandlingCompute.apply(this, args);
|
|
20382
|
-
}
|
|
20383
|
-
const getArgOffset = (i, j) => args.map((arg, index) => {
|
|
20384
|
-
switch (vectorArgsType?.[index]) {
|
|
20385
|
-
case "matrix":
|
|
20386
|
-
return arg[i][j];
|
|
20387
|
-
case "horizontal":
|
|
20388
|
-
return arg[i][0];
|
|
20389
|
-
case "vertical":
|
|
20390
|
-
return arg[0][j];
|
|
20391
|
-
case undefined:
|
|
20392
|
-
return arg;
|
|
20393
|
-
}
|
|
20394
|
-
});
|
|
20395
|
-
return generateMatrix(countVectorizableCol, countVectorizableRow, (col, row) => {
|
|
20396
|
-
if (col > vectorizableColLimit - 1 || row > vectorizableRowLimit - 1) {
|
|
20397
|
-
return notAvailableError;
|
|
20398
|
-
}
|
|
20399
|
-
const singleCellComputeResult = errorHandlingCompute.apply(this, getArgOffset(col, row));
|
|
20400
|
-
// In the case where the user tries to vectorize arguments of an array formula, we will get an
|
|
20401
|
-
// array for every combination of the vectorized arguments, which will lead to a 3D matrix and
|
|
20402
|
-
// we won't be able to return the values.
|
|
20403
|
-
// In this case, we keep the first element of each spreading part, just as Excel does, and
|
|
20404
|
-
// create an array with these parts.
|
|
20405
|
-
// For exemple, we have MUNIT(x) that return an unitary matrix of x*x. If we use it with a
|
|
20406
|
-
// range, like MUNIT(A1:A2), we will get two unitary matrices (one for the value in A1 and one
|
|
20407
|
-
// for the value in A2). In this case, we will simply take the first value of each matrix and
|
|
20408
|
-
// return the array [First value of MUNIT(A1), First value of MUNIT(A2)].
|
|
20409
|
-
return isMatrix(singleCellComputeResult)
|
|
20410
|
-
? singleCellComputeResult[0][0]
|
|
20411
|
-
: singleCellComputeResult;
|
|
20412
|
-
});
|
|
20509
|
+
return applyVectorization(errorHandlingCompute.bind(this), args, acceptToVectorize);
|
|
20413
20510
|
}
|
|
20414
20511
|
function errorHandlingCompute(...args) {
|
|
20415
20512
|
for (let i = 0; i < args.length; i++) {
|
|
@@ -21254,7 +21351,7 @@ class AbstractComposerStore extends SpreadsheetStore {
|
|
|
21254
21351
|
proposals &&
|
|
21255
21352
|
!["ARG_SEPARATOR", "LEFT_PAREN", "OPERATOR"].includes(tokenAtCursor.type)) {
|
|
21256
21353
|
const filteredProposals = fuzzyLookup(searchTerm, proposals, (p) => p.fuzzySearchKey || p.text);
|
|
21257
|
-
if (!exactMatch || filteredProposals.length
|
|
21354
|
+
if (!exactMatch || filteredProposals.length) {
|
|
21258
21355
|
proposals = filteredProposals;
|
|
21259
21356
|
}
|
|
21260
21357
|
}
|
|
@@ -41346,12 +41443,13 @@ class StandaloneComposerStore extends AbstractComposerStore {
|
|
|
41346
41443
|
return providersDefinitions;
|
|
41347
41444
|
}
|
|
41348
41445
|
getComposerContent() {
|
|
41446
|
+
let content = this._currentContent;
|
|
41349
41447
|
if (this.editionMode === "inactive") {
|
|
41350
41448
|
// References in the content might not be linked to the current active sheet
|
|
41351
41449
|
// We here force the sheet name prefix for all references that are not in
|
|
41352
41450
|
// the current active sheet
|
|
41353
41451
|
const defaultRangeSheetId = this.args().defaultRangeSheetId;
|
|
41354
|
-
|
|
41452
|
+
content = rangeTokenize(this.args().content)
|
|
41355
41453
|
.map((token) => {
|
|
41356
41454
|
if (token.type === "REFERENCE") {
|
|
41357
41455
|
const range = this.getters.getRangeFromSheetXC(defaultRangeSheetId, token.value);
|
|
@@ -41361,7 +41459,7 @@ class StandaloneComposerStore extends AbstractComposerStore {
|
|
|
41361
41459
|
})
|
|
41362
41460
|
.join("");
|
|
41363
41461
|
}
|
|
41364
|
-
return this.
|
|
41462
|
+
return localizeContent(content, this.getters.getLocale());
|
|
41365
41463
|
}
|
|
41366
41464
|
stopEdition() {
|
|
41367
41465
|
this._stopEdition();
|
|
@@ -45107,6 +45205,9 @@ class PivotMeasureEditor extends owl.Component {
|
|
|
45107
45205
|
}
|
|
45108
45206
|
return undefined;
|
|
45109
45207
|
}
|
|
45208
|
+
get isCalculatedMeasureInvalid() {
|
|
45209
|
+
return this.env.model.getters.getMeasureCompiledFormula(this.props.measure).isBadExpression;
|
|
45210
|
+
}
|
|
45110
45211
|
}
|
|
45111
45212
|
|
|
45112
45213
|
css /* scss */ `
|
|
@@ -46117,7 +46218,9 @@ function compareDimensionValues(dimension, a, b) {
|
|
|
46117
46218
|
return dimension.order === "asc" ? -1 : 1;
|
|
46118
46219
|
}
|
|
46119
46220
|
if (dimension.type === "integer" || dimension.type === "datetime") {
|
|
46120
|
-
return dimension.order === "asc"
|
|
46221
|
+
return dimension.order === "asc"
|
|
46222
|
+
? toNumber(a, DEFAULT_LOCALE) - toNumber(b, DEFAULT_LOCALE)
|
|
46223
|
+
: toNumber(b, DEFAULT_LOCALE) - toNumber(a, DEFAULT_LOCALE);
|
|
46121
46224
|
}
|
|
46122
46225
|
return dimension.order === "asc" ? a.localeCompare(b) : b.localeCompare(a);
|
|
46123
46226
|
}
|
|
@@ -48883,8 +48986,7 @@ class CellComposerStore extends AbstractComposerStore {
|
|
|
48883
48986
|
if (!spreader) {
|
|
48884
48987
|
return undefined;
|
|
48885
48988
|
}
|
|
48886
|
-
|
|
48887
|
-
return cell?.content;
|
|
48989
|
+
return this.getters.getCellText(spreader, { showFormula: true });
|
|
48888
48990
|
}
|
|
48889
48991
|
get currentEditedCell() {
|
|
48890
48992
|
return {
|
|
@@ -51695,6 +51797,7 @@ function useGridDrawing(refName, model, canvasSize) {
|
|
|
51695
51797
|
const friction = 0.95;
|
|
51696
51798
|
const verticalScrollFactor = 1;
|
|
51697
51799
|
const horizontalScrollFactor = 1;
|
|
51800
|
+
const resetTimeoutDuration = 100;
|
|
51698
51801
|
function useTouchScroll(ref, updateScroll, canMoveUp) {
|
|
51699
51802
|
let lastX = 0;
|
|
51700
51803
|
let lastY = 0;
|
|
@@ -51702,6 +51805,7 @@ function useTouchScroll(ref, updateScroll, canMoveUp) {
|
|
|
51702
51805
|
let velocityY = 0;
|
|
51703
51806
|
let isMouseDown = false;
|
|
51704
51807
|
let lastTime = 0;
|
|
51808
|
+
let resetTimeout = null;
|
|
51705
51809
|
useRefListener(ref, "touchstart", onTouchStart, { capture: false });
|
|
51706
51810
|
useRefListener(ref, "touchmove", onTouchMove, { capture: false });
|
|
51707
51811
|
useRefListener(ref, "touchend", onTouchEnd, { capture: false });
|
|
@@ -51714,6 +51818,10 @@ function useTouchScroll(ref, updateScroll, canMoveUp) {
|
|
|
51714
51818
|
function onTouchMove(event) {
|
|
51715
51819
|
if (!isMouseDown)
|
|
51716
51820
|
return;
|
|
51821
|
+
if (resetTimeout) {
|
|
51822
|
+
clearTimeout(resetTimeout);
|
|
51823
|
+
resetTimeout = null;
|
|
51824
|
+
}
|
|
51717
51825
|
const currentTime = Date.now();
|
|
51718
51826
|
const { clientX, clientY } = event.touches[0];
|
|
51719
51827
|
let deltaX = lastX - clientX;
|
|
@@ -51730,6 +51838,10 @@ function useTouchScroll(ref, updateScroll, canMoveUp) {
|
|
|
51730
51838
|
}
|
|
51731
51839
|
event.stopPropagation();
|
|
51732
51840
|
}
|
|
51841
|
+
resetTimeout = setTimeout(() => {
|
|
51842
|
+
velocityX = 0;
|
|
51843
|
+
velocityY = 0;
|
|
51844
|
+
}, resetTimeoutDuration);
|
|
51733
51845
|
updateScroll(deltaX * horizontalScrollFactor, deltaY * verticalScrollFactor);
|
|
51734
51846
|
}
|
|
51735
51847
|
function onTouchEnd(ev) {
|
|
@@ -60479,10 +60591,9 @@ class Evaluator {
|
|
|
60479
60591
|
return this.evaluatedCells.keysForSheet(sheetId);
|
|
60480
60592
|
}
|
|
60481
60593
|
getArrayFormulaSpreadingOn(position) {
|
|
60482
|
-
const
|
|
60483
|
-
|
|
60484
|
-
|
|
60485
|
-
return this.spreadingRelations.isArrayFormula(position) ? position : undefined;
|
|
60594
|
+
const isEmpty = this.getEvaluatedCell(position).type === CellValueType.empty;
|
|
60595
|
+
if (isEmpty) {
|
|
60596
|
+
return undefined;
|
|
60486
60597
|
}
|
|
60487
60598
|
const arrayFormulas = this.spreadingRelations.searchFormulaPositionsSpreadingOn(position.sheetId, positionToZone(position));
|
|
60488
60599
|
return Array.from(arrayFormulas).find((position) => !this.blockedArrayFormulas.has(position));
|
|
@@ -76326,6 +76437,6 @@ exports.tokenColors = tokenColors;
|
|
|
76326
76437
|
exports.tokenize = tokenize;
|
|
76327
76438
|
|
|
76328
76439
|
|
|
76329
|
-
__info__.version = "18.1.
|
|
76330
|
-
__info__.date = "2025-05-
|
|
76331
|
-
__info__.hash = "
|
|
76440
|
+
__info__.version = "18.1.22";
|
|
76441
|
+
__info__.date = "2025-05-26T12:35:56.145Z";
|
|
76442
|
+
__info__.hash = "ff4b0ba";
|