@odoo/o-spreadsheet 16.5.0-alpha.7 → 16.5.0-alpha.8
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 +638 -58
- package/dist/o-spreadsheet.d.ts +7 -10
- package/dist/o-spreadsheet.esm.js +638 -58
- package/dist/o-spreadsheet.iife.js +638 -58
- package/dist/o-spreadsheet.iife.min.js +315 -314
- package/package.json +1 -2
|
@@ -1,3 +1,12 @@
|
|
|
1
|
+
|
|
2
|
+
/**
|
|
3
|
+
* This file is generated by o-spreadsheet build tools. Do not edit it.
|
|
4
|
+
* @see https://github.com/odoo/o-spreadsheet
|
|
5
|
+
* @version 16.5.0-alpha.8
|
|
6
|
+
* @date 2023-09-21T15:59:10.959Z
|
|
7
|
+
* @hash 0b7527d
|
|
8
|
+
*/
|
|
9
|
+
|
|
1
10
|
(function (exports, owl) {
|
|
2
11
|
'use strict';
|
|
3
12
|
|
|
@@ -232,13 +241,26 @@
|
|
|
232
241
|
const MENU_ITEM_PADDING_VERTICAL = 4;
|
|
233
242
|
const MENU_SEPARATOR_BORDER_WIDTH = 1;
|
|
234
243
|
const MENU_SEPARATOR_PADDING = 5;
|
|
244
|
+
// Style
|
|
245
|
+
const DEFAULT_STYLE = {
|
|
246
|
+
align: "left",
|
|
247
|
+
verticalAlign: "bottom",
|
|
248
|
+
wrapping: "overflow",
|
|
249
|
+
bold: false,
|
|
250
|
+
italic: false,
|
|
251
|
+
strikethrough: false,
|
|
252
|
+
underline: false,
|
|
253
|
+
fontSize: 10,
|
|
254
|
+
fillColor: "",
|
|
255
|
+
textColor: "#000000",
|
|
256
|
+
};
|
|
257
|
+
const DEFAULT_VERTICAL_ALIGN = DEFAULT_STYLE.verticalAlign;
|
|
258
|
+
const DEFAULT_WRAPPING_MODE = DEFAULT_STYLE.wrapping;
|
|
235
259
|
// Fonts
|
|
236
260
|
const DEFAULT_FONT_WEIGHT = "400";
|
|
237
|
-
const DEFAULT_FONT_SIZE =
|
|
261
|
+
const DEFAULT_FONT_SIZE = DEFAULT_STYLE.fontSize;
|
|
238
262
|
const HEADER_FONT_SIZE = 11;
|
|
239
263
|
const DEFAULT_FONT = "'Roboto', arial";
|
|
240
|
-
const DEFAULT_VERTICAL_ALIGN = "bottom";
|
|
241
|
-
const DEFAULT_WRAPPING_MODE = "overflow";
|
|
242
264
|
// Borders
|
|
243
265
|
const DEFAULT_BORDER_DESC = { style: "thin", color: "#000000" };
|
|
244
266
|
const DEFAULT_FILTER_BORDER_DESC = { style: "thin", color: FILTERS_COLOR };
|
|
@@ -616,13 +638,17 @@
|
|
|
616
638
|
return position === "after" ? base + 1 : base;
|
|
617
639
|
}
|
|
618
640
|
/**
|
|
619
|
-
*
|
|
641
|
+
* Compares two objects.
|
|
620
642
|
*/
|
|
621
643
|
function deepEquals(o1, o2) {
|
|
622
644
|
if (o1 === o2)
|
|
623
645
|
return true;
|
|
624
646
|
if ((o1 && !o2) || (o2 && !o1))
|
|
625
647
|
return false;
|
|
648
|
+
if (typeof o1 !== typeof o2)
|
|
649
|
+
return false;
|
|
650
|
+
if (typeof o1 !== "object")
|
|
651
|
+
return o1 === o2;
|
|
626
652
|
// Objects can have different keys if the values are undefined
|
|
627
653
|
const keys = new Set();
|
|
628
654
|
Object.keys(o1).forEach((key) => keys.add(key));
|
|
@@ -738,6 +764,10 @@
|
|
|
738
764
|
newArray.splice(index, 0, ...items);
|
|
739
765
|
return newArray;
|
|
740
766
|
}
|
|
767
|
+
function trimContent(content) {
|
|
768
|
+
const contentLines = content.split("\n");
|
|
769
|
+
return contentLines.map((line) => line.replace(/\s+/g, " ").trim()).join("\n");
|
|
770
|
+
}
|
|
741
771
|
|
|
742
772
|
const RBA_REGEX = /rgba?\(|\s+|\)/gi;
|
|
743
773
|
const HEX_MATCH = /^#([A-F\d]{2}){3,4}$/;
|
|
@@ -4404,6 +4434,16 @@
|
|
|
4404
4434
|
return 0;
|
|
4405
4435
|
}
|
|
4406
4436
|
}
|
|
4437
|
+
function tryCastAsNumberMatrix(data, argName) {
|
|
4438
|
+
data.forEach((row) => {
|
|
4439
|
+
row.forEach((cell) => {
|
|
4440
|
+
if (typeof cell !== "number") {
|
|
4441
|
+
throw new Error(_t("Function [[FUNCTION_NAME]] expects number values for %s, but got a %s.", typeof cell, argName));
|
|
4442
|
+
}
|
|
4443
|
+
});
|
|
4444
|
+
});
|
|
4445
|
+
return data;
|
|
4446
|
+
}
|
|
4407
4447
|
function strictToNumber(value, locale) {
|
|
4408
4448
|
if (value === "") {
|
|
4409
4449
|
throw new Error(expectNumberValueError(value));
|
|
@@ -5348,6 +5388,7 @@
|
|
|
5348
5388
|
padding: 10px;
|
|
5349
5389
|
width: ${ERROR_TOOLTIP_WIDTH}px;
|
|
5350
5390
|
box-sizing: border-box !important;
|
|
5391
|
+
overflow-wrap: break-word;
|
|
5351
5392
|
}
|
|
5352
5393
|
`;
|
|
5353
5394
|
class ErrorToolTip extends owl.Component {
|
|
@@ -8353,43 +8394,53 @@
|
|
|
8353
8394
|
TRUNC: TRUNC
|
|
8354
8395
|
});
|
|
8355
8396
|
|
|
8356
|
-
|
|
8357
|
-
|
|
8358
|
-
|
|
8359
|
-
|
|
8397
|
+
function assertSameNumberOfElements(...args) {
|
|
8398
|
+
const dims = args[0].length;
|
|
8399
|
+
args.forEach((arg, i) => assert(() => arg.length === dims, _t("[[FUNCTION_NAME]] has mismatched dimensions for argument %s (%s vs %s).", i.toString(), dims.toString(), arg.length.toString())));
|
|
8400
|
+
}
|
|
8401
|
+
|
|
8402
|
+
function filterAndFlatData(dataY, dataX) {
|
|
8403
|
+
const _flatDataY = [];
|
|
8404
|
+
const _flatDataX = [];
|
|
8360
8405
|
let lenY = 0;
|
|
8361
8406
|
let lenX = 0;
|
|
8362
8407
|
visitAny([dataY], (y) => {
|
|
8363
|
-
|
|
8408
|
+
_flatDataY.push(y);
|
|
8364
8409
|
lenY += 1;
|
|
8365
8410
|
});
|
|
8366
8411
|
visitAny([dataX], (x) => {
|
|
8367
|
-
|
|
8412
|
+
_flatDataX.push(x);
|
|
8368
8413
|
lenX += 1;
|
|
8369
8414
|
});
|
|
8370
|
-
assert(() => lenY === lenX, _t("[[FUNCTION_NAME]] has mismatched argument count %s vs %s.", lenY
|
|
8371
|
-
|
|
8372
|
-
|
|
8373
|
-
let sumX = 0;
|
|
8415
|
+
assert(() => lenY === lenX, _t("[[FUNCTION_NAME]] has mismatched argument count %s vs %s.", lenY, lenX));
|
|
8416
|
+
const flatDataX = [];
|
|
8417
|
+
const flatDataY = [];
|
|
8374
8418
|
for (let i = 0; i < lenY; i++) {
|
|
8375
|
-
const valueY =
|
|
8376
|
-
const valueX =
|
|
8419
|
+
const valueY = _flatDataY[i];
|
|
8420
|
+
const valueX = _flatDataX[i];
|
|
8377
8421
|
if (typeof valueY === "number" && typeof valueX === "number") {
|
|
8378
|
-
|
|
8379
|
-
|
|
8380
|
-
sumX += valueX;
|
|
8422
|
+
flatDataY.push(valueY);
|
|
8423
|
+
flatDataX.push(valueX);
|
|
8381
8424
|
}
|
|
8382
8425
|
}
|
|
8426
|
+
return { flatDataX, flatDataY };
|
|
8427
|
+
}
|
|
8428
|
+
// Note: dataY and dataX may not have the same dimension
|
|
8429
|
+
function covariance(dataY, dataX, isSample) {
|
|
8430
|
+
const { flatDataX, flatDataY } = filterAndFlatData(dataY, dataX);
|
|
8431
|
+
const count = flatDataY.length;
|
|
8383
8432
|
assert(() => count !== 0 && (!isSample || count !== 1), _t(`Evaluation of function [[FUNCTION_NAME]] caused a divide by zero error.`));
|
|
8433
|
+
let sumY = 0;
|
|
8434
|
+
let sumX = 0;
|
|
8435
|
+
for (let i = 0; i < count; i++) {
|
|
8436
|
+
sumY += flatDataY[i];
|
|
8437
|
+
sumX += flatDataX[i];
|
|
8438
|
+
}
|
|
8384
8439
|
const averageY = sumY / count;
|
|
8385
8440
|
const averageX = sumX / count;
|
|
8386
8441
|
let acc = 0;
|
|
8387
|
-
for (let i = 0; i <
|
|
8388
|
-
|
|
8389
|
-
const valueX = flatDataX[i];
|
|
8390
|
-
if (typeof valueY === "number" && typeof valueX === "number") {
|
|
8391
|
-
acc += (valueY - averageY) * (valueX - averageX);
|
|
8392
|
-
}
|
|
8442
|
+
for (let i = 0; i < count; i++) {
|
|
8443
|
+
acc += (flatDataY[i] - averageY) * (flatDataX[i] - averageX);
|
|
8393
8444
|
}
|
|
8394
8445
|
return acc / (count - (isSample ? 1 : 0));
|
|
8395
8446
|
}
|
|
@@ -8426,6 +8477,161 @@
|
|
|
8426
8477
|
}
|
|
8427
8478
|
return percentile(sortedArray, _percent, isInclusive);
|
|
8428
8479
|
}
|
|
8480
|
+
function prepareDataForRegression(X, Y, newX) {
|
|
8481
|
+
const _X = X.length ? X : [range(1, Y.flat().length + 1)];
|
|
8482
|
+
const nVar = _X.length;
|
|
8483
|
+
let _newX = newX.length ? newX : _X;
|
|
8484
|
+
_newX = _newX.length === nVar ? transposeMatrix(_newX) : _newX;
|
|
8485
|
+
return { _X, _newX };
|
|
8486
|
+
}
|
|
8487
|
+
/*
|
|
8488
|
+
* This function performs a linear regression on the data set. It returns an array with two elements.
|
|
8489
|
+
* The first element is the slope, and the second element is the intercept.
|
|
8490
|
+
* The linear regression line is: y = slope*x + intercept
|
|
8491
|
+
* The function use the least squares method to find the best fit for the data set :
|
|
8492
|
+
* see https://www.mathsisfun.com/data/least-squares-regression.html
|
|
8493
|
+
* https://www.statology.org/standard-error-of-estimate/
|
|
8494
|
+
* https://agronomy4future.org/?p=16670
|
|
8495
|
+
* https://vitalflux.com/interpreting-f-statistics-in-linear-regression-formula-examples/
|
|
8496
|
+
* https://web.ist.utl.pt/~ist11038/compute/errtheory/,regression/regrthroughorigin.pdf
|
|
8497
|
+
*/
|
|
8498
|
+
function fullLinearRegression(X, Y, computeIntercept = true, verbose = false) {
|
|
8499
|
+
const y = Y.flat();
|
|
8500
|
+
const n = y.length;
|
|
8501
|
+
let { _X } = prepareDataForRegression(X, Y, []);
|
|
8502
|
+
_X = _X.length === n ? transposeMatrix(_X) : _X.slice();
|
|
8503
|
+
assertSameNumberOfElements(_X[0], y);
|
|
8504
|
+
const nVar = _X.length;
|
|
8505
|
+
const nDeg = n - nVar - (computeIntercept ? 1 : 0);
|
|
8506
|
+
const yMatrix = [y];
|
|
8507
|
+
const xMatrix = transposeMatrix(_X.reverse());
|
|
8508
|
+
let avgX = [];
|
|
8509
|
+
for (let i = 0; i < nVar; i++) {
|
|
8510
|
+
avgX.push(0);
|
|
8511
|
+
if (computeIntercept) {
|
|
8512
|
+
for (const xij of _X[i]) {
|
|
8513
|
+
avgX[i] += xij;
|
|
8514
|
+
}
|
|
8515
|
+
avgX[i] /= n;
|
|
8516
|
+
}
|
|
8517
|
+
}
|
|
8518
|
+
let avgY = 0;
|
|
8519
|
+
if (computeIntercept) {
|
|
8520
|
+
for (const yi of y) {
|
|
8521
|
+
avgY += yi;
|
|
8522
|
+
}
|
|
8523
|
+
avgY /= n;
|
|
8524
|
+
}
|
|
8525
|
+
const redX = xMatrix.map((row) => row.map((value, i) => value - avgX[i]));
|
|
8526
|
+
if (computeIntercept) {
|
|
8527
|
+
xMatrix.forEach((row) => row.push(1));
|
|
8528
|
+
}
|
|
8529
|
+
const coeffs = getLMSCoefficients(xMatrix, yMatrix);
|
|
8530
|
+
if (!computeIntercept) {
|
|
8531
|
+
coeffs.push([0]);
|
|
8532
|
+
}
|
|
8533
|
+
if (!verbose) {
|
|
8534
|
+
return coeffs;
|
|
8535
|
+
}
|
|
8536
|
+
const dot1 = multiplyMatrices(redX, transposeMatrix(redX));
|
|
8537
|
+
const { inverted: dotInv } = invertMatrix(dot1);
|
|
8538
|
+
if (dotInv === undefined) {
|
|
8539
|
+
throw new Error(_t("Matrix is not invertible"));
|
|
8540
|
+
}
|
|
8541
|
+
let SSE = 0, SSR = 0;
|
|
8542
|
+
for (let i = 0; i < n; i++) {
|
|
8543
|
+
const yi = y[i] - avgY;
|
|
8544
|
+
let temp = 0;
|
|
8545
|
+
for (let j = 0; j < nVar; j++) {
|
|
8546
|
+
const xi = redX[i][j];
|
|
8547
|
+
temp += xi * coeffs[j][0];
|
|
8548
|
+
}
|
|
8549
|
+
const ei = yi - temp;
|
|
8550
|
+
SSE += ei * ei;
|
|
8551
|
+
SSR += temp * temp;
|
|
8552
|
+
}
|
|
8553
|
+
const RMSE = Math.sqrt(SSE / nDeg);
|
|
8554
|
+
const r2 = SSR / (SSR + SSE);
|
|
8555
|
+
const f_stat = SSR / nVar / (SSE / nDeg);
|
|
8556
|
+
const deltaCoeffs = [];
|
|
8557
|
+
for (let i = 0; i < nVar; i++) {
|
|
8558
|
+
deltaCoeffs.push(RMSE * Math.sqrt(dotInv[i][i]));
|
|
8559
|
+
}
|
|
8560
|
+
if (computeIntercept) {
|
|
8561
|
+
const dot2 = multiplyMatrices(dotInv, [avgX]);
|
|
8562
|
+
const dot3 = multiplyMatrices(transposeMatrix([avgX]), dot2);
|
|
8563
|
+
deltaCoeffs.push(RMSE * Math.sqrt(dot3[0][0] + 1 / y.length));
|
|
8564
|
+
}
|
|
8565
|
+
const returned = [
|
|
8566
|
+
[coeffs[0][0], deltaCoeffs[0], r2, f_stat, SSR],
|
|
8567
|
+
[coeffs[1][0], deltaCoeffs[1], RMSE, nDeg, SSE],
|
|
8568
|
+
];
|
|
8569
|
+
for (let i = 2; i < nVar; i++) {
|
|
8570
|
+
returned.push([coeffs[i][0], deltaCoeffs[i], "", "", ""]);
|
|
8571
|
+
}
|
|
8572
|
+
if (computeIntercept) {
|
|
8573
|
+
returned.push([coeffs[nVar][0], deltaCoeffs[nVar], "", "", ""]);
|
|
8574
|
+
}
|
|
8575
|
+
else {
|
|
8576
|
+
returned.push([0, "", "", "", ""]);
|
|
8577
|
+
}
|
|
8578
|
+
return returned;
|
|
8579
|
+
}
|
|
8580
|
+
/*
|
|
8581
|
+
This function performs a polynomial regression on the data set. It returns the coefficients of
|
|
8582
|
+
the polynomial function that best fits the data set.
|
|
8583
|
+
The polynomial function is: y = c0 + c1*x + c2*x^2 + ... + cn*x^n, where n is the order (degree)
|
|
8584
|
+
of the polynomial. The returned coefficients are then in the form: [c0, c1, c2, ..., cn]
|
|
8585
|
+
The function is based on the method of least squares :
|
|
8586
|
+
see: https://mathworld.wolfram.com/LeastSquaresFittingPolynomial.html
|
|
8587
|
+
*/
|
|
8588
|
+
function polynomialRegression(flatY, flatX, order, intercept) {
|
|
8589
|
+
assertSameNumberOfElements(flatX, flatY);
|
|
8590
|
+
assert(() => order >= 1, _t(`Function [[FUNCTION_NAME]] A regression of order less than 1 cannot be possible.`));
|
|
8591
|
+
const yMatrix = [flatY];
|
|
8592
|
+
const xMatrix = flatX.map((x) => range(0, order).map((i) => Math.pow(x, order - i)));
|
|
8593
|
+
if (intercept) {
|
|
8594
|
+
xMatrix.forEach((row) => row.push(1));
|
|
8595
|
+
}
|
|
8596
|
+
const coeffs = getLMSCoefficients(xMatrix, yMatrix);
|
|
8597
|
+
if (!intercept) {
|
|
8598
|
+
coeffs.push([0]);
|
|
8599
|
+
}
|
|
8600
|
+
return coeffs;
|
|
8601
|
+
}
|
|
8602
|
+
function getLMSCoefficients(xMatrix, yMatrix) {
|
|
8603
|
+
const xMatrixT = transposeMatrix(xMatrix);
|
|
8604
|
+
const dot1 = multiplyMatrices(xMatrix, xMatrixT);
|
|
8605
|
+
const { inverted: dotInv } = invertMatrix(dot1);
|
|
8606
|
+
if (dotInv === undefined) {
|
|
8607
|
+
throw new Error(_t("Matrix is not invertible"));
|
|
8608
|
+
}
|
|
8609
|
+
const dot2 = multiplyMatrices(xMatrix, yMatrix);
|
|
8610
|
+
return transposeMatrix(multiplyMatrices(dotInv, dot2));
|
|
8611
|
+
}
|
|
8612
|
+
function evaluatePolynomial(coeffs, x, order) {
|
|
8613
|
+
return coeffs.reduce((acc, coeff, i) => acc + coeff * Math.pow(x, order - i), 0);
|
|
8614
|
+
}
|
|
8615
|
+
function expM(M) {
|
|
8616
|
+
return M.map((col) => col.map((cell) => Math.exp(cell)));
|
|
8617
|
+
}
|
|
8618
|
+
function logM(M) {
|
|
8619
|
+
return M.map((col) => col.map((cell) => Math.log(cell)));
|
|
8620
|
+
}
|
|
8621
|
+
function predictLinearValues(Y, X, newX, computeIntercept) {
|
|
8622
|
+
const { _X, _newX } = prepareDataForRegression(X, Y, newX);
|
|
8623
|
+
const coeffs = fullLinearRegression(_X, Y, computeIntercept, false);
|
|
8624
|
+
const nVar = coeffs.length - 1;
|
|
8625
|
+
const newY = _newX.map((col) => {
|
|
8626
|
+
let value = 0;
|
|
8627
|
+
for (let i = 0; i < nVar; i++) {
|
|
8628
|
+
value += coeffs[i][0] * col[nVar - i - 1];
|
|
8629
|
+
}
|
|
8630
|
+
value += coeffs[nVar][0];
|
|
8631
|
+
return [value];
|
|
8632
|
+
});
|
|
8633
|
+
return newY.length === newX.length ? newY : transposeMatrix(newY);
|
|
8634
|
+
}
|
|
8429
8635
|
// -----------------------------------------------------------------------------
|
|
8430
8636
|
// AVEDEV
|
|
8431
8637
|
// -----------------------------------------------------------------------------
|
|
@@ -8710,6 +8916,56 @@
|
|
|
8710
8916
|
isExported: true,
|
|
8711
8917
|
};
|
|
8712
8918
|
// -----------------------------------------------------------------------------
|
|
8919
|
+
// FORECAST
|
|
8920
|
+
// -----------------------------------------------------------------------------
|
|
8921
|
+
const FORECAST = {
|
|
8922
|
+
description: _t("Calculates the expected y-value for a specified x based on a linear regression of a dataset."),
|
|
8923
|
+
args: [
|
|
8924
|
+
arg("x (number, range<number>)", _t("The value(s) on the x-axis to forecast.")),
|
|
8925
|
+
arg("data_y (range<number>)", _t("The range representing the array or matrix of dependent data.")),
|
|
8926
|
+
arg("data_x (range<number>)", _t("The range representing the array or matrix of independent data.")),
|
|
8927
|
+
],
|
|
8928
|
+
returns: ["NUMBER"],
|
|
8929
|
+
compute: function (x, dataY, dataX) {
|
|
8930
|
+
const { flatDataX, flatDataY } = filterAndFlatData(dataY, dataX);
|
|
8931
|
+
return predictLinearValues([flatDataY], [flatDataX], matrixMap(toMatrix(x), (value) => toNumber(value, this.locale)), true);
|
|
8932
|
+
},
|
|
8933
|
+
isExported: true,
|
|
8934
|
+
};
|
|
8935
|
+
// -----------------------------------------------------------------------------
|
|
8936
|
+
// GROWTH
|
|
8937
|
+
// -----------------------------------------------------------------------------
|
|
8938
|
+
const GROWTH = {
|
|
8939
|
+
description: _t("Fits points to exponential growth trend."),
|
|
8940
|
+
args: [
|
|
8941
|
+
arg("known_data_y (range<number>)", _t("The array or range containing dependent (y) values that are already known, used to curve fit an ideal exponential growth curve.")),
|
|
8942
|
+
arg("known_data_x (range<number>, default={1;2;3;...})", _t("The values of the independent variable(s) corresponding with known_data_y.")),
|
|
8943
|
+
arg("new_data_x (any, range, default=known_data_x)", _t("The data points to return the y values for on the ideal curve fit.")),
|
|
8944
|
+
arg("b (boolean, default=TRUE)", _t("Given a general exponential form of y = b*m^x for a curve fit, calculates b if TRUE or forces b to be 1 and only calculates the m values if FALSE.")),
|
|
8945
|
+
],
|
|
8946
|
+
returns: ["NUMBER"],
|
|
8947
|
+
compute: function (knownDataY, knownDataX = [], newDataX = [], b = true) {
|
|
8948
|
+
return expM(predictLinearValues(logM(tryCastAsNumberMatrix(knownDataY, "the first argument (known_data_y)")), tryCastAsNumberMatrix(knownDataX, "the second argument (known_data_x)"), tryCastAsNumberMatrix(newDataX, "the third argument (new_data_y)"), toBoolean(b)));
|
|
8949
|
+
},
|
|
8950
|
+
};
|
|
8951
|
+
// -----------------------------------------------------------------------------
|
|
8952
|
+
// INTERCEPT
|
|
8953
|
+
// -----------------------------------------------------------------------------
|
|
8954
|
+
const INTERCEPT = {
|
|
8955
|
+
description: _t("Compute the intercept of the linear regression."),
|
|
8956
|
+
args: [
|
|
8957
|
+
arg("data_y (range<number>)", _t("The range representing the array or matrix of dependent data.")),
|
|
8958
|
+
arg("data_x (range<number>)", _t("The range representing the array or matrix of independent data.")),
|
|
8959
|
+
],
|
|
8960
|
+
returns: ["NUMBER"],
|
|
8961
|
+
compute: function (dataY, dataX) {
|
|
8962
|
+
const { flatDataX, flatDataY } = filterAndFlatData(dataY, dataX);
|
|
8963
|
+
const [[], [intercept]] = fullLinearRegression([flatDataX], [flatDataY]);
|
|
8964
|
+
return intercept;
|
|
8965
|
+
},
|
|
8966
|
+
isExported: true,
|
|
8967
|
+
};
|
|
8968
|
+
// -----------------------------------------------------------------------------
|
|
8713
8969
|
// LARGE
|
|
8714
8970
|
// -----------------------------------------------------------------------------
|
|
8715
8971
|
const LARGE = {
|
|
@@ -8737,12 +8993,94 @@
|
|
|
8737
8993
|
});
|
|
8738
8994
|
const result = largests.shift();
|
|
8739
8995
|
assert(() => result !== undefined, _t(`[[FUNCTION_NAME]] has no valid input data.`));
|
|
8740
|
-
assert(() => count >= _n, _t("Function [[FUNCTION_NAME]] parameter 2 value (%s) is out of range.", _n
|
|
8996
|
+
assert(() => count >= _n, _t("Function [[FUNCTION_NAME]] parameter 2 value (%s) is out of range.", _n));
|
|
8741
8997
|
return result;
|
|
8742
8998
|
},
|
|
8743
8999
|
isExported: true,
|
|
8744
9000
|
};
|
|
8745
9001
|
// -----------------------------------------------------------------------------
|
|
9002
|
+
// LINEST
|
|
9003
|
+
// -----------------------------------------------------------------------------
|
|
9004
|
+
const LINEST = {
|
|
9005
|
+
description: _t("Compute the intercept of the linear regression."),
|
|
9006
|
+
args: [
|
|
9007
|
+
arg("data_y (range<number>)", _t("The range representing the array or matrix of dependent data.")),
|
|
9008
|
+
arg("data_x (range<number>, default={1;2;3;...})", _t("The range representing the array or matrix of independent data.")),
|
|
9009
|
+
arg("calculate_b (boolean, default=TRUE)", _t("A flag specifying wheter to compute the slope or not")),
|
|
9010
|
+
arg("verbose (boolean, default=FALSE)", _t("A flag specifying whether to return additional regression statistics or only the linear coefficients and the y-intercept")),
|
|
9011
|
+
],
|
|
9012
|
+
returns: ["NUMBER"],
|
|
9013
|
+
compute: function (dataY, dataX = [], calculateB = true, verbose = false) {
|
|
9014
|
+
return fullLinearRegression(tryCastAsNumberMatrix(dataX, "the first argument (data_y)"), tryCastAsNumberMatrix(dataY, "the second argument (data_x)"), toBoolean(calculateB), toBoolean(verbose));
|
|
9015
|
+
},
|
|
9016
|
+
isExported: true,
|
|
9017
|
+
};
|
|
9018
|
+
// -----------------------------------------------------------------------------
|
|
9019
|
+
// LOGEST
|
|
9020
|
+
// -----------------------------------------------------------------------------
|
|
9021
|
+
const LOGEST = {
|
|
9022
|
+
description: _t("Compute the intercept of the linear regression."),
|
|
9023
|
+
args: [
|
|
9024
|
+
arg("data_y (range<number>)", _t("The range representing the array or matrix of dependent data.")),
|
|
9025
|
+
arg("data_x (range<number>, optional, default={1;2;3;...})", _t("The range representing the array or matrix of independent data.")),
|
|
9026
|
+
arg("calculate_b (boolean, default=TRUE)", _t("A flag specifying wheter to compute the slope or not")),
|
|
9027
|
+
arg("verbose (boolean, default=FALSE)", _t("A flag specifying whether to return additional regression statistics or only the linear coefficients and the y-intercept")),
|
|
9028
|
+
],
|
|
9029
|
+
returns: ["NUMBER"],
|
|
9030
|
+
compute: function (dataY, dataX = [], calculateB = true, verbose = false) {
|
|
9031
|
+
const coeffs = fullLinearRegression(tryCastAsNumberMatrix(dataX, "the second argument (data_x)"), logM(tryCastAsNumberMatrix(dataY, "the first argument (data_y)")), toBoolean(calculateB), toBoolean(verbose));
|
|
9032
|
+
for (let i = 0; i < coeffs.length; i++) {
|
|
9033
|
+
coeffs[i][0] = Math.exp(coeffs[i][0]);
|
|
9034
|
+
}
|
|
9035
|
+
return coeffs;
|
|
9036
|
+
},
|
|
9037
|
+
isExported: true,
|
|
9038
|
+
};
|
|
9039
|
+
// -----------------------------------------------------------------------------
|
|
9040
|
+
// MATTHEWS
|
|
9041
|
+
// -----------------------------------------------------------------------------
|
|
9042
|
+
const MATTHEWS = {
|
|
9043
|
+
description: _t("Compute the Matthews correlation coefficient of a dataset."),
|
|
9044
|
+
args: [
|
|
9045
|
+
arg("data_x (range)", _t("The range representing the array or matrix of observed data.")),
|
|
9046
|
+
arg("data_y (range)", _t("The range representing the array or matrix of predicted data.")),
|
|
9047
|
+
],
|
|
9048
|
+
returns: ["NUMBER"],
|
|
9049
|
+
compute: function (dataX, dataY) {
|
|
9050
|
+
const flatX = dataX.flat();
|
|
9051
|
+
const flatY = dataY.flat();
|
|
9052
|
+
assertSameNumberOfElements(flatX, flatY);
|
|
9053
|
+
if (flatX.length === 0) {
|
|
9054
|
+
throw new Error(_t("[[FUNCTION_NAME]] expects non-empty ranges for both parameters."));
|
|
9055
|
+
}
|
|
9056
|
+
const n = flatX.length;
|
|
9057
|
+
let trueN = 0, trueP = 0, falseP = 0, falseN = 0;
|
|
9058
|
+
for (let i = 0; i < n; ++i) {
|
|
9059
|
+
const isTrue1 = toBoolean(flatX[i]);
|
|
9060
|
+
const isTrue2 = toBoolean(flatY[i]);
|
|
9061
|
+
if (isTrue1 === isTrue2) {
|
|
9062
|
+
if (isTrue1) {
|
|
9063
|
+
trueP++;
|
|
9064
|
+
}
|
|
9065
|
+
else {
|
|
9066
|
+
trueN++;
|
|
9067
|
+
}
|
|
9068
|
+
}
|
|
9069
|
+
else {
|
|
9070
|
+
if (isTrue1) {
|
|
9071
|
+
falseN++;
|
|
9072
|
+
}
|
|
9073
|
+
else {
|
|
9074
|
+
falseP++;
|
|
9075
|
+
}
|
|
9076
|
+
}
|
|
9077
|
+
}
|
|
9078
|
+
return ((trueP * trueN - falseP * falseN) /
|
|
9079
|
+
Math.sqrt((trueP + falseP) * (trueP + falseN) * (trueN + falseP) * (trueN + falseN)));
|
|
9080
|
+
},
|
|
9081
|
+
isExported: false,
|
|
9082
|
+
};
|
|
9083
|
+
// -----------------------------------------------------------------------------
|
|
8746
9084
|
// MAX
|
|
8747
9085
|
// -----------------------------------------------------------------------------
|
|
8748
9086
|
const MAX = {
|
|
@@ -8897,6 +9235,41 @@
|
|
|
8897
9235
|
isExported: true,
|
|
8898
9236
|
};
|
|
8899
9237
|
// -----------------------------------------------------------------------------
|
|
9238
|
+
// PEARSON
|
|
9239
|
+
// -----------------------------------------------------------------------------
|
|
9240
|
+
const PEARSON = {
|
|
9241
|
+
description: _t("Compute the Pearson product-moment correlation coefficient of a dataset."),
|
|
9242
|
+
args: [
|
|
9243
|
+
arg("data_y (range<number>)", _t("The range representing the array or matrix of dependent data.")),
|
|
9244
|
+
arg("data_x (range<number>)", _t("The range representing the array or matrix of independent data.")),
|
|
9245
|
+
],
|
|
9246
|
+
returns: ["NUMBER"],
|
|
9247
|
+
compute: function (dataY, dataX) {
|
|
9248
|
+
const { flatDataX, flatDataY } = filterAndFlatData(dataX, dataY);
|
|
9249
|
+
if (flatDataX.length === 0) {
|
|
9250
|
+
throw new Error(_t("[[FUNCTION_NAME]] expects non-empty ranges for both parameters."));
|
|
9251
|
+
}
|
|
9252
|
+
if (flatDataX.length < 2) {
|
|
9253
|
+
throw new Error(_t("[[FUNCTION_NAME]] needs at least two values for both parameters"));
|
|
9254
|
+
}
|
|
9255
|
+
const n = flatDataX.length;
|
|
9256
|
+
let sumX = 0, sumY = 0, sumXY = 0, sumXX = 0, sumYY = 0;
|
|
9257
|
+
for (let i = 0; i < n; i++) {
|
|
9258
|
+
const xij = flatDataX[i];
|
|
9259
|
+
const yij = flatDataY[i];
|
|
9260
|
+
sumX += xij;
|
|
9261
|
+
sumY += yij;
|
|
9262
|
+
sumXY += xij * yij;
|
|
9263
|
+
sumXX += xij * xij;
|
|
9264
|
+
sumYY += yij * yij;
|
|
9265
|
+
}
|
|
9266
|
+
return ((n * sumXY - sumX * sumY) / Math.sqrt((n * sumXX - sumX * sumX) * (n * sumYY - sumY * sumY)));
|
|
9267
|
+
},
|
|
9268
|
+
isExported: true,
|
|
9269
|
+
};
|
|
9270
|
+
// In GSheet, CORREL is just an alias to PEARSON
|
|
9271
|
+
const CORREL = PEARSON;
|
|
9272
|
+
// -----------------------------------------------------------------------------
|
|
8900
9273
|
// PERCENTILE
|
|
8901
9274
|
// -----------------------------------------------------------------------------
|
|
8902
9275
|
const PERCENTILE = {
|
|
@@ -8951,6 +9324,45 @@
|
|
|
8951
9324
|
isExported: true,
|
|
8952
9325
|
};
|
|
8953
9326
|
// -----------------------------------------------------------------------------
|
|
9327
|
+
// POLYFIT
|
|
9328
|
+
// -----------------------------------------------------------------------------
|
|
9329
|
+
const POLYFIT_COEFFS = {
|
|
9330
|
+
description: _t("Compute the coefficients of polynomial regression of the dataset."),
|
|
9331
|
+
args: [
|
|
9332
|
+
arg("data_y (range<number>)", _t("The range representing the array or matrix of dependent data.")),
|
|
9333
|
+
arg("data_x (range<number>)", _t("The range representing the array or matrix of independent data.")),
|
|
9334
|
+
arg("order (number)", _t("The order of the polynomial to fit the data, between 1 and 6.")),
|
|
9335
|
+
arg("intercept (boolean, default=TRUE)", _t("A flag specifying whether to compute the intercept or not.")),
|
|
9336
|
+
],
|
|
9337
|
+
returns: ["RANGE<NUMBER>"],
|
|
9338
|
+
compute: function (dataY, dataX, order, intercept = true) {
|
|
9339
|
+
const { flatDataX, flatDataY } = filterAndFlatData(dataY, dataX);
|
|
9340
|
+
return polynomialRegression(flatDataY, flatDataX, toNumber(order, this.locale), toBoolean(intercept));
|
|
9341
|
+
},
|
|
9342
|
+
isExported: false,
|
|
9343
|
+
};
|
|
9344
|
+
// -----------------------------------------------------------------------------
|
|
9345
|
+
// POLYFIT.FORECAST
|
|
9346
|
+
// -----------------------------------------------------------------------------
|
|
9347
|
+
const POLYFIT_FORECAST = {
|
|
9348
|
+
description: _t("Predict value by computing a polynomial regression of the dataset."),
|
|
9349
|
+
args: [
|
|
9350
|
+
arg("x (number, range<number>)", _t("The value(s) on the x-axis to forecast.")),
|
|
9351
|
+
arg("data_y (range<number>)", _t("The range representing the array or matrix of dependent data.")),
|
|
9352
|
+
arg("data_x (range<number>)", _t("The range representing the array or matrix of independent data.")),
|
|
9353
|
+
arg("order (number)", _t("The order of the polynomial to fit the data, between 1 and 6.")),
|
|
9354
|
+
arg("intercept (boolean, default=TRUE)", _t("A flag specifying whether to compute the intercept or not.")),
|
|
9355
|
+
],
|
|
9356
|
+
returns: ["NUMBER"],
|
|
9357
|
+
compute: function (x, dataY, dataX, order, intercept = true) {
|
|
9358
|
+
const _order = toNumber(order, this.locale);
|
|
9359
|
+
const { flatDataX, flatDataY } = filterAndFlatData(dataY, dataX);
|
|
9360
|
+
const coeffs = polynomialRegression(flatDataY, flatDataX, _order, toBoolean(intercept)).flat();
|
|
9361
|
+
return matrixMap(toMatrix(x), (xij) => evaluatePolynomial(coeffs, toNumber(xij, this.locale), _order));
|
|
9362
|
+
},
|
|
9363
|
+
isExported: false,
|
|
9364
|
+
};
|
|
9365
|
+
// -----------------------------------------------------------------------------
|
|
8954
9366
|
// QUARTILE
|
|
8955
9367
|
// -----------------------------------------------------------------------------
|
|
8956
9368
|
const QUARTILE = {
|
|
@@ -9007,6 +9419,39 @@
|
|
|
9007
9419
|
isExported: true,
|
|
9008
9420
|
};
|
|
9009
9421
|
// -----------------------------------------------------------------------------
|
|
9422
|
+
// RSQ
|
|
9423
|
+
// -----------------------------------------------------------------------------
|
|
9424
|
+
const RSQ = {
|
|
9425
|
+
description: _t("Compute the square of r, the Pearson product-moment correlation coefficient of a dataset."),
|
|
9426
|
+
args: [
|
|
9427
|
+
arg("data_y (range<number>)", _t("The range representing the array or matrix of dependent data.")),
|
|
9428
|
+
arg("data_x (range<number>)", _t("The range representing the array or matrix of independent data.")),
|
|
9429
|
+
],
|
|
9430
|
+
returns: ["NUMBER"],
|
|
9431
|
+
compute: function (dataY, dataX) {
|
|
9432
|
+
const pearson = PEARSON.compute.bind(this);
|
|
9433
|
+
return Math.pow(pearson(dataX, dataY), 2.0);
|
|
9434
|
+
},
|
|
9435
|
+
isExported: true,
|
|
9436
|
+
};
|
|
9437
|
+
// -----------------------------------------------------------------------------
|
|
9438
|
+
// SLOPE
|
|
9439
|
+
// -----------------------------------------------------------------------------
|
|
9440
|
+
const SLOPE = {
|
|
9441
|
+
description: _t("Compute the slope of the linear regression."),
|
|
9442
|
+
args: [
|
|
9443
|
+
arg("data_y (range<number>)", _t("The range representing the array or matrix of dependent data.")),
|
|
9444
|
+
arg("data_x (range<number>)", _t("The range representing the array or matrix of independent data.")),
|
|
9445
|
+
],
|
|
9446
|
+
returns: ["NUMBER"],
|
|
9447
|
+
compute: function (dataY, dataX) {
|
|
9448
|
+
const { flatDataX, flatDataY } = filterAndFlatData(dataY, dataX);
|
|
9449
|
+
const [[slope]] = fullLinearRegression([flatDataX], [flatDataY]);
|
|
9450
|
+
return slope;
|
|
9451
|
+
},
|
|
9452
|
+
isExported: true,
|
|
9453
|
+
};
|
|
9454
|
+
// -----------------------------------------------------------------------------
|
|
9010
9455
|
// SMALL
|
|
9011
9456
|
// -----------------------------------------------------------------------------
|
|
9012
9457
|
const SMALL = {
|
|
@@ -9034,12 +9479,39 @@
|
|
|
9034
9479
|
});
|
|
9035
9480
|
const result = largests.pop();
|
|
9036
9481
|
assert(() => result !== undefined, _t(`[[FUNCTION_NAME]] has no valid input data.`));
|
|
9037
|
-
assert(() => count >= _n, _t("Function [[FUNCTION_NAME]] parameter 2 value (%s) is out of range.", _n
|
|
9482
|
+
assert(() => count >= _n, _t("Function [[FUNCTION_NAME]] parameter 2 value (%s) is out of range.", _n));
|
|
9038
9483
|
return result;
|
|
9039
9484
|
},
|
|
9040
9485
|
isExported: true,
|
|
9041
9486
|
};
|
|
9042
9487
|
// -----------------------------------------------------------------------------
|
|
9488
|
+
// SPEARMAN
|
|
9489
|
+
// -----------------------------------------------------------------------------
|
|
9490
|
+
const SPEARMAN = {
|
|
9491
|
+
description: _t("Compute the Spearman rank correlation coefficient of a dataset."),
|
|
9492
|
+
args: [
|
|
9493
|
+
arg("data_y (range<number>)", _t("The range representing the array or matrix of dependent data.")),
|
|
9494
|
+
arg("data_x (range<number>)", _t("The range representing the array or matrix of independent data.")),
|
|
9495
|
+
],
|
|
9496
|
+
returns: ["NUMBER"],
|
|
9497
|
+
compute: function (dataX, dataY) {
|
|
9498
|
+
const { flatDataX, flatDataY } = filterAndFlatData(dataY, dataX);
|
|
9499
|
+
const n = flatDataX.length;
|
|
9500
|
+
const order = flatDataX.map((e, i) => [e, flatDataY[i]]);
|
|
9501
|
+
order.sort((a, b) => a[0] - b[0]);
|
|
9502
|
+
for (let i = 0; i < n; ++i) {
|
|
9503
|
+
order[i][0] = i;
|
|
9504
|
+
}
|
|
9505
|
+
order.sort((a, b) => a[1] - b[1]);
|
|
9506
|
+
let sum = 0.0;
|
|
9507
|
+
for (let i = 0; i < n; ++i) {
|
|
9508
|
+
sum += (order[i][0] - i) ** 2;
|
|
9509
|
+
}
|
|
9510
|
+
return 1 - (6 * sum) / (n ** 3 - n);
|
|
9511
|
+
},
|
|
9512
|
+
isExported: false,
|
|
9513
|
+
};
|
|
9514
|
+
// -----------------------------------------------------------------------------
|
|
9043
9515
|
// STDEV
|
|
9044
9516
|
// -----------------------------------------------------------------------------
|
|
9045
9517
|
const STDEV = {
|
|
@@ -9130,6 +9602,39 @@
|
|
|
9130
9602
|
isExported: true,
|
|
9131
9603
|
};
|
|
9132
9604
|
// -----------------------------------------------------------------------------
|
|
9605
|
+
// STEYX
|
|
9606
|
+
// -----------------------------------------------------------------------------
|
|
9607
|
+
const STEYX = {
|
|
9608
|
+
description: _t("Calculates the standard error of the predicted y-value for each x in the regression of a dataset."),
|
|
9609
|
+
args: [
|
|
9610
|
+
arg("data_y (range<number>)", _t("The range representing the array or matrix of dependent data.")),
|
|
9611
|
+
arg("data_x (range<number>)", _t("The range representing the array or matrix of independent data.")),
|
|
9612
|
+
],
|
|
9613
|
+
returns: ["NUMBER"],
|
|
9614
|
+
compute: function (dataY, dataX) {
|
|
9615
|
+
const { flatDataX, flatDataY } = filterAndFlatData(dataY, dataX);
|
|
9616
|
+
const data = fullLinearRegression([flatDataX], [flatDataY], true, true);
|
|
9617
|
+
return data[1][2];
|
|
9618
|
+
},
|
|
9619
|
+
isExported: true,
|
|
9620
|
+
};
|
|
9621
|
+
// -----------------------------------------------------------------------------
|
|
9622
|
+
// TREND
|
|
9623
|
+
// -----------------------------------------------------------------------------
|
|
9624
|
+
const TREND = {
|
|
9625
|
+
description: _t("Fits points to linear trend derived via least-squares."),
|
|
9626
|
+
args: [
|
|
9627
|
+
arg("known_data_y (number, range<number>)", _t("The array or range containing dependent (y) values that are already known, used to curve fit an ideal linear trend.")),
|
|
9628
|
+
arg("known_data_x (number, range<number>, optional, default={1;2;3;...})", _t("The values of the independent variable(s) corresponding with known_data_y.")),
|
|
9629
|
+
arg("new_data_x (number, range<number>, optional, default=known_data_x)", _t("The data points to return the y values for on the ideal curve fit.")),
|
|
9630
|
+
arg("b (boolean, optional, default=TRUE)", _t("Given a general linear form of y = m*x+b for a curve fit, calculates b if TRUE or forces b to be 0 and only calculates the m values if FALSE, i.e. forces the curve fit to pass through the origin.")),
|
|
9631
|
+
],
|
|
9632
|
+
returns: ["NUMBER"],
|
|
9633
|
+
compute: function (knownDataY, knownDataX = [], newDataX = [], b = true) {
|
|
9634
|
+
return predictLinearValues(tryCastAsNumberMatrix(knownDataY, "the first argument (known_data_y)"), tryCastAsNumberMatrix(knownDataX, "the second argument (known_data_x)"), tryCastAsNumberMatrix(newDataX, "the third argument (new_data_y)"), toBoolean(b));
|
|
9635
|
+
},
|
|
9636
|
+
};
|
|
9637
|
+
// -----------------------------------------------------------------------------
|
|
9133
9638
|
// VAR
|
|
9134
9639
|
// -----------------------------------------------------------------------------
|
|
9135
9640
|
const VAR = {
|
|
@@ -9228,12 +9733,19 @@
|
|
|
9228
9733
|
AVERAGEIF: AVERAGEIF,
|
|
9229
9734
|
AVERAGEIFS: AVERAGEIFS,
|
|
9230
9735
|
AVERAGE_WEIGHTED: AVERAGE_WEIGHTED,
|
|
9736
|
+
CORREL: CORREL,
|
|
9231
9737
|
COUNT: COUNT,
|
|
9232
9738
|
COUNTA: COUNTA,
|
|
9233
9739
|
COVAR: COVAR,
|
|
9234
9740
|
COVARIANCE_P: COVARIANCE_P,
|
|
9235
9741
|
COVARIANCE_S: COVARIANCE_S,
|
|
9742
|
+
FORECAST: FORECAST,
|
|
9743
|
+
GROWTH: GROWTH,
|
|
9744
|
+
INTERCEPT: INTERCEPT,
|
|
9236
9745
|
LARGE: LARGE,
|
|
9746
|
+
LINEST: LINEST,
|
|
9747
|
+
LOGEST: LOGEST,
|
|
9748
|
+
MATTHEWS: MATTHEWS,
|
|
9237
9749
|
MAX: MAX,
|
|
9238
9750
|
MAXA: MAXA,
|
|
9239
9751
|
MAXIFS: MAXIFS,
|
|
@@ -9241,19 +9753,27 @@
|
|
|
9241
9753
|
MIN: MIN,
|
|
9242
9754
|
MINA: MINA,
|
|
9243
9755
|
MINIFS: MINIFS,
|
|
9756
|
+
PEARSON: PEARSON,
|
|
9244
9757
|
PERCENTILE: PERCENTILE,
|
|
9245
9758
|
PERCENTILE_EXC: PERCENTILE_EXC,
|
|
9246
9759
|
PERCENTILE_INC: PERCENTILE_INC,
|
|
9760
|
+
POLYFIT_COEFFS: POLYFIT_COEFFS,
|
|
9761
|
+
POLYFIT_FORECAST: POLYFIT_FORECAST,
|
|
9247
9762
|
QUARTILE: QUARTILE,
|
|
9248
9763
|
QUARTILE_EXC: QUARTILE_EXC,
|
|
9249
9764
|
QUARTILE_INC: QUARTILE_INC,
|
|
9765
|
+
RSQ: RSQ,
|
|
9766
|
+
SLOPE: SLOPE,
|
|
9250
9767
|
SMALL: SMALL,
|
|
9768
|
+
SPEARMAN: SPEARMAN,
|
|
9251
9769
|
STDEV: STDEV,
|
|
9252
9770
|
STDEVA: STDEVA,
|
|
9253
9771
|
STDEVP: STDEVP,
|
|
9254
9772
|
STDEVPA: STDEVPA,
|
|
9255
9773
|
STDEV_P: STDEV_P,
|
|
9256
9774
|
STDEV_S: STDEV_S,
|
|
9775
|
+
STEYX: STEYX,
|
|
9776
|
+
TREND: TREND,
|
|
9257
9777
|
VAR: VAR,
|
|
9258
9778
|
VARA: VARA,
|
|
9259
9779
|
VARP: VARP,
|
|
@@ -13888,7 +14408,7 @@
|
|
|
13888
14408
|
],
|
|
13889
14409
|
returns: ["STRING"],
|
|
13890
14410
|
compute: function (text) {
|
|
13891
|
-
return toString(text)
|
|
14411
|
+
return trimContent(toString(text));
|
|
13892
14412
|
},
|
|
13893
14413
|
isExported: true,
|
|
13894
14414
|
};
|
|
@@ -19437,6 +19957,12 @@
|
|
|
19437
19957
|
env.openSidePanel("RemoveDuplicates", {});
|
|
19438
19958
|
},
|
|
19439
19959
|
};
|
|
19960
|
+
const trimWhitespace = {
|
|
19961
|
+
name: _t("Trim whitespace"),
|
|
19962
|
+
execute: (env) => {
|
|
19963
|
+
env.model.dispatch("TRIM_WHITESPACE");
|
|
19964
|
+
},
|
|
19965
|
+
};
|
|
19440
19966
|
const sortDescending = {
|
|
19441
19967
|
name: _t("Descending (Z ⟶ A)"),
|
|
19442
19968
|
execute: (env) => {
|
|
@@ -20796,6 +21322,10 @@
|
|
|
20796
21322
|
.addChild("remove_duplicates", ["data", "data_cleanup"], {
|
|
20797
21323
|
...removeDuplicates,
|
|
20798
21324
|
sequence: 10,
|
|
21325
|
+
})
|
|
21326
|
+
.addChild("trim_whitespace", ["data", "data_cleanup"], {
|
|
21327
|
+
...trimWhitespace,
|
|
21328
|
+
sequence: 20,
|
|
20799
21329
|
})
|
|
20800
21330
|
.addChild("split_to_columns", ["data"], {
|
|
20801
21331
|
...splitToColumns,
|
|
@@ -25971,9 +26501,11 @@
|
|
|
25971
26501
|
}
|
|
25972
26502
|
}
|
|
25973
26503
|
function updateMousePosition(e) {
|
|
25974
|
-
|
|
25975
|
-
|
|
25976
|
-
|
|
26504
|
+
if (gridRef.el === e.target) {
|
|
26505
|
+
x = e.offsetX;
|
|
26506
|
+
y = e.offsetY;
|
|
26507
|
+
lastMoved = Date.now();
|
|
26508
|
+
}
|
|
25977
26509
|
}
|
|
25978
26510
|
function recompute() {
|
|
25979
26511
|
const { col, row } = getPosition();
|
|
@@ -32455,8 +32987,9 @@
|
|
|
32455
32987
|
for (const position of positions) {
|
|
32456
32988
|
const cell = this.getters.getCell(position);
|
|
32457
32989
|
const xc = toXC(position.col, position.row);
|
|
32990
|
+
const style = this.removeDefaultStyleValues(cell.style);
|
|
32458
32991
|
cells[xc] = {
|
|
32459
|
-
style:
|
|
32992
|
+
style: Object.keys(style).length ? getItemId(style, styles) : undefined,
|
|
32460
32993
|
format: cell.format ? getItemId(cell.format, formats) : undefined,
|
|
32461
32994
|
content: cell.content || undefined,
|
|
32462
32995
|
};
|
|
@@ -32475,6 +33008,15 @@
|
|
|
32475
33008
|
exportForExcel(data) {
|
|
32476
33009
|
this.export(data);
|
|
32477
33010
|
}
|
|
33011
|
+
removeDefaultStyleValues(style) {
|
|
33012
|
+
const cleanedStyle = { ...style };
|
|
33013
|
+
for (const [property, defaultValue] of Object.entries(DEFAULT_STYLE)) {
|
|
33014
|
+
if (cleanedStyle[property] === defaultValue) {
|
|
33015
|
+
delete cleanedStyle[property];
|
|
33016
|
+
}
|
|
33017
|
+
}
|
|
33018
|
+
return cleanedStyle;
|
|
33019
|
+
}
|
|
32478
33020
|
// ---------------------------------------------------------------------------
|
|
32479
33021
|
// GETTERS
|
|
32480
33022
|
// ---------------------------------------------------------------------------
|
|
@@ -39813,6 +40355,7 @@
|
|
|
39813
40355
|
cells;
|
|
39814
40356
|
copiedTables;
|
|
39815
40357
|
zones;
|
|
40358
|
+
uuidGenerator = new UuidGenerator();
|
|
39816
40359
|
constructor(zones, operation, getters, dispatch, selection) {
|
|
39817
40360
|
super(operation, getters, dispatch, selection);
|
|
39818
40361
|
if (!zones.length) {
|
|
@@ -40265,8 +40808,9 @@
|
|
|
40265
40808
|
this.adaptCFRules(origin.sheetId, cf, [xc], toRemoveRange);
|
|
40266
40809
|
}
|
|
40267
40810
|
else {
|
|
40268
|
-
this.adaptCFRules(target.sheetId, cf, [xc], []);
|
|
40269
40811
|
this.adaptCFRules(origin.sheetId, cf, [], toRemoveRange);
|
|
40812
|
+
const cfToCopyTo = this.getCFToCopyTo(target.sheetId, cf);
|
|
40813
|
+
this.adaptCFRules(target.sheetId, cfToCopyTo, [xc], []);
|
|
40270
40814
|
}
|
|
40271
40815
|
}
|
|
40272
40816
|
}
|
|
@@ -40290,6 +40834,12 @@
|
|
|
40290
40834
|
sheetId,
|
|
40291
40835
|
});
|
|
40292
40836
|
}
|
|
40837
|
+
getCFToCopyTo(targetSheetId, originCF) {
|
|
40838
|
+
const cfInTarget = this.getters
|
|
40839
|
+
.getConditionalFormats(targetSheetId)
|
|
40840
|
+
.find((cf) => cf.stopIfTrue === originCF.stopIfTrue && deepEquals(cf.rule, originCF.rule));
|
|
40841
|
+
return cfInTarget ? cfInTarget : { ...originCF, id: this.uuidGenerator.uuidv4(), ranges: [] };
|
|
40842
|
+
}
|
|
40293
40843
|
}
|
|
40294
40844
|
|
|
40295
40845
|
class DataCleanupPlugin extends UIPlugin {
|
|
@@ -40308,6 +40858,9 @@
|
|
|
40308
40858
|
case "REMOVE_DUPLICATES":
|
|
40309
40859
|
this.removeDuplicates(cmd.columns, cmd.hasHeader);
|
|
40310
40860
|
break;
|
|
40861
|
+
case "TRIM_WHITESPACE":
|
|
40862
|
+
this.trimWhitespace();
|
|
40863
|
+
break;
|
|
40311
40864
|
}
|
|
40312
40865
|
}
|
|
40313
40866
|
// ---------------------------------------------------------------------------
|
|
@@ -40428,6 +40981,35 @@
|
|
|
40428
40981
|
}
|
|
40429
40982
|
return "Success" /* CommandResult.Success */;
|
|
40430
40983
|
}
|
|
40984
|
+
trimWhitespace() {
|
|
40985
|
+
const zones = this.getters.getSelectedZones();
|
|
40986
|
+
const sheetId = this.getters.getActiveSheetId();
|
|
40987
|
+
let count = 0;
|
|
40988
|
+
for (const { col, row } of zones.map(positions).flat()) {
|
|
40989
|
+
const cell = this.getters.getCell({ col, row, sheetId });
|
|
40990
|
+
if (!cell) {
|
|
40991
|
+
continue;
|
|
40992
|
+
}
|
|
40993
|
+
const trimmedContent = trimContent(cell.content);
|
|
40994
|
+
if (trimmedContent !== cell.content) {
|
|
40995
|
+
count += 1;
|
|
40996
|
+
this.dispatch("UPDATE_CELL", {
|
|
40997
|
+
sheetId,
|
|
40998
|
+
col,
|
|
40999
|
+
row,
|
|
41000
|
+
content: trimmedContent,
|
|
41001
|
+
});
|
|
41002
|
+
}
|
|
41003
|
+
}
|
|
41004
|
+
const text = count
|
|
41005
|
+
? _t("Trimmed whitespace from %s cells.", count)
|
|
41006
|
+
: _t("No selected cells had whitespace trimmed.");
|
|
41007
|
+
this.ui.notifyUI({
|
|
41008
|
+
type: "info",
|
|
41009
|
+
text: text,
|
|
41010
|
+
sticky: false,
|
|
41011
|
+
});
|
|
41012
|
+
}
|
|
40431
41013
|
}
|
|
40432
41014
|
|
|
40433
41015
|
const BORDER_COLOR = "#8B008B";
|
|
@@ -43226,7 +43808,7 @@
|
|
|
43226
43808
|
allowDispatch(cmd) {
|
|
43227
43809
|
switch (cmd.type) {
|
|
43228
43810
|
case "CUT":
|
|
43229
|
-
const zones =
|
|
43811
|
+
const zones = this.getters.getSelectedZones();
|
|
43230
43812
|
const state = this.getClipboardState(zones, cmd.type);
|
|
43231
43813
|
return state.isCutAllowed(zones);
|
|
43232
43814
|
case "PASTE":
|
|
@@ -43261,7 +43843,7 @@
|
|
|
43261
43843
|
switch (cmd.type) {
|
|
43262
43844
|
case "COPY":
|
|
43263
43845
|
case "CUT":
|
|
43264
|
-
const zones =
|
|
43846
|
+
const zones = this.getters.getSelectedZones();
|
|
43265
43847
|
this.state = this.getClipboardState(zones, cmd.type);
|
|
43266
43848
|
this.status = "visible";
|
|
43267
43849
|
this.originSheetId = this.getters.getActiveSheetId();
|
|
@@ -44925,26 +45507,24 @@
|
|
|
44925
45507
|
const isBasedBefore = cmd.base < start;
|
|
44926
45508
|
const deltaCol = isBasedBefore && isCol ? thickness : 0;
|
|
44927
45509
|
const deltaRow = isBasedBefore && !isCol ? thickness : 0;
|
|
44928
|
-
|
|
44929
|
-
|
|
44930
|
-
|
|
44931
|
-
|
|
44932
|
-
|
|
44933
|
-
|
|
44934
|
-
|
|
44935
|
-
|
|
44936
|
-
|
|
44937
|
-
|
|
44938
|
-
|
|
44939
|
-
|
|
44940
|
-
|
|
44941
|
-
|
|
44942
|
-
|
|
44943
|
-
|
|
44944
|
-
|
|
44945
|
-
|
|
44946
|
-
],
|
|
44947
|
-
});
|
|
45510
|
+
const target = [
|
|
45511
|
+
{
|
|
45512
|
+
left: isCol ? start + deltaCol : 0,
|
|
45513
|
+
right: isCol ? end + deltaCol : this.getters.getNumberCols(cmd.sheetId) - 1,
|
|
45514
|
+
top: !isCol ? start + deltaRow : 0,
|
|
45515
|
+
bottom: !isCol ? end + deltaRow : this.getters.getNumberRows(cmd.sheetId) - 1,
|
|
45516
|
+
},
|
|
45517
|
+
];
|
|
45518
|
+
const state = new ClipboardCellsState(target, "CUT", this.getters, this.dispatch, this.selection);
|
|
45519
|
+
const pasteTarget = [
|
|
45520
|
+
{
|
|
45521
|
+
left: isCol ? cmd.base : 0,
|
|
45522
|
+
right: isCol ? cmd.base + thickness - 1 : this.getters.getNumberCols(cmd.sheetId) - 1,
|
|
45523
|
+
top: !isCol ? cmd.base : 0,
|
|
45524
|
+
bottom: !isCol ? cmd.base + thickness - 1 : this.getters.getNumberRows(cmd.sheetId) - 1,
|
|
45525
|
+
},
|
|
45526
|
+
];
|
|
45527
|
+
state.paste(pasteTarget, { selectTarget: true });
|
|
44948
45528
|
const toRemove = isBasedBefore ? cmd.elements.map((el) => el + thickness) : cmd.elements;
|
|
44949
45529
|
let currentIndex = cmd.base;
|
|
44950
45530
|
for (const element of toRemove) {
|
|
@@ -52293,9 +52873,9 @@
|
|
|
52293
52873
|
exports.tokenize = tokenize;
|
|
52294
52874
|
|
|
52295
52875
|
|
|
52296
|
-
__info__.version =
|
|
52297
|
-
__info__.date =
|
|
52298
|
-
__info__.hash =
|
|
52876
|
+
__info__.version = "16.5.0-alpha.8";
|
|
52877
|
+
__info__.date = "2023-09-21T15:59:10.959Z";
|
|
52878
|
+
__info__.hash = "0b7527d";
|
|
52299
52879
|
|
|
52300
52880
|
|
|
52301
52881
|
})(this.o_spreadsheet = this.o_spreadsheet || {}, owl);
|