@odoo/o-spreadsheet 18.4.0-alpha.1 → 18.4.0-alpha.2
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 +1695 -879
- package/dist/o-spreadsheet.d.ts +245 -135
- package/dist/o-spreadsheet.esm.js +1695 -879
- package/dist/o-spreadsheet.iife.js +1695 -879
- package/dist/o-spreadsheet.iife.min.js +401 -398
- package/dist/o_spreadsheet.xml +77 -11
- 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.4.0-alpha.
|
|
6
|
-
* @date 2025-05-
|
|
7
|
-
* @hash
|
|
5
|
+
* @version 18.4.0-alpha.2
|
|
6
|
+
* @date 2025-05-12T05:28:06.109Z
|
|
7
|
+
* @hash a11158e
|
|
8
8
|
*/
|
|
9
9
|
|
|
10
10
|
import { useEnv, useSubEnv, onWillUnmount, useComponent, status, Component, useRef, onMounted, useEffect, App, blockDom, useState, onPatched, useExternalListener, onWillUpdateProps, onWillPatch, xml, onWillStart, useChildSubEnv, markRaw, toRaw } from '@odoo/owl';
|
|
@@ -63,12 +63,25 @@ function createAction(item) {
|
|
|
63
63
|
class Registry {
|
|
64
64
|
content = {};
|
|
65
65
|
/**
|
|
66
|
-
* Add an item to the registry
|
|
66
|
+
* Add an item to the registry, you can only add if there is no item
|
|
67
|
+
* already present in the registery with the given key
|
|
67
68
|
*
|
|
68
69
|
* Note that this also returns the registry, so another add method call can
|
|
69
70
|
* be chained
|
|
70
71
|
*/
|
|
71
72
|
add(key, value) {
|
|
73
|
+
if (key in this.content) {
|
|
74
|
+
throw new Error(`${key} is already present in this registry!`);
|
|
75
|
+
}
|
|
76
|
+
return this.replace(key, value);
|
|
77
|
+
}
|
|
78
|
+
/**
|
|
79
|
+
* Replace (or add) an item to the registry
|
|
80
|
+
*
|
|
81
|
+
* Note that this also returns the registry, so another add method call can
|
|
82
|
+
* be chained
|
|
83
|
+
*/
|
|
84
|
+
replace(key, value) {
|
|
72
85
|
this.content[key] = value;
|
|
73
86
|
return this;
|
|
74
87
|
}
|
|
@@ -3546,6 +3559,7 @@ const readonlyAllowedCommands = new Set([
|
|
|
3546
3559
|
"EVALUATE_CHARTS",
|
|
3547
3560
|
"SET_FORMULA_VISIBILITY",
|
|
3548
3561
|
"UPDATE_FILTER",
|
|
3562
|
+
"UPDATE_CHART",
|
|
3549
3563
|
]);
|
|
3550
3564
|
const coreTypes = new Set([
|
|
3551
3565
|
/** CELLS */
|
|
@@ -3901,14 +3915,14 @@ class SplillBlockedError extends EvaluationError {
|
|
|
3901
3915
|
super(message, CellErrorType.SpilledBlocked);
|
|
3902
3916
|
}
|
|
3903
3917
|
}
|
|
3918
|
+
class DivisionByZeroError extends EvaluationError {
|
|
3919
|
+
constructor(message = _t("Division by zero")) {
|
|
3920
|
+
super(message, CellErrorType.DivisionByZero);
|
|
3921
|
+
}
|
|
3922
|
+
}
|
|
3904
3923
|
|
|
3905
3924
|
// HELPERS
|
|
3906
3925
|
const SORT_TYPES_ORDER = ["number", "string", "boolean", "undefined"];
|
|
3907
|
-
function assert(condition, message, value) {
|
|
3908
|
-
if (!condition()) {
|
|
3909
|
-
throw new EvaluationError(message, value);
|
|
3910
|
-
}
|
|
3911
|
-
}
|
|
3912
3926
|
function inferFormat(data) {
|
|
3913
3927
|
if (data === undefined) {
|
|
3914
3928
|
return undefined;
|
|
@@ -3987,12 +4001,6 @@ function toInteger(value, locale) {
|
|
|
3987
4001
|
function strictToInteger(value, locale) {
|
|
3988
4002
|
return Math.trunc(strictToNumber(value, locale));
|
|
3989
4003
|
}
|
|
3990
|
-
function assertNumberGreaterThanOrEqualToOne(value) {
|
|
3991
|
-
assert(() => value >= 1, _t("The function [[FUNCTION_NAME]] expects a number value to be greater than or equal to 1, but receives %s.", value.toString()));
|
|
3992
|
-
}
|
|
3993
|
-
function assertNotZero(value) {
|
|
3994
|
-
assert(() => value !== 0, _t("Evaluation of function [[FUNCTION_NAME]] caused a divide by zero error."), CellErrorType.DivisionByZero);
|
|
3995
|
-
}
|
|
3996
4004
|
function toString(data) {
|
|
3997
4005
|
const value = toValue(data);
|
|
3998
4006
|
switch (typeof value) {
|
|
@@ -6531,6 +6539,25 @@ function moveHeaderIndexesOnHeaderDeletion(deletedHeaders, headers) {
|
|
|
6531
6539
|
})
|
|
6532
6540
|
.filter(isDefined);
|
|
6533
6541
|
}
|
|
6542
|
+
function getNextSheetName(existingNames, baseName = "Sheet") {
|
|
6543
|
+
let i = 1;
|
|
6544
|
+
let name = `${baseName}${i}`;
|
|
6545
|
+
while (existingNames.includes(name)) {
|
|
6546
|
+
name = `${baseName}${i}`;
|
|
6547
|
+
i++;
|
|
6548
|
+
}
|
|
6549
|
+
return name;
|
|
6550
|
+
}
|
|
6551
|
+
function getDuplicateSheetName(nameToDuplicate, existingNames) {
|
|
6552
|
+
let i = 1;
|
|
6553
|
+
const baseName = _t("Copy of %s", nameToDuplicate);
|
|
6554
|
+
let name = baseName.toString();
|
|
6555
|
+
while (existingNames.includes(name)) {
|
|
6556
|
+
name = `${baseName} (${i})`;
|
|
6557
|
+
i++;
|
|
6558
|
+
}
|
|
6559
|
+
return name;
|
|
6560
|
+
}
|
|
6534
6561
|
|
|
6535
6562
|
function computeTextLinesHeight(textLineHeight, numberOfLines = 1) {
|
|
6536
6563
|
return numberOfLines * (textLineHeight + MIN_CELL_TEXT_MARGIN) - MIN_CELL_TEXT_MARGIN;
|
|
@@ -7573,6 +7600,37 @@ function countUnique(args) {
|
|
|
7573
7600
|
return reduceAny(args, (acc, a) => (isDataNonEmpty(a) ? acc.add(a?.value) : acc), new Set()).size;
|
|
7574
7601
|
}
|
|
7575
7602
|
|
|
7603
|
+
function assert(condition, message) {
|
|
7604
|
+
if (!condition) {
|
|
7605
|
+
throw new EvaluationError(message);
|
|
7606
|
+
}
|
|
7607
|
+
}
|
|
7608
|
+
function assertNotZero(value, message = _t("Evaluation of function [[FUNCTION_NAME]] caused a divide by zero error.")) {
|
|
7609
|
+
if (value === 0) {
|
|
7610
|
+
throw new DivisionByZeroError(message);
|
|
7611
|
+
}
|
|
7612
|
+
}
|
|
7613
|
+
function isSingleColOrRow(arg) {
|
|
7614
|
+
return arg.length === 1 || arg[0].length === 1;
|
|
7615
|
+
}
|
|
7616
|
+
function areSameDimensions(...args) {
|
|
7617
|
+
if (args.every(isMatrix)) {
|
|
7618
|
+
const cols = args[0].length;
|
|
7619
|
+
const rows = args[0][0].length;
|
|
7620
|
+
for (const arg of args) {
|
|
7621
|
+
if (arg.length !== cols || arg[0].length !== rows) {
|
|
7622
|
+
return false;
|
|
7623
|
+
}
|
|
7624
|
+
}
|
|
7625
|
+
return true;
|
|
7626
|
+
}
|
|
7627
|
+
return !args.some((arg) => Array.isArray(arg) && (arg.length !== 1 || arg[0].length !== 1));
|
|
7628
|
+
}
|
|
7629
|
+
function isSquareMatrix(arg) {
|
|
7630
|
+
return arg.length === arg[0].length;
|
|
7631
|
+
}
|
|
7632
|
+
const expectNumberGreaterThanOrEqualToOne = (value) => _t("The function [[FUNCTION_NAME]] expects a number value to be greater than or equal to 1, but receives %s.", value);
|
|
7633
|
+
|
|
7576
7634
|
function getUnitMatrix(n) {
|
|
7577
7635
|
const matrix = Array(n);
|
|
7578
7636
|
for (let i = 0; i < n; i++) {
|
|
@@ -7701,7 +7759,7 @@ function toScalar(matrix) {
|
|
|
7701
7759
|
|
|
7702
7760
|
function assertSameNumberOfElements(...args) {
|
|
7703
7761
|
const dims = args[0].length;
|
|
7704
|
-
args.forEach((arg, i) => assert(
|
|
7762
|
+
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())));
|
|
7705
7763
|
}
|
|
7706
7764
|
function average(values, locale) {
|
|
7707
7765
|
let count = 0;
|
|
@@ -7865,7 +7923,7 @@ function fullLinearRegression(X, Y, computeIntercept = true, verbose = false) {
|
|
|
7865
7923
|
*/
|
|
7866
7924
|
function polynomialRegression(flatY, flatX, order, intercept) {
|
|
7867
7925
|
assertSameNumberOfElements(flatX, flatY);
|
|
7868
|
-
assert(
|
|
7926
|
+
assert(order >= 1, _t("Function [[FUNCTION_NAME]] A regression of order less than 1 cannot be possible."));
|
|
7869
7927
|
const yMatrix = [flatY];
|
|
7870
7928
|
const xMatrix = flatX.map((x) => range(0, order).map((i) => Math.pow(x, order - i)));
|
|
7871
7929
|
if (intercept) {
|
|
@@ -8273,6 +8331,24 @@ const monthNumberAdapter = {
|
|
|
8273
8331
|
return `${normalizedValue}`;
|
|
8274
8332
|
},
|
|
8275
8333
|
};
|
|
8334
|
+
/**
|
|
8335
|
+
* normalizes month number + year
|
|
8336
|
+
*/
|
|
8337
|
+
const monthAdapter = {
|
|
8338
|
+
normalizeFunctionValue(value) {
|
|
8339
|
+
const date = toNumber(value, DEFAULT_LOCALE);
|
|
8340
|
+
return formatValue(date, { locale: DEFAULT_LOCALE, format: "mm/yyyy" });
|
|
8341
|
+
},
|
|
8342
|
+
toValueAndFormat(normalizedValue) {
|
|
8343
|
+
return {
|
|
8344
|
+
value: toNumber(normalizedValue, DEFAULT_LOCALE),
|
|
8345
|
+
format: "mmmm yyyy",
|
|
8346
|
+
};
|
|
8347
|
+
},
|
|
8348
|
+
toFunctionValue(normalizedValue) {
|
|
8349
|
+
return `"${normalizedValue}"`;
|
|
8350
|
+
},
|
|
8351
|
+
};
|
|
8276
8352
|
/**
|
|
8277
8353
|
* normalizes quarter number
|
|
8278
8354
|
*/
|
|
@@ -8408,6 +8484,7 @@ pivotTimeAdapterRegistry
|
|
|
8408
8484
|
.add("day_of_month", nullHandlerDecorator(dayOfMonthAdapter))
|
|
8409
8485
|
.add("iso_week_number", nullHandlerDecorator(isoWeekNumberAdapter))
|
|
8410
8486
|
.add("month_number", nullHandlerDecorator(monthNumberAdapter))
|
|
8487
|
+
.add("month", nullHandlerDecorator(monthAdapter))
|
|
8411
8488
|
.add("quarter_number", nullHandlerDecorator(quarterNumberAdapter))
|
|
8412
8489
|
.add("day_of_week", nullHandlerDecorator(dayOfWeekAdapter))
|
|
8413
8490
|
.add("hour_number", nullHandlerDecorator(hourNumberAdapter))
|
|
@@ -8588,10 +8665,7 @@ function toNormalizedPivotValue(dimension, groupValue) {
|
|
|
8588
8665
|
return normalizer(groupValueString, dimension.granularity);
|
|
8589
8666
|
}
|
|
8590
8667
|
function normalizeDateTime(value, granularity) {
|
|
8591
|
-
|
|
8592
|
-
throw new Error("Missing granularity");
|
|
8593
|
-
}
|
|
8594
|
-
return pivotTimeAdapter(granularity).normalizeFunctionValue(value);
|
|
8668
|
+
return pivotTimeAdapter(granularity ?? "month").normalizeFunctionValue(value);
|
|
8595
8669
|
}
|
|
8596
8670
|
function toFunctionPivotValue(value, dimension) {
|
|
8597
8671
|
if (value === null) {
|
|
@@ -8603,10 +8677,7 @@ function toFunctionPivotValue(value, dimension) {
|
|
|
8603
8677
|
return pivotToFunctionValueRegistry.get(dimension.type)(value, dimension.granularity);
|
|
8604
8678
|
}
|
|
8605
8679
|
function toFunctionValueDateTime(value, granularity) {
|
|
8606
|
-
|
|
8607
|
-
throw new Error("Missing granularity");
|
|
8608
|
-
}
|
|
8609
|
-
return pivotTimeAdapter(granularity).toFunctionValue(value);
|
|
8680
|
+
return pivotTimeAdapter(granularity ?? "month").toFunctionValue(value);
|
|
8610
8681
|
}
|
|
8611
8682
|
const pivotNormalizationValueRegistry = new Registry();
|
|
8612
8683
|
pivotNormalizationValueRegistry
|
|
@@ -10080,20 +10151,24 @@ function getElementMargins(el) {
|
|
|
10080
10151
|
}
|
|
10081
10152
|
|
|
10082
10153
|
const chartJsExtensionRegistry = new Registry();
|
|
10083
|
-
|
|
10084
|
-
|
|
10085
|
-
|
|
10086
|
-
|
|
10087
|
-
|
|
10088
|
-
|
|
10089
|
-
|
|
10090
|
-
|
|
10091
|
-
|
|
10092
|
-
|
|
10093
|
-
|
|
10094
|
-
|
|
10154
|
+
function areChartJSExtensionsLoaded() {
|
|
10155
|
+
return !!window.Chart.registry.plugins.get("chartShowValuesPlugin");
|
|
10156
|
+
}
|
|
10157
|
+
function registerChartJSExtensions() {
|
|
10158
|
+
if (!window.Chart || areChartJSExtensionsLoaded()) {
|
|
10159
|
+
return;
|
|
10160
|
+
}
|
|
10161
|
+
for (const registryItem of chartJsExtensionRegistry.getAll()) {
|
|
10162
|
+
registryItem.register(window.Chart);
|
|
10163
|
+
}
|
|
10164
|
+
}
|
|
10165
|
+
function unregisterChartJsExtensions() {
|
|
10166
|
+
if (!window.Chart) {
|
|
10167
|
+
return;
|
|
10168
|
+
}
|
|
10169
|
+
for (const registryItem of chartJsExtensionRegistry.getAll()) {
|
|
10170
|
+
registryItem.unregister(window.Chart);
|
|
10095
10171
|
}
|
|
10096
|
-
return window.Chart;
|
|
10097
10172
|
}
|
|
10098
10173
|
|
|
10099
10174
|
function getFunnelChartController() {
|
|
@@ -11716,13 +11791,35 @@ css /* scss */ `
|
|
|
11716
11791
|
}
|
|
11717
11792
|
}
|
|
11718
11793
|
`;
|
|
11719
|
-
chartJsExtensionRegistry.add("chartShowValuesPlugin",
|
|
11720
|
-
|
|
11721
|
-
|
|
11722
|
-
|
|
11723
|
-
chartJsExtensionRegistry.add("
|
|
11724
|
-
|
|
11725
|
-
|
|
11794
|
+
chartJsExtensionRegistry.add("chartShowValuesPlugin", {
|
|
11795
|
+
register: (Chart) => Chart.register(chartShowValuesPlugin),
|
|
11796
|
+
unregister: (Chart) => Chart.unregister(chartShowValuesPlugin),
|
|
11797
|
+
});
|
|
11798
|
+
chartJsExtensionRegistry.add("waterfallLinesPlugin", {
|
|
11799
|
+
register: (Chart) => Chart.register(waterfallLinesPlugin),
|
|
11800
|
+
unregister: (Chart) => Chart.unregister(waterfallLinesPlugin),
|
|
11801
|
+
});
|
|
11802
|
+
chartJsExtensionRegistry.add("funnelController", {
|
|
11803
|
+
register: (Chart) => Chart.register(getFunnelChartController()),
|
|
11804
|
+
unregister: (Chart) => Chart.unregister(getFunnelChartController()),
|
|
11805
|
+
});
|
|
11806
|
+
chartJsExtensionRegistry.add("funnelElement", {
|
|
11807
|
+
register: (Chart) => Chart.register(getFunnelChartElement()),
|
|
11808
|
+
unregister: (Chart) => Chart.unregister(getFunnelChartElement()),
|
|
11809
|
+
});
|
|
11810
|
+
chartJsExtensionRegistry.add("funnelTooltipPositioner", {
|
|
11811
|
+
register: (Chart) => (Chart.Tooltip.positioners.funnelTooltipPositioner = funnelTooltipPositioner),
|
|
11812
|
+
// @ts-expect-error
|
|
11813
|
+
unregister: (Chart) => (Chart.Tooltip.positioners.funnelTooltipPositioner = undefined),
|
|
11814
|
+
});
|
|
11815
|
+
chartJsExtensionRegistry.add("sunburstLabelsPlugin", {
|
|
11816
|
+
register: (Chart) => Chart.register(sunburstLabelsPlugin),
|
|
11817
|
+
unregister: (Chart) => Chart.unregister(sunburstLabelsPlugin),
|
|
11818
|
+
});
|
|
11819
|
+
chartJsExtensionRegistry.add("sunburstHoverPlugin", {
|
|
11820
|
+
register: (Chart) => Chart.register(sunburstHoverPlugin),
|
|
11821
|
+
unregister: (Chart) => Chart.unregister(sunburstHoverPlugin),
|
|
11822
|
+
});
|
|
11726
11823
|
class ChartJsComponent extends Component {
|
|
11727
11824
|
static template = "o-spreadsheet-ChartJsComponent";
|
|
11728
11825
|
static props = {
|
|
@@ -11774,8 +11871,7 @@ class ChartJsComponent extends Component {
|
|
|
11774
11871
|
createChart(chartData) {
|
|
11775
11872
|
const canvas = this.canvas.el;
|
|
11776
11873
|
const ctx = canvas.getContext("2d");
|
|
11777
|
-
|
|
11778
|
-
this.chart = new Chart(ctx, chartData);
|
|
11874
|
+
this.chart = new window.Chart(ctx, chartData);
|
|
11779
11875
|
}
|
|
11780
11876
|
updateChartJs(chartData) {
|
|
11781
11877
|
if (chartData.data && chartData.data.datasets) {
|
|
@@ -12401,6 +12497,230 @@ class ScorecardChart extends Component {
|
|
|
12401
12497
|
}
|
|
12402
12498
|
}
|
|
12403
12499
|
|
|
12500
|
+
const macRegex = /Mac/i;
|
|
12501
|
+
const MODIFIER_KEYS = ["Shift", "Control", "Alt", "Meta"];
|
|
12502
|
+
/**
|
|
12503
|
+
* Return true if the event was triggered from
|
|
12504
|
+
* a child element.
|
|
12505
|
+
*/
|
|
12506
|
+
function isChildEvent(parent, ev) {
|
|
12507
|
+
if (!parent)
|
|
12508
|
+
return false;
|
|
12509
|
+
return !!ev.target && parent.contains(ev.target);
|
|
12510
|
+
}
|
|
12511
|
+
function gridOverlayPosition() {
|
|
12512
|
+
const spreadsheetElement = document.querySelector(".o-grid-overlay");
|
|
12513
|
+
if (spreadsheetElement) {
|
|
12514
|
+
const { top, left } = spreadsheetElement.getBoundingClientRect();
|
|
12515
|
+
return { top, left };
|
|
12516
|
+
}
|
|
12517
|
+
throw new Error("Can't find spreadsheet position");
|
|
12518
|
+
}
|
|
12519
|
+
function getBoundingRectAsPOJO(el) {
|
|
12520
|
+
const rect = el.getBoundingClientRect();
|
|
12521
|
+
return {
|
|
12522
|
+
x: rect.x,
|
|
12523
|
+
y: rect.y,
|
|
12524
|
+
width: rect.width,
|
|
12525
|
+
height: rect.height,
|
|
12526
|
+
};
|
|
12527
|
+
}
|
|
12528
|
+
/**
|
|
12529
|
+
* Iterate over all the children of `el` in the dom tree starting at `el`, depth first.
|
|
12530
|
+
*/
|
|
12531
|
+
function* iterateChildren(el) {
|
|
12532
|
+
yield el;
|
|
12533
|
+
if (el.hasChildNodes()) {
|
|
12534
|
+
for (const child of el.childNodes) {
|
|
12535
|
+
yield* iterateChildren(child);
|
|
12536
|
+
}
|
|
12537
|
+
}
|
|
12538
|
+
}
|
|
12539
|
+
function getOpenedMenus() {
|
|
12540
|
+
return Array.from(document.querySelectorAll(".o-spreadsheet .o-menu"));
|
|
12541
|
+
}
|
|
12542
|
+
function getCurrentSelection(el) {
|
|
12543
|
+
const { startElement, endElement, startSelectionOffset, endSelectionOffset } = getStartAndEndSelection(el);
|
|
12544
|
+
const startSizeBefore = findSelectionIndex(el, startElement, startSelectionOffset);
|
|
12545
|
+
const endSizeBefore = findSelectionIndex(el, endElement, endSelectionOffset);
|
|
12546
|
+
return {
|
|
12547
|
+
start: startSizeBefore,
|
|
12548
|
+
end: endSizeBefore,
|
|
12549
|
+
};
|
|
12550
|
+
}
|
|
12551
|
+
function getStartAndEndSelection(el) {
|
|
12552
|
+
const selection = document.getSelection();
|
|
12553
|
+
return {
|
|
12554
|
+
startElement: selection.anchorNode || el,
|
|
12555
|
+
startSelectionOffset: selection.anchorOffset,
|
|
12556
|
+
endElement: selection.focusNode || el,
|
|
12557
|
+
endSelectionOffset: selection.focusOffset,
|
|
12558
|
+
};
|
|
12559
|
+
}
|
|
12560
|
+
/**
|
|
12561
|
+
* Computes the text 'index' inside this.el based on the currently selected node and its offset.
|
|
12562
|
+
* The selected node is either a Text node or an Element node.
|
|
12563
|
+
*
|
|
12564
|
+
* case 1 -Text node:
|
|
12565
|
+
* the offset is the number of characters from the start of the node. We have to add this offset to the
|
|
12566
|
+
* content length of all previous nodes.
|
|
12567
|
+
*
|
|
12568
|
+
* case 2 - Element node:
|
|
12569
|
+
* the offset is the number of child nodes before the selected node. We have to add the content length of
|
|
12570
|
+
* all the nodes prior to the selected node as well as the content of the child node before the offset.
|
|
12571
|
+
*
|
|
12572
|
+
* See the MDN documentation for more details.
|
|
12573
|
+
* https://developer.mozilla.org/en-US/docs/Web/API/Range/startOffset
|
|
12574
|
+
* https://developer.mozilla.org/en-US/docs/Web/API/Range/endOffset
|
|
12575
|
+
*
|
|
12576
|
+
*/
|
|
12577
|
+
function findSelectionIndex(el, nodeToFind, nodeOffset) {
|
|
12578
|
+
let usedCharacters = 0;
|
|
12579
|
+
const it = iterateChildren(el);
|
|
12580
|
+
let current = it.next();
|
|
12581
|
+
let isFirstParagraph = true;
|
|
12582
|
+
while (!current.done && current.value !== nodeToFind) {
|
|
12583
|
+
if (!current.value.hasChildNodes()) {
|
|
12584
|
+
if (current.value.textContent) {
|
|
12585
|
+
usedCharacters += current.value.textContent.length;
|
|
12586
|
+
}
|
|
12587
|
+
}
|
|
12588
|
+
// One new paragraph = one new line character, except for the first paragraph
|
|
12589
|
+
if (current.value.nodeName === "P" ||
|
|
12590
|
+
(current.value.nodeName === "DIV" && current.value !== el) // On paste, the HTML may contain <div> instead of <p>
|
|
12591
|
+
) {
|
|
12592
|
+
if (isFirstParagraph) {
|
|
12593
|
+
isFirstParagraph = false;
|
|
12594
|
+
}
|
|
12595
|
+
else {
|
|
12596
|
+
usedCharacters++;
|
|
12597
|
+
}
|
|
12598
|
+
}
|
|
12599
|
+
current = it.next();
|
|
12600
|
+
}
|
|
12601
|
+
if (current.value !== nodeToFind) {
|
|
12602
|
+
/** This situation can happen if the code is called while the selection is not currently on the element.
|
|
12603
|
+
* In this case, we return 0 because we don't know the size of the text before the selection.
|
|
12604
|
+
*
|
|
12605
|
+
* A known occurrence is triggered since the introduction of commit d4663158 (PR #2038).
|
|
12606
|
+
*/
|
|
12607
|
+
return 0;
|
|
12608
|
+
}
|
|
12609
|
+
else {
|
|
12610
|
+
if (!current.value.hasChildNodes()) {
|
|
12611
|
+
usedCharacters += nodeOffset;
|
|
12612
|
+
}
|
|
12613
|
+
else {
|
|
12614
|
+
const children = [...current.value.childNodes].slice(0, nodeOffset);
|
|
12615
|
+
usedCharacters += children.reduce((acc, child, index) => {
|
|
12616
|
+
if (child.textContent !== null) {
|
|
12617
|
+
// need to account for paragraph nodes that implicitly add a new line
|
|
12618
|
+
// except for the last paragraph
|
|
12619
|
+
let chars = child.textContent.length;
|
|
12620
|
+
if (child.nodeName === "P" && index !== children.length - 1) {
|
|
12621
|
+
chars++;
|
|
12622
|
+
}
|
|
12623
|
+
return acc + chars;
|
|
12624
|
+
}
|
|
12625
|
+
else {
|
|
12626
|
+
return acc;
|
|
12627
|
+
}
|
|
12628
|
+
}, 0);
|
|
12629
|
+
}
|
|
12630
|
+
}
|
|
12631
|
+
if (nodeToFind.nodeName === "P" && !isFirstParagraph && nodeToFind.textContent === "") {
|
|
12632
|
+
usedCharacters++;
|
|
12633
|
+
}
|
|
12634
|
+
return usedCharacters;
|
|
12635
|
+
}
|
|
12636
|
+
const letterRegex = /^[a-zA-Z]$/;
|
|
12637
|
+
/**
|
|
12638
|
+
* Transform a keyboard event into a shortcut string that represent this event. The letters keys will be uppercased.
|
|
12639
|
+
*
|
|
12640
|
+
* @argument ev - The keyboard event to transform
|
|
12641
|
+
* @argument mode - Use either ev.key of ev.code to get the string shortcut
|
|
12642
|
+
*
|
|
12643
|
+
* @example
|
|
12644
|
+
* event : { ctrlKey: true, key: "a" } => "Ctrl+A"
|
|
12645
|
+
* event : { shift: true, alt: true, key: "Home" } => "Alt+Shift+Home"
|
|
12646
|
+
*/
|
|
12647
|
+
function keyboardEventToShortcutString(ev, mode = "key") {
|
|
12648
|
+
let keyDownString = "";
|
|
12649
|
+
if (!MODIFIER_KEYS.includes(ev.key)) {
|
|
12650
|
+
if (isCtrlKey(ev))
|
|
12651
|
+
keyDownString += "Ctrl+";
|
|
12652
|
+
if (ev.altKey)
|
|
12653
|
+
keyDownString += "Alt+";
|
|
12654
|
+
if (ev.shiftKey)
|
|
12655
|
+
keyDownString += "Shift+";
|
|
12656
|
+
}
|
|
12657
|
+
const key = mode === "key" ? ev.key : ev.code;
|
|
12658
|
+
keyDownString += letterRegex.test(key) ? key.toUpperCase() : key;
|
|
12659
|
+
return keyDownString;
|
|
12660
|
+
}
|
|
12661
|
+
function isMacOS() {
|
|
12662
|
+
return Boolean(macRegex.test(navigator.userAgent));
|
|
12663
|
+
}
|
|
12664
|
+
/**
|
|
12665
|
+
* @param {KeyboardEvent | MouseEvent} ev
|
|
12666
|
+
* @returns Returns true if the event was triggered with the "ctrl" modifier pressed.
|
|
12667
|
+
* On Mac, this is the "meta" or "command" key.
|
|
12668
|
+
*/
|
|
12669
|
+
function isCtrlKey(ev) {
|
|
12670
|
+
return isMacOS() ? ev.metaKey : ev.ctrlKey;
|
|
12671
|
+
}
|
|
12672
|
+
/**
|
|
12673
|
+
* @param {MouseEvent} ev - The mouse event.
|
|
12674
|
+
* @returns {boolean} Returns true if the event was triggered by a middle-click
|
|
12675
|
+
* or a Ctrl + Click (Cmd + Click on Mac).
|
|
12676
|
+
*/
|
|
12677
|
+
function isMiddleClickOrCtrlClick(ev) {
|
|
12678
|
+
return ev.button === 1 || (isCtrlKey(ev) && ev.button === 0);
|
|
12679
|
+
}
|
|
12680
|
+
async function convertImageToPng(imageUrl) {
|
|
12681
|
+
return new Promise((resolve, reject) => {
|
|
12682
|
+
const image = new Image();
|
|
12683
|
+
image.addEventListener("load", () => {
|
|
12684
|
+
const canvas = document.createElement("canvas");
|
|
12685
|
+
canvas.width = image.width;
|
|
12686
|
+
canvas.height = image.height;
|
|
12687
|
+
const ctx = canvas.getContext("2d");
|
|
12688
|
+
ctx?.drawImage(image, 0, 0);
|
|
12689
|
+
canvas.toBlob(resolve, "image/png");
|
|
12690
|
+
});
|
|
12691
|
+
image.addEventListener("error", reject);
|
|
12692
|
+
image.src = imageUrl;
|
|
12693
|
+
});
|
|
12694
|
+
}
|
|
12695
|
+
function downloadFile(dataUrl, fileName) {
|
|
12696
|
+
const a = document.createElement("a");
|
|
12697
|
+
a.href = dataUrl;
|
|
12698
|
+
a.download = fileName;
|
|
12699
|
+
document.body.appendChild(a);
|
|
12700
|
+
a.click();
|
|
12701
|
+
document.body.removeChild(a);
|
|
12702
|
+
}
|
|
12703
|
+
/**
|
|
12704
|
+
* Detects if the current browser is Firefox
|
|
12705
|
+
*/
|
|
12706
|
+
function isBrowserFirefox() {
|
|
12707
|
+
return /Firefox/i.test(navigator.userAgent);
|
|
12708
|
+
}
|
|
12709
|
+
|
|
12710
|
+
/**
|
|
12711
|
+
* Convert a JS color hexadecimal to an excel compatible color.
|
|
12712
|
+
*
|
|
12713
|
+
* In Excel the color don't start with a '#' and the format is AARRGGBB instead of RRGGBBAA
|
|
12714
|
+
*/
|
|
12715
|
+
function toXlsxHexColor(color) {
|
|
12716
|
+
color = toHex(color).replace("#", "");
|
|
12717
|
+
// alpha channel goes first
|
|
12718
|
+
if (color.length === 8) {
|
|
12719
|
+
return color.slice(6) + color.slice(0, 6);
|
|
12720
|
+
}
|
|
12721
|
+
return color;
|
|
12722
|
+
}
|
|
12723
|
+
|
|
12404
12724
|
const GAUGE_PADDING_SIDE = 30;
|
|
12405
12725
|
const GAUGE_PADDING_TOP = 10;
|
|
12406
12726
|
const GAUGE_PADDING_BOTTOM = 20;
|
|
@@ -12740,38 +13060,6 @@ function getRectangleTangentToCircle(angle, radius, circleCenterX, circleCenterY
|
|
|
12740
13060
|
return { bottomLeft, bottomRight, topRight, topLeft };
|
|
12741
13061
|
}
|
|
12742
13062
|
|
|
12743
|
-
class GaugeChartComponent extends Component {
|
|
12744
|
-
static template = "o-spreadsheet-GaugeChartComponent";
|
|
12745
|
-
canvas = useRef("chartContainer");
|
|
12746
|
-
get runtime() {
|
|
12747
|
-
return this.env.model.getters.getChartRuntime(this.props.figureUI.id);
|
|
12748
|
-
}
|
|
12749
|
-
setup() {
|
|
12750
|
-
useEffect(() => drawGaugeChart(this.canvas.el, this.runtime), () => {
|
|
12751
|
-
const canvas = this.canvas.el;
|
|
12752
|
-
const rect = canvas.getBoundingClientRect();
|
|
12753
|
-
return [rect.width, rect.height, this.runtime, this.canvas.el, window.devicePixelRatio];
|
|
12754
|
-
});
|
|
12755
|
-
}
|
|
12756
|
-
}
|
|
12757
|
-
GaugeChartComponent.props = {
|
|
12758
|
-
figureUI: Object,
|
|
12759
|
-
};
|
|
12760
|
-
|
|
12761
|
-
/**
|
|
12762
|
-
* Convert a JS color hexadecimal to an excel compatible color.
|
|
12763
|
-
*
|
|
12764
|
-
* In Excel the color don't start with a '#' and the format is AARRGGBB instead of RRGGBBAA
|
|
12765
|
-
*/
|
|
12766
|
-
function toXlsxHexColor(color) {
|
|
12767
|
-
color = toHex(color).replace("#", "");
|
|
12768
|
-
// alpha channel goes first
|
|
12769
|
-
if (color.length === 8) {
|
|
12770
|
-
return color.slice(6) + color.slice(0, 6);
|
|
12771
|
-
}
|
|
12772
|
-
return color;
|
|
12773
|
-
}
|
|
12774
|
-
|
|
12775
13063
|
const CHART_COMMON_OPTIONS = {
|
|
12776
13064
|
// https://www.chartjs.org/docs/latest/general/responsive.html
|
|
12777
13065
|
responsive: true, // will resize when its container is resized
|
|
@@ -12785,6 +13073,7 @@ const CHART_COMMON_OPTIONS = {
|
|
|
12785
13073
|
},
|
|
12786
13074
|
},
|
|
12787
13075
|
animation: false,
|
|
13076
|
+
events: ["mousemove", "mouseout", "click", "touchstart", "touchmove", "mouseup"],
|
|
12788
13077
|
};
|
|
12789
13078
|
function chartToImageUrl(runtime, figure, type) {
|
|
12790
13079
|
// wrap the canvas in a div with a fixed size because chart.js would
|
|
@@ -12834,8 +13123,7 @@ async function chartToImageFile(runtime, figure, type) {
|
|
|
12834
13123
|
if ("chartJsConfig" in runtime) {
|
|
12835
13124
|
const config = deepCopy(runtime.chartJsConfig);
|
|
12836
13125
|
config.plugins = [backgroundColorChartJSPlugin];
|
|
12837
|
-
const
|
|
12838
|
-
const chart = new Chart(canvas, config);
|
|
13126
|
+
const chart = new window.Chart(canvas, config);
|
|
12839
13127
|
chartBlob = await new Promise((resolve) => canvas.toBlob(resolve, "image/png"));
|
|
12840
13128
|
chart.destroy();
|
|
12841
13129
|
}
|
|
@@ -13236,10 +13524,11 @@ function getBarChartData(definition, dataSets, labelRange, getters) {
|
|
|
13236
13524
|
axisFormats,
|
|
13237
13525
|
labels,
|
|
13238
13526
|
locale: getters.getLocale(),
|
|
13527
|
+
topPadding: getTopPaddingForDashboard(definition, getters),
|
|
13239
13528
|
};
|
|
13240
13529
|
}
|
|
13241
13530
|
function getPyramidChartData(definition, dataSets, labelRange, getters) {
|
|
13242
|
-
const barChartData = getBarChartData(definition, dataSets, labelRange, getters);
|
|
13531
|
+
const barChartData = getBarChartData(definition, dataSets.slice(0, 2), labelRange, getters);
|
|
13243
13532
|
const barDataset = barChartData.dataSetsValues.filter((ds) => !ds.hidden);
|
|
13244
13533
|
const pyramidDatasetValues = [];
|
|
13245
13534
|
if (barDataset[0]) {
|
|
@@ -13295,6 +13584,7 @@ function getLineChartData(definition, dataSets, labelRange, getters) {
|
|
|
13295
13584
|
locale: getters.getLocale(),
|
|
13296
13585
|
trendDataSetsValues,
|
|
13297
13586
|
axisType,
|
|
13587
|
+
topPadding: getTopPaddingForDashboard(definition, getters),
|
|
13298
13588
|
};
|
|
13299
13589
|
}
|
|
13300
13590
|
function getPieChartData(definition, dataSets, labelRange, getters) {
|
|
@@ -13315,6 +13605,7 @@ function getPieChartData(definition, dataSets, labelRange, getters) {
|
|
|
13315
13605
|
axisFormats: { y: dataSetFormat },
|
|
13316
13606
|
labels,
|
|
13317
13607
|
locale: getters.getLocale(),
|
|
13608
|
+
topPadding: getTopPaddingForDashboard(definition, getters),
|
|
13318
13609
|
};
|
|
13319
13610
|
}
|
|
13320
13611
|
function getRadarChartData(definition, dataSets, labelRange, getters) {
|
|
@@ -13338,7 +13629,8 @@ function getRadarChartData(definition, dataSets, labelRange, getters) {
|
|
|
13338
13629
|
locale: getters.getLocale(),
|
|
13339
13630
|
};
|
|
13340
13631
|
}
|
|
13341
|
-
function getGeoChartData(definition,
|
|
13632
|
+
function getGeoChartData(definition, fullDataSets, labelRange, getters) {
|
|
13633
|
+
const dataSets = fullDataSets.slice(0, 1);
|
|
13342
13634
|
const labelValues = getChartLabelValues(getters, dataSets, labelRange);
|
|
13343
13635
|
let labels = labelValues.formattedValues;
|
|
13344
13636
|
if (shouldRemoveFirstLabel(labelRange, dataSets[0], definition.dataSetsHaveTitle || false)) {
|
|
@@ -13587,11 +13879,10 @@ function canBeLinearChart(definition, dataSets, labelRange, getters) {
|
|
|
13587
13879
|
}
|
|
13588
13880
|
let missingTimeAdapterAlreadyWarned = false;
|
|
13589
13881
|
function isLuxonTimeAdapterInstalled() {
|
|
13590
|
-
|
|
13591
|
-
if (!Chart) {
|
|
13882
|
+
if (!window.Chart) {
|
|
13592
13883
|
return false;
|
|
13593
13884
|
}
|
|
13594
|
-
const adapter = new Chart._adapters._date({});
|
|
13885
|
+
const adapter = new window.Chart._adapters._date({});
|
|
13595
13886
|
// @ts-ignore
|
|
13596
13887
|
const isInstalled = adapter._id === "luxon";
|
|
13597
13888
|
if (!isInstalled && !missingTimeAdapterAlreadyWarned) {
|
|
@@ -13894,13 +14185,18 @@ function makeDatasetsCumulative(datasets, order) {
|
|
|
13894
14185
|
return { ...dataset, data };
|
|
13895
14186
|
});
|
|
13896
14187
|
}
|
|
14188
|
+
function getTopPaddingForDashboard(definition, getters) {
|
|
14189
|
+
const { title, legendPosition } = definition;
|
|
14190
|
+
const hasTitleOrLegendTop = (title && title.text) || legendPosition === "top";
|
|
14191
|
+
return getters.isDashboard() && !hasTitleOrLegendTop ? 30 : 0;
|
|
14192
|
+
}
|
|
13897
14193
|
|
|
13898
|
-
function getChartLayout(definition) {
|
|
14194
|
+
function getChartLayout(definition, args) {
|
|
13899
14195
|
return {
|
|
13900
14196
|
padding: {
|
|
13901
14197
|
left: CHART_PADDING$1,
|
|
13902
14198
|
right: CHART_PADDING$1,
|
|
13903
|
-
top: CHART_PADDING_TOP,
|
|
14199
|
+
top: Math.max(CHART_PADDING_TOP, args.topPadding || 0),
|
|
13904
14200
|
bottom: CHART_PADDING_BOTTOM,
|
|
13905
14201
|
},
|
|
13906
14202
|
};
|
|
@@ -14089,6 +14385,9 @@ const INTERACTIVE_LEGEND_CONFIG = {
|
|
|
14089
14385
|
target.style.cursor = "default";
|
|
14090
14386
|
},
|
|
14091
14387
|
onClick: (event, legendItem, legend) => {
|
|
14388
|
+
if (event.type !== "click") {
|
|
14389
|
+
return;
|
|
14390
|
+
}
|
|
14092
14391
|
const index = legendItem.datasetIndex;
|
|
14093
14392
|
if (!legend.legendItems || index === undefined) {
|
|
14094
14393
|
return;
|
|
@@ -14917,6 +15216,7 @@ var CHART_RUNTIME_HELPERS = /*#__PURE__*/Object.freeze({
|
|
|
14917
15216
|
getSunburstChartLegend: getSunburstChartLegend,
|
|
14918
15217
|
getSunburstChartTooltip: getSunburstChartTooltip,
|
|
14919
15218
|
getSunburstShowValues: getSunburstShowValues,
|
|
15219
|
+
getTopPaddingForDashboard: getTopPaddingForDashboard,
|
|
14920
15220
|
getTreeMapChartDatasets: getTreeMapChartDatasets,
|
|
14921
15221
|
getTreeMapChartTooltip: getTreeMapChartTooltip,
|
|
14922
15222
|
getTrendDatasetForBarChart: getTrendDatasetForBarChart,
|
|
@@ -15066,7 +15366,7 @@ function createBarChartRuntime(chart, getters) {
|
|
|
15066
15366
|
options: {
|
|
15067
15367
|
...CHART_COMMON_OPTIONS,
|
|
15068
15368
|
indexAxis: chart.horizontal ? "y" : "x",
|
|
15069
|
-
layout: getChartLayout(),
|
|
15369
|
+
layout: getChartLayout(definition, chartData),
|
|
15070
15370
|
scales: getBarChartScales(definition, chartData),
|
|
15071
15371
|
plugins: {
|
|
15072
15372
|
title: getChartTitle(definition),
|
|
@@ -15079,6 +15379,24 @@ function createBarChartRuntime(chart, getters) {
|
|
|
15079
15379
|
return { chartJsConfig: config, background: chart.background || BACKGROUND_CHART_COLOR };
|
|
15080
15380
|
}
|
|
15081
15381
|
|
|
15382
|
+
class GaugeChartComponent extends Component {
|
|
15383
|
+
static template = "o-spreadsheet-GaugeChartComponent";
|
|
15384
|
+
canvas = useRef("chartContainer");
|
|
15385
|
+
get runtime() {
|
|
15386
|
+
return this.env.model.getters.getChartRuntime(this.props.figureUI.id);
|
|
15387
|
+
}
|
|
15388
|
+
setup() {
|
|
15389
|
+
useEffect(() => drawGaugeChart(this.canvas.el, this.runtime), () => {
|
|
15390
|
+
const canvas = this.canvas.el;
|
|
15391
|
+
const rect = canvas.getBoundingClientRect();
|
|
15392
|
+
return [rect.width, rect.height, this.runtime, this.canvas.el, window.devicePixelRatio];
|
|
15393
|
+
});
|
|
15394
|
+
}
|
|
15395
|
+
}
|
|
15396
|
+
GaugeChartComponent.props = {
|
|
15397
|
+
figureUI: Object,
|
|
15398
|
+
};
|
|
15399
|
+
|
|
15082
15400
|
class ComboChart extends AbstractChart {
|
|
15083
15401
|
dataSets;
|
|
15084
15402
|
labelRange;
|
|
@@ -15218,7 +15536,7 @@ function createComboChartRuntime(chart, getters) {
|
|
|
15218
15536
|
},
|
|
15219
15537
|
options: {
|
|
15220
15538
|
...CHART_COMMON_OPTIONS,
|
|
15221
|
-
layout: getChartLayout(),
|
|
15539
|
+
layout: getChartLayout(definition, chartData),
|
|
15222
15540
|
scales: getBarChartScales(definition, chartData),
|
|
15223
15541
|
plugins: {
|
|
15224
15542
|
title: getChartTitle(definition),
|
|
@@ -15362,7 +15680,7 @@ function createFunnelChartRuntime(chart, getters) {
|
|
|
15362
15680
|
options: {
|
|
15363
15681
|
...CHART_COMMON_OPTIONS,
|
|
15364
15682
|
indexAxis: "y",
|
|
15365
|
-
layout: getChartLayout(),
|
|
15683
|
+
layout: getChartLayout(definition, chartData),
|
|
15366
15684
|
scales: getFunnelChartScales(definition, chartData),
|
|
15367
15685
|
plugins: {
|
|
15368
15686
|
title: getChartTitle(definition),
|
|
@@ -15766,7 +16084,7 @@ function createGeoChartRuntime(chart, getters) {
|
|
|
15766
16084
|
},
|
|
15767
16085
|
options: {
|
|
15768
16086
|
...CHART_COMMON_OPTIONS,
|
|
15769
|
-
layout: getChartLayout(),
|
|
16087
|
+
layout: getChartLayout(definition, chartData),
|
|
15770
16088
|
scales: getGeoChartScales(definition, chartData),
|
|
15771
16089
|
plugins: {
|
|
15772
16090
|
title: getChartTitle(definition),
|
|
@@ -15924,7 +16242,7 @@ function createLineChartRuntime(chart, getters) {
|
|
|
15924
16242
|
},
|
|
15925
16243
|
options: {
|
|
15926
16244
|
...CHART_COMMON_OPTIONS,
|
|
15927
|
-
layout: getChartLayout(),
|
|
16245
|
+
layout: getChartLayout(definition, chartData),
|
|
15928
16246
|
scales: getLineChartScales(definition, chartData),
|
|
15929
16247
|
plugins: {
|
|
15930
16248
|
title: getChartTitle(definition),
|
|
@@ -15950,6 +16268,7 @@ class PieChart extends AbstractChart {
|
|
|
15950
16268
|
dataSetsHaveTitle;
|
|
15951
16269
|
isDoughnut;
|
|
15952
16270
|
showValues;
|
|
16271
|
+
pieHolePercentage;
|
|
15953
16272
|
constructor(definition, sheetId, getters) {
|
|
15954
16273
|
super(definition, sheetId, getters);
|
|
15955
16274
|
this.dataSets = createDataSets(getters, definition.dataSets, sheetId, definition.dataSetsHaveTitle);
|
|
@@ -15960,6 +16279,7 @@ class PieChart extends AbstractChart {
|
|
|
15960
16279
|
this.dataSetsHaveTitle = definition.dataSetsHaveTitle;
|
|
15961
16280
|
this.isDoughnut = definition.isDoughnut;
|
|
15962
16281
|
this.showValues = definition.showValues;
|
|
16282
|
+
this.pieHolePercentage = definition.pieHolePercentage;
|
|
15963
16283
|
}
|
|
15964
16284
|
static transformDefinition(definition, executed) {
|
|
15965
16285
|
return transformChartDefinitionWithDataSetsWithZone(definition, executed);
|
|
@@ -16011,6 +16331,7 @@ class PieChart extends AbstractChart {
|
|
|
16011
16331
|
aggregated: this.aggregated,
|
|
16012
16332
|
isDoughnut: this.isDoughnut,
|
|
16013
16333
|
showValues: this.showValues,
|
|
16334
|
+
pieHolePercentage: this.pieHolePercentage,
|
|
16014
16335
|
};
|
|
16015
16336
|
}
|
|
16016
16337
|
duplicateInDuplicatedSheet(newSheetId) {
|
|
@@ -16056,7 +16377,10 @@ function createPieChartRuntime(chart, getters) {
|
|
|
16056
16377
|
},
|
|
16057
16378
|
options: {
|
|
16058
16379
|
...CHART_COMMON_OPTIONS,
|
|
16059
|
-
|
|
16380
|
+
cutout: chart.isDoughnut && definition.pieHolePercentage !== undefined
|
|
16381
|
+
? definition.pieHolePercentage + "%"
|
|
16382
|
+
: undefined,
|
|
16383
|
+
layout: getChartLayout(definition, chartData),
|
|
16060
16384
|
plugins: {
|
|
16061
16385
|
title: getChartTitle(definition),
|
|
16062
16386
|
legend: getPieChartLegend(definition, chartData),
|
|
@@ -16083,7 +16407,7 @@ class PyramidChart extends AbstractChart {
|
|
|
16083
16407
|
showValues;
|
|
16084
16408
|
constructor(definition, sheetId, getters) {
|
|
16085
16409
|
super(definition, sheetId, getters);
|
|
16086
|
-
this.dataSets = createDataSets(getters, definition.dataSets, sheetId, definition.dataSetsHaveTitle)
|
|
16410
|
+
this.dataSets = createDataSets(getters, definition.dataSets, sheetId, definition.dataSetsHaveTitle);
|
|
16087
16411
|
this.labelRange = createValidRange(getters, sheetId, definition.labelRange);
|
|
16088
16412
|
this.background = definition.background;
|
|
16089
16413
|
this.legendPosition = definition.legendPosition;
|
|
@@ -16193,7 +16517,7 @@ function createPyramidChartRuntime(chart, getters) {
|
|
|
16193
16517
|
options: {
|
|
16194
16518
|
...CHART_COMMON_OPTIONS,
|
|
16195
16519
|
indexAxis: "y",
|
|
16196
|
-
layout: getChartLayout(),
|
|
16520
|
+
layout: getChartLayout(definition, chartData),
|
|
16197
16521
|
scales: getPyramidChartScales(definition, chartData),
|
|
16198
16522
|
plugins: {
|
|
16199
16523
|
title: getChartTitle(definition),
|
|
@@ -16342,7 +16666,7 @@ function createRadarChartRuntime(chart, getters) {
|
|
|
16342
16666
|
},
|
|
16343
16667
|
options: {
|
|
16344
16668
|
...CHART_COMMON_OPTIONS,
|
|
16345
|
-
layout: getChartLayout(),
|
|
16669
|
+
layout: getChartLayout(definition, chartData),
|
|
16346
16670
|
scales: getRadarChartScales(definition, chartData),
|
|
16347
16671
|
plugins: {
|
|
16348
16672
|
title: getChartTitle(definition),
|
|
@@ -16495,7 +16819,7 @@ function createScatterChartRuntime(chart, getters) {
|
|
|
16495
16819
|
},
|
|
16496
16820
|
options: {
|
|
16497
16821
|
...CHART_COMMON_OPTIONS,
|
|
16498
|
-
layout: getChartLayout(),
|
|
16822
|
+
layout: getChartLayout(definition, chartData),
|
|
16499
16823
|
scales: getScatterChartScales(definition, chartData),
|
|
16500
16824
|
plugins: {
|
|
16501
16825
|
title: getChartTitle(definition),
|
|
@@ -16522,6 +16846,7 @@ class SunburstChart extends AbstractChart {
|
|
|
16522
16846
|
showLabels;
|
|
16523
16847
|
valuesDesign;
|
|
16524
16848
|
groupColors;
|
|
16849
|
+
pieHolePercentage;
|
|
16525
16850
|
constructor(definition, sheetId, getters) {
|
|
16526
16851
|
super(definition, sheetId, getters);
|
|
16527
16852
|
this.dataSets = createDataSets(getters, definition.dataSets, sheetId, definition.dataSetsHaveTitle);
|
|
@@ -16533,6 +16858,7 @@ class SunburstChart extends AbstractChart {
|
|
|
16533
16858
|
this.showLabels = definition.showLabels;
|
|
16534
16859
|
this.valuesDesign = definition.valuesDesign;
|
|
16535
16860
|
this.groupColors = definition.groupColors;
|
|
16861
|
+
this.pieHolePercentage = definition.pieHolePercentage;
|
|
16536
16862
|
}
|
|
16537
16863
|
static transformDefinition(definition, executed) {
|
|
16538
16864
|
return transformChartDefinitionWithDataSetsWithZone(definition, executed);
|
|
@@ -16595,6 +16921,7 @@ class SunburstChart extends AbstractChart {
|
|
|
16595
16921
|
showLabels: this.showLabels,
|
|
16596
16922
|
valuesDesign: this.valuesDesign,
|
|
16597
16923
|
groupColors: this.groupColors,
|
|
16924
|
+
pieHolePercentage: this.pieHolePercentage,
|
|
16598
16925
|
};
|
|
16599
16926
|
}
|
|
16600
16927
|
duplicateInDuplicatedSheet(newSheetId) {
|
|
@@ -16628,9 +16955,9 @@ function createSunburstChartRuntime(chart, getters) {
|
|
|
16628
16955
|
datasets: getSunburstChartDatasets(definition, chartData),
|
|
16629
16956
|
},
|
|
16630
16957
|
options: {
|
|
16631
|
-
cutout: "25%"
|
|
16958
|
+
cutout: chart.pieHolePercentage === undefined ? "25%" : `${chart.pieHolePercentage}%`,
|
|
16632
16959
|
...CHART_COMMON_OPTIONS,
|
|
16633
|
-
layout: getChartLayout(),
|
|
16960
|
+
layout: getChartLayout(definition, chartData),
|
|
16634
16961
|
plugins: {
|
|
16635
16962
|
title: getChartTitle(definition),
|
|
16636
16963
|
legend: getSunburstChartLegend(definition),
|
|
@@ -16778,7 +17105,7 @@ function createTreeMapChartRuntime(chart, getters) {
|
|
|
16778
17105
|
},
|
|
16779
17106
|
options: {
|
|
16780
17107
|
...CHART_COMMON_OPTIONS,
|
|
16781
|
-
layout: getChartLayout(),
|
|
17108
|
+
layout: getChartLayout(definition, chartData),
|
|
16782
17109
|
plugins: {
|
|
16783
17110
|
title: getChartTitle(definition),
|
|
16784
17111
|
legend: { display: false },
|
|
@@ -16934,7 +17261,7 @@ function createWaterfallChartRuntime(chart, getters) {
|
|
|
16934
17261
|
},
|
|
16935
17262
|
options: {
|
|
16936
17263
|
...CHART_COMMON_OPTIONS,
|
|
16937
|
-
layout: getChartLayout(),
|
|
17264
|
+
layout: getChartLayout(definition, chartData),
|
|
16938
17265
|
scales: getWaterfallChartScales(definition, chartData),
|
|
16939
17266
|
plugins: {
|
|
16940
17267
|
title: getChartTitle(definition),
|
|
@@ -17033,6 +17360,7 @@ chartRegistry.add("pyramid", {
|
|
|
17033
17360
|
transformDefinition: PyramidChart.transformDefinition,
|
|
17034
17361
|
getChartDefinitionFromContextCreation: PyramidChart.getDefinitionFromContextCreation,
|
|
17035
17362
|
sequence: 80,
|
|
17363
|
+
dataSeriesLimit: 2,
|
|
17036
17364
|
});
|
|
17037
17365
|
chartRegistry.add("radar", {
|
|
17038
17366
|
match: (type) => type === "radar",
|
|
@@ -17051,6 +17379,7 @@ chartRegistry.add("geo", {
|
|
|
17051
17379
|
transformDefinition: GeoChart.transformDefinition,
|
|
17052
17380
|
getChartDefinitionFromContextCreation: GeoChart.getDefinitionFromContextCreation,
|
|
17053
17381
|
sequence: 90,
|
|
17382
|
+
dataSeriesLimit: 1,
|
|
17054
17383
|
});
|
|
17055
17384
|
chartRegistry.add("funnel", {
|
|
17056
17385
|
match: (type) => type === "funnel",
|
|
@@ -17060,6 +17389,7 @@ chartRegistry.add("funnel", {
|
|
|
17060
17389
|
transformDefinition: FunnelChart.transformDefinition,
|
|
17061
17390
|
getChartDefinitionFromContextCreation: FunnelChart.getDefinitionFromContextCreation,
|
|
17062
17391
|
sequence: 100,
|
|
17392
|
+
dataSeriesLimit: 1,
|
|
17063
17393
|
});
|
|
17064
17394
|
chartRegistry.add("sunburst", {
|
|
17065
17395
|
match: (type) => type === "sunburst",
|
|
@@ -17286,268 +17616,6 @@ chartSubtypeRegistry
|
|
|
17286
17616
|
preview: "o-spreadsheet-ChartPreview.TREE_MAP_CHART",
|
|
17287
17617
|
});
|
|
17288
17618
|
|
|
17289
|
-
// -----------------------------------------------------------------------------
|
|
17290
|
-
// STYLE
|
|
17291
|
-
// -----------------------------------------------------------------------------
|
|
17292
|
-
css /* scss */ `
|
|
17293
|
-
.o-chart-container {
|
|
17294
|
-
width: 100%;
|
|
17295
|
-
height: 100%;
|
|
17296
|
-
position: relative;
|
|
17297
|
-
}
|
|
17298
|
-
`;
|
|
17299
|
-
class ChartFigure extends Component {
|
|
17300
|
-
static template = "o-spreadsheet-ChartFigure";
|
|
17301
|
-
static props = {
|
|
17302
|
-
figureUI: Object,
|
|
17303
|
-
onFigureDeleted: Function,
|
|
17304
|
-
};
|
|
17305
|
-
static components = {};
|
|
17306
|
-
onDoubleClick() {
|
|
17307
|
-
this.env.model.dispatch("SELECT_FIGURE", { figureId: this.props.figureUI.id });
|
|
17308
|
-
this.env.openSidePanel("ChartPanel");
|
|
17309
|
-
}
|
|
17310
|
-
get chartType() {
|
|
17311
|
-
return this.env.model.getters.getChartType(this.props.figureUI.id);
|
|
17312
|
-
}
|
|
17313
|
-
get chartComponent() {
|
|
17314
|
-
const type = this.chartType;
|
|
17315
|
-
const component = chartComponentRegistry.get(type);
|
|
17316
|
-
if (!component) {
|
|
17317
|
-
throw new Error(`Component is not defined for type ${type}`);
|
|
17318
|
-
}
|
|
17319
|
-
return component;
|
|
17320
|
-
}
|
|
17321
|
-
}
|
|
17322
|
-
|
|
17323
|
-
class ImageFigure extends Component {
|
|
17324
|
-
static template = "o-spreadsheet-ImageFigure";
|
|
17325
|
-
static props = {
|
|
17326
|
-
figureUI: Object,
|
|
17327
|
-
onFigureDeleted: Function,
|
|
17328
|
-
};
|
|
17329
|
-
static components = {};
|
|
17330
|
-
// ---------------------------------------------------------------------------
|
|
17331
|
-
// Getters
|
|
17332
|
-
// ---------------------------------------------------------------------------
|
|
17333
|
-
get figureId() {
|
|
17334
|
-
return this.props.figureUI.id;
|
|
17335
|
-
}
|
|
17336
|
-
get getImagePath() {
|
|
17337
|
-
return this.env.model.getters.getImagePath(this.figureId);
|
|
17338
|
-
}
|
|
17339
|
-
}
|
|
17340
|
-
|
|
17341
|
-
const macRegex = /Mac/i;
|
|
17342
|
-
const MODIFIER_KEYS = ["Shift", "Control", "Alt", "Meta"];
|
|
17343
|
-
/**
|
|
17344
|
-
* Return true if the event was triggered from
|
|
17345
|
-
* a child element.
|
|
17346
|
-
*/
|
|
17347
|
-
function isChildEvent(parent, ev) {
|
|
17348
|
-
if (!parent)
|
|
17349
|
-
return false;
|
|
17350
|
-
return !!ev.target && parent.contains(ev.target);
|
|
17351
|
-
}
|
|
17352
|
-
function gridOverlayPosition() {
|
|
17353
|
-
const spreadsheetElement = document.querySelector(".o-grid-overlay");
|
|
17354
|
-
if (spreadsheetElement) {
|
|
17355
|
-
const { top, left } = spreadsheetElement.getBoundingClientRect();
|
|
17356
|
-
return { top, left };
|
|
17357
|
-
}
|
|
17358
|
-
throw new Error("Can't find spreadsheet position");
|
|
17359
|
-
}
|
|
17360
|
-
function getBoundingRectAsPOJO(el) {
|
|
17361
|
-
const rect = el.getBoundingClientRect();
|
|
17362
|
-
return {
|
|
17363
|
-
x: rect.x,
|
|
17364
|
-
y: rect.y,
|
|
17365
|
-
width: rect.width,
|
|
17366
|
-
height: rect.height,
|
|
17367
|
-
};
|
|
17368
|
-
}
|
|
17369
|
-
/**
|
|
17370
|
-
* Iterate over all the children of `el` in the dom tree starting at `el`, depth first.
|
|
17371
|
-
*/
|
|
17372
|
-
function* iterateChildren(el) {
|
|
17373
|
-
yield el;
|
|
17374
|
-
if (el.hasChildNodes()) {
|
|
17375
|
-
for (const child of el.childNodes) {
|
|
17376
|
-
yield* iterateChildren(child);
|
|
17377
|
-
}
|
|
17378
|
-
}
|
|
17379
|
-
}
|
|
17380
|
-
function getOpenedMenus() {
|
|
17381
|
-
return Array.from(document.querySelectorAll(".o-spreadsheet .o-menu"));
|
|
17382
|
-
}
|
|
17383
|
-
function getCurrentSelection(el) {
|
|
17384
|
-
const { startElement, endElement, startSelectionOffset, endSelectionOffset } = getStartAndEndSelection(el);
|
|
17385
|
-
const startSizeBefore = findSelectionIndex(el, startElement, startSelectionOffset);
|
|
17386
|
-
const endSizeBefore = findSelectionIndex(el, endElement, endSelectionOffset);
|
|
17387
|
-
return {
|
|
17388
|
-
start: startSizeBefore,
|
|
17389
|
-
end: endSizeBefore,
|
|
17390
|
-
};
|
|
17391
|
-
}
|
|
17392
|
-
function getStartAndEndSelection(el) {
|
|
17393
|
-
const selection = document.getSelection();
|
|
17394
|
-
return {
|
|
17395
|
-
startElement: selection.anchorNode || el,
|
|
17396
|
-
startSelectionOffset: selection.anchorOffset,
|
|
17397
|
-
endElement: selection.focusNode || el,
|
|
17398
|
-
endSelectionOffset: selection.focusOffset,
|
|
17399
|
-
};
|
|
17400
|
-
}
|
|
17401
|
-
/**
|
|
17402
|
-
* Computes the text 'index' inside this.el based on the currently selected node and its offset.
|
|
17403
|
-
* The selected node is either a Text node or an Element node.
|
|
17404
|
-
*
|
|
17405
|
-
* case 1 -Text node:
|
|
17406
|
-
* the offset is the number of characters from the start of the node. We have to add this offset to the
|
|
17407
|
-
* content length of all previous nodes.
|
|
17408
|
-
*
|
|
17409
|
-
* case 2 - Element node:
|
|
17410
|
-
* the offset is the number of child nodes before the selected node. We have to add the content length of
|
|
17411
|
-
* all the nodes prior to the selected node as well as the content of the child node before the offset.
|
|
17412
|
-
*
|
|
17413
|
-
* See the MDN documentation for more details.
|
|
17414
|
-
* https://developer.mozilla.org/en-US/docs/Web/API/Range/startOffset
|
|
17415
|
-
* https://developer.mozilla.org/en-US/docs/Web/API/Range/endOffset
|
|
17416
|
-
*
|
|
17417
|
-
*/
|
|
17418
|
-
function findSelectionIndex(el, nodeToFind, nodeOffset) {
|
|
17419
|
-
let usedCharacters = 0;
|
|
17420
|
-
const it = iterateChildren(el);
|
|
17421
|
-
let current = it.next();
|
|
17422
|
-
let isFirstParagraph = true;
|
|
17423
|
-
while (!current.done && current.value !== nodeToFind) {
|
|
17424
|
-
if (!current.value.hasChildNodes()) {
|
|
17425
|
-
if (current.value.textContent) {
|
|
17426
|
-
usedCharacters += current.value.textContent.length;
|
|
17427
|
-
}
|
|
17428
|
-
}
|
|
17429
|
-
// One new paragraph = one new line character, except for the first paragraph
|
|
17430
|
-
if (current.value.nodeName === "P" ||
|
|
17431
|
-
(current.value.nodeName === "DIV" && current.value !== el) // On paste, the HTML may contain <div> instead of <p>
|
|
17432
|
-
) {
|
|
17433
|
-
if (isFirstParagraph) {
|
|
17434
|
-
isFirstParagraph = false;
|
|
17435
|
-
}
|
|
17436
|
-
else {
|
|
17437
|
-
usedCharacters++;
|
|
17438
|
-
}
|
|
17439
|
-
}
|
|
17440
|
-
current = it.next();
|
|
17441
|
-
}
|
|
17442
|
-
if (current.value !== nodeToFind) {
|
|
17443
|
-
/** This situation can happen if the code is called while the selection is not currently on the element.
|
|
17444
|
-
* In this case, we return 0 because we don't know the size of the text before the selection.
|
|
17445
|
-
*
|
|
17446
|
-
* A known occurrence is triggered since the introduction of commit d4663158 (PR #2038).
|
|
17447
|
-
*/
|
|
17448
|
-
return 0;
|
|
17449
|
-
}
|
|
17450
|
-
else {
|
|
17451
|
-
if (!current.value.hasChildNodes()) {
|
|
17452
|
-
usedCharacters += nodeOffset;
|
|
17453
|
-
}
|
|
17454
|
-
else {
|
|
17455
|
-
const children = [...current.value.childNodes].slice(0, nodeOffset);
|
|
17456
|
-
usedCharacters += children.reduce((acc, child, index) => {
|
|
17457
|
-
if (child.textContent !== null) {
|
|
17458
|
-
// need to account for paragraph nodes that implicitly add a new line
|
|
17459
|
-
// except for the last paragraph
|
|
17460
|
-
let chars = child.textContent.length;
|
|
17461
|
-
if (child.nodeName === "P" && index !== children.length - 1) {
|
|
17462
|
-
chars++;
|
|
17463
|
-
}
|
|
17464
|
-
return acc + chars;
|
|
17465
|
-
}
|
|
17466
|
-
else {
|
|
17467
|
-
return acc;
|
|
17468
|
-
}
|
|
17469
|
-
}, 0);
|
|
17470
|
-
}
|
|
17471
|
-
}
|
|
17472
|
-
if (nodeToFind.nodeName === "P" && !isFirstParagraph && nodeToFind.textContent === "") {
|
|
17473
|
-
usedCharacters++;
|
|
17474
|
-
}
|
|
17475
|
-
return usedCharacters;
|
|
17476
|
-
}
|
|
17477
|
-
const letterRegex = /^[a-zA-Z]$/;
|
|
17478
|
-
/**
|
|
17479
|
-
* Transform a keyboard event into a shortcut string that represent this event. The letters keys will be uppercased.
|
|
17480
|
-
*
|
|
17481
|
-
* @argument ev - The keyboard event to transform
|
|
17482
|
-
* @argument mode - Use either ev.key of ev.code to get the string shortcut
|
|
17483
|
-
*
|
|
17484
|
-
* @example
|
|
17485
|
-
* event : { ctrlKey: true, key: "a" } => "Ctrl+A"
|
|
17486
|
-
* event : { shift: true, alt: true, key: "Home" } => "Alt+Shift+Home"
|
|
17487
|
-
*/
|
|
17488
|
-
function keyboardEventToShortcutString(ev, mode = "key") {
|
|
17489
|
-
let keyDownString = "";
|
|
17490
|
-
if (!MODIFIER_KEYS.includes(ev.key)) {
|
|
17491
|
-
if (isCtrlKey(ev))
|
|
17492
|
-
keyDownString += "Ctrl+";
|
|
17493
|
-
if (ev.altKey)
|
|
17494
|
-
keyDownString += "Alt+";
|
|
17495
|
-
if (ev.shiftKey)
|
|
17496
|
-
keyDownString += "Shift+";
|
|
17497
|
-
}
|
|
17498
|
-
const key = mode === "key" ? ev.key : ev.code;
|
|
17499
|
-
keyDownString += letterRegex.test(key) ? key.toUpperCase() : key;
|
|
17500
|
-
return keyDownString;
|
|
17501
|
-
}
|
|
17502
|
-
function isMacOS() {
|
|
17503
|
-
return Boolean(macRegex.test(navigator.userAgent));
|
|
17504
|
-
}
|
|
17505
|
-
/**
|
|
17506
|
-
* @param {KeyboardEvent | MouseEvent} ev
|
|
17507
|
-
* @returns Returns true if the event was triggered with the "ctrl" modifier pressed.
|
|
17508
|
-
* On Mac, this is the "meta" or "command" key.
|
|
17509
|
-
*/
|
|
17510
|
-
function isCtrlKey(ev) {
|
|
17511
|
-
return isMacOS() ? ev.metaKey : ev.ctrlKey;
|
|
17512
|
-
}
|
|
17513
|
-
/**
|
|
17514
|
-
* @param {MouseEvent} ev - The mouse event.
|
|
17515
|
-
* @returns {boolean} Returns true if the event was triggered by a middle-click
|
|
17516
|
-
* or a Ctrl + Click (Cmd + Click on Mac).
|
|
17517
|
-
*/
|
|
17518
|
-
function isMiddleClickOrCtrlClick(ev) {
|
|
17519
|
-
return ev.button === 1 || (isCtrlKey(ev) && ev.button === 0);
|
|
17520
|
-
}
|
|
17521
|
-
async function convertImageToPng(imageUrl) {
|
|
17522
|
-
return new Promise((resolve, reject) => {
|
|
17523
|
-
const image = new Image();
|
|
17524
|
-
image.addEventListener("load", () => {
|
|
17525
|
-
const canvas = document.createElement("canvas");
|
|
17526
|
-
canvas.width = image.width;
|
|
17527
|
-
canvas.height = image.height;
|
|
17528
|
-
const ctx = canvas.getContext("2d");
|
|
17529
|
-
ctx?.drawImage(image, 0, 0);
|
|
17530
|
-
canvas.toBlob(resolve, "image/png");
|
|
17531
|
-
});
|
|
17532
|
-
image.addEventListener("error", reject);
|
|
17533
|
-
image.src = imageUrl;
|
|
17534
|
-
});
|
|
17535
|
-
}
|
|
17536
|
-
function downloadFile(dataUrl, fileName) {
|
|
17537
|
-
const a = document.createElement("a");
|
|
17538
|
-
a.href = dataUrl;
|
|
17539
|
-
a.download = fileName;
|
|
17540
|
-
document.body.appendChild(a);
|
|
17541
|
-
a.click();
|
|
17542
|
-
document.body.removeChild(a);
|
|
17543
|
-
}
|
|
17544
|
-
/**
|
|
17545
|
-
* Detects if the current browser is Firefox
|
|
17546
|
-
*/
|
|
17547
|
-
function isBrowserFirefox() {
|
|
17548
|
-
return /Firefox/i.test(navigator.userAgent);
|
|
17549
|
-
}
|
|
17550
|
-
|
|
17551
17619
|
/**
|
|
17552
17620
|
* Create a function used to create a Chart based on the definition
|
|
17553
17621
|
*/
|
|
@@ -17980,20 +18048,7 @@ function escapeQueryNameSpaces(query) {
|
|
|
17980
18048
|
return query.replaceAll(/([a-zA-Z0-9]+):([a-zA-Z0-9]+)/g, "NAMESPACE" + "$1" + "NAMESPACE" + "$2");
|
|
17981
18049
|
}
|
|
17982
18050
|
|
|
17983
|
-
|
|
17984
|
-
figureRegistry.add("chart", {
|
|
17985
|
-
Component: ChartFigure,
|
|
17986
|
-
SidePanelComponent: "ChartPanel",
|
|
17987
|
-
menuBuilder: getChartMenu,
|
|
17988
|
-
});
|
|
17989
|
-
figureRegistry.add("image", {
|
|
17990
|
-
Component: ImageFigure,
|
|
17991
|
-
keepRatio: true,
|
|
17992
|
-
minFigSize: 20,
|
|
17993
|
-
borderWidth: 0,
|
|
17994
|
-
menuBuilder: getImageMenuRegistry,
|
|
17995
|
-
});
|
|
17996
|
-
function getChartMenu(figureId, onFigureDeleted, env) {
|
|
18051
|
+
function getChartMenuActions(figureId, onFigureDeleted, env) {
|
|
17997
18052
|
const menuItemSpecs = [
|
|
17998
18053
|
{
|
|
17999
18054
|
id: "edit",
|
|
@@ -18024,7 +18079,9 @@ function getChartMenu(figureId, onFigureDeleted, env) {
|
|
|
18024
18079
|
"text/html": innerHTML,
|
|
18025
18080
|
"image/png": blob,
|
|
18026
18081
|
});
|
|
18082
|
+
env.notifyUser({ sticky: false, type: "success", text: _t("Chart copied to clipboard") });
|
|
18027
18083
|
},
|
|
18084
|
+
isReadonlyAllowed: true,
|
|
18028
18085
|
},
|
|
18029
18086
|
{
|
|
18030
18087
|
id: "download",
|
|
@@ -18039,14 +18096,15 @@ function getChartMenu(figureId, onFigureDeleted, env) {
|
|
|
18039
18096
|
const url = chartToImageUrl(runtime, figure, chartType);
|
|
18040
18097
|
downloadFile(url, "chart");
|
|
18041
18098
|
},
|
|
18099
|
+
isReadonlyAllowed: true,
|
|
18042
18100
|
},
|
|
18043
18101
|
getDeleteMenuItem(figureId, onFigureDeleted, env),
|
|
18044
18102
|
];
|
|
18045
|
-
return createActions(menuItemSpecs);
|
|
18103
|
+
return createActions(menuItemSpecs).filter((action) => env.model.getters.isReadonly() ? action.isReadonlyAllowed : true);
|
|
18046
18104
|
}
|
|
18047
|
-
function
|
|
18105
|
+
function getImageMenuActions(figureId, onFigureDeleted, env) {
|
|
18048
18106
|
const menuItemSpecs = [
|
|
18049
|
-
getCopyMenuItem(figureId, env),
|
|
18107
|
+
getCopyMenuItem(figureId, env, _t("Image copied to clipboard")),
|
|
18050
18108
|
getCutMenuItem(figureId, env),
|
|
18051
18109
|
{
|
|
18052
18110
|
id: "reset_size",
|
|
@@ -18093,7 +18151,7 @@ function getImageMenuRegistry(figureId, onFigureDeleted, env) {
|
|
|
18093
18151
|
];
|
|
18094
18152
|
return createActions(menuItemSpecs);
|
|
18095
18153
|
}
|
|
18096
|
-
function getCopyMenuItem(figureId, env) {
|
|
18154
|
+
function getCopyMenuItem(figureId, env, copiedNotificationMessage) {
|
|
18097
18155
|
return {
|
|
18098
18156
|
id: "copy",
|
|
18099
18157
|
name: _t("Copy"),
|
|
@@ -18104,6 +18162,9 @@ function getCopyMenuItem(figureId, env) {
|
|
|
18104
18162
|
env.model.dispatch("COPY");
|
|
18105
18163
|
const osClipboardContent = await env.model.getters.getClipboardTextAndImageContent();
|
|
18106
18164
|
await env.clipboard.write(osClipboardContent);
|
|
18165
|
+
if (copiedNotificationMessage) {
|
|
18166
|
+
env.notifyUser({ sticky: false, type: "success", text: copiedNotificationMessage });
|
|
18167
|
+
}
|
|
18107
18168
|
},
|
|
18108
18169
|
icon: "o-spreadsheet-Icon.CLIPBOARD",
|
|
18109
18170
|
};
|
|
@@ -18772,6 +18833,153 @@ class Menu extends Component {
|
|
|
18772
18833
|
}
|
|
18773
18834
|
}
|
|
18774
18835
|
|
|
18836
|
+
class ChartDashboardMenu extends Component {
|
|
18837
|
+
static template = "spreadsheet.ChartDashboardMenu";
|
|
18838
|
+
static components = { Menu };
|
|
18839
|
+
static props = { figureUI: Object };
|
|
18840
|
+
originalChartDefinition;
|
|
18841
|
+
menuState = useState({ isOpen: false, anchorRect: null, menuItems: [] });
|
|
18842
|
+
setup() {
|
|
18843
|
+
super.setup();
|
|
18844
|
+
this.originalChartDefinition = this.env.model.getters.getChartDefinition(this.props.figureUI.id);
|
|
18845
|
+
onWillUpdateProps(({ figureUI }) => {
|
|
18846
|
+
if (figureUI.id !== this.props.figureUI.id) {
|
|
18847
|
+
this.originalChartDefinition = this.env.model.getters.getChartDefinition(figureUI.id);
|
|
18848
|
+
}
|
|
18849
|
+
});
|
|
18850
|
+
}
|
|
18851
|
+
getAvailableTypes() {
|
|
18852
|
+
const definition = this.env.model.getters.getChartDefinition(this.props.figureUI.id);
|
|
18853
|
+
if (!["line", "bar", "pie"].includes(definition.type)) {
|
|
18854
|
+
return [];
|
|
18855
|
+
}
|
|
18856
|
+
return ["column", "line", "pie"].map((type) => {
|
|
18857
|
+
const item = chartSubtypeRegistry.get(type);
|
|
18858
|
+
return {
|
|
18859
|
+
...item,
|
|
18860
|
+
icon: this.getIconClasses(item.chartType),
|
|
18861
|
+
};
|
|
18862
|
+
});
|
|
18863
|
+
}
|
|
18864
|
+
getIconClasses(type) {
|
|
18865
|
+
if (type.includes("bar")) {
|
|
18866
|
+
return "fa fa-bar-chart";
|
|
18867
|
+
}
|
|
18868
|
+
if (type.includes("line")) {
|
|
18869
|
+
return "fa fa-line-chart";
|
|
18870
|
+
}
|
|
18871
|
+
if (type.includes("pie")) {
|
|
18872
|
+
return "fa fa-pie-chart";
|
|
18873
|
+
}
|
|
18874
|
+
return "";
|
|
18875
|
+
}
|
|
18876
|
+
onTypeChange(type) {
|
|
18877
|
+
const figureId = this.props.figureUI.id;
|
|
18878
|
+
const currentDefinition = this.env.model.getters.getChartDefinition(figureId);
|
|
18879
|
+
if (currentDefinition.type === type) {
|
|
18880
|
+
return;
|
|
18881
|
+
}
|
|
18882
|
+
let definition;
|
|
18883
|
+
if (this.originalChartDefinition.type === type) {
|
|
18884
|
+
definition = this.originalChartDefinition;
|
|
18885
|
+
}
|
|
18886
|
+
else {
|
|
18887
|
+
const newChartInfo = chartSubtypeRegistry.get(type);
|
|
18888
|
+
const ChartClass = chartRegistry.get(newChartInfo.chartType);
|
|
18889
|
+
const chartCreationContext = this.env.model.getters.getContextCreationChart(figureId);
|
|
18890
|
+
if (!chartCreationContext)
|
|
18891
|
+
return;
|
|
18892
|
+
definition = {
|
|
18893
|
+
...ChartClass.getChartDefinitionFromContextCreation(chartCreationContext),
|
|
18894
|
+
...newChartInfo.subtypeDefinition,
|
|
18895
|
+
};
|
|
18896
|
+
}
|
|
18897
|
+
this.env.model.dispatch("UPDATE_CHART", {
|
|
18898
|
+
definition,
|
|
18899
|
+
figureId,
|
|
18900
|
+
sheetId: this.env.model.getters.getActiveSheetId(),
|
|
18901
|
+
});
|
|
18902
|
+
}
|
|
18903
|
+
get selectedChartType() {
|
|
18904
|
+
return this.env.model.getters.getChartDefinition(this.props.figureUI.id).type;
|
|
18905
|
+
}
|
|
18906
|
+
get backgroundColor() {
|
|
18907
|
+
const color = this.env.model.getters.getChartDefinition(this.props.figureUI.id).background;
|
|
18908
|
+
return "background-color: " + (color || BACKGROUND_CHART_COLOR);
|
|
18909
|
+
}
|
|
18910
|
+
openContextMenu(ev) {
|
|
18911
|
+
this.menuState.isOpen = true;
|
|
18912
|
+
this.menuState.anchorRect = { x: ev.clientX, y: ev.clientY, width: 0, height: 0 };
|
|
18913
|
+
this.menuState.menuItems = getChartMenuActions(this.props.figureUI.id, () => { }, this.env);
|
|
18914
|
+
}
|
|
18915
|
+
}
|
|
18916
|
+
|
|
18917
|
+
// -----------------------------------------------------------------------------
|
|
18918
|
+
// STYLE
|
|
18919
|
+
// -----------------------------------------------------------------------------
|
|
18920
|
+
css /* scss */ `
|
|
18921
|
+
.o-chart-container {
|
|
18922
|
+
width: 100%;
|
|
18923
|
+
height: 100%;
|
|
18924
|
+
position: relative;
|
|
18925
|
+
}
|
|
18926
|
+
`;
|
|
18927
|
+
class ChartFigure extends Component {
|
|
18928
|
+
static template = "o-spreadsheet-ChartFigure";
|
|
18929
|
+
static props = {
|
|
18930
|
+
figureUI: Object,
|
|
18931
|
+
onFigureDeleted: Function,
|
|
18932
|
+
};
|
|
18933
|
+
static components = { ChartDashboardMenu };
|
|
18934
|
+
onDoubleClick() {
|
|
18935
|
+
this.env.model.dispatch("SELECT_FIGURE", { figureId: this.props.figureUI.id });
|
|
18936
|
+
this.env.openSidePanel("ChartPanel");
|
|
18937
|
+
}
|
|
18938
|
+
get chartType() {
|
|
18939
|
+
return this.env.model.getters.getChartType(this.props.figureUI.id);
|
|
18940
|
+
}
|
|
18941
|
+
get chartComponent() {
|
|
18942
|
+
const type = this.chartType;
|
|
18943
|
+
const component = chartComponentRegistry.get(type);
|
|
18944
|
+
if (!component) {
|
|
18945
|
+
throw new Error(`Component is not defined for type ${type}`);
|
|
18946
|
+
}
|
|
18947
|
+
return component;
|
|
18948
|
+
}
|
|
18949
|
+
}
|
|
18950
|
+
|
|
18951
|
+
class ImageFigure extends Component {
|
|
18952
|
+
static template = "o-spreadsheet-ImageFigure";
|
|
18953
|
+
static props = {
|
|
18954
|
+
figureUI: Object,
|
|
18955
|
+
onFigureDeleted: Function,
|
|
18956
|
+
};
|
|
18957
|
+
static components = {};
|
|
18958
|
+
// ---------------------------------------------------------------------------
|
|
18959
|
+
// Getters
|
|
18960
|
+
// ---------------------------------------------------------------------------
|
|
18961
|
+
get figureId() {
|
|
18962
|
+
return this.props.figureUI.id;
|
|
18963
|
+
}
|
|
18964
|
+
get getImagePath() {
|
|
18965
|
+
return this.env.model.getters.getImagePath(this.figureId);
|
|
18966
|
+
}
|
|
18967
|
+
}
|
|
18968
|
+
|
|
18969
|
+
const figureRegistry = new Registry();
|
|
18970
|
+
figureRegistry.add("chart", {
|
|
18971
|
+
Component: ChartFigure,
|
|
18972
|
+
SidePanelComponent: "ChartPanel",
|
|
18973
|
+
menuBuilder: getChartMenuActions,
|
|
18974
|
+
});
|
|
18975
|
+
figureRegistry.add("image", {
|
|
18976
|
+
Component: ImageFigure,
|
|
18977
|
+
keepRatio: true,
|
|
18978
|
+
minFigSize: 20,
|
|
18979
|
+
borderWidth: 0,
|
|
18980
|
+
menuBuilder: getImageMenuActions,
|
|
18981
|
+
});
|
|
18982
|
+
|
|
18775
18983
|
// -----------------------------------------------------------------------------
|
|
18776
18984
|
// STYLE
|
|
18777
18985
|
// -----------------------------------------------------------------------------
|
|
@@ -19764,10 +19972,13 @@ const duplicateSheet = {
|
|
|
19764
19972
|
name: _t("Duplicate"),
|
|
19765
19973
|
execute: (env) => {
|
|
19766
19974
|
const sheetIdFrom = env.model.getters.getActiveSheetId();
|
|
19975
|
+
const sheetNameFrom = env.model.getters.getSheetName(sheetIdFrom);
|
|
19767
19976
|
const sheetIdTo = env.model.uuidGenerator.smallUuid();
|
|
19977
|
+
const sheetNameTo = env.model.getters.getDuplicateSheetName(sheetNameFrom);
|
|
19768
19978
|
env.model.dispatch("DUPLICATE_SHEET", {
|
|
19769
19979
|
sheetId: sheetIdFrom,
|
|
19770
19980
|
sheetIdTo,
|
|
19981
|
+
sheetNameTo,
|
|
19771
19982
|
});
|
|
19772
19983
|
env.model.dispatch("ACTIVATE_SHEET", { sheetIdFrom, sheetIdTo });
|
|
19773
19984
|
},
|
|
@@ -19827,7 +20038,7 @@ class MenuItemRegistry extends Registry {
|
|
|
19827
20038
|
/**
|
|
19828
20039
|
* @override
|
|
19829
20040
|
*/
|
|
19830
|
-
|
|
20041
|
+
replace(key, value) {
|
|
19831
20042
|
if (value.id === undefined) {
|
|
19832
20043
|
value.id = key;
|
|
19833
20044
|
}
|
|
@@ -19839,7 +20050,13 @@ class MenuItemRegistry extends Registry {
|
|
|
19839
20050
|
* @param path Path of items to add this subitem
|
|
19840
20051
|
* @param value Subitem to add
|
|
19841
20052
|
*/
|
|
19842
|
-
addChild(key, path, value
|
|
20053
|
+
addChild(key, path, value) {
|
|
20054
|
+
return this._replaceChild(key, path, value, { force: false });
|
|
20055
|
+
}
|
|
20056
|
+
replaceChild(key, path, value) {
|
|
20057
|
+
return this._replaceChild(key, path, value, { force: true });
|
|
20058
|
+
}
|
|
20059
|
+
_replaceChild(key, path, value, options = { force: true }) {
|
|
19843
20060
|
if (typeof value !== "function" && value.id === undefined) {
|
|
19844
20061
|
value.id = key;
|
|
19845
20062
|
}
|
|
@@ -24887,6 +25104,7 @@ function repairInitialMessages(data, initialMessages) {
|
|
|
24887
25104
|
initialMessages = dropCommands(initialMessages, "SET_DECIMAL");
|
|
24888
25105
|
initialMessages = fixChartDefinitions(data, initialMessages);
|
|
24889
25106
|
initialMessages = fixFigureOffset(data, initialMessages);
|
|
25107
|
+
initialMessages = fixTranslatedDuplicateSheetName(data, initialMessages);
|
|
24890
25108
|
return initialMessages;
|
|
24891
25109
|
}
|
|
24892
25110
|
/**
|
|
@@ -25019,6 +25237,42 @@ function fixFigureOffset(data, messages) {
|
|
|
25019
25237
|
}
|
|
25020
25238
|
return messages;
|
|
25021
25239
|
}
|
|
25240
|
+
function fixTranslatedDuplicateSheetName(data, initialMessages) {
|
|
25241
|
+
const sheetNames = {};
|
|
25242
|
+
for (const sheet of data.sheets || []) {
|
|
25243
|
+
sheetNames[sheet.id] = sheet.name;
|
|
25244
|
+
}
|
|
25245
|
+
const messages = [];
|
|
25246
|
+
for (const message of initialMessages) {
|
|
25247
|
+
if (message.type === "REMOTE_REVISION") {
|
|
25248
|
+
const commands = [];
|
|
25249
|
+
for (const cmd of message.commands) {
|
|
25250
|
+
switch (cmd.type) {
|
|
25251
|
+
case "DUPLICATE_SHEET":
|
|
25252
|
+
cmd.sheetNameTo =
|
|
25253
|
+
cmd.sheetNameTo ??
|
|
25254
|
+
getDuplicateSheetName(sheetNames[cmd.sheetId], Object.values(sheetNames));
|
|
25255
|
+
break;
|
|
25256
|
+
case "CREATE_SHEET":
|
|
25257
|
+
sheetNames[cmd.sheetId] = cmd.name || getNextSheetName(Object.values(sheetNames));
|
|
25258
|
+
break;
|
|
25259
|
+
case "RENAME_SHEET":
|
|
25260
|
+
sheetNames[cmd.sheetId] = cmd.newName || getNextSheetName(Object.values(sheetNames));
|
|
25261
|
+
break;
|
|
25262
|
+
}
|
|
25263
|
+
commands.push(cmd);
|
|
25264
|
+
}
|
|
25265
|
+
messages.push({
|
|
25266
|
+
...message,
|
|
25267
|
+
commands,
|
|
25268
|
+
});
|
|
25269
|
+
}
|
|
25270
|
+
else {
|
|
25271
|
+
messages.push(message);
|
|
25272
|
+
}
|
|
25273
|
+
}
|
|
25274
|
+
return initialMessages;
|
|
25275
|
+
}
|
|
25022
25276
|
// -----------------------------------------------------------------------------
|
|
25023
25277
|
// Helpers
|
|
25024
25278
|
// -----------------------------------------------------------------------------
|
|
@@ -26872,29 +27126,6 @@ function validateArguments(descr) {
|
|
|
26872
27126
|
}
|
|
26873
27127
|
}
|
|
26874
27128
|
|
|
26875
|
-
function assertSingleColOrRow(errorStr, arg) {
|
|
26876
|
-
assert(() => arg.length === 1 || arg[0].length === 1, errorStr);
|
|
26877
|
-
}
|
|
26878
|
-
function assertSameDimensions(errorStr, ...args) {
|
|
26879
|
-
if (args.every(isMatrix)) {
|
|
26880
|
-
const cols = args[0].length;
|
|
26881
|
-
const rows = args[0][0].length;
|
|
26882
|
-
for (const arg of args) {
|
|
26883
|
-
assert(() => arg.length === cols && arg[0].length === rows, errorStr);
|
|
26884
|
-
}
|
|
26885
|
-
return;
|
|
26886
|
-
}
|
|
26887
|
-
if (args.some((arg) => Array.isArray(arg) && (arg.length !== 1 || arg[0].length !== 1))) {
|
|
26888
|
-
throw new EvaluationError(errorStr);
|
|
26889
|
-
}
|
|
26890
|
-
}
|
|
26891
|
-
function assertPositive(errorStr, arg) {
|
|
26892
|
-
assert(() => arg > 0, errorStr);
|
|
26893
|
-
}
|
|
26894
|
-
function assertSquareMatrix(errorStr, arg) {
|
|
26895
|
-
assert(() => arg.length === arg[0].length, errorStr);
|
|
26896
|
-
}
|
|
26897
|
-
|
|
26898
27129
|
// -----------------------------------------------------------------------------
|
|
26899
27130
|
// ARRAY_CONSTRAIN
|
|
26900
27131
|
// -----------------------------------------------------------------------------
|
|
@@ -26909,8 +27140,12 @@ const ARRAY_CONSTRAIN = {
|
|
|
26909
27140
|
const _array = toMatrix(array);
|
|
26910
27141
|
const _rowsArg = toInteger(rows?.value, this.locale);
|
|
26911
27142
|
const _columnsArg = toInteger(columns?.value, this.locale);
|
|
26912
|
-
|
|
26913
|
-
|
|
27143
|
+
if (_rowsArg <= 0) {
|
|
27144
|
+
return new EvaluationError(_t("The rows argument (%s) must be strictly positive.", _rowsArg.toString()));
|
|
27145
|
+
}
|
|
27146
|
+
if (_columnsArg <= 0) {
|
|
27147
|
+
return new EvaluationError(_t("The columns argument (%s) must be strictly positive.", _columnsArg.toString()));
|
|
27148
|
+
}
|
|
26914
27149
|
const _nbRows = Math.min(_rowsArg, _array[0].length);
|
|
26915
27150
|
const _nbColumns = Math.min(_columnsArg, _array.length);
|
|
26916
27151
|
return generateMatrix(_nbColumns, _nbRows, (col, row) => _array[col][row]);
|
|
@@ -26931,7 +27166,9 @@ const CHOOSECOLS = {
|
|
|
26931
27166
|
const _array = toMatrix(array);
|
|
26932
27167
|
const _columns = flattenRowFirst(columns, (item) => toInteger(item?.value, this.locale));
|
|
26933
27168
|
const argOutOfRange = _columns.filter((col) => col === 0 || _array.length < Math.abs(col));
|
|
26934
|
-
|
|
27169
|
+
if (argOutOfRange.length !== 0) {
|
|
27170
|
+
return new EvaluationError(_t("The columns arguments must be between -%s and %s (got %s), excluding 0.", _array.length.toString(), _array.length.toString(), argOutOfRange.join(",")));
|
|
27171
|
+
}
|
|
26935
27172
|
const result = Array(_columns.length);
|
|
26936
27173
|
for (let col = 0; col < _columns.length; col++) {
|
|
26937
27174
|
if (_columns[col] > 0) {
|
|
@@ -26960,7 +27197,9 @@ const CHOOSEROWS = {
|
|
|
26960
27197
|
const _rows = flattenRowFirst(rows, (item) => toInteger(item?.value, this.locale));
|
|
26961
27198
|
const _nbColumns = _array.length;
|
|
26962
27199
|
const argOutOfRange = _rows.filter((row) => row === 0 || _array[0].length < Math.abs(row));
|
|
26963
|
-
|
|
27200
|
+
if (argOutOfRange.length !== 0) {
|
|
27201
|
+
return new EvaluationError(_t("The rows arguments must be between -%s and %s (got %s), excluding 0.", _array[0].length.toString(), _array[0].length.toString(), argOutOfRange.join(",")));
|
|
27202
|
+
}
|
|
26964
27203
|
return generateMatrix(_nbColumns, _rows.length, (col, row) => {
|
|
26965
27204
|
if (_rows[row] > 0) {
|
|
26966
27205
|
return _array[col][_rows[row] - 1]; // -1 because columns arguments are 1-indexed
|
|
@@ -26986,8 +27225,12 @@ const EXPAND = {
|
|
|
26986
27225
|
const _array = toMatrix(arg);
|
|
26987
27226
|
const _nbRows = toInteger(rows?.value, this.locale);
|
|
26988
27227
|
const _nbColumns = columns !== undefined ? toInteger(columns.value, this.locale) : _array.length;
|
|
26989
|
-
|
|
26990
|
-
|
|
27228
|
+
if (_nbRows < _array[0].length) {
|
|
27229
|
+
return new EvaluationError(_t("The rows arguments (%s) must be greater or equal than the number of rows of the array.", _nbRows.toString()));
|
|
27230
|
+
}
|
|
27231
|
+
if (_nbColumns < _array.length) {
|
|
27232
|
+
return new EvaluationError(_t("The columns arguments (%s) must be greater or equal than the number of columns of the array.", _nbColumns.toString()));
|
|
27233
|
+
}
|
|
26991
27234
|
return generateMatrix(_nbColumns, _nbRows, (col, row) => col >= _array.length || row >= _array[col].length ? padWith : _array[col][row]);
|
|
26992
27235
|
},
|
|
26993
27236
|
isExported: true,
|
|
@@ -27090,7 +27333,9 @@ const MDETERM = {
|
|
|
27090
27333
|
],
|
|
27091
27334
|
compute: function (matrix) {
|
|
27092
27335
|
const _matrix = toNumberMatrix(matrix, "square_matrix");
|
|
27093
|
-
|
|
27336
|
+
if (!isSquareMatrix(_matrix)) {
|
|
27337
|
+
return new EvaluationError(_t("The argument square_matrix must have the same number of columns and rows."));
|
|
27338
|
+
}
|
|
27094
27339
|
return invertMatrix(_matrix).determinant;
|
|
27095
27340
|
},
|
|
27096
27341
|
isExported: true,
|
|
@@ -27105,7 +27350,9 @@ const MINVERSE = {
|
|
|
27105
27350
|
],
|
|
27106
27351
|
compute: function (matrix) {
|
|
27107
27352
|
const _matrix = toNumberMatrix(matrix, "square_matrix");
|
|
27108
|
-
|
|
27353
|
+
if (!isSquareMatrix(_matrix)) {
|
|
27354
|
+
return new EvaluationError(_t("The argument square_matrix must have the same number of columns and rows."));
|
|
27355
|
+
}
|
|
27109
27356
|
const { inverted } = invertMatrix(_matrix);
|
|
27110
27357
|
if (!inverted) {
|
|
27111
27358
|
return new EvaluationError(_t("The matrix is not invertible."));
|
|
@@ -27126,8 +27373,10 @@ const MMULT = {
|
|
|
27126
27373
|
compute: function (matrix1, matrix2) {
|
|
27127
27374
|
const _matrix1 = toNumberMatrix(matrix1, "matrix1");
|
|
27128
27375
|
const _matrix2 = toNumberMatrix(matrix2, "matrix2");
|
|
27129
|
-
|
|
27130
|
-
|
|
27376
|
+
if (_matrix1.length !== _matrix2[0].length) {
|
|
27377
|
+
return new EvaluationError(_t("In [[FUNCTION_NAME]], the number of columns of the first matrix (%s) must be equal to the \
|
|
27378
|
+
number of rows of the second matrix (%s).", _matrix1.length.toString(), _matrix2[0].length.toString()));
|
|
27379
|
+
}
|
|
27131
27380
|
return multiplyMatrices(_matrix1, _matrix2);
|
|
27132
27381
|
},
|
|
27133
27382
|
isExported: true,
|
|
@@ -27142,7 +27391,9 @@ const SUMPRODUCT = {
|
|
|
27142
27391
|
arg("range2 (number, range<number>, repeating)", _t("The other range whose entries will be multiplied with corresponding entries in the other ranges.")),
|
|
27143
27392
|
],
|
|
27144
27393
|
compute: function (...args) {
|
|
27145
|
-
|
|
27394
|
+
if (!areSameDimensions(...args)) {
|
|
27395
|
+
return new EvaluationError(_t("All the ranges must have the same dimensions."));
|
|
27396
|
+
}
|
|
27146
27397
|
const _args = args.map(toMatrix);
|
|
27147
27398
|
let result = 0;
|
|
27148
27399
|
for (let col = 0; col < _args[0].length; col++) {
|
|
@@ -27170,7 +27421,9 @@ const SUMPRODUCT = {
|
|
|
27170
27421
|
* Ignore the pairs X,Y where one of the value isn't a number. Throw an error if no pair of numbers is found.
|
|
27171
27422
|
*/
|
|
27172
27423
|
function getSumXAndY(arrayX, arrayY, cb) {
|
|
27173
|
-
|
|
27424
|
+
if (!areSameDimensions(arrayX, arrayY)) {
|
|
27425
|
+
return new EvaluationError(_t("The arguments array_x and array_y must have the same dimensions."));
|
|
27426
|
+
}
|
|
27174
27427
|
const _arrayX = toMatrix(arrayX);
|
|
27175
27428
|
const _arrayY = toMatrix(arrayY);
|
|
27176
27429
|
let validPairFound = false;
|
|
@@ -27352,7 +27605,9 @@ const WRAPCOLS = {
|
|
|
27352
27605
|
compute: function (range, wrapCount, padWith = { value: 0 }) {
|
|
27353
27606
|
const _array = toMatrix(range);
|
|
27354
27607
|
const nbRows = toInteger(wrapCount?.value, this.locale);
|
|
27355
|
-
|
|
27608
|
+
if (!isSingleColOrRow(_array)) {
|
|
27609
|
+
return new EvaluationError(_t("Argument range must be a single row or column."));
|
|
27610
|
+
}
|
|
27356
27611
|
const array = _array.flat();
|
|
27357
27612
|
const nbColumns = Math.ceil(array.length / nbRows);
|
|
27358
27613
|
return generateMatrix(nbColumns, nbRows, (col, row) => {
|
|
@@ -27376,7 +27631,9 @@ const WRAPROWS = {
|
|
|
27376
27631
|
compute: function (range, wrapCount, padWith = { value: 0 }) {
|
|
27377
27632
|
const _array = toMatrix(range);
|
|
27378
27633
|
const nbColumns = toInteger(wrapCount?.value, this.locale);
|
|
27379
|
-
|
|
27634
|
+
if (!isSingleColOrRow(_array)) {
|
|
27635
|
+
return new EvaluationError(_t("Argument range must be a single row or column."));
|
|
27636
|
+
}
|
|
27380
27637
|
const array = _array.flat();
|
|
27381
27638
|
const nbRows = Math.ceil(array.length / nbColumns);
|
|
27382
27639
|
return generateMatrix(nbColumns, nbRows, (col, row) => {
|
|
@@ -27459,7 +27716,9 @@ const ACOS = {
|
|
|
27459
27716
|
],
|
|
27460
27717
|
compute: function (value) {
|
|
27461
27718
|
const _value = toNumber(value, this.locale);
|
|
27462
|
-
|
|
27719
|
+
if (Math.abs(_value) > 1) {
|
|
27720
|
+
return new EvaluationError(_t("The value (%s) must be between -1 and 1 inclusive.", _value));
|
|
27721
|
+
}
|
|
27463
27722
|
return Math.acos(_value);
|
|
27464
27723
|
},
|
|
27465
27724
|
isExported: true,
|
|
@@ -27474,7 +27733,9 @@ const ACOSH = {
|
|
|
27474
27733
|
],
|
|
27475
27734
|
compute: function (value) {
|
|
27476
27735
|
const _value = toNumber(value, this.locale);
|
|
27477
|
-
|
|
27736
|
+
if (_value < 1) {
|
|
27737
|
+
return new EvaluationError(_t("The value (%s) must be greater than or equal to 1.", _value));
|
|
27738
|
+
}
|
|
27478
27739
|
return Math.acosh(_value);
|
|
27479
27740
|
},
|
|
27480
27741
|
isExported: true,
|
|
@@ -27505,7 +27766,9 @@ const ACOTH = {
|
|
|
27505
27766
|
],
|
|
27506
27767
|
compute: function (value) {
|
|
27507
27768
|
const _value = toNumber(value, this.locale);
|
|
27508
|
-
|
|
27769
|
+
if (Math.abs(_value) <= 1) {
|
|
27770
|
+
return new EvaluationError(_t("The value (%s) cannot be between -1 and 1 inclusive.", _value));
|
|
27771
|
+
}
|
|
27509
27772
|
return Math.log((_value + 1) / (_value - 1)) / 2;
|
|
27510
27773
|
},
|
|
27511
27774
|
isExported: true,
|
|
@@ -27520,7 +27783,9 @@ const ASIN = {
|
|
|
27520
27783
|
],
|
|
27521
27784
|
compute: function (value) {
|
|
27522
27785
|
const _value = toNumber(value, this.locale);
|
|
27523
|
-
|
|
27786
|
+
if (Math.abs(_value) > 1) {
|
|
27787
|
+
return new EvaluationError(_t("The value (%s) must be between -1 and 1 inclusive.", _value));
|
|
27788
|
+
}
|
|
27524
27789
|
return Math.asin(_value);
|
|
27525
27790
|
},
|
|
27526
27791
|
isExported: true,
|
|
@@ -27561,7 +27826,9 @@ const ATAN2 = {
|
|
|
27561
27826
|
compute: function (x, y) {
|
|
27562
27827
|
const _x = toNumber(x, this.locale);
|
|
27563
27828
|
const _y = toNumber(y, this.locale);
|
|
27564
|
-
|
|
27829
|
+
if (_x === 0 && _y === 0) {
|
|
27830
|
+
return new DivisionByZeroError(_t("Function [[FUNCTION_NAME]] caused a divide by zero error."));
|
|
27831
|
+
}
|
|
27565
27832
|
return Math.atan2(_y, _x);
|
|
27566
27833
|
},
|
|
27567
27834
|
isExported: true,
|
|
@@ -27576,7 +27843,9 @@ const ATANH = {
|
|
|
27576
27843
|
],
|
|
27577
27844
|
compute: function (value) {
|
|
27578
27845
|
const _value = toNumber(value, this.locale);
|
|
27579
|
-
|
|
27846
|
+
if (Math.abs(_value) >= 1) {
|
|
27847
|
+
return new EvaluationError(_t("The value (%s) must be between -1 and 1 exclusive.", _value));
|
|
27848
|
+
}
|
|
27580
27849
|
return Math.atanh(_value);
|
|
27581
27850
|
},
|
|
27582
27851
|
isExported: true,
|
|
@@ -27593,7 +27862,9 @@ const CEILING = {
|
|
|
27593
27862
|
compute: function (value, factor = { value: DEFAULT_FACTOR }) {
|
|
27594
27863
|
const _value = toNumber(value, this.locale);
|
|
27595
27864
|
const _factor = toNumber(factor, this.locale);
|
|
27596
|
-
|
|
27865
|
+
if (_factor < 0 && _value > 0) {
|
|
27866
|
+
return new EvaluationError(_t("The factor (%s) must be positive when the value (%s) is positive.", _factor, _value));
|
|
27867
|
+
}
|
|
27597
27868
|
return {
|
|
27598
27869
|
value: _factor ? Math.ceil(_value / _factor) * _factor : 0,
|
|
27599
27870
|
format: value?.format,
|
|
@@ -27684,7 +27955,9 @@ const COT = {
|
|
|
27684
27955
|
args: [arg("angle (number)", _t("The angle to find the cotangent of, in radians."))],
|
|
27685
27956
|
compute: function (angle) {
|
|
27686
27957
|
const _angle = toNumber(angle, this.locale);
|
|
27687
|
-
|
|
27958
|
+
if (_angle === 0) {
|
|
27959
|
+
return new DivisionByZeroError(_t("Function [[FUNCTION_NAME]] caused a divide by zero error."));
|
|
27960
|
+
}
|
|
27688
27961
|
return 1 / Math.tan(_angle);
|
|
27689
27962
|
},
|
|
27690
27963
|
isExported: true,
|
|
@@ -27697,7 +27970,9 @@ const COTH = {
|
|
|
27697
27970
|
args: [arg("value (number)", _t("Any real value to calculate the hyperbolic cotangent of."))],
|
|
27698
27971
|
compute: function (value) {
|
|
27699
27972
|
const _value = toNumber(value, this.locale);
|
|
27700
|
-
|
|
27973
|
+
if (_value === 0) {
|
|
27974
|
+
return new DivisionByZeroError(_t("Function [[FUNCTION_NAME]] caused a divide by zero error."));
|
|
27975
|
+
}
|
|
27701
27976
|
return 1 / Math.tanh(_value);
|
|
27702
27977
|
},
|
|
27703
27978
|
isExported: true,
|
|
@@ -27809,7 +28084,9 @@ const CSC = {
|
|
|
27809
28084
|
args: [arg("angle (number)", _t("The angle to find the cosecant of, in radians."))],
|
|
27810
28085
|
compute: function (angle) {
|
|
27811
28086
|
const _angle = toNumber(angle, this.locale);
|
|
27812
|
-
|
|
28087
|
+
if (_angle === 0) {
|
|
28088
|
+
return new DivisionByZeroError(_t("Function [[FUNCTION_NAME]] caused a divide by zero error."));
|
|
28089
|
+
}
|
|
27813
28090
|
return 1 / Math.sin(_angle);
|
|
27814
28091
|
},
|
|
27815
28092
|
isExported: true,
|
|
@@ -27822,7 +28099,9 @@ const CSCH = {
|
|
|
27822
28099
|
args: [arg("value (number)", _t("Any real value to calculate the hyperbolic cosecant of."))],
|
|
27823
28100
|
compute: function (value) {
|
|
27824
28101
|
const _value = toNumber(value, this.locale);
|
|
27825
|
-
|
|
28102
|
+
if (_value === 0) {
|
|
28103
|
+
return new DivisionByZeroError(_t("Function [[FUNCTION_NAME]] caused a divide by zero error."));
|
|
28104
|
+
}
|
|
27826
28105
|
return 1 / Math.sinh(_value);
|
|
27827
28106
|
},
|
|
27828
28107
|
isExported: true,
|
|
@@ -27839,7 +28118,9 @@ const DECIMAL = {
|
|
|
27839
28118
|
compute: function (value, base) {
|
|
27840
28119
|
let _base = toNumber(base, this.locale);
|
|
27841
28120
|
_base = Math.floor(_base);
|
|
27842
|
-
|
|
28121
|
+
if (2 > _base || _base > 36) {
|
|
28122
|
+
return new EvaluationError(_t("The base (%s) must be between 2 and 36 inclusive.", _base));
|
|
28123
|
+
}
|
|
27843
28124
|
const _value = toString(value);
|
|
27844
28125
|
if (_value === "") {
|
|
27845
28126
|
return 0;
|
|
@@ -27849,9 +28130,13 @@ const DECIMAL = {
|
|
|
27849
28130
|
* Return error if 'value' is positive.
|
|
27850
28131
|
* Remove '-?' in the next regex to catch this error.
|
|
27851
28132
|
*/
|
|
27852
|
-
|
|
28133
|
+
if (!DECIMAL_REPRESENTATION.test(_value)) {
|
|
28134
|
+
return new EvaluationError(_t("The value (%s) must be a valid base %s representation.", _value, _base));
|
|
28135
|
+
}
|
|
27853
28136
|
const deci = parseInt(_value, _base);
|
|
27854
|
-
|
|
28137
|
+
if (isNaN(deci)) {
|
|
28138
|
+
return new EvaluationError(_t("The value (%s) must be a valid base %s representation.", _value, _base));
|
|
28139
|
+
}
|
|
27855
28140
|
return deci;
|
|
27856
28141
|
},
|
|
27857
28142
|
isExported: true,
|
|
@@ -27890,7 +28175,9 @@ const FLOOR = {
|
|
|
27890
28175
|
compute: function (value, factor = { value: DEFAULT_FACTOR }) {
|
|
27891
28176
|
const _value = toNumber(value, this.locale);
|
|
27892
28177
|
const _factor = toNumber(factor, this.locale);
|
|
27893
|
-
|
|
28178
|
+
if (_factor < 0 && _value > 0) {
|
|
28179
|
+
return new EvaluationError(_t("The factor (%s) must be positive when the value (%s) is positive.", _factor, _value));
|
|
28180
|
+
}
|
|
27894
28181
|
return {
|
|
27895
28182
|
value: _factor ? Math.floor(_value / _factor) * _factor : 0,
|
|
27896
28183
|
format: value?.format,
|
|
@@ -28002,7 +28289,9 @@ const LN = {
|
|
|
28002
28289
|
args: [arg("value (number)", _t("The value for which to calculate the logarithm, base e."))],
|
|
28003
28290
|
compute: function (value) {
|
|
28004
28291
|
const _value = toNumber(value, this.locale);
|
|
28005
|
-
|
|
28292
|
+
if (_value <= 0) {
|
|
28293
|
+
return new EvaluationError(_t("The value (%s) must be strictly positive.", _value));
|
|
28294
|
+
}
|
|
28006
28295
|
return Math.log(_value);
|
|
28007
28296
|
},
|
|
28008
28297
|
isExported: true,
|
|
@@ -28019,9 +28308,15 @@ const LOG = {
|
|
|
28019
28308
|
compute: function (value, base = { value: 10 }) {
|
|
28020
28309
|
const _value = toNumber(value, this.locale);
|
|
28021
28310
|
const _base = toNumber(base, this.locale);
|
|
28022
|
-
|
|
28023
|
-
|
|
28024
|
-
|
|
28311
|
+
if (_value <= 0) {
|
|
28312
|
+
return new EvaluationError(_t("The value (%s) must be strictly positive.", _value));
|
|
28313
|
+
}
|
|
28314
|
+
if (_base <= 0) {
|
|
28315
|
+
return new EvaluationError(_t("The base (%s) must be strictly positive.", _base));
|
|
28316
|
+
}
|
|
28317
|
+
if (_base === 1) {
|
|
28318
|
+
return new EvaluationError(_t("The base must be different from 1."));
|
|
28319
|
+
}
|
|
28025
28320
|
return Math.log10(_value) / Math.log10(_base);
|
|
28026
28321
|
},
|
|
28027
28322
|
isExported: true,
|
|
@@ -28030,7 +28325,7 @@ const LOG = {
|
|
|
28030
28325
|
// MOD
|
|
28031
28326
|
// -----------------------------------------------------------------------------
|
|
28032
28327
|
function mod(dividend, divisor) {
|
|
28033
|
-
|
|
28328
|
+
assertNotZero(divisor, _t("The divisor must be different from 0."));
|
|
28034
28329
|
const modulus = dividend % divisor;
|
|
28035
28330
|
// -42 % 10 = -2 but we want 8, so need the code below
|
|
28036
28331
|
if ((modulus > 0 && divisor < 0) || (modulus < 0 && divisor > 0)) {
|
|
@@ -28064,7 +28359,9 @@ const MUNIT = {
|
|
|
28064
28359
|
],
|
|
28065
28360
|
compute: function (n) {
|
|
28066
28361
|
const _n = toInteger(n, this.locale);
|
|
28067
|
-
|
|
28362
|
+
if (_n < 1) {
|
|
28363
|
+
return new EvaluationError(_t("The argument dimension must be positive"));
|
|
28364
|
+
}
|
|
28068
28365
|
return getUnitMatrix(_n);
|
|
28069
28366
|
},
|
|
28070
28367
|
isExported: true,
|
|
@@ -28109,7 +28406,9 @@ const POWER = {
|
|
|
28109
28406
|
compute: function (base, exponent) {
|
|
28110
28407
|
const _base = toNumber(base, this.locale);
|
|
28111
28408
|
const _exponent = toNumber(exponent, this.locale);
|
|
28112
|
-
|
|
28409
|
+
if (_base < 0 && !Number.isInteger(_exponent)) {
|
|
28410
|
+
return new EvaluationError(_t("The exponent (%s) must be an integer when the base is negative.", _exponent));
|
|
28411
|
+
}
|
|
28113
28412
|
return { value: Math.pow(_base, _exponent), format: base?.format };
|
|
28114
28413
|
},
|
|
28115
28414
|
isExported: true,
|
|
@@ -28182,11 +28481,19 @@ const RANDARRAY = {
|
|
|
28182
28481
|
const _min = toNumber(min, this.locale);
|
|
28183
28482
|
const _max = toNumber(max, this.locale);
|
|
28184
28483
|
const _whole_number = toBoolean(wholeNumber);
|
|
28185
|
-
|
|
28186
|
-
|
|
28187
|
-
|
|
28484
|
+
if (_cols < 1) {
|
|
28485
|
+
return new EvaluationError(_t("The number of columns (%s) must be positive.", _cols));
|
|
28486
|
+
}
|
|
28487
|
+
if (_rows < 1) {
|
|
28488
|
+
return new EvaluationError(_t("The number of rows (%s) must be positive.", _rows));
|
|
28489
|
+
}
|
|
28490
|
+
if (_min > _max) {
|
|
28491
|
+
return new EvaluationError(_t("The maximum (%s) must be greater than or equal to the minimum (%s).", _max, _min));
|
|
28492
|
+
}
|
|
28188
28493
|
if (_whole_number) {
|
|
28189
|
-
|
|
28494
|
+
if (!Number.isInteger(_min) || !Number.isInteger(_max)) {
|
|
28495
|
+
return new EvaluationError(_t("The maximum (%s) and minimum (%s) must be integers when whole_number is TRUE.", _max.toString(), _min.toString()));
|
|
28496
|
+
}
|
|
28190
28497
|
}
|
|
28191
28498
|
const result = Array(_cols);
|
|
28192
28499
|
for (let col = 0; col < _cols; col++) {
|
|
@@ -28222,7 +28529,9 @@ const RANDBETWEEN = {
|
|
|
28222
28529
|
if (!Number.isInteger(_high)) {
|
|
28223
28530
|
_high = Math.floor(_high);
|
|
28224
28531
|
}
|
|
28225
|
-
|
|
28532
|
+
if (_low > _high) {
|
|
28533
|
+
return new EvaluationError(_t("The high (%s) must be greater than or equal to the low (%s).", _high, _low));
|
|
28534
|
+
}
|
|
28226
28535
|
return {
|
|
28227
28536
|
value: _low + Math.ceil((_high - _low + 1) * Math.random()) - 1,
|
|
28228
28537
|
format: low?.format,
|
|
@@ -28358,8 +28667,12 @@ const SEQUENCE = {
|
|
|
28358
28667
|
const _step = toNumber(step, this.locale);
|
|
28359
28668
|
const _rows = toInteger(rows, this.locale);
|
|
28360
28669
|
const _columns = toInteger(columns, this.locale);
|
|
28361
|
-
|
|
28362
|
-
|
|
28670
|
+
if (_columns < 1) {
|
|
28671
|
+
return new EvaluationError(_t("The number of columns (%s) must be positive.", _columns));
|
|
28672
|
+
}
|
|
28673
|
+
if (_rows < 1) {
|
|
28674
|
+
return new EvaluationError(_t("The number of rows (%s) must be positive.", _rows));
|
|
28675
|
+
}
|
|
28363
28676
|
return generateMatrix(_columns, _rows, (col, row) => {
|
|
28364
28677
|
return {
|
|
28365
28678
|
value: _start + row * _columns * _step + col * _step,
|
|
@@ -28398,7 +28711,9 @@ const SQRT = {
|
|
|
28398
28711
|
args: [arg("value (number)", _t("The number for which to calculate the positive square root."))],
|
|
28399
28712
|
compute: function (value) {
|
|
28400
28713
|
const _value = toNumber(value, this.locale);
|
|
28401
|
-
|
|
28714
|
+
if (_value < 0) {
|
|
28715
|
+
return new EvaluationError(_t("The value (%s) must be positive or null.", _value));
|
|
28716
|
+
}
|
|
28402
28717
|
return { value: Math.sqrt(_value), format: value?.format };
|
|
28403
28718
|
},
|
|
28404
28719
|
isExported: true,
|
|
@@ -28606,7 +28921,7 @@ function filterAndFlatData(dataY, dataX) {
|
|
|
28606
28921
|
_flatDataX.push(x);
|
|
28607
28922
|
lenX += 1;
|
|
28608
28923
|
});
|
|
28609
|
-
assert(
|
|
28924
|
+
assert(lenY === lenX, _t("[[FUNCTION_NAME]] has mismatched argument count %s vs %s.", lenY, lenX));
|
|
28610
28925
|
const flatDataX = [];
|
|
28611
28926
|
const flatDataY = [];
|
|
28612
28927
|
for (let i = 0; i < lenY; i++) {
|
|
@@ -28623,7 +28938,10 @@ function filterAndFlatData(dataY, dataX) {
|
|
|
28623
28938
|
function covariance(dataY, dataX, isSample) {
|
|
28624
28939
|
const { flatDataX, flatDataY } = filterAndFlatData(dataY, dataX);
|
|
28625
28940
|
const count = flatDataY.length;
|
|
28626
|
-
|
|
28941
|
+
assertNotZero(count);
|
|
28942
|
+
if (isSample) {
|
|
28943
|
+
assertNotZero(count - 1);
|
|
28944
|
+
}
|
|
28627
28945
|
let sumY = 0;
|
|
28628
28946
|
let sumX = 0;
|
|
28629
28947
|
for (let i = 0; i < count; i++) {
|
|
@@ -28645,14 +28963,17 @@ function variance(args, isSample, textAs0, locale) {
|
|
|
28645
28963
|
count += 1;
|
|
28646
28964
|
return acc + a;
|
|
28647
28965
|
}, 0, locale);
|
|
28648
|
-
|
|
28966
|
+
assertNotZero(count);
|
|
28967
|
+
if (isSample) {
|
|
28968
|
+
assertNotZero(count - 1);
|
|
28969
|
+
}
|
|
28649
28970
|
const average = sum / count;
|
|
28650
28971
|
return (reduceFunction(args, (acc, a) => acc + Math.pow(a - average, 2), 0, locale) /
|
|
28651
28972
|
(count - (isSample ? 1 : 0)));
|
|
28652
28973
|
}
|
|
28653
28974
|
function centile(data, percent, isInclusive, locale) {
|
|
28654
28975
|
const _percent = toNumber(percent, locale);
|
|
28655
|
-
assert(
|
|
28976
|
+
assert(isInclusive ? 0 <= _percent && _percent <= 1 : 0 < _percent && _percent < 1, _t("Function [[FUNCTION_NAME]] parameter 2 value is out of range."));
|
|
28656
28977
|
const sortedArray = [];
|
|
28657
28978
|
let index;
|
|
28658
28979
|
let count = 0;
|
|
@@ -28664,10 +28985,10 @@ function centile(data, percent, isInclusive, locale) {
|
|
|
28664
28985
|
count++;
|
|
28665
28986
|
}
|
|
28666
28987
|
});
|
|
28667
|
-
assert(
|
|
28988
|
+
assert(count !== 0, _t("[[FUNCTION_NAME]] has no valid input data."));
|
|
28668
28989
|
if (!isInclusive) {
|
|
28669
28990
|
// 2nd argument must be between 1/(n+1) and n/(n+1) with n the number of data
|
|
28670
|
-
assert(
|
|
28991
|
+
assert(1 / (count + 1) <= _percent && _percent <= count / (count + 1), _t("Function [[FUNCTION_NAME]] parameter 2 value is out of range."));
|
|
28671
28992
|
}
|
|
28672
28993
|
return percentile(sortedArray, _percent, isInclusive);
|
|
28673
28994
|
}
|
|
@@ -28686,7 +29007,9 @@ const AVEDEV = {
|
|
|
28686
29007
|
count += 1;
|
|
28687
29008
|
return acc + a;
|
|
28688
29009
|
}, 0, this.locale);
|
|
28689
|
-
|
|
29010
|
+
if (count === 0) {
|
|
29011
|
+
return new DivisionByZeroError(_t("Evaluation of function [[FUNCTION_NAME]] caused a divide by zero error."));
|
|
29012
|
+
}
|
|
28690
29013
|
const average = sum / count;
|
|
28691
29014
|
return reduceNumbers(values, (acc, a) => acc + Math.abs(average - a), 0, this.locale) / count;
|
|
28692
29015
|
},
|
|
@@ -28728,7 +29051,9 @@ const AVERAGE_WEIGHTED = {
|
|
|
28728
29051
|
for (let n = 0; n < args.length - 1; n += 2) {
|
|
28729
29052
|
const argN = args[n];
|
|
28730
29053
|
const argN1 = args[n + 1];
|
|
28731
|
-
|
|
29054
|
+
if (!areSameDimensions(argN, argN1)) {
|
|
29055
|
+
return new EvaluationError(rangeError);
|
|
29056
|
+
}
|
|
28732
29057
|
if (isMatrix(argN)) {
|
|
28733
29058
|
for (let i = 0; i < argN.length; i++) {
|
|
28734
29059
|
for (let j = 0; j < argN[0].length; j++) {
|
|
@@ -28737,12 +29062,16 @@ const AVERAGE_WEIGHTED = {
|
|
|
28737
29062
|
const valueIsNumber = typeof value === "number";
|
|
28738
29063
|
const weightIsNumber = typeof weight === "number";
|
|
28739
29064
|
if (valueIsNumber && weightIsNumber) {
|
|
28740
|
-
|
|
29065
|
+
if (weight < 0) {
|
|
29066
|
+
return new EvaluationError(negativeWeightError);
|
|
29067
|
+
}
|
|
28741
29068
|
sum += value * weight;
|
|
28742
29069
|
count += weight;
|
|
28743
29070
|
continue;
|
|
28744
29071
|
}
|
|
28745
|
-
|
|
29072
|
+
if (valueIsNumber !== weightIsNumber) {
|
|
29073
|
+
return new EvaluationError(_t("[[FUNCTION_NAME]] expects number values."));
|
|
29074
|
+
}
|
|
28746
29075
|
}
|
|
28747
29076
|
}
|
|
28748
29077
|
}
|
|
@@ -28750,13 +29079,17 @@ const AVERAGE_WEIGHTED = {
|
|
|
28750
29079
|
const value = toNumber(argN, this.locale);
|
|
28751
29080
|
const weight = isMatrix(argN1) ? argN1?.[0][0].value : toNumber(argN1, this.locale);
|
|
28752
29081
|
if (typeof weight === "number") {
|
|
28753
|
-
|
|
29082
|
+
if (weight < 0) {
|
|
29083
|
+
return new EvaluationError(negativeWeightError);
|
|
29084
|
+
}
|
|
28754
29085
|
sum += value * weight;
|
|
28755
29086
|
count += weight;
|
|
28756
29087
|
}
|
|
28757
29088
|
}
|
|
28758
29089
|
}
|
|
28759
|
-
|
|
29090
|
+
if (count === 0) {
|
|
29091
|
+
return new DivisionByZeroError(_t("Evaluation of function [[FUNCTION_NAME]] caused a divide by zero error."));
|
|
29092
|
+
}
|
|
28760
29093
|
return { value: sum / count, format: inferFormat(args[0]) };
|
|
28761
29094
|
},
|
|
28762
29095
|
};
|
|
@@ -28775,7 +29108,9 @@ const AVERAGEA = {
|
|
|
28775
29108
|
count += 1;
|
|
28776
29109
|
return acc + a;
|
|
28777
29110
|
}, 0, this.locale);
|
|
28778
|
-
|
|
29111
|
+
if (count === 0) {
|
|
29112
|
+
return new DivisionByZeroError(_t("Evaluation of function [[FUNCTION_NAME]] caused a divide by zero error."));
|
|
29113
|
+
}
|
|
28779
29114
|
return {
|
|
28780
29115
|
value: sum / count,
|
|
28781
29116
|
format: inferFormat(args[0]),
|
|
@@ -28804,7 +29139,9 @@ const AVERAGEIF = {
|
|
|
28804
29139
|
sum += value;
|
|
28805
29140
|
}
|
|
28806
29141
|
}, this.locale);
|
|
28807
|
-
|
|
29142
|
+
if (count === 0) {
|
|
29143
|
+
return new DivisionByZeroError(_t("Evaluation of function [[FUNCTION_NAME]] caused a divide by zero error."));
|
|
29144
|
+
}
|
|
28808
29145
|
return sum / count;
|
|
28809
29146
|
},
|
|
28810
29147
|
isExported: true,
|
|
@@ -28832,7 +29169,9 @@ const AVERAGEIFS = {
|
|
|
28832
29169
|
sum += value;
|
|
28833
29170
|
}
|
|
28834
29171
|
}, this.locale);
|
|
28835
|
-
|
|
29172
|
+
if (count === 0) {
|
|
29173
|
+
return new DivisionByZeroError(_t("Evaluation of function [[FUNCTION_NAME]] caused a divide by zero error."));
|
|
29174
|
+
}
|
|
28836
29175
|
return sum / count;
|
|
28837
29176
|
},
|
|
28838
29177
|
isExported: true,
|
|
@@ -28982,8 +29321,12 @@ const LARGE = {
|
|
|
28982
29321
|
}
|
|
28983
29322
|
});
|
|
28984
29323
|
const result = largests.shift();
|
|
28985
|
-
|
|
28986
|
-
|
|
29324
|
+
if (result === undefined) {
|
|
29325
|
+
return new EvaluationError(_t("[[FUNCTION_NAME]] has no valid input data."));
|
|
29326
|
+
}
|
|
29327
|
+
if (count < _n) {
|
|
29328
|
+
return new EvaluationError(_t("Function [[FUNCTION_NAME]] parameter 2 value (%s) is out of range.", _n));
|
|
29329
|
+
}
|
|
28987
29330
|
return result;
|
|
28988
29331
|
},
|
|
28989
29332
|
isExported: true,
|
|
@@ -29463,8 +29806,12 @@ const SMALL = {
|
|
|
29463
29806
|
}
|
|
29464
29807
|
});
|
|
29465
29808
|
const result = largests.pop();
|
|
29466
|
-
|
|
29467
|
-
|
|
29809
|
+
if (result === undefined) {
|
|
29810
|
+
return new EvaluationError(_t("[[FUNCTION_NAME]] has no valid input data."));
|
|
29811
|
+
}
|
|
29812
|
+
if (count < _n) {
|
|
29813
|
+
return new EvaluationError(_t("Function [[FUNCTION_NAME]] parameter 2 value (%s) is out of range.", _n));
|
|
29814
|
+
}
|
|
29468
29815
|
return result;
|
|
29469
29816
|
},
|
|
29470
29817
|
isExported: true,
|
|
@@ -29891,7 +30238,9 @@ const DGET = {
|
|
|
29891
30238
|
args: databaseArgs,
|
|
29892
30239
|
compute: function (database, field, criteria) {
|
|
29893
30240
|
const cells = getMatchingCells(database, field, criteria, this.locale);
|
|
29894
|
-
|
|
30241
|
+
if (cells.length !== 1) {
|
|
30242
|
+
return new EvaluationError(_t("More than one match found in DGET evaluation."));
|
|
30243
|
+
}
|
|
29895
30244
|
return cells[0];
|
|
29896
30245
|
},
|
|
29897
30246
|
isExported: true,
|
|
@@ -30035,14 +30384,18 @@ const DATE = {
|
|
|
30035
30384
|
const _month = Math.trunc(toNumber(month, this.locale));
|
|
30036
30385
|
const _day = Math.trunc(toNumber(day, this.locale));
|
|
30037
30386
|
// For years less than 0 or greater than 10000, return #ERROR.
|
|
30038
|
-
|
|
30387
|
+
if (_year < 0 || _year > 9999) {
|
|
30388
|
+
return new EvaluationError(_t("The year (%s) must be between 0 and 9999 inclusive.", _year.toString()));
|
|
30389
|
+
}
|
|
30039
30390
|
// Between 0 and 1899, we add that value to 1900 to calculate the year
|
|
30040
30391
|
if (_year < 1900) {
|
|
30041
30392
|
_year += 1900;
|
|
30042
30393
|
}
|
|
30043
30394
|
const jsDate = new DateTime(_year, _month - 1, _day);
|
|
30044
30395
|
const result = jsDateToRoundNumber(jsDate);
|
|
30045
|
-
|
|
30396
|
+
if (result < 0) {
|
|
30397
|
+
return new EvaluationError(_t("The function [[FUNCTION_NAME]] result must be greater than or equal 01/01/1900."));
|
|
30398
|
+
}
|
|
30046
30399
|
return {
|
|
30047
30400
|
value: result,
|
|
30048
30401
|
format: this.locale.dateFormat,
|
|
@@ -30062,12 +30415,16 @@ const DATEDIF = {
|
|
|
30062
30415
|
],
|
|
30063
30416
|
compute: function (startDate, endDate, unit) {
|
|
30064
30417
|
const _unit = toString(unit).toUpperCase();
|
|
30065
|
-
|
|
30418
|
+
if (!Object.values(TIME_UNIT).includes(_unit)) {
|
|
30419
|
+
return new EvaluationError(expectStringSetError(Object.values(TIME_UNIT), toString(unit)));
|
|
30420
|
+
}
|
|
30066
30421
|
const _startDate = Math.trunc(toNumber(startDate, this.locale));
|
|
30067
30422
|
const _endDate = Math.trunc(toNumber(endDate, this.locale));
|
|
30068
30423
|
const jsStartDate = numberToJsDate(_startDate);
|
|
30069
30424
|
const jsEndDate = numberToJsDate(_endDate);
|
|
30070
|
-
|
|
30425
|
+
if (_endDate < _startDate) {
|
|
30426
|
+
return new EvaluationError(_t("start_date (%s) should be on or before end_date (%s).", jsStartDate.toLocaleDateString(), jsEndDate.toLocaleDateString()));
|
|
30427
|
+
}
|
|
30071
30428
|
switch (_unit) {
|
|
30072
30429
|
case TIME_UNIT.WHOLE_YEARS:
|
|
30073
30430
|
return getTimeDifferenceInWholeYears(jsStartDate, jsEndDate);
|
|
@@ -30115,7 +30472,9 @@ const DATEVALUE = {
|
|
|
30115
30472
|
compute: function (dateString) {
|
|
30116
30473
|
const _dateString = toString(dateString);
|
|
30117
30474
|
const internalDate = parseDateTime(_dateString, this.locale);
|
|
30118
|
-
|
|
30475
|
+
if (internalDate === null) {
|
|
30476
|
+
return new EvaluationError(_t("The date_string (%s) cannot be parsed to date/time.", _dateString.toString()));
|
|
30477
|
+
}
|
|
30119
30478
|
return Math.trunc(internalDate.value);
|
|
30120
30479
|
},
|
|
30121
30480
|
isExported: true,
|
|
@@ -30359,17 +30718,7 @@ function weekendToDayNumber(data) {
|
|
|
30359
30718
|
const weekend = data?.value;
|
|
30360
30719
|
// case "string"
|
|
30361
30720
|
if (typeof weekend === "string") {
|
|
30362
|
-
assert(() =>
|
|
30363
|
-
if (weekend.length !== 7) {
|
|
30364
|
-
return false;
|
|
30365
|
-
}
|
|
30366
|
-
for (const day of weekend) {
|
|
30367
|
-
if (day !== "0" && day !== "1") {
|
|
30368
|
-
return false;
|
|
30369
|
-
}
|
|
30370
|
-
}
|
|
30371
|
-
return true;
|
|
30372
|
-
}, _t('When weekend is a string (%s) it must be composed of "0" or "1".', weekend));
|
|
30721
|
+
assert(weekend.length === 7 && [...weekend].every((c) => c === "0" || c === "1"), _t('When weekend is a string (%s) it must be composed of "0" or "1".', weekend));
|
|
30373
30722
|
const result = [];
|
|
30374
30723
|
for (let i = 0; i < 7; i++) {
|
|
30375
30724
|
if (weekend[i] === "1") {
|
|
@@ -30380,7 +30729,7 @@ function weekendToDayNumber(data) {
|
|
|
30380
30729
|
}
|
|
30381
30730
|
//case "number"
|
|
30382
30731
|
if (typeof weekend === "number") {
|
|
30383
|
-
assert((
|
|
30732
|
+
assert((1 <= weekend && weekend <= 7) || (11 <= weekend && weekend <= 17), _t("The weekend (%s) must be a string or a number in the range 1-7 or 11-17.", weekend.toString()));
|
|
30384
30733
|
// case 1 <= weekend <= 7
|
|
30385
30734
|
if (weekend <= 7) {
|
|
30386
30735
|
// 1 = Saturday/Sunday are weekends
|
|
@@ -30481,7 +30830,9 @@ const TIME = {
|
|
|
30481
30830
|
_hour += Math.floor(_minute / 60);
|
|
30482
30831
|
_minute = (_minute % 60) + (_minute < 0 ? 60 : 0);
|
|
30483
30832
|
_hour %= 24;
|
|
30484
|
-
|
|
30833
|
+
if (_hour < 0) {
|
|
30834
|
+
return new EvaluationError(_t("The function [[FUNCTION_NAME]] result cannot be negative"));
|
|
30835
|
+
}
|
|
30485
30836
|
return {
|
|
30486
30837
|
value: _hour / 24 + _minute / (24 * 60) + _second / (24 * 60 * 60),
|
|
30487
30838
|
format: this.locale.timeFormat,
|
|
@@ -30498,7 +30849,9 @@ const TIMEVALUE = {
|
|
|
30498
30849
|
compute: function (timeString) {
|
|
30499
30850
|
const _timeString = toString(timeString);
|
|
30500
30851
|
const internalDate = parseDateTime(_timeString, this.locale);
|
|
30501
|
-
|
|
30852
|
+
if (internalDate === null) {
|
|
30853
|
+
return new EvaluationError(_t("The time_string (%s) cannot be parsed to date/time.", _timeString));
|
|
30854
|
+
}
|
|
30502
30855
|
const result = internalDate.value - Math.trunc(internalDate.value);
|
|
30503
30856
|
return result < 0 ? 1 + result : result;
|
|
30504
30857
|
},
|
|
@@ -30533,7 +30886,9 @@ const WEEKDAY = {
|
|
|
30533
30886
|
const _date = toJsDate(date, this.locale);
|
|
30534
30887
|
const _type = Math.round(toNumber(type, this.locale));
|
|
30535
30888
|
const m = _date.getDay();
|
|
30536
|
-
|
|
30889
|
+
if (![1, 2, 3].includes(_type)) {
|
|
30890
|
+
return new EvaluationError(_t("The type (%s) must be 1, 2 or 3.", _type));
|
|
30891
|
+
}
|
|
30537
30892
|
if (_type === 1)
|
|
30538
30893
|
return m + 1;
|
|
30539
30894
|
if (_type === 2)
|
|
@@ -30554,7 +30909,9 @@ const WEEKNUM = {
|
|
|
30554
30909
|
compute: function (date, type = { value: DEFAULT_TYPE }) {
|
|
30555
30910
|
const _date = toJsDate(date, this.locale);
|
|
30556
30911
|
const _type = Math.round(toNumber(type, this.locale));
|
|
30557
|
-
|
|
30912
|
+
if (![1, 2, 11, 12, 13, 14, 15, 16, 17, 21].includes(_type)) {
|
|
30913
|
+
return new EvaluationError(_t("The type (%s) is out of range.", _type.toString()));
|
|
30914
|
+
}
|
|
30558
30915
|
if (_type === 21) {
|
|
30559
30916
|
return ISOWEEKNUM.compute.bind(this)(date);
|
|
30560
30917
|
}
|
|
@@ -30610,8 +30967,8 @@ const WORKDAY_INTL = {
|
|
|
30610
30967
|
compute: function (startDate, numDays, weekend = { value: DEFAULT_WEEKEND }, holidays) {
|
|
30611
30968
|
const _startDate = toJsDate(startDate, this.locale);
|
|
30612
30969
|
const _numDays = Math.trunc(toNumber(numDays, this.locale));
|
|
30613
|
-
if (
|
|
30614
|
-
|
|
30970
|
+
if (weekend.value === "1111111") {
|
|
30971
|
+
return new EvaluationError(_t("The weekend must be different from '1111111'."));
|
|
30615
30972
|
}
|
|
30616
30973
|
const daysWeekend = weekendToDayNumber(weekend);
|
|
30617
30974
|
const timesHoliday = new Set();
|
|
@@ -30666,9 +31023,15 @@ const YEARFRAC = {
|
|
|
30666
31023
|
const _startDate = Math.trunc(toNumber(startDate, this.locale));
|
|
30667
31024
|
const _endDate = Math.trunc(toNumber(endDate, this.locale));
|
|
30668
31025
|
const _dayCountConvention = Math.trunc(toNumber(dayCountConvention, this.locale));
|
|
30669
|
-
|
|
30670
|
-
|
|
30671
|
-
|
|
31026
|
+
if (_startDate < 0) {
|
|
31027
|
+
return new EvaluationError(_t("The start_date (%s) must be positive or null.", _startDate));
|
|
31028
|
+
}
|
|
31029
|
+
if (_endDate < 0) {
|
|
31030
|
+
return new EvaluationError(_t("The end_date (%s) must be positive or null.", _endDate));
|
|
31031
|
+
}
|
|
31032
|
+
if (0 > _dayCountConvention || _dayCountConvention > 4) {
|
|
31033
|
+
return new EvaluationError(_t("The day_count_convention (%s) must be between 0 and 4 inclusive.", _dayCountConvention));
|
|
31034
|
+
}
|
|
30672
31035
|
return getYearFrac(_startDate, _endDate, _dayCountConvention);
|
|
30673
31036
|
},
|
|
30674
31037
|
};
|
|
@@ -30833,7 +31196,7 @@ var engineering = /*#__PURE__*/Object.freeze({
|
|
|
30833
31196
|
|
|
30834
31197
|
function sortMatrix(matrix, locale, ...criteria) {
|
|
30835
31198
|
for (const [i, value] of criteria.entries()) {
|
|
30836
|
-
assert(
|
|
31199
|
+
assert(value !== undefined, _t("Value for parameter %d is missing, while the function [[FUNCTION_NAME]] expect a number or a range.", i + 1));
|
|
30837
31200
|
}
|
|
30838
31201
|
const sortingOrders = [];
|
|
30839
31202
|
const sortColumns = [];
|
|
@@ -30842,7 +31205,7 @@ function sortMatrix(matrix, locale, ...criteria) {
|
|
|
30842
31205
|
sortingOrders.push(toBoolean(toScalar(criteria[i + 1])?.value) ? "asc" : "desc");
|
|
30843
31206
|
const sortColumn = criteria[i];
|
|
30844
31207
|
if (isMatrix(sortColumn) && (sortColumn.length > 1 || sortColumn[0].length > 1)) {
|
|
30845
|
-
assert(
|
|
31208
|
+
assert(sortColumn.length === 1 && sortColumn[0].length === nRows, _t("Wrong size for %s. Expected a range of size 1x%s. Got %sx%s.", `sort_column${i + 1}`, nRows, sortColumn.length, sortColumn[0].length));
|
|
30846
31209
|
sortColumns.push(sortColumn.flat().map((c) => c.value));
|
|
30847
31210
|
}
|
|
30848
31211
|
else {
|
|
@@ -30907,12 +31270,20 @@ const FILTER = {
|
|
|
30907
31270
|
compute: function (range, ...conditions) {
|
|
30908
31271
|
let _array = toMatrix(range);
|
|
30909
31272
|
const _conditionsMatrices = conditions.map((cond) => matrixMap(toMatrix(cond), (data) => data.value));
|
|
30910
|
-
|
|
30911
|
-
|
|
31273
|
+
for (const c of _conditionsMatrices) {
|
|
31274
|
+
if (!isSingleColOrRow(c)) {
|
|
31275
|
+
return new EvaluationError(_t("The arguments condition must be a single column or row."));
|
|
31276
|
+
}
|
|
31277
|
+
}
|
|
31278
|
+
if (!areSameDimensions(...conditions)) {
|
|
31279
|
+
return new EvaluationError(_t("The arguments conditions must have the same dimensions."));
|
|
31280
|
+
}
|
|
30912
31281
|
const _conditions = _conditionsMatrices.map((c) => c.flat());
|
|
30913
31282
|
const mode = _conditionsMatrices[0].length === 1 ? "row" : "col";
|
|
30914
31283
|
_array = mode === "row" ? transposeMatrix(_array) : _array;
|
|
30915
|
-
|
|
31284
|
+
if (_conditions.some((cond) => cond.length !== _array.length)) {
|
|
31285
|
+
return new EvaluationError(_t("FILTER has mismatched sizes on the range and conditions."));
|
|
31286
|
+
}
|
|
30916
31287
|
const result = [];
|
|
30917
31288
|
for (let i = 0; i < _array.length; i++) {
|
|
30918
31289
|
const row = _array[i];
|
|
@@ -30963,8 +31334,12 @@ const SORTN = {
|
|
|
30963
31334
|
const sortingCriteria = displayTiesMode_sortingCriteria.length % 2 === 0
|
|
30964
31335
|
? displayTiesMode_sortingCriteria
|
|
30965
31336
|
: displayTiesMode_sortingCriteria.slice(1);
|
|
30966
|
-
|
|
30967
|
-
|
|
31337
|
+
if (_n < 0) {
|
|
31338
|
+
return new EvaluationError(_t("Wrong value of 'n'. Expected a positive number. Got %s.", _n));
|
|
31339
|
+
}
|
|
31340
|
+
if (_displayTiesMode < 0 || _displayTiesMode > 3) {
|
|
31341
|
+
return new EvaluationError(_t("Wrong value of 'display_ties_mode'. Expected a positive number between 0 and 3. Got %s.", _displayTiesMode));
|
|
31342
|
+
}
|
|
30968
31343
|
const sortedData = sortMatrix(transposeMatrix(range), this.locale, ...sortingCriteria);
|
|
30969
31344
|
const sameRows = (i, j) => JSON.stringify(sortedData[i].map((c) => c.value)) ===
|
|
30970
31345
|
JSON.stringify(sortedData[j].map((c) => c.value));
|
|
@@ -31069,127 +31444,83 @@ var filter = /*#__PURE__*/Object.freeze({
|
|
|
31069
31444
|
UNIQUE: UNIQUE
|
|
31070
31445
|
});
|
|
31071
31446
|
|
|
31072
|
-
|
|
31073
|
-
|
|
31074
|
-
|
|
31075
|
-
|
|
31076
|
-
|
|
31077
|
-
|
|
31078
|
-
|
|
31079
|
-
|
|
31080
|
-
|
|
31081
|
-
|
|
31082
|
-
|
|
31083
|
-
|
|
31084
|
-
|
|
31085
|
-
|
|
31086
|
-
|
|
31087
|
-
|
|
31088
|
-
|
|
31089
|
-
|
|
31090
|
-
|
|
31091
|
-
|
|
31092
|
-
|
|
31093
|
-
|
|
31094
|
-
|
|
31095
|
-
|
|
31096
|
-
|
|
31097
|
-
|
|
31098
|
-
|
|
31099
|
-
|
|
31100
|
-
|
|
31101
|
-
|
|
31102
|
-
|
|
31103
|
-
|
|
31104
|
-
|
|
31105
|
-
|
|
31106
|
-
|
|
31107
|
-
|
|
31108
|
-
|
|
31109
|
-
|
|
31110
|
-
|
|
31111
|
-
|
|
31112
|
-
|
|
31113
|
-
|
|
31114
|
-
|
|
31115
|
-
|
|
31116
|
-
|
|
31117
|
-
|
|
31118
|
-
|
|
31119
|
-
|
|
31120
|
-
|
|
31121
|
-
|
|
31122
|
-
|
|
31123
|
-
}
|
|
31124
|
-
|
|
31125
|
-
|
|
31126
|
-
}
|
|
31127
|
-
|
|
31128
|
-
|
|
31129
|
-
|
|
31130
|
-
|
|
31131
|
-
|
|
31132
|
-
|
|
31133
|
-
|
|
31134
|
-
|
|
31135
|
-
|
|
31136
|
-
function assertDiscountStrictlyPositive(discount) {
|
|
31137
|
-
assert(() => discount > 0, _t("The discount (%s) must be strictly positive.", discount.toString()));
|
|
31447
|
+
const expectCashFlowsAndDatesHaveSameDimension = _t("The cashflow_amounts and cashflow_dates ranges must have the same dimensions.");
|
|
31448
|
+
const expectCashFlowsHavePositiveAndNegativesValues = _t("There must be both positive and negative values in cashflow_amounts.");
|
|
31449
|
+
const expectCostPositiveOrZero = (cost) => _t("The cost (%s) must be positive or null.", cost);
|
|
31450
|
+
const expectCostStrictlyPositive = (cost) => _t("The cost (%s) must be strictly positive.", cost);
|
|
31451
|
+
const expectCouponFrequencyIsValid = (frequency) => _t("The frequency (%s) must be one of %s", frequency, [1, 2, 4].toString());
|
|
31452
|
+
const expectDayCountConventionIsValid = (dayCountConvention) => _t("The day_count_convention (%s) must be between 0 and 4 inclusive.", dayCountConvention);
|
|
31453
|
+
const expectDeprecationFactorStrictlyPositive = (factor) => _t("The depreciation factor (%s) must be strictly positive.", factor);
|
|
31454
|
+
const expectDiscountDifferentFromMinusOne = (discount) => _t("The discount (%s) must be different from -1.", discount);
|
|
31455
|
+
const expectDiscountStrictlyPositive = (discount) => _t("The discount (%s) must be strictly positive.", discount);
|
|
31456
|
+
const expectDiscountStrictlySmallerThanOne = (discount) => _t("The discount (%s) must be smaller than 1.", discount);
|
|
31457
|
+
const expectEffectiveRateStrictlyPositive = (effectiveRate) => _t("The effective_rate (%s) must be strictly positive.", effectiveRate);
|
|
31458
|
+
const expectEndPeriodPositiveOrZero = (endPeriod) => _t("The end_period (%s) must be positive or null.", endPeriod);
|
|
31459
|
+
const expectEndPeriodSmallerOrEqualToLife = (end, life) => _t("The end_period (%(end)s) must be smaller or equal to the life (%(life)s).", { end, life });
|
|
31460
|
+
const expectEveryDateGreaterThanFirstDateOfCashFlowDates = (firstDate) => _t("All the dates should be greater or equal to the first date in cashflow_dates (%s).", firstDate);
|
|
31461
|
+
const expectFirstPeriodSmallerOrEqualLastPeriod = (first, last) => _t("The first_period (%(first)s) must be smaller or equal to the last_period (%(last)s).", {
|
|
31462
|
+
first,
|
|
31463
|
+
last,
|
|
31464
|
+
});
|
|
31465
|
+
const expectFirstPeriodStrictlyPositive = (period) => _t("The first_period (%s) must be strictly positive.", period);
|
|
31466
|
+
const expectFutureValueStrictlyPositive = (pv) => _t("The future_value (%s) must be strictly positive.", pv);
|
|
31467
|
+
const expectInvestmentStrictlyPositive = (investment) => _t("The investment (%s) must be strictly positive.", investment);
|
|
31468
|
+
const expectIssuePositiveOrZero = (issue) => _t("The issue (%s) must be positive or null.", issue);
|
|
31469
|
+
const expectLastPeriodSmallerOrEqualNumberOfPeriods = (last, nPeriods) => _t("The last_period (%(last)s) must be smaller or equal to the number_of_periods (%(nPeriods)s).", { last, nPeriods });
|
|
31470
|
+
const expectLastPeriodStrictlyPositive = (period) => _t("The last_period (%s) must be strictly positive.", period);
|
|
31471
|
+
const expectLifeStrictlyPositive = (life) => _t("The life (%s) must be strictly positive.", life);
|
|
31472
|
+
const expectMaturityStrictlyGreaterThanSettlement = (settlement, maturity) => _t("The maturity (%(maturity)s) must be strictly greater than the settlement (%(settlement)s).", {
|
|
31473
|
+
maturity,
|
|
31474
|
+
settlement,
|
|
31475
|
+
});
|
|
31476
|
+
const expectMonthBetweenOneAndTwelve = (month) => _t("The month (%s) must be between 1 and 12 inclusive.", month);
|
|
31477
|
+
const expectNominalRateStrictlyPositive = (nominalRate) => _t("The nominal_rate (%s) must be strictly positive.", nominalRate);
|
|
31478
|
+
const expectNumberOfPeriodDifferentFromZero = (nPeriods) => _t("The number_of_periods (%s) must be different from zero.", nPeriods);
|
|
31479
|
+
const expectNumberOfPeriodsStrictlyPositive = (nPeriods) => _t("The number_of_periods (%s) must be strictly positive.", nPeriods);
|
|
31480
|
+
const expectPeriodBetweenOneAndNumberOfPeriods = (nPeriods) => _t("The period must be between 1 and number_of_periods (%s)", nPeriods);
|
|
31481
|
+
const expectPeriodLessOrEqualToLifeLimit = (period, lifeLimit) => _t("The period (%(period)s) must be less than or equal to %(lifeLimit)s.", { period, lifeLimit });
|
|
31482
|
+
const expectPeriodPositiveOrZero = (period) => _t("The period (%s) must be positive or null.", period);
|
|
31483
|
+
const expectPeriodsByYearStrictlyPositive = (periodsByYear) => _t("The periods_by_year (%s) must be strictly positive.", periodsByYear);
|
|
31484
|
+
const expectPeriodSmallerOrEqualToLife = (period, life) => _t("The period (%(period)s) must be less than or equal life (%(life)s).", { period, life });
|
|
31485
|
+
const expectPeriodStrictlyPositive = (period) => _t("The period (%s) must be strictly positive.", period);
|
|
31486
|
+
const expectPresentValueStrictlyPositive = (pv) => _t("The present_value (%s) must be strictly positive.", pv);
|
|
31487
|
+
const expectPriceStrictlyPositive = (price) => _t("The price (%s) must be strictly positive.", price);
|
|
31488
|
+
const expectPurchaseDateBeforeFirstPeriodEnd = (purchaseDate, firstPeriodEnd) => _t("The purchase_date (%(purchaseDate)s) must be before the first_period_end (%(firstPeriodEnd)s).", { purchaseDate, firstPeriodEnd });
|
|
31489
|
+
const expectPurchaseDatePositiveOrZero = (purchaseDate) => _t("The purchase_date (%s) must be positive or null.", purchaseDate);
|
|
31490
|
+
const expectRateGuessStrictlyGreaterThanMinusOne = (guess) => _t("The rate_guess (%s) must be strictly greater than -1.", guess);
|
|
31491
|
+
const expectRatePositiveOrZero = (rate) => _t("The rate (%s) must be positive or null.", rate);
|
|
31492
|
+
const expectRateStrictlyPositive = (rate) => _t("The rate (%s) must be strictly positive.", rate);
|
|
31493
|
+
const expectRedemptionStrictlyPositive = (redemption) => _t("The redemption (%s) must be strictly positive.", redemption);
|
|
31494
|
+
const expectSalvagePositiveOrZero = (salvage) => _t("The salvage (%s) must be positive or null.", salvage);
|
|
31495
|
+
const expectSalvageSmallerOrEqualThanCost = (salvage, cost) => _t("The salvage (%(salvage)s) must be smaller or equal than the cost (%(cost)s).", {
|
|
31496
|
+
salvage,
|
|
31497
|
+
cost,
|
|
31498
|
+
});
|
|
31499
|
+
const expectSettlementGreaterOrEqualToIssue = (settlement, issue) => _t("The settlement date (%(settlement)s) must be greater or equal to the issue date (%(issue)s).", { settlement, issue });
|
|
31500
|
+
const expectSettlementLessThanOneYearBeforeMaturity = (settlement, maturity) => _t("The settlement date (%(settlement)s) must at most one year after the maturity date (%(maturity)s).", { settlement, maturity });
|
|
31501
|
+
const expectSettlementStrictlyGreaterThanIssue = (settlement, issue) => _t("The settlement date (%(settlement)s) must be strictly greater than the issue date (%(issue)s).", { settlement, issue });
|
|
31502
|
+
const expectStartPeriodPositiveOrZero = (startPeriod) => _t("The start_period (%s) must be positive or null.", startPeriod);
|
|
31503
|
+
const expectStartPeriodSmallerOrEqualEndPeriod = (start, end) => _t("The start_period (%(start)s) must be smaller or equal to the end_period (%(end)s).", {
|
|
31504
|
+
start,
|
|
31505
|
+
end,
|
|
31506
|
+
});
|
|
31507
|
+
const expectUnitStrictlyPositive = (unit) => _t("The unit (%s) must be strictly positive.", unit);
|
|
31508
|
+
const expectYieldPositiveOrZero = (yeld) => _t("The yield (%s) must be positive or null.", yeld);
|
|
31509
|
+
function havePositiveAndNegativeValues(arrayNumbers) {
|
|
31510
|
+
return arrayNumbers.some((val) => val > 0) && arrayNumbers.some((val) => val < 0);
|
|
31138
31511
|
}
|
|
31139
|
-
function
|
|
31140
|
-
|
|
31512
|
+
function isInvalidDayCountConvention(dayCountConvention) {
|
|
31513
|
+
return ![0, 1, 2, 3, 4].includes(dayCountConvention);
|
|
31141
31514
|
}
|
|
31142
|
-
function
|
|
31143
|
-
|
|
31515
|
+
function isInvalidFrequency(frequency) {
|
|
31516
|
+
return ![1, 2, 4].includes(frequency);
|
|
31144
31517
|
}
|
|
31145
|
-
function
|
|
31518
|
+
function isSettlementLessThanOneYearBeforeMaturity(settlement, maturity, locale) {
|
|
31146
31519
|
const startDate = toJsDate(settlement, locale);
|
|
31147
31520
|
const endDate = toJsDate(maturity, locale);
|
|
31148
31521
|
const startDatePlusOneYear = toJsDate(settlement, locale);
|
|
31149
31522
|
startDatePlusOneYear.setFullYear(startDate.getFullYear() + 1);
|
|
31150
|
-
|
|
31151
|
-
}
|
|
31152
|
-
/**
|
|
31153
|
-
* Check if the given periods are valid. This will assert :
|
|
31154
|
-
*
|
|
31155
|
-
* - 0 < numberOfPeriods
|
|
31156
|
-
* - 0 < firstPeriod <= lastPeriod
|
|
31157
|
-
* - 0 < lastPeriod <= numberOfPeriods
|
|
31158
|
-
*
|
|
31159
|
-
*/
|
|
31160
|
-
function assertFirstAndLastPeriodsAreValid(firstPeriod, lastPeriod, numberOfPeriods) {
|
|
31161
|
-
assertNumberOfPeriodsStrictlyPositive(numberOfPeriods);
|
|
31162
|
-
assert(() => firstPeriod > 0, _t("The first_period (%s) must be strictly positive.", firstPeriod.toString()));
|
|
31163
|
-
assert(() => lastPeriod > 0, _t("The last_period (%s) must be strictly positive.", lastPeriod.toString()));
|
|
31164
|
-
assert(() => firstPeriod <= lastPeriod, _t("The first_period (%s) must be smaller or equal to the last_period (%s).", firstPeriod.toString(), lastPeriod.toString()));
|
|
31165
|
-
assert(() => lastPeriod <= numberOfPeriods, _t("The last_period (%s) must be smaller or equal to the number_of_periods (%s).", firstPeriod.toString(), numberOfPeriods.toString()));
|
|
31166
|
-
}
|
|
31167
|
-
/**
|
|
31168
|
-
* Check if the given periods are valid. This will assert :
|
|
31169
|
-
*
|
|
31170
|
-
* - 0 < life
|
|
31171
|
-
* - 0 <= startPeriod <= endPeriod
|
|
31172
|
-
* - 0 <= endPeriod <= life
|
|
31173
|
-
*
|
|
31174
|
-
*/
|
|
31175
|
-
function assertStartAndEndPeriodAreValid(startPeriod, endPeriod, life) {
|
|
31176
|
-
assertLifeStrictlyPositive(life);
|
|
31177
|
-
assert(() => startPeriod >= 0, _t("The start_period (%s) must be greater or equal than 0.", startPeriod.toString()));
|
|
31178
|
-
assert(() => endPeriod >= 0, _t("The end_period (%s) must be greater or equal than 0.", endPeriod.toString()));
|
|
31179
|
-
assert(() => startPeriod <= endPeriod, _t("The start_period (%s) must be smaller or equal to the end_period (%s).", startPeriod.toString(), endPeriod.toString()));
|
|
31180
|
-
assert(() => endPeriod <= life, _t("The end_period (%s) must be smaller or equal to the life (%s).", startPeriod.toString(), life.toString()));
|
|
31181
|
-
}
|
|
31182
|
-
function assertRateGuessStrictlyGreaterThanMinusOne(guess) {
|
|
31183
|
-
assert(() => guess > -1, _t("The rate_guess (%s) must be strictly greater than -1.", guess.toString()));
|
|
31184
|
-
}
|
|
31185
|
-
function assertCashFlowsAndDatesHaveSameDimension(cashFlows, dates) {
|
|
31186
|
-
assert(() => cashFlows.length === dates.length && cashFlows[0].length === dates[0].length, _t("The cashflow_amounts and cashflow_dates ranges must have the same dimensions."));
|
|
31187
|
-
}
|
|
31188
|
-
function assertCashFlowsHavePositiveAndNegativesValues(cashFlow) {
|
|
31189
|
-
assert(() => cashFlow.some((val) => val > 0) && cashFlow.some((val) => val < 0), _t("There must be both positive and negative values in cashflow_amounts."));
|
|
31190
|
-
}
|
|
31191
|
-
function assertEveryDateGreaterThanFirstDateOfCashFlowDates(dates) {
|
|
31192
|
-
assert(() => dates.every((date) => date >= dates[0]), _t("All the dates should be greater or equal to the first date in cashflow_dates (%s).", dates[0].toString()));
|
|
31523
|
+
return endDate.getTime() <= startDatePlusOneYear.getTime();
|
|
31193
31524
|
}
|
|
31194
31525
|
|
|
31195
31526
|
const DEFAULT_DAY_COUNT_CONVENTION = 0;
|
|
@@ -31224,7 +31555,7 @@ function newtonMethod(func, derivFunc, startValue, maxIterations, epsMax = 1e-10
|
|
|
31224
31555
|
do {
|
|
31225
31556
|
y = func(x);
|
|
31226
31557
|
if (isNaN(y)) {
|
|
31227
|
-
assert(
|
|
31558
|
+
assert(count < maxIterations && nanFallback !== undefined, _t("Function [[FUNCTION_NAME]] didn't find any result."));
|
|
31228
31559
|
count++;
|
|
31229
31560
|
x = nanFallback(previousFallback);
|
|
31230
31561
|
previousFallback = x;
|
|
@@ -31234,7 +31565,7 @@ function newtonMethod(func, derivFunc, startValue, maxIterations, epsMax = 1e-10
|
|
|
31234
31565
|
xDelta = Math.abs(newX - x);
|
|
31235
31566
|
x = newX;
|
|
31236
31567
|
yEqual0 = xDelta < epsMax || Math.abs(y) < epsMax;
|
|
31237
|
-
assert(
|
|
31568
|
+
assert(count < maxIterations, _t("Function [[FUNCTION_NAME]] didn't find any result."));
|
|
31238
31569
|
count++;
|
|
31239
31570
|
} while (!yEqual0);
|
|
31240
31571
|
return x;
|
|
@@ -31257,11 +31588,21 @@ const ACCRINTM = {
|
|
|
31257
31588
|
const _redemption = toNumber(redemption, this.locale);
|
|
31258
31589
|
const _rate = toNumber(rate, this.locale);
|
|
31259
31590
|
const _dayCountConvention = Math.trunc(toNumber(dayCountConvention, this.locale));
|
|
31260
|
-
|
|
31261
|
-
|
|
31262
|
-
|
|
31263
|
-
|
|
31264
|
-
|
|
31591
|
+
if (start < 0) {
|
|
31592
|
+
return new EvaluationError(expectIssuePositiveOrZero(start));
|
|
31593
|
+
}
|
|
31594
|
+
if (start >= end) {
|
|
31595
|
+
return new EvaluationError(expectMaturityStrictlyGreaterThanSettlement(start, end));
|
|
31596
|
+
}
|
|
31597
|
+
if (isInvalidDayCountConvention(_dayCountConvention)) {
|
|
31598
|
+
return new EvaluationError(expectDayCountConventionIsValid(_dayCountConvention));
|
|
31599
|
+
}
|
|
31600
|
+
if (_redemption <= 0) {
|
|
31601
|
+
return new EvaluationError(expectRedemptionStrictlyPositive(_redemption));
|
|
31602
|
+
}
|
|
31603
|
+
if (_rate <= 0) {
|
|
31604
|
+
return new EvaluationError(expectRateStrictlyPositive(_rate));
|
|
31605
|
+
}
|
|
31265
31606
|
const yearFrac = getYearFrac(start, end, _dayCountConvention);
|
|
31266
31607
|
return _redemption * _rate * yearFrac;
|
|
31267
31608
|
},
|
|
@@ -31290,14 +31631,30 @@ const AMORLINC = {
|
|
|
31290
31631
|
const _period = toNumber(period, this.locale);
|
|
31291
31632
|
const _rate = toNumber(rate, this.locale);
|
|
31292
31633
|
const _dayCountConvention = Math.trunc(toNumber(dayCountConvention, this.locale));
|
|
31293
|
-
|
|
31294
|
-
|
|
31295
|
-
|
|
31296
|
-
|
|
31297
|
-
|
|
31298
|
-
|
|
31299
|
-
|
|
31300
|
-
|
|
31634
|
+
if (_cost <= 0) {
|
|
31635
|
+
return new EvaluationError(expectCostStrictlyPositive(_cost));
|
|
31636
|
+
}
|
|
31637
|
+
if (_purchaseDate < 0) {
|
|
31638
|
+
return new EvaluationError(expectPurchaseDatePositiveOrZero(_purchaseDate));
|
|
31639
|
+
}
|
|
31640
|
+
if (_salvage < 0) {
|
|
31641
|
+
return new EvaluationError(expectSalvagePositiveOrZero(_salvage));
|
|
31642
|
+
}
|
|
31643
|
+
if (_salvage > _cost) {
|
|
31644
|
+
return new EvaluationError(expectSalvageSmallerOrEqualThanCost(_salvage, _cost));
|
|
31645
|
+
}
|
|
31646
|
+
if (_period < 0) {
|
|
31647
|
+
return new EvaluationError(expectPeriodPositiveOrZero(_period));
|
|
31648
|
+
}
|
|
31649
|
+
if (_rate <= 0) {
|
|
31650
|
+
return new EvaluationError(expectRateStrictlyPositive(_rate));
|
|
31651
|
+
}
|
|
31652
|
+
if (isInvalidDayCountConvention(_dayCountConvention)) {
|
|
31653
|
+
return new EvaluationError(expectDayCountConventionIsValid(_dayCountConvention));
|
|
31654
|
+
}
|
|
31655
|
+
if (_purchaseDate > _firstPeriodEnd) {
|
|
31656
|
+
return new EvaluationError(expectPurchaseDateBeforeFirstPeriodEnd(_purchaseDate, _firstPeriodEnd));
|
|
31657
|
+
}
|
|
31301
31658
|
/**
|
|
31302
31659
|
* https://wiki.documentfoundation.org/Documentation/Calc_Functions/AMORLINC
|
|
31303
31660
|
*
|
|
@@ -31335,14 +31692,20 @@ const COUPDAYS = {
|
|
|
31335
31692
|
const end = Math.trunc(toNumber(maturity, this.locale));
|
|
31336
31693
|
const _frequency = Math.trunc(toNumber(frequency, this.locale));
|
|
31337
31694
|
const _dayCountConvention = Math.trunc(toNumber(dayCountConvention, this.locale));
|
|
31338
|
-
|
|
31339
|
-
|
|
31340
|
-
|
|
31695
|
+
if (start >= end) {
|
|
31696
|
+
return new EvaluationError(expectMaturityStrictlyGreaterThanSettlement(start, end));
|
|
31697
|
+
}
|
|
31698
|
+
if (isInvalidFrequency(_frequency)) {
|
|
31699
|
+
return new EvaluationError(expectCouponFrequencyIsValid(_frequency));
|
|
31700
|
+
}
|
|
31701
|
+
if (isInvalidDayCountConvention(_dayCountConvention)) {
|
|
31702
|
+
return new EvaluationError(expectDayCountConventionIsValid(_dayCountConvention));
|
|
31703
|
+
}
|
|
31341
31704
|
// https://wiki.documentfoundation.org/Documentation/Calc_Functions/COUPDAYS
|
|
31342
31705
|
if (_dayCountConvention === 1) {
|
|
31343
31706
|
const before = COUPPCD.compute.bind(this)(settlement, maturity, frequency, dayCountConvention).value;
|
|
31344
31707
|
const after = COUPNCD.compute.bind(this)(settlement, maturity, frequency, dayCountConvention).value;
|
|
31345
|
-
return after - before;
|
|
31708
|
+
return toNumber(after, this.locale) - toNumber(before, this.locale);
|
|
31346
31709
|
}
|
|
31347
31710
|
const daysInYear = _dayCountConvention === 3 ? 365 : 360;
|
|
31348
31711
|
return daysInYear / _frequency;
|
|
@@ -31361,19 +31724,26 @@ const COUPDAYBS = {
|
|
|
31361
31724
|
const end = Math.trunc(toNumber(maturity, this.locale));
|
|
31362
31725
|
const _frequency = Math.trunc(toNumber(frequency, this.locale));
|
|
31363
31726
|
const _dayCountConvention = Math.trunc(toNumber(dayCountConvention, this.locale));
|
|
31364
|
-
|
|
31365
|
-
|
|
31366
|
-
|
|
31727
|
+
if (start >= end) {
|
|
31728
|
+
return new EvaluationError(expectMaturityStrictlyGreaterThanSettlement(start, end));
|
|
31729
|
+
}
|
|
31730
|
+
if (isInvalidFrequency(_frequency)) {
|
|
31731
|
+
return new EvaluationError(expectCouponFrequencyIsValid(_frequency));
|
|
31732
|
+
}
|
|
31733
|
+
if (isInvalidDayCountConvention(_dayCountConvention)) {
|
|
31734
|
+
return new EvaluationError(expectDayCountConventionIsValid(_dayCountConvention));
|
|
31735
|
+
}
|
|
31367
31736
|
const couponBeforeStart = COUPPCD.compute.bind(this)(settlement, maturity, frequency, dayCountConvention).value;
|
|
31737
|
+
const _couponBeforeStart = toNumber(couponBeforeStart, this.locale);
|
|
31368
31738
|
if ([1, 2, 3].includes(_dayCountConvention)) {
|
|
31369
|
-
return start -
|
|
31739
|
+
return start - _couponBeforeStart;
|
|
31370
31740
|
}
|
|
31371
31741
|
if (_dayCountConvention === 4) {
|
|
31372
|
-
const yearFrac = getYearFrac(
|
|
31742
|
+
const yearFrac = getYearFrac(_couponBeforeStart, start, _dayCountConvention);
|
|
31373
31743
|
return Math.round(yearFrac * 360);
|
|
31374
31744
|
}
|
|
31375
31745
|
const startDate = toJsDate(start, this.locale);
|
|
31376
|
-
const dateCouponBeforeStart = toJsDate(
|
|
31746
|
+
const dateCouponBeforeStart = toJsDate(_couponBeforeStart, this.locale);
|
|
31377
31747
|
const y1 = dateCouponBeforeStart.getFullYear();
|
|
31378
31748
|
const y2 = startDate.getFullYear();
|
|
31379
31749
|
const m1 = dateCouponBeforeStart.getMonth() + 1; // +1 because months in js start at 0 and it's confusing
|
|
@@ -31417,20 +31787,27 @@ const COUPDAYSNC = {
|
|
|
31417
31787
|
const end = Math.trunc(toNumber(maturity, this.locale));
|
|
31418
31788
|
const _frequency = Math.trunc(toNumber(frequency, this.locale));
|
|
31419
31789
|
const _dayCountConvention = Math.trunc(toNumber(dayCountConvention, this.locale));
|
|
31420
|
-
|
|
31421
|
-
|
|
31422
|
-
|
|
31790
|
+
if (start >= end) {
|
|
31791
|
+
return new EvaluationError(expectMaturityStrictlyGreaterThanSettlement(start, end));
|
|
31792
|
+
}
|
|
31793
|
+
if (isInvalidFrequency(_frequency)) {
|
|
31794
|
+
return new EvaluationError(expectCouponFrequencyIsValid(_frequency));
|
|
31795
|
+
}
|
|
31796
|
+
if (isInvalidDayCountConvention(_dayCountConvention)) {
|
|
31797
|
+
return new EvaluationError(expectDayCountConventionIsValid(_dayCountConvention));
|
|
31798
|
+
}
|
|
31423
31799
|
const couponAfterStart = COUPNCD.compute.bind(this)(settlement, maturity, frequency, dayCountConvention).value;
|
|
31800
|
+
const _couponAfterStart = toNumber(couponAfterStart, this.locale);
|
|
31424
31801
|
if ([1, 2, 3].includes(_dayCountConvention)) {
|
|
31425
|
-
return
|
|
31802
|
+
return _couponAfterStart - start;
|
|
31426
31803
|
}
|
|
31427
31804
|
if (_dayCountConvention === 4) {
|
|
31428
|
-
const yearFrac = getYearFrac(start,
|
|
31805
|
+
const yearFrac = getYearFrac(start, _couponAfterStart, _dayCountConvention);
|
|
31429
31806
|
return Math.round(yearFrac * 360);
|
|
31430
31807
|
}
|
|
31431
31808
|
const coupDayBs = COUPDAYBS.compute.bind(this)(settlement, maturity, frequency, dayCountConvention);
|
|
31432
31809
|
const coupDays = COUPDAYS.compute.bind(this)(settlement, maturity, frequency, dayCountConvention);
|
|
31433
|
-
return coupDays - coupDayBs;
|
|
31810
|
+
return toNumber(coupDays, this.locale) - toNumber(coupDayBs, this.locale);
|
|
31434
31811
|
},
|
|
31435
31812
|
isExported: true,
|
|
31436
31813
|
};
|
|
@@ -31446,12 +31823,18 @@ const COUPNCD = {
|
|
|
31446
31823
|
const end = Math.trunc(toNumber(maturity, this.locale));
|
|
31447
31824
|
const _frequency = Math.trunc(toNumber(frequency, this.locale));
|
|
31448
31825
|
const _dayCountConvention = Math.trunc(toNumber(dayCountConvention, this.locale));
|
|
31449
|
-
|
|
31450
|
-
|
|
31451
|
-
|
|
31826
|
+
if (start >= end) {
|
|
31827
|
+
return new EvaluationError(expectMaturityStrictlyGreaterThanSettlement(start, end));
|
|
31828
|
+
}
|
|
31829
|
+
if (isInvalidFrequency(_frequency)) {
|
|
31830
|
+
return new EvaluationError(expectCouponFrequencyIsValid(_frequency));
|
|
31831
|
+
}
|
|
31832
|
+
if (isInvalidDayCountConvention(_dayCountConvention)) {
|
|
31833
|
+
return new EvaluationError(expectDayCountConventionIsValid(_dayCountConvention));
|
|
31834
|
+
}
|
|
31452
31835
|
const monthsPerPeriod = 12 / _frequency;
|
|
31453
31836
|
const coupNum = COUPNUM.compute.bind(this)(settlement, maturity, frequency, dayCountConvention);
|
|
31454
|
-
const date = addMonthsToDate(toJsDate(end, this.locale), -(coupNum - 1) * monthsPerPeriod, true);
|
|
31837
|
+
const date = addMonthsToDate(toJsDate(end, this.locale), -(toNumber(coupNum, this.locale) - 1) * monthsPerPeriod, true);
|
|
31455
31838
|
return {
|
|
31456
31839
|
value: jsDateToRoundNumber(date),
|
|
31457
31840
|
format: this.locale.dateFormat,
|
|
@@ -31471,9 +31854,15 @@ const COUPNUM = {
|
|
|
31471
31854
|
const end = Math.trunc(toNumber(maturity, this.locale));
|
|
31472
31855
|
const _frequency = Math.trunc(toNumber(frequency, this.locale));
|
|
31473
31856
|
const _dayCountConvention = Math.trunc(toNumber(dayCountConvention, this.locale));
|
|
31474
|
-
|
|
31475
|
-
|
|
31476
|
-
|
|
31857
|
+
if (start >= end) {
|
|
31858
|
+
return new EvaluationError(expectMaturityStrictlyGreaterThanSettlement(start, end));
|
|
31859
|
+
}
|
|
31860
|
+
if (isInvalidFrequency(_frequency)) {
|
|
31861
|
+
return new EvaluationError(expectCouponFrequencyIsValid(_frequency));
|
|
31862
|
+
}
|
|
31863
|
+
if (isInvalidDayCountConvention(_dayCountConvention)) {
|
|
31864
|
+
return new EvaluationError(expectDayCountConventionIsValid(_dayCountConvention));
|
|
31865
|
+
}
|
|
31477
31866
|
let num = 1;
|
|
31478
31867
|
let currentDate = end;
|
|
31479
31868
|
const monthsPerPeriod = 12 / _frequency;
|
|
@@ -31497,9 +31886,15 @@ const COUPPCD = {
|
|
|
31497
31886
|
const end = Math.trunc(toNumber(maturity, this.locale));
|
|
31498
31887
|
const _frequency = Math.trunc(toNumber(frequency, this.locale));
|
|
31499
31888
|
const _dayCountConvention = Math.trunc(toNumber(dayCountConvention, this.locale));
|
|
31500
|
-
|
|
31501
|
-
|
|
31502
|
-
|
|
31889
|
+
if (start >= end) {
|
|
31890
|
+
return new EvaluationError(expectMaturityStrictlyGreaterThanSettlement(start, end));
|
|
31891
|
+
}
|
|
31892
|
+
if (isInvalidFrequency(_frequency)) {
|
|
31893
|
+
return new EvaluationError(expectCouponFrequencyIsValid(_frequency));
|
|
31894
|
+
}
|
|
31895
|
+
if (isInvalidDayCountConvention(_dayCountConvention)) {
|
|
31896
|
+
return new EvaluationError(expectDayCountConventionIsValid(_dayCountConvention));
|
|
31897
|
+
}
|
|
31503
31898
|
const monthsPerPeriod = 12 / _frequency;
|
|
31504
31899
|
const coupNum = COUPNUM.compute.bind(this)(settlement, maturity, frequency, dayCountConvention);
|
|
31505
31900
|
const date = addMonthsToDate(toJsDate(end, this.locale), -coupNum * monthsPerPeriod, true);
|
|
@@ -31530,9 +31925,27 @@ const CUMIPMT = {
|
|
|
31530
31925
|
const pv = toNumber(presentValue, this.locale);
|
|
31531
31926
|
const n = toNumber(numberOfPeriods, this.locale);
|
|
31532
31927
|
const type = toBoolean(endOrBeginning) ? 1 : 0;
|
|
31533
|
-
|
|
31534
|
-
|
|
31535
|
-
|
|
31928
|
+
if (n <= 0) {
|
|
31929
|
+
return new EvaluationError(expectNumberOfPeriodsStrictlyPositive(n));
|
|
31930
|
+
}
|
|
31931
|
+
if (first <= 0) {
|
|
31932
|
+
return new EvaluationError(expectFirstPeriodStrictlyPositive(first));
|
|
31933
|
+
}
|
|
31934
|
+
if (last <= 0) {
|
|
31935
|
+
return new EvaluationError(expectLastPeriodStrictlyPositive(last));
|
|
31936
|
+
}
|
|
31937
|
+
if (first > last) {
|
|
31938
|
+
return new EvaluationError(expectFirstPeriodSmallerOrEqualLastPeriod(first, last));
|
|
31939
|
+
}
|
|
31940
|
+
if (last > n) {
|
|
31941
|
+
return new EvaluationError(expectLastPeriodSmallerOrEqualNumberOfPeriods(last, n));
|
|
31942
|
+
}
|
|
31943
|
+
if (r <= 0) {
|
|
31944
|
+
return new EvaluationError(expectRateStrictlyPositive(r));
|
|
31945
|
+
}
|
|
31946
|
+
if (pv <= 0) {
|
|
31947
|
+
return new EvaluationError(expectPresentValueStrictlyPositive(pv));
|
|
31948
|
+
}
|
|
31536
31949
|
let cumSum = 0;
|
|
31537
31950
|
for (let i = first; i <= last; i++) {
|
|
31538
31951
|
cumSum += impt(r, i, n, pv, 0, type);
|
|
@@ -31561,9 +31974,27 @@ const CUMPRINC = {
|
|
|
31561
31974
|
const pv = toNumber(presentValue, this.locale);
|
|
31562
31975
|
const n = toNumber(numberOfPeriods, this.locale);
|
|
31563
31976
|
const type = toBoolean(endOrBeginning) ? 1 : 0;
|
|
31564
|
-
|
|
31565
|
-
|
|
31566
|
-
|
|
31977
|
+
if (n <= 0) {
|
|
31978
|
+
return new EvaluationError(expectNumberOfPeriodsStrictlyPositive(n));
|
|
31979
|
+
}
|
|
31980
|
+
if (first <= 0) {
|
|
31981
|
+
return new EvaluationError(expectFirstPeriodStrictlyPositive(first));
|
|
31982
|
+
}
|
|
31983
|
+
if (last <= 0) {
|
|
31984
|
+
return new EvaluationError(expectLastPeriodStrictlyPositive(last));
|
|
31985
|
+
}
|
|
31986
|
+
if (first > last) {
|
|
31987
|
+
return new EvaluationError(expectFirstPeriodSmallerOrEqualLastPeriod(first, last));
|
|
31988
|
+
}
|
|
31989
|
+
if (last > n) {
|
|
31990
|
+
return new EvaluationError(expectLastPeriodSmallerOrEqualNumberOfPeriods(last, n));
|
|
31991
|
+
}
|
|
31992
|
+
if (r <= 0) {
|
|
31993
|
+
return new EvaluationError(expectRateStrictlyPositive(r));
|
|
31994
|
+
}
|
|
31995
|
+
if (pv <= 0) {
|
|
31996
|
+
return new EvaluationError(expectPresentValueStrictlyPositive(pv));
|
|
31997
|
+
}
|
|
31567
31998
|
let cumSum = 0;
|
|
31568
31999
|
for (let i = first; i <= last; i++) {
|
|
31569
32000
|
cumSum += ppmt(r, i, n, pv, 0, type);
|
|
@@ -31592,12 +32023,24 @@ const DB = {
|
|
|
31592
32023
|
const _period = Math.trunc(toNumber(period, this.locale));
|
|
31593
32024
|
const _month = args.length ? Math.trunc(toNumber(args[0], this.locale)) : 12;
|
|
31594
32025
|
const lifeLimit = _life + (_month === 12 ? 0 : 1);
|
|
31595
|
-
|
|
31596
|
-
|
|
31597
|
-
|
|
31598
|
-
|
|
31599
|
-
|
|
31600
|
-
|
|
32026
|
+
if (_cost < 0) {
|
|
32027
|
+
return new EvaluationError(expectCostPositiveOrZero(_cost));
|
|
32028
|
+
}
|
|
32029
|
+
if (_salvage < 0) {
|
|
32030
|
+
return new EvaluationError(expectSalvagePositiveOrZero(_salvage));
|
|
32031
|
+
}
|
|
32032
|
+
if (_period <= 0) {
|
|
32033
|
+
return new EvaluationError(expectPeriodStrictlyPositive(_period));
|
|
32034
|
+
}
|
|
32035
|
+
if (_life <= 0) {
|
|
32036
|
+
return new EvaluationError(expectLifeStrictlyPositive(_life));
|
|
32037
|
+
}
|
|
32038
|
+
if (1 > _month || _month > 12) {
|
|
32039
|
+
return new EvaluationError(expectMonthBetweenOneAndTwelve(_month));
|
|
32040
|
+
}
|
|
32041
|
+
if (_period > lifeLimit) {
|
|
32042
|
+
return new EvaluationError(expectPeriodLessOrEqualToLifeLimit(_period, lifeLimit));
|
|
32043
|
+
}
|
|
31601
32044
|
const monthPart = _month / 12;
|
|
31602
32045
|
let rate = 1 - Math.pow(_salvage / _cost, 1 / _life);
|
|
31603
32046
|
// round to 3 decimal places
|
|
@@ -31622,27 +32065,7 @@ const DB = {
|
|
|
31622
32065
|
// DDB
|
|
31623
32066
|
// -----------------------------------------------------------------------------
|
|
31624
32067
|
const DEFAULT_DDB_DEPRECIATION_FACTOR = 2;
|
|
31625
|
-
|
|
31626
|
-
assertCostPositiveOrZero(cost);
|
|
31627
|
-
assertSalvagePositiveOrZero(salvage);
|
|
31628
|
-
assertPeriodStrictlyPositive(period);
|
|
31629
|
-
assertLifeStrictlyPositive(life);
|
|
31630
|
-
assertPeriodSmallerOrEqualToLife(period, life);
|
|
31631
|
-
assertDeprecationFactorStrictlyPositive(factor);
|
|
31632
|
-
if (cost === 0 || salvage >= cost)
|
|
31633
|
-
return 0;
|
|
31634
|
-
const deprecFactor = factor / life;
|
|
31635
|
-
if (deprecFactor > 1) {
|
|
31636
|
-
return period === 1 ? cost - salvage : 0;
|
|
31637
|
-
}
|
|
31638
|
-
if (period <= 1) {
|
|
31639
|
-
return cost * deprecFactor;
|
|
31640
|
-
}
|
|
31641
|
-
const previousCost = cost * Math.pow(1 - deprecFactor, period - 1);
|
|
31642
|
-
const nextCost = cost * Math.pow(1 - deprecFactor, period);
|
|
31643
|
-
const deprec = nextCost < salvage ? previousCost - salvage : previousCost - nextCost;
|
|
31644
|
-
return Math.max(deprec, 0);
|
|
31645
|
-
}
|
|
32068
|
+
const DEFAULT_DDB_FORMAT = "#,##0.00";
|
|
31646
32069
|
const DDB = {
|
|
31647
32070
|
description: _t("Depreciation via double-declining balance method."),
|
|
31648
32071
|
args: [
|
|
@@ -31658,9 +32081,43 @@ const DDB = {
|
|
|
31658
32081
|
const _life = toNumber(life, this.locale);
|
|
31659
32082
|
const _period = toNumber(period, this.locale);
|
|
31660
32083
|
const _factor = toNumber(factor, this.locale);
|
|
32084
|
+
if (_cost < 0) {
|
|
32085
|
+
return new EvaluationError(expectCostPositiveOrZero(_cost));
|
|
32086
|
+
}
|
|
32087
|
+
if (_salvage < 0) {
|
|
32088
|
+
return new EvaluationError(expectSalvagePositiveOrZero(_salvage));
|
|
32089
|
+
}
|
|
32090
|
+
if (_period <= 0) {
|
|
32091
|
+
return new EvaluationError(expectPeriodStrictlyPositive(_period));
|
|
32092
|
+
}
|
|
32093
|
+
if (_life <= 0) {
|
|
32094
|
+
return new EvaluationError(expectLifeStrictlyPositive(_life));
|
|
32095
|
+
}
|
|
32096
|
+
if (_period > _life) {
|
|
32097
|
+
return new EvaluationError(expectPeriodSmallerOrEqualToLife(_period, _life));
|
|
32098
|
+
}
|
|
32099
|
+
if (_factor <= 0) {
|
|
32100
|
+
return new EvaluationError(expectDeprecationFactorStrictlyPositive(_factor));
|
|
32101
|
+
}
|
|
32102
|
+
if (_cost === 0 || _salvage >= _cost) {
|
|
32103
|
+
return { value: 0, format: DEFAULT_DDB_FORMAT };
|
|
32104
|
+
}
|
|
32105
|
+
const deprecFactor = _factor / _life;
|
|
32106
|
+
if (deprecFactor > 1) {
|
|
32107
|
+
return { value: _period === 1 ? _cost - _salvage : 0, format: DEFAULT_DDB_FORMAT };
|
|
32108
|
+
}
|
|
32109
|
+
if (_period <= 1) {
|
|
32110
|
+
return {
|
|
32111
|
+
value: _cost * deprecFactor,
|
|
32112
|
+
format: DEFAULT_DDB_FORMAT,
|
|
32113
|
+
};
|
|
32114
|
+
}
|
|
32115
|
+
const previousCost = _cost * Math.pow(1 - deprecFactor, _period - 1);
|
|
32116
|
+
const nextCost = _cost * Math.pow(1 - deprecFactor, _period);
|
|
32117
|
+
const deprec = nextCost < _salvage ? previousCost - _salvage : previousCost - nextCost;
|
|
31661
32118
|
return {
|
|
31662
|
-
value:
|
|
31663
|
-
format:
|
|
32119
|
+
value: Math.max(deprec, 0),
|
|
32120
|
+
format: DEFAULT_DDB_FORMAT,
|
|
31664
32121
|
};
|
|
31665
32122
|
},
|
|
31666
32123
|
isExported: true,
|
|
@@ -31684,10 +32141,18 @@ const DISC = {
|
|
|
31684
32141
|
const _price = toNumber(price, this.locale);
|
|
31685
32142
|
const _redemption = toNumber(redemption, this.locale);
|
|
31686
32143
|
const _dayCountConvention = Math.trunc(toNumber(dayCountConvention, this.locale));
|
|
31687
|
-
|
|
31688
|
-
|
|
31689
|
-
|
|
31690
|
-
|
|
32144
|
+
if (_settlement >= _maturity) {
|
|
32145
|
+
return new EvaluationError(expectMaturityStrictlyGreaterThanSettlement(_settlement, _maturity));
|
|
32146
|
+
}
|
|
32147
|
+
if (isInvalidDayCountConvention(_dayCountConvention)) {
|
|
32148
|
+
return new EvaluationError(expectDayCountConventionIsValid(_dayCountConvention));
|
|
32149
|
+
}
|
|
32150
|
+
if (_price <= 0) {
|
|
32151
|
+
return new EvaluationError(expectPriceStrictlyPositive(_price));
|
|
32152
|
+
}
|
|
32153
|
+
if (_redemption <= 0) {
|
|
32154
|
+
return new EvaluationError(expectRedemptionStrictlyPositive(_redemption));
|
|
32155
|
+
}
|
|
31691
32156
|
/**
|
|
31692
32157
|
* https://support.microsoft.com/en-us/office/disc-function-71fce9f3-3f05-4acf-a5a3-eac6ef4daa53
|
|
31693
32158
|
*
|
|
@@ -31715,7 +32180,9 @@ const DOLLARDE = {
|
|
|
31715
32180
|
compute: function (fractionalPrice, unit) {
|
|
31716
32181
|
const price = toNumber(fractionalPrice, this.locale);
|
|
31717
32182
|
const _unit = Math.trunc(toNumber(unit, this.locale));
|
|
31718
|
-
|
|
32183
|
+
if (_unit <= 0) {
|
|
32184
|
+
return new EvaluationError(expectUnitStrictlyPositive(_unit));
|
|
32185
|
+
}
|
|
31719
32186
|
const truncatedPrice = Math.trunc(price);
|
|
31720
32187
|
const priceFractionalPart = price - truncatedPrice;
|
|
31721
32188
|
const frac = 10 ** Math.ceil(Math.log10(_unit)) / _unit;
|
|
@@ -31735,7 +32202,9 @@ const DOLLARFR = {
|
|
|
31735
32202
|
compute: function (decimalPrice, unit) {
|
|
31736
32203
|
const price = toNumber(decimalPrice, this.locale);
|
|
31737
32204
|
const _unit = Math.trunc(toNumber(unit, this.locale));
|
|
31738
|
-
|
|
32205
|
+
if (_unit <= 0) {
|
|
32206
|
+
return new EvaluationError(expectUnitStrictlyPositive(_unit));
|
|
32207
|
+
}
|
|
31739
32208
|
const truncatedPrice = Math.trunc(price);
|
|
31740
32209
|
const priceFractionalPart = price - truncatedPrice;
|
|
31741
32210
|
const frac = _unit / 10 ** Math.ceil(Math.log10(_unit));
|
|
@@ -31763,11 +32232,21 @@ const DURATION = {
|
|
|
31763
32232
|
const _yield = toNumber(securityYield, this.locale);
|
|
31764
32233
|
const _frequency = Math.trunc(toNumber(frequency, this.locale));
|
|
31765
32234
|
const _dayCountConvention = Math.trunc(toNumber(dayCountConvention, this.locale));
|
|
31766
|
-
|
|
31767
|
-
|
|
31768
|
-
|
|
31769
|
-
|
|
31770
|
-
|
|
32235
|
+
if (start >= end) {
|
|
32236
|
+
return new EvaluationError(expectMaturityStrictlyGreaterThanSettlement(start, end));
|
|
32237
|
+
}
|
|
32238
|
+
if (isInvalidFrequency(_frequency)) {
|
|
32239
|
+
return new EvaluationError(expectCouponFrequencyIsValid(_frequency));
|
|
32240
|
+
}
|
|
32241
|
+
if (isInvalidDayCountConvention(_dayCountConvention)) {
|
|
32242
|
+
return new EvaluationError(expectDayCountConventionIsValid(_dayCountConvention));
|
|
32243
|
+
}
|
|
32244
|
+
if (_rate < 0) {
|
|
32245
|
+
return new EvaluationError(expectRatePositiveOrZero(_rate));
|
|
32246
|
+
}
|
|
32247
|
+
if (_yield < 0) {
|
|
32248
|
+
return new EvaluationError(expectYieldPositiveOrZero(_yield));
|
|
32249
|
+
}
|
|
31771
32250
|
const years = getYearFrac(start, end, _dayCountConvention);
|
|
31772
32251
|
const timeFirstYear = years - Math.trunc(years) || 1 / _frequency;
|
|
31773
32252
|
const nbrCoupons = Math.ceil(years * _frequency);
|
|
@@ -31799,8 +32278,12 @@ const EFFECT = {
|
|
|
31799
32278
|
compute: function (nominal_rate, periods_per_year) {
|
|
31800
32279
|
const nominal = toNumber(nominal_rate, this.locale);
|
|
31801
32280
|
const periods = Math.trunc(toNumber(periods_per_year, this.locale));
|
|
31802
|
-
|
|
31803
|
-
|
|
32281
|
+
if (nominal <= 0) {
|
|
32282
|
+
return new EvaluationError(expectNominalRateStrictlyPositive(nominal));
|
|
32283
|
+
}
|
|
32284
|
+
if (periods <= 0) {
|
|
32285
|
+
return new EvaluationError(expectPeriodsByYearStrictlyPositive(periods));
|
|
32286
|
+
}
|
|
31804
32287
|
// https://en.wikipedia.org/wiki/Nominal_interest_rate#Nominal_versus_effective_interest_rate
|
|
31805
32288
|
return Math.pow(1 + nominal / periods, periods) - 1;
|
|
31806
32289
|
},
|
|
@@ -31874,10 +32357,18 @@ const INTRATE = {
|
|
|
31874
32357
|
const _redemption = toNumber(redemption, this.locale);
|
|
31875
32358
|
const _investment = toNumber(investment, this.locale);
|
|
31876
32359
|
const _dayCountConvention = Math.trunc(toNumber(dayCountConvention, this.locale));
|
|
31877
|
-
|
|
31878
|
-
|
|
31879
|
-
|
|
31880
|
-
|
|
32360
|
+
if (_settlement >= _maturity) {
|
|
32361
|
+
return new EvaluationError(expectMaturityStrictlyGreaterThanSettlement(_settlement, _maturity));
|
|
32362
|
+
}
|
|
32363
|
+
if (_investment <= 0) {
|
|
32364
|
+
return new EvaluationError(expectInvestmentStrictlyPositive(_investment));
|
|
32365
|
+
}
|
|
32366
|
+
if (_redemption <= 0) {
|
|
32367
|
+
return new EvaluationError(expectRedemptionStrictlyPositive(_redemption));
|
|
32368
|
+
}
|
|
32369
|
+
if (isInvalidDayCountConvention(_dayCountConvention)) {
|
|
32370
|
+
return new EvaluationError(expectDayCountConventionIsValid(_dayCountConvention));
|
|
32371
|
+
}
|
|
31881
32372
|
/**
|
|
31882
32373
|
* https://wiki.documentfoundation.org/Documentation/Calc_Functions/INTRATE
|
|
31883
32374
|
*
|
|
@@ -31932,7 +32423,9 @@ const IRR = {
|
|
|
31932
32423
|
],
|
|
31933
32424
|
compute: function (cashFlowAmounts, rateGuess = { value: DEFAULT_RATE_GUESS }) {
|
|
31934
32425
|
const _rateGuess = toNumber(rateGuess, this.locale);
|
|
31935
|
-
|
|
32426
|
+
if (_rateGuess <= -1) {
|
|
32427
|
+
return new EvaluationError(expectRateGuessStrictlyGreaterThanMinusOne(_rateGuess));
|
|
32428
|
+
}
|
|
31936
32429
|
// check that values contains at least one positive value and one negative value
|
|
31937
32430
|
// and extract number present in the cashFlowAmount argument
|
|
31938
32431
|
let positive = false;
|
|
@@ -31945,7 +32438,9 @@ const IRR = {
|
|
|
31945
32438
|
negative = true;
|
|
31946
32439
|
amounts.push(amount);
|
|
31947
32440
|
}, this.locale);
|
|
31948
|
-
|
|
32441
|
+
if (!positive || !negative) {
|
|
32442
|
+
return new EvaluationError(expectCashFlowsHavePositiveAndNegativesValues);
|
|
32443
|
+
}
|
|
31949
32444
|
const firstAmount = amounts.shift();
|
|
31950
32445
|
// The result of IRR is the rate at which the NPV() function will return zero with the given values.
|
|
31951
32446
|
// This algorithm uses the Newton's method on the NPV function to determine the result
|
|
@@ -31996,7 +32491,9 @@ const ISPMT = {
|
|
|
31996
32491
|
const period = toNumber(currentPeriod, this.locale);
|
|
31997
32492
|
const nOfPeriods = toNumber(numberOfPeriods, this.locale);
|
|
31998
32493
|
const investment = toNumber(presentValue, this.locale);
|
|
31999
|
-
|
|
32494
|
+
if (nOfPeriods === 0) {
|
|
32495
|
+
return new EvaluationError(expectNumberOfPeriodDifferentFromZero(nOfPeriods));
|
|
32496
|
+
}
|
|
32000
32497
|
const currentInvestment = investment - investment * (period / nOfPeriods);
|
|
32001
32498
|
return -1 * currentInvestment * interestRate;
|
|
32002
32499
|
},
|
|
@@ -32019,7 +32516,7 @@ const MDURATION = {
|
|
|
32019
32516
|
const duration = DURATION.compute.bind(this)(settlement, maturity, rate, securityYield, frequency, dayCountConvention);
|
|
32020
32517
|
const y = toNumber(securityYield, this.locale);
|
|
32021
32518
|
const k = Math.trunc(toNumber(frequency, this.locale));
|
|
32022
|
-
return duration / (1 + y / k);
|
|
32519
|
+
return toNumber(duration, this.locale) / (1 + y / k);
|
|
32023
32520
|
},
|
|
32024
32521
|
isExported: true,
|
|
32025
32522
|
};
|
|
@@ -32069,7 +32566,9 @@ const MIRR = {
|
|
|
32069
32566
|
pv += amount / (fRate + 1) ** i;
|
|
32070
32567
|
}
|
|
32071
32568
|
}
|
|
32072
|
-
|
|
32569
|
+
if (pv === 0 || fv === 0) {
|
|
32570
|
+
return new EvaluationError(expectCashFlowsHavePositiveAndNegativesValues);
|
|
32571
|
+
}
|
|
32073
32572
|
const exponent = 1 / (n - 1);
|
|
32074
32573
|
return (-fv / pv) ** exponent - 1;
|
|
32075
32574
|
},
|
|
@@ -32087,8 +32586,12 @@ const NOMINAL = {
|
|
|
32087
32586
|
compute: function (effective_rate, periods_per_year) {
|
|
32088
32587
|
const effective = toNumber(effective_rate, this.locale);
|
|
32089
32588
|
const periods = Math.trunc(toNumber(periods_per_year, this.locale));
|
|
32090
|
-
|
|
32091
|
-
|
|
32589
|
+
if (effective <= 0) {
|
|
32590
|
+
return new EvaluationError(expectEffectiveRateStrictlyPositive(effective));
|
|
32591
|
+
}
|
|
32592
|
+
if (periods <= 0) {
|
|
32593
|
+
return new EvaluationError(expectPeriodsByYearStrictlyPositive(periods));
|
|
32594
|
+
}
|
|
32092
32595
|
// https://en.wikipedia.org/wiki/Nominal_interest_rate#Nominal_versus_effective_interest_rate
|
|
32093
32596
|
return (Math.pow(effective + 1, 1 / periods) - 1) * periods;
|
|
32094
32597
|
},
|
|
@@ -32156,7 +32659,9 @@ const NPV = {
|
|
|
32156
32659
|
// to do: replace by dollar format
|
|
32157
32660
|
compute: function (discount, ...values) {
|
|
32158
32661
|
const _discount = toNumber(discount, this.locale);
|
|
32159
|
-
|
|
32662
|
+
if (_discount === -1) {
|
|
32663
|
+
return new EvaluationError(expectDiscountDifferentFromMinusOne(_discount));
|
|
32664
|
+
}
|
|
32160
32665
|
return {
|
|
32161
32666
|
value: npvResult(_discount, 0, values, this.locale),
|
|
32162
32667
|
format: "#,##0.00",
|
|
@@ -32178,9 +32683,15 @@ const PDURATION = {
|
|
|
32178
32683
|
const _rate = toNumber(rate, this.locale);
|
|
32179
32684
|
const _presentValue = toNumber(presentValue, this.locale);
|
|
32180
32685
|
const _futureValue = toNumber(futureValue, this.locale);
|
|
32181
|
-
|
|
32182
|
-
|
|
32183
|
-
|
|
32686
|
+
if (_rate <= 0) {
|
|
32687
|
+
return new EvaluationError(expectRateStrictlyPositive(_rate));
|
|
32688
|
+
}
|
|
32689
|
+
if (_presentValue <= 0) {
|
|
32690
|
+
return new EvaluationError(expectPresentValueStrictlyPositive(_presentValue));
|
|
32691
|
+
}
|
|
32692
|
+
if (_futureValue <= 0) {
|
|
32693
|
+
return new EvaluationError(expectFutureValueStrictlyPositive(_futureValue));
|
|
32694
|
+
}
|
|
32184
32695
|
return (Math.log(_futureValue) - Math.log(_presentValue)) / Math.log(1 + _rate);
|
|
32185
32696
|
},
|
|
32186
32697
|
isExported: true,
|
|
@@ -32189,7 +32700,9 @@ const PDURATION = {
|
|
|
32189
32700
|
// PMT
|
|
32190
32701
|
// -----------------------------------------------------------------------------
|
|
32191
32702
|
function pmt(r, n, pv, fv, t) {
|
|
32192
|
-
|
|
32703
|
+
if (n <= 0) {
|
|
32704
|
+
throw new EvaluationError(expectNumberOfPeriodsStrictlyPositive(n));
|
|
32705
|
+
}
|
|
32193
32706
|
/**
|
|
32194
32707
|
* https://wiki.documentfoundation.org/Documentation/Calc_Functions/PMT
|
|
32195
32708
|
*
|
|
@@ -32227,8 +32740,12 @@ const PMT = {
|
|
|
32227
32740
|
// PPMT
|
|
32228
32741
|
// -----------------------------------------------------------------------------
|
|
32229
32742
|
function ppmt(r, per, n, pValue, fValue, t) {
|
|
32230
|
-
|
|
32231
|
-
|
|
32743
|
+
if (n <= 0) {
|
|
32744
|
+
throw new EvaluationError(expectNumberOfPeriodsStrictlyPositive(n));
|
|
32745
|
+
}
|
|
32746
|
+
if (per <= 0 || per > n) {
|
|
32747
|
+
throw new EvaluationError(expectPeriodBetweenOneAndNumberOfPeriods(n));
|
|
32748
|
+
}
|
|
32232
32749
|
const payment = pmt(r, n, pValue, fValue, t);
|
|
32233
32750
|
if (t === 1 && per === 1)
|
|
32234
32751
|
return payment;
|
|
@@ -32316,12 +32833,24 @@ const PRICE = {
|
|
|
32316
32833
|
const _redemption = toNumber(redemption, this.locale);
|
|
32317
32834
|
const _frequency = Math.trunc(toNumber(frequency, this.locale));
|
|
32318
32835
|
const _dayCountConvention = Math.trunc(toNumber(dayCountConvention, this.locale));
|
|
32319
|
-
|
|
32320
|
-
|
|
32321
|
-
|
|
32322
|
-
|
|
32323
|
-
|
|
32324
|
-
|
|
32836
|
+
if (_settlement >= _maturity) {
|
|
32837
|
+
return new EvaluationError(expectMaturityStrictlyGreaterThanSettlement(_settlement, _maturity));
|
|
32838
|
+
}
|
|
32839
|
+
if (isInvalidFrequency(_frequency)) {
|
|
32840
|
+
return new EvaluationError(expectCouponFrequencyIsValid(_frequency));
|
|
32841
|
+
}
|
|
32842
|
+
if (isInvalidDayCountConvention(_dayCountConvention)) {
|
|
32843
|
+
return new EvaluationError(expectDayCountConventionIsValid(_dayCountConvention));
|
|
32844
|
+
}
|
|
32845
|
+
if (_rate < 0) {
|
|
32846
|
+
return new EvaluationError(expectRatePositiveOrZero(_rate));
|
|
32847
|
+
}
|
|
32848
|
+
if (_yield < 0) {
|
|
32849
|
+
return new EvaluationError(expectYieldPositiveOrZero(_yield));
|
|
32850
|
+
}
|
|
32851
|
+
if (_redemption <= 0) {
|
|
32852
|
+
return new EvaluationError(expectRedemptionStrictlyPositive(_redemption));
|
|
32853
|
+
}
|
|
32325
32854
|
const years = getYearFrac(_settlement, _maturity, _dayCountConvention);
|
|
32326
32855
|
const nbrRealCoupons = years * _frequency;
|
|
32327
32856
|
const nbrFullCoupons = Math.ceil(nbrRealCoupons);
|
|
@@ -32361,10 +32890,18 @@ const PRICEDISC = {
|
|
|
32361
32890
|
const _discount = toNumber(discount, this.locale);
|
|
32362
32891
|
const _redemption = toNumber(redemption, this.locale);
|
|
32363
32892
|
const _dayCountConvention = Math.trunc(toNumber(dayCountConvention, this.locale));
|
|
32364
|
-
|
|
32365
|
-
|
|
32366
|
-
|
|
32367
|
-
|
|
32893
|
+
if (_settlement >= _maturity) {
|
|
32894
|
+
return new EvaluationError(expectMaturityStrictlyGreaterThanSettlement(_settlement, _maturity));
|
|
32895
|
+
}
|
|
32896
|
+
if (isInvalidDayCountConvention(_dayCountConvention)) {
|
|
32897
|
+
return new EvaluationError(expectDayCountConventionIsValid(_dayCountConvention));
|
|
32898
|
+
}
|
|
32899
|
+
if (_discount <= 0) {
|
|
32900
|
+
return new EvaluationError(expectDiscountStrictlyPositive(_discount));
|
|
32901
|
+
}
|
|
32902
|
+
if (_redemption <= 0) {
|
|
32903
|
+
return new EvaluationError(expectRedemptionStrictlyPositive(_redemption));
|
|
32904
|
+
}
|
|
32368
32905
|
/**
|
|
32369
32906
|
* https://support.microsoft.com/en-us/office/pricedisc-function-d06ad7c1-380e-4be7-9fd9-75e3079acfd3
|
|
32370
32907
|
*
|
|
@@ -32399,11 +32936,21 @@ const PRICEMAT = {
|
|
|
32399
32936
|
const _rate = toNumber(rate, this.locale);
|
|
32400
32937
|
const _yield = toNumber(securityYield, this.locale);
|
|
32401
32938
|
const _dayCount = Math.trunc(toNumber(dayCountConvention, this.locale));
|
|
32402
|
-
|
|
32403
|
-
|
|
32404
|
-
|
|
32405
|
-
|
|
32406
|
-
|
|
32939
|
+
if (_settlement <= _issue) {
|
|
32940
|
+
return new EvaluationError(expectSettlementStrictlyGreaterThanIssue(_settlement, _issue));
|
|
32941
|
+
}
|
|
32942
|
+
if (_settlement >= _maturity) {
|
|
32943
|
+
return new EvaluationError(expectMaturityStrictlyGreaterThanSettlement(_settlement, _maturity));
|
|
32944
|
+
}
|
|
32945
|
+
if (isInvalidDayCountConvention(_dayCount)) {
|
|
32946
|
+
return new EvaluationError(expectDayCountConventionIsValid(_dayCount));
|
|
32947
|
+
}
|
|
32948
|
+
if (_rate < 0) {
|
|
32949
|
+
return new EvaluationError(expectRatePositiveOrZero(_rate));
|
|
32950
|
+
}
|
|
32951
|
+
if (_yield < 0) {
|
|
32952
|
+
return new EvaluationError(expectYieldPositiveOrZero(_yield));
|
|
32953
|
+
}
|
|
32407
32954
|
/**
|
|
32408
32955
|
* https://support.microsoft.com/en-us/office/pricemat-function-52c3b4da-bc7e-476a-989f-a95f675cae77
|
|
32409
32956
|
*
|
|
@@ -32461,9 +33008,15 @@ const RATE = {
|
|
|
32461
33008
|
const guess = toNumber(rateGuess, this.locale) || RATE_GUESS_DEFAULT;
|
|
32462
33009
|
let fv = toNumber(futureValue, this.locale);
|
|
32463
33010
|
let pv = toNumber(presentValue, this.locale);
|
|
32464
|
-
|
|
32465
|
-
|
|
32466
|
-
|
|
33011
|
+
if (n <= 0) {
|
|
33012
|
+
return new EvaluationError(expectNumberOfPeriodsStrictlyPositive(n));
|
|
33013
|
+
}
|
|
33014
|
+
if (!havePositiveAndNegativeValues([payment, pv, fv])) {
|
|
33015
|
+
return new EvaluationError(_t("There must be both positive and negative values in [payment_amount, present_value, future_value]."));
|
|
33016
|
+
}
|
|
33017
|
+
if (guess <= -1) {
|
|
33018
|
+
return new EvaluationError(expectRateGuessStrictlyGreaterThanMinusOne(guess));
|
|
33019
|
+
}
|
|
32467
33020
|
fv -= payment * type;
|
|
32468
33021
|
pv += payment * type;
|
|
32469
33022
|
// https://github.com/apache/openoffice/blob/trunk/main/sc/source/core/tool/interpr2.cxx
|
|
@@ -32506,10 +33059,18 @@ const RECEIVED = {
|
|
|
32506
33059
|
const _investment = toNumber(investment, this.locale);
|
|
32507
33060
|
const _discount = toNumber(discount, this.locale);
|
|
32508
33061
|
const _dayCountConvention = Math.trunc(toNumber(dayCountConvention, this.locale));
|
|
32509
|
-
|
|
32510
|
-
|
|
32511
|
-
|
|
32512
|
-
|
|
33062
|
+
if (_settlement >= _maturity) {
|
|
33063
|
+
return new EvaluationError(expectMaturityStrictlyGreaterThanSettlement(_settlement, _maturity));
|
|
33064
|
+
}
|
|
33065
|
+
if (isInvalidDayCountConvention(_dayCountConvention)) {
|
|
33066
|
+
return new EvaluationError(expectDayCountConventionIsValid(_dayCountConvention));
|
|
33067
|
+
}
|
|
33068
|
+
if (_investment <= 0) {
|
|
33069
|
+
return new EvaluationError(expectInvestmentStrictlyPositive(_investment));
|
|
33070
|
+
}
|
|
33071
|
+
if (_discount <= 0) {
|
|
33072
|
+
return new EvaluationError(expectDiscountStrictlyPositive(_discount));
|
|
33073
|
+
}
|
|
32513
33074
|
/**
|
|
32514
33075
|
* https://support.microsoft.com/en-us/office/received-function-7a3f8b93-6611-4f81-8576-828312c9b5e5
|
|
32515
33076
|
*
|
|
@@ -32540,7 +33101,9 @@ const RRI = {
|
|
|
32540
33101
|
const n = toNumber(numberOfPeriods, this.locale);
|
|
32541
33102
|
const pv = toNumber(presentValue, this.locale);
|
|
32542
33103
|
const fv = toNumber(futureValue, this.locale);
|
|
32543
|
-
|
|
33104
|
+
if (n <= 0) {
|
|
33105
|
+
return new EvaluationError(expectNumberOfPeriodsStrictlyPositive(n));
|
|
33106
|
+
}
|
|
32544
33107
|
/**
|
|
32545
33108
|
* https://support.microsoft.com/en-us/office/rri-function-6f5822d8-7ef1-4233-944c-79e8172930f4
|
|
32546
33109
|
*
|
|
@@ -32589,9 +33152,15 @@ const SYD = {
|
|
|
32589
33152
|
const _salvage = toNumber(salvage, this.locale);
|
|
32590
33153
|
const _life = toNumber(life, this.locale);
|
|
32591
33154
|
const _period = toNumber(period, this.locale);
|
|
32592
|
-
|
|
32593
|
-
|
|
32594
|
-
|
|
33155
|
+
if (_period <= 0) {
|
|
33156
|
+
return new EvaluationError(expectPeriodStrictlyPositive(_period));
|
|
33157
|
+
}
|
|
33158
|
+
if (_life <= 0) {
|
|
33159
|
+
return new EvaluationError(expectLifeStrictlyPositive(_life));
|
|
33160
|
+
}
|
|
33161
|
+
if (_period > _life) {
|
|
33162
|
+
return new EvaluationError(expectPeriodSmallerOrEqualToLife(_period, _life));
|
|
33163
|
+
}
|
|
32595
33164
|
/**
|
|
32596
33165
|
* This deprecation method use the sum of digits of the periods of the life as the deprecation factor.
|
|
32597
33166
|
* For example for a life = 5, we have a deprecation factor or 1 + 2 + 3 + 4 + 5 = 15 = life * (life + 1) / 2 = F.
|
|
@@ -32636,10 +33205,18 @@ const TBILLPRICE = {
|
|
|
32636
33205
|
const start = Math.trunc(toNumber(settlement, this.locale));
|
|
32637
33206
|
const end = Math.trunc(toNumber(maturity, this.locale));
|
|
32638
33207
|
const disc = toNumber(discount, this.locale);
|
|
32639
|
-
|
|
32640
|
-
|
|
32641
|
-
|
|
32642
|
-
|
|
33208
|
+
if (start >= end) {
|
|
33209
|
+
return new EvaluationError(expectMaturityStrictlyGreaterThanSettlement(start, end));
|
|
33210
|
+
}
|
|
33211
|
+
if (!isSettlementLessThanOneYearBeforeMaturity(start, end, this.locale)) {
|
|
33212
|
+
return new EvaluationError(expectSettlementLessThanOneYearBeforeMaturity(start, end));
|
|
33213
|
+
}
|
|
33214
|
+
if (disc <= 0) {
|
|
33215
|
+
return new EvaluationError(expectDiscountStrictlyPositive(disc));
|
|
33216
|
+
}
|
|
33217
|
+
if (disc >= 1) {
|
|
33218
|
+
return new EvaluationError(expectDiscountStrictlySmallerThanOne(disc));
|
|
33219
|
+
}
|
|
32643
33220
|
return tBillPrice(start, end, disc);
|
|
32644
33221
|
},
|
|
32645
33222
|
isExported: true,
|
|
@@ -32658,10 +33235,18 @@ const TBILLEQ = {
|
|
|
32658
33235
|
const start = Math.trunc(toNumber(settlement, this.locale));
|
|
32659
33236
|
const end = Math.trunc(toNumber(maturity, this.locale));
|
|
32660
33237
|
const disc = toNumber(discount, this.locale);
|
|
32661
|
-
|
|
32662
|
-
|
|
32663
|
-
|
|
32664
|
-
|
|
33238
|
+
if (start >= end) {
|
|
33239
|
+
return new EvaluationError(expectMaturityStrictlyGreaterThanSettlement(start, end));
|
|
33240
|
+
}
|
|
33241
|
+
if (!isSettlementLessThanOneYearBeforeMaturity(start, end, this.locale)) {
|
|
33242
|
+
return new EvaluationError(expectSettlementLessThanOneYearBeforeMaturity(start, end));
|
|
33243
|
+
}
|
|
33244
|
+
if (disc <= 0) {
|
|
33245
|
+
return new EvaluationError(expectDiscountStrictlyPositive(disc));
|
|
33246
|
+
}
|
|
33247
|
+
if (disc >= 1) {
|
|
33248
|
+
return new EvaluationError(expectDiscountStrictlySmallerThanOne(disc));
|
|
33249
|
+
}
|
|
32665
33250
|
/**
|
|
32666
33251
|
* https://support.microsoft.com/en-us/office/tbilleq-function-2ab72d90-9b4d-4efe-9fc2-0f81f2c19c8c
|
|
32667
33252
|
*
|
|
@@ -32715,9 +33300,15 @@ const TBILLYIELD = {
|
|
|
32715
33300
|
const start = Math.trunc(toNumber(settlement, this.locale));
|
|
32716
33301
|
const end = Math.trunc(toNumber(maturity, this.locale));
|
|
32717
33302
|
const p = toNumber(price, this.locale);
|
|
32718
|
-
|
|
32719
|
-
|
|
32720
|
-
|
|
33303
|
+
if (start >= end) {
|
|
33304
|
+
return new EvaluationError(expectMaturityStrictlyGreaterThanSettlement(start, end));
|
|
33305
|
+
}
|
|
33306
|
+
if (!isSettlementLessThanOneYearBeforeMaturity(start, end, this.locale)) {
|
|
33307
|
+
return new EvaluationError(expectSettlementLessThanOneYearBeforeMaturity(start, end));
|
|
33308
|
+
}
|
|
33309
|
+
if (p <= 0) {
|
|
33310
|
+
return new EvaluationError(expectPriceStrictlyPositive(p));
|
|
33311
|
+
}
|
|
32721
33312
|
/**
|
|
32722
33313
|
* https://support.microsoft.com/en-us/office/tbillyield-function-6d381232-f4b0-4cd5-8e97-45b9c03468ba
|
|
32723
33314
|
*
|
|
@@ -32763,10 +33354,30 @@ const VDB = {
|
|
|
32763
33354
|
const _endPeriod = Math.trunc(toNumber(endPeriod, this.locale));
|
|
32764
33355
|
const _factor = toNumber(factor, this.locale);
|
|
32765
33356
|
const _noSwitch = toBoolean(noSwitch);
|
|
32766
|
-
|
|
32767
|
-
|
|
32768
|
-
|
|
32769
|
-
|
|
33357
|
+
if (_cost < 0) {
|
|
33358
|
+
return new EvaluationError(expectCostPositiveOrZero(_cost));
|
|
33359
|
+
}
|
|
33360
|
+
if (_salvage < 0) {
|
|
33361
|
+
return new EvaluationError(expectSalvagePositiveOrZero(_salvage));
|
|
33362
|
+
}
|
|
33363
|
+
if (_life <= 0) {
|
|
33364
|
+
return new EvaluationError(expectLifeStrictlyPositive(_life));
|
|
33365
|
+
}
|
|
33366
|
+
if (_startPeriod < 0) {
|
|
33367
|
+
return new EvaluationError(expectStartPeriodPositiveOrZero(_startPeriod));
|
|
33368
|
+
}
|
|
33369
|
+
if (_endPeriod < 0) {
|
|
33370
|
+
return new EvaluationError(expectEndPeriodPositiveOrZero(_endPeriod));
|
|
33371
|
+
}
|
|
33372
|
+
if (_startPeriod > _endPeriod) {
|
|
33373
|
+
return new EvaluationError(expectStartPeriodSmallerOrEqualEndPeriod(_startPeriod, _endPeriod));
|
|
33374
|
+
}
|
|
33375
|
+
if (_endPeriod > _life) {
|
|
33376
|
+
return new EvaluationError(expectEndPeriodSmallerOrEqualToLife(_endPeriod, _life));
|
|
33377
|
+
}
|
|
33378
|
+
if (_factor <= 0) {
|
|
33379
|
+
return new EvaluationError(expectDeprecationFactorStrictlyPositive(_factor));
|
|
33380
|
+
}
|
|
32770
33381
|
if (_cost === 0)
|
|
32771
33382
|
return 0;
|
|
32772
33383
|
if (_salvage >= _cost) {
|
|
@@ -32816,12 +33427,20 @@ const XIRR = {
|
|
|
32816
33427
|
],
|
|
32817
33428
|
compute: function (cashflowAmounts, cashflowDates, rateGuess = { value: RATE_GUESS_DEFAULT }) {
|
|
32818
33429
|
const guess = toNumber(rateGuess, this.locale);
|
|
33430
|
+
if (!areSameDimensions(cashflowAmounts, cashflowDates)) {
|
|
33431
|
+
return new EvaluationError(expectCashFlowsAndDatesHaveSameDimension);
|
|
33432
|
+
}
|
|
32819
33433
|
const _cashFlows = cashflowAmounts.flat().map((val) => toNumber(val, this.locale));
|
|
32820
33434
|
const _dates = cashflowDates.flat().map((val) => toNumber(val, this.locale));
|
|
32821
|
-
|
|
32822
|
-
|
|
32823
|
-
|
|
32824
|
-
|
|
33435
|
+
if (!havePositiveAndNegativeValues(_cashFlows)) {
|
|
33436
|
+
return new EvaluationError(expectCashFlowsHavePositiveAndNegativesValues);
|
|
33437
|
+
}
|
|
33438
|
+
if (_dates.some((date) => date < _dates[0])) {
|
|
33439
|
+
return new EvaluationError(expectEveryDateGreaterThanFirstDateOfCashFlowDates(_dates[0]));
|
|
33440
|
+
}
|
|
33441
|
+
if (guess <= -1) {
|
|
33442
|
+
return new EvaluationError(expectRateGuessStrictlyGreaterThanMinusOne(guess));
|
|
33443
|
+
}
|
|
32825
33444
|
const map = new Map();
|
|
32826
33445
|
for (const i of range(0, _dates.length)) {
|
|
32827
33446
|
const date = _dates[i];
|
|
@@ -32886,20 +33505,21 @@ const XNPV = {
|
|
|
32886
33505
|
],
|
|
32887
33506
|
compute: function (discount, cashflowAmounts, cashflowDates) {
|
|
32888
33507
|
const rate = toNumber(discount, this.locale);
|
|
32889
|
-
|
|
32890
|
-
|
|
32891
|
-
: [strictToNumber(cashflowAmounts, this.locale)];
|
|
32892
|
-
const _dates = isMatrix(cashflowDates)
|
|
32893
|
-
? cashflowDates.flat().map((data) => strictToNumber(data, this.locale))
|
|
32894
|
-
: [strictToNumber(cashflowDates, this.locale)];
|
|
32895
|
-
if (isMatrix(cashflowDates) && isMatrix(cashflowAmounts)) {
|
|
32896
|
-
assertCashFlowsAndDatesHaveSameDimension(cashflowAmounts, cashflowDates);
|
|
33508
|
+
if (!areSameDimensions(cashflowAmounts, cashflowDates)) {
|
|
33509
|
+
return new EvaluationError(expectCashFlowsAndDatesHaveSameDimension);
|
|
32897
33510
|
}
|
|
32898
|
-
|
|
32899
|
-
|
|
33511
|
+
const _cashFlows = toMatrix(cashflowAmounts)
|
|
33512
|
+
.flat()
|
|
33513
|
+
.map((val) => strictToNumber(val, this.locale));
|
|
33514
|
+
const _dates = toMatrix(cashflowDates)
|
|
33515
|
+
.flat()
|
|
33516
|
+
.map((val) => strictToNumber(val, this.locale));
|
|
33517
|
+
if (_dates.some((date) => date < _dates[0])) {
|
|
33518
|
+
return new EvaluationError(expectEveryDateGreaterThanFirstDateOfCashFlowDates(_dates[0]));
|
|
33519
|
+
}
|
|
33520
|
+
if (rate <= 0) {
|
|
33521
|
+
return new EvaluationError(expectRateStrictlyPositive(rate));
|
|
32900
33522
|
}
|
|
32901
|
-
assertEveryDateGreaterThanFirstDateOfCashFlowDates(_dates);
|
|
32902
|
-
assertRateStrictlyPositive(rate);
|
|
32903
33523
|
if (_cashFlows.length === 1)
|
|
32904
33524
|
return _cashFlows[0];
|
|
32905
33525
|
// aggregate values of the same date
|
|
@@ -32959,12 +33579,24 @@ const YIELD = {
|
|
|
32959
33579
|
const _redemption = toNumber(redemption, this.locale);
|
|
32960
33580
|
const _frequency = Math.trunc(toNumber(frequency, this.locale));
|
|
32961
33581
|
const _dayCountConvention = Math.trunc(toNumber(dayCountConvention, this.locale));
|
|
32962
|
-
|
|
32963
|
-
|
|
32964
|
-
|
|
32965
|
-
|
|
32966
|
-
|
|
32967
|
-
|
|
33582
|
+
if (_settlement >= _maturity) {
|
|
33583
|
+
return new EvaluationError(expectMaturityStrictlyGreaterThanSettlement(_settlement, _maturity));
|
|
33584
|
+
}
|
|
33585
|
+
if (isInvalidFrequency(_frequency)) {
|
|
33586
|
+
return new EvaluationError(expectCouponFrequencyIsValid(_frequency));
|
|
33587
|
+
}
|
|
33588
|
+
if (isInvalidDayCountConvention(_dayCountConvention)) {
|
|
33589
|
+
return new EvaluationError(expectDayCountConventionIsValid(_dayCountConvention));
|
|
33590
|
+
}
|
|
33591
|
+
if (_rate < 0) {
|
|
33592
|
+
return new EvaluationError(expectRatePositiveOrZero(_rate));
|
|
33593
|
+
}
|
|
33594
|
+
if (_price <= 0) {
|
|
33595
|
+
return new EvaluationError(expectPriceStrictlyPositive(_price));
|
|
33596
|
+
}
|
|
33597
|
+
if (_redemption <= 0) {
|
|
33598
|
+
return new EvaluationError(expectRedemptionStrictlyPositive(_redemption));
|
|
33599
|
+
}
|
|
32968
33600
|
const years = getYearFrac(_settlement, _maturity, _dayCountConvention);
|
|
32969
33601
|
const nbrRealCoupons = years * _frequency;
|
|
32970
33602
|
const nbrFullCoupons = Math.ceil(nbrRealCoupons);
|
|
@@ -33031,10 +33663,18 @@ const YIELDDISC = {
|
|
|
33031
33663
|
const _price = toNumber(price, this.locale);
|
|
33032
33664
|
const _redemption = toNumber(redemption, this.locale);
|
|
33033
33665
|
const _dayCountConvention = Math.trunc(toNumber(dayCountConvention, this.locale));
|
|
33034
|
-
|
|
33035
|
-
|
|
33036
|
-
|
|
33037
|
-
|
|
33666
|
+
if (_settlement >= _maturity) {
|
|
33667
|
+
return new EvaluationError(expectMaturityStrictlyGreaterThanSettlement(_settlement, _maturity));
|
|
33668
|
+
}
|
|
33669
|
+
if (isInvalidDayCountConvention(_dayCountConvention)) {
|
|
33670
|
+
return new EvaluationError(expectDayCountConventionIsValid(_dayCountConvention));
|
|
33671
|
+
}
|
|
33672
|
+
if (_price <= 0) {
|
|
33673
|
+
return new EvaluationError(expectPriceStrictlyPositive(_price));
|
|
33674
|
+
}
|
|
33675
|
+
if (_redemption <= 0) {
|
|
33676
|
+
return new EvaluationError(expectRedemptionStrictlyPositive(_redemption));
|
|
33677
|
+
}
|
|
33038
33678
|
/**
|
|
33039
33679
|
* https://wiki.documentfoundation.org/Documentation/Calc_Functions/YIELDDISC
|
|
33040
33680
|
*
|
|
@@ -33068,11 +33708,21 @@ const YIELDMAT = {
|
|
|
33068
33708
|
const _rate = toNumber(rate, this.locale);
|
|
33069
33709
|
const _price = toNumber(price, this.locale);
|
|
33070
33710
|
const _dayCountConvention = Math.trunc(toNumber(dayCountConvention, this.locale));
|
|
33071
|
-
|
|
33072
|
-
|
|
33073
|
-
|
|
33074
|
-
|
|
33075
|
-
|
|
33711
|
+
if (_settlement >= _maturity) {
|
|
33712
|
+
return new EvaluationError(expectMaturityStrictlyGreaterThanSettlement(_settlement, _maturity));
|
|
33713
|
+
}
|
|
33714
|
+
if (isInvalidDayCountConvention(_dayCountConvention)) {
|
|
33715
|
+
return new EvaluationError(expectDayCountConventionIsValid(_dayCountConvention));
|
|
33716
|
+
}
|
|
33717
|
+
if (_settlement < _issue) {
|
|
33718
|
+
return new EvaluationError(expectSettlementGreaterOrEqualToIssue(_settlement, _issue));
|
|
33719
|
+
}
|
|
33720
|
+
if (_rate < 0) {
|
|
33721
|
+
return new EvaluationError(expectRatePositiveOrZero(_rate));
|
|
33722
|
+
}
|
|
33723
|
+
if (_price <= 0) {
|
|
33724
|
+
return new EvaluationError(expectPriceStrictlyPositive(_price));
|
|
33725
|
+
}
|
|
33076
33726
|
const issueToMaturity = getYearFrac(_issue, _maturity, _dayCountConvention);
|
|
33077
33727
|
const issueToSettlement = getYearFrac(_issue, _settlement, _dayCountConvention);
|
|
33078
33728
|
const settlementToMaturity = getYearFrac(_settlement, _maturity, _dayCountConvention);
|
|
@@ -33806,7 +34456,9 @@ const CELL = {
|
|
|
33806
34456
|
],
|
|
33807
34457
|
compute: function (info, reference) {
|
|
33808
34458
|
const _info = toString(info).toLowerCase();
|
|
33809
|
-
|
|
34459
|
+
if (!CELL_INFO_TYPES.includes(_info)) {
|
|
34460
|
+
return new EvaluationError(_t("The info_type should be one of %s.", CELL_INFO_TYPES.join(", ")));
|
|
34461
|
+
}
|
|
33810
34462
|
const sheetId = this.__originSheetId;
|
|
33811
34463
|
const _reference = toString(reference);
|
|
33812
34464
|
const topLeftReference = _reference.includes(":") ? _reference.split(":")[0] : _reference;
|
|
@@ -33975,7 +34627,9 @@ const AND = {
|
|
|
33975
34627
|
],
|
|
33976
34628
|
compute: function (...logicalExpressions) {
|
|
33977
34629
|
const { result, foundBoolean } = boolAnd(logicalExpressions);
|
|
33978
|
-
|
|
34630
|
+
if (!foundBoolean) {
|
|
34631
|
+
return new EvaluationError(_t("[[FUNCTION_NAME]] has no valid input data."));
|
|
34632
|
+
}
|
|
33979
34633
|
return result;
|
|
33980
34634
|
},
|
|
33981
34635
|
isExported: true,
|
|
@@ -34007,7 +34661,7 @@ const IF = {
|
|
|
34007
34661
|
return { value: "" };
|
|
34008
34662
|
}
|
|
34009
34663
|
if (result.value === null) {
|
|
34010
|
-
result
|
|
34664
|
+
return { ...result, value: "" };
|
|
34011
34665
|
}
|
|
34012
34666
|
return result;
|
|
34013
34667
|
},
|
|
@@ -34028,7 +34682,7 @@ const IFERROR = {
|
|
|
34028
34682
|
return { value: "" };
|
|
34029
34683
|
}
|
|
34030
34684
|
if (result.value === null) {
|
|
34031
|
-
result
|
|
34685
|
+
return { ...result, value: "" };
|
|
34032
34686
|
}
|
|
34033
34687
|
return result;
|
|
34034
34688
|
},
|
|
@@ -34049,7 +34703,7 @@ const IFNA = {
|
|
|
34049
34703
|
return { value: "" };
|
|
34050
34704
|
}
|
|
34051
34705
|
if (result.value === null) {
|
|
34052
|
-
result
|
|
34706
|
+
return { ...result, value: "" };
|
|
34053
34707
|
}
|
|
34054
34708
|
return result;
|
|
34055
34709
|
},
|
|
@@ -34067,7 +34721,9 @@ const IFS = {
|
|
|
34067
34721
|
arg("value2 (any, repeating)", _t("Additional values to be returned if their corresponding conditions are TRUE.")),
|
|
34068
34722
|
],
|
|
34069
34723
|
compute: function (...values) {
|
|
34070
|
-
|
|
34724
|
+
if (values.length % 2 !== 0) {
|
|
34725
|
+
return new EvaluationError(_t("Wrong number of arguments. Expected an even number of arguments."));
|
|
34726
|
+
}
|
|
34071
34727
|
for (let n = 0; n < values.length - 1; n += 2) {
|
|
34072
34728
|
if (toBoolean(values[n]?.value)) {
|
|
34073
34729
|
const result = values[n + 1];
|
|
@@ -34075,7 +34731,7 @@ const IFS = {
|
|
|
34075
34731
|
return { value: "" };
|
|
34076
34732
|
}
|
|
34077
34733
|
if (result.value === null) {
|
|
34078
|
-
result
|
|
34734
|
+
return { ...result, value: "" };
|
|
34079
34735
|
}
|
|
34080
34736
|
return result;
|
|
34081
34737
|
}
|
|
@@ -34108,7 +34764,9 @@ const OR = {
|
|
|
34108
34764
|
],
|
|
34109
34765
|
compute: function (...logicalExpressions) {
|
|
34110
34766
|
const { result, foundBoolean } = boolOr(logicalExpressions);
|
|
34111
|
-
|
|
34767
|
+
if (!foundBoolean) {
|
|
34768
|
+
return new EvaluationError(_t("[[FUNCTION_NAME]] has no valid input data."));
|
|
34769
|
+
}
|
|
34112
34770
|
return result;
|
|
34113
34771
|
},
|
|
34114
34772
|
isExported: true,
|
|
@@ -34169,7 +34827,9 @@ const XOR = {
|
|
|
34169
34827
|
acc = acc ? !arg : arg;
|
|
34170
34828
|
return true; // no stop condition
|
|
34171
34829
|
});
|
|
34172
|
-
|
|
34830
|
+
if (!foundBoolean) {
|
|
34831
|
+
return new EvaluationError(_t("[[FUNCTION_NAME]] has no valid input data."));
|
|
34832
|
+
}
|
|
34173
34833
|
return acc;
|
|
34174
34834
|
},
|
|
34175
34835
|
isExported: true,
|
|
@@ -34222,6 +34882,11 @@ function addPivotDependencies(evalContext, coreDefinition, forMeasures) {
|
|
|
34222
34882
|
if (range === undefined || range.invalidXc || range.invalidSheetName) {
|
|
34223
34883
|
throw new InvalidReferenceError();
|
|
34224
34884
|
}
|
|
34885
|
+
if (evalContext.__originCellPosition &&
|
|
34886
|
+
range.sheetId === evalContext.__originSheetId &&
|
|
34887
|
+
isZoneInside(positionToZone(evalContext.__originCellPosition), zone)) {
|
|
34888
|
+
throw new CircularDependencyError();
|
|
34889
|
+
}
|
|
34225
34890
|
dependencies.push(range);
|
|
34226
34891
|
}
|
|
34227
34892
|
for (const measure of forMeasures) {
|
|
@@ -34259,10 +34924,16 @@ const ADDRESS = {
|
|
|
34259
34924
|
compute: function (row, column, absoluteRelativeMode = { value: DEFAULT_ABSOLUTE_RELATIVE_MODE }, useA1Notation = { value: true }, sheet) {
|
|
34260
34925
|
const rowNumber = strictToInteger(row, this.locale);
|
|
34261
34926
|
const colNumber = strictToInteger(column, this.locale);
|
|
34262
|
-
|
|
34263
|
-
|
|
34927
|
+
if (rowNumber < 1) {
|
|
34928
|
+
return new EvaluationError(expectNumberGreaterThanOrEqualToOne(rowNumber));
|
|
34929
|
+
}
|
|
34930
|
+
if (colNumber < 1) {
|
|
34931
|
+
return new EvaluationError(expectNumberGreaterThanOrEqualToOne(colNumber));
|
|
34932
|
+
}
|
|
34264
34933
|
const _absoluteRelativeMode = strictToInteger(absoluteRelativeMode, this.locale);
|
|
34265
|
-
|
|
34934
|
+
if (![1, 2, 3, 4].includes(_absoluteRelativeMode)) {
|
|
34935
|
+
return new EvaluationError(expectNumberRangeError(1, 4, _absoluteRelativeMode));
|
|
34936
|
+
}
|
|
34266
34937
|
const _useA1Notation = toBoolean(useA1Notation);
|
|
34267
34938
|
let cellReference;
|
|
34268
34939
|
if (_useA1Notation) {
|
|
@@ -34299,7 +34970,9 @@ const COLUMN = {
|
|
|
34299
34970
|
const column = cellReference === undefined
|
|
34300
34971
|
? this.__originCellPosition?.col
|
|
34301
34972
|
: toZone(cellReference.value).left;
|
|
34302
|
-
|
|
34973
|
+
if (column === undefined) {
|
|
34974
|
+
return new EvaluationError(_t("In this context, the function [[FUNCTION_NAME]] needs to have a cell or range in parameter."));
|
|
34975
|
+
}
|
|
34303
34976
|
return column + 1;
|
|
34304
34977
|
},
|
|
34305
34978
|
isExported: true,
|
|
@@ -34333,7 +35006,9 @@ const HLOOKUP = {
|
|
|
34333
35006
|
compute: function (searchKey, range, index, isSorted = { value: DEFAULT_IS_SORTED }) {
|
|
34334
35007
|
const _index = Math.trunc(toNumber(index?.value, this.locale));
|
|
34335
35008
|
const _range = toMatrix(range);
|
|
34336
|
-
|
|
35009
|
+
if (1 > _index || _index > _range[0].length) {
|
|
35010
|
+
return new EvaluationError(_t("[[FUNCTION_NAME]] evaluates to an out of bounds range."));
|
|
35011
|
+
}
|
|
34337
35012
|
const getValueFromRange = (range, index) => range[index][0].value;
|
|
34338
35013
|
const _isSorted = toBoolean(isSorted.value);
|
|
34339
35014
|
const colIndex = _isSorted
|
|
@@ -34361,10 +35036,12 @@ const INDEX = {
|
|
|
34361
35036
|
const _reference = toMatrix(reference);
|
|
34362
35037
|
const _row = toNumber(row.value, this.locale);
|
|
34363
35038
|
const _column = toNumber(column.value, this.locale);
|
|
34364
|
-
|
|
34365
|
-
_column - 1
|
|
34366
|
-
_row
|
|
34367
|
-
_row - 1
|
|
35039
|
+
if (_column < 0 ||
|
|
35040
|
+
_column - 1 >= _reference.length ||
|
|
35041
|
+
_row < 0 ||
|
|
35042
|
+
_row - 1 >= _reference[0].length) {
|
|
35043
|
+
return new EvaluationError(_t("Index out of range."));
|
|
35044
|
+
}
|
|
34368
35045
|
if (_row === 0 && _column === 0) {
|
|
34369
35046
|
return _reference;
|
|
34370
35047
|
}
|
|
@@ -34455,12 +35132,18 @@ const LOOKUP = {
|
|
|
34455
35132
|
}
|
|
34456
35133
|
nbCol = _resultRange.length;
|
|
34457
35134
|
nbRow = _resultRange[0].length;
|
|
34458
|
-
|
|
35135
|
+
if (nbCol !== 1 && nbRow !== 1) {
|
|
35136
|
+
return new EvaluationError(_t("The result_range must be a single row or a single column."));
|
|
35137
|
+
}
|
|
34459
35138
|
if (nbCol > 1) {
|
|
34460
|
-
|
|
35139
|
+
if (index > nbCol - 1) {
|
|
35140
|
+
return new EvaluationError(_t("[[FUNCTION_NAME]] evaluates to an out of range row value %s.", index + 1));
|
|
35141
|
+
}
|
|
34461
35142
|
return _resultRange[index][0];
|
|
34462
35143
|
}
|
|
34463
|
-
|
|
35144
|
+
if (index > nbRow - 1) {
|
|
35145
|
+
return new EvaluationError(_t("[[FUNCTION_NAME]] evaluates to an out of range column value %s.", index + 1));
|
|
35146
|
+
}
|
|
34464
35147
|
return _resultRange[0][index];
|
|
34465
35148
|
},
|
|
34466
35149
|
isExported: true,
|
|
@@ -34481,7 +35164,9 @@ const MATCH = {
|
|
|
34481
35164
|
const _range = toMatrix(range);
|
|
34482
35165
|
const nbCol = _range.length;
|
|
34483
35166
|
const nbRow = _range[0].length;
|
|
34484
|
-
|
|
35167
|
+
if (nbCol !== 1 && nbRow !== 1) {
|
|
35168
|
+
return new EvaluationError(_t("The range must be a single row or a single column."));
|
|
35169
|
+
}
|
|
34485
35170
|
let index = -1;
|
|
34486
35171
|
const getElement = nbCol === 1
|
|
34487
35172
|
? (_range, index) => _range[0][index].value
|
|
@@ -34522,7 +35207,9 @@ const ROW = {
|
|
|
34522
35207
|
const row = cellReference === undefined
|
|
34523
35208
|
? this.__originCellPosition?.row
|
|
34524
35209
|
: toZone(cellReference.value).top;
|
|
34525
|
-
|
|
35210
|
+
if (row === undefined) {
|
|
35211
|
+
return new EvaluationError(_t("In this context, the function [[FUNCTION_NAME]] needs to have a cell or range in parameter."));
|
|
35212
|
+
}
|
|
34526
35213
|
return row + 1;
|
|
34527
35214
|
},
|
|
34528
35215
|
isExported: true,
|
|
@@ -34556,7 +35243,9 @@ const VLOOKUP = {
|
|
|
34556
35243
|
compute: function (searchKey, range, index, isSorted = { value: DEFAULT_IS_SORTED }) {
|
|
34557
35244
|
const _index = Math.trunc(toNumber(index?.value, this.locale));
|
|
34558
35245
|
const _range = toMatrix(range);
|
|
34559
|
-
|
|
35246
|
+
if (1 > _index || _index > _range.length) {
|
|
35247
|
+
return new EvaluationError(_t("[[FUNCTION_NAME]] evaluates to an out of bounds range."));
|
|
35248
|
+
}
|
|
34560
35249
|
const getValueFromRange = (range, index) => range[0][index].value;
|
|
34561
35250
|
const _isSorted = toBoolean(isSorted.value);
|
|
34562
35251
|
const rowIndex = _isSorted
|
|
@@ -34601,14 +35290,24 @@ const XLOOKUP = {
|
|
|
34601
35290
|
const _searchMode = Math.trunc(toNumber(searchMode.value, this.locale));
|
|
34602
35291
|
const _lookupRange = toMatrix(lookupRange);
|
|
34603
35292
|
const _returnRange = toMatrix(returnRange);
|
|
34604
|
-
|
|
34605
|
-
|
|
34606
|
-
|
|
35293
|
+
if (_lookupRange.length !== 1 && _lookupRange[0].length !== 1) {
|
|
35294
|
+
return new EvaluationError(_t("lookup_range should be either a single row or single column."));
|
|
35295
|
+
}
|
|
35296
|
+
if (![1, -1, 2, -2].includes(_searchMode)) {
|
|
35297
|
+
return new EvaluationError(_t("search_mode should be a value in [-1, 1, -2, 2]."));
|
|
35298
|
+
}
|
|
35299
|
+
if (![-1, 0, 1, 2].includes(_matchMode)) {
|
|
35300
|
+
return new EvaluationError(_t("match_mode should be a value in [-1, 0, 1, 2]."));
|
|
35301
|
+
}
|
|
34607
35302
|
const lookupDirection = _lookupRange.length === 1 ? "col" : "row";
|
|
34608
|
-
|
|
34609
|
-
|
|
34610
|
-
|
|
34611
|
-
|
|
35303
|
+
if (_matchMode === 2 && [-2, 2].includes(_searchMode)) {
|
|
35304
|
+
return new EvaluationError(_t("The search and match mode combination is not supported for XLOOKUP evaluation."));
|
|
35305
|
+
}
|
|
35306
|
+
if (lookupDirection === "col"
|
|
35307
|
+
? _returnRange[0].length !== _lookupRange[0].length
|
|
35308
|
+
: _returnRange.length !== _lookupRange.length) {
|
|
35309
|
+
return new EvaluationError(_t("return_range should have the same dimensions as lookup_range."));
|
|
35310
|
+
}
|
|
34612
35311
|
const getElement = lookupDirection === "col"
|
|
34613
35312
|
? (range, index) => range[0][index].value
|
|
34614
35313
|
: (range, index) => range[index][0].value;
|
|
@@ -34800,12 +35499,16 @@ const OFFSET = {
|
|
|
34800
35499
|
let offsetWidth = zone.right - zone.left + 1;
|
|
34801
35500
|
if (height) {
|
|
34802
35501
|
const _height = toNumber(height, this.locale);
|
|
34803
|
-
|
|
35502
|
+
if (_height < 1) {
|
|
35503
|
+
return new EvaluationError(_t("Height value is %(_height)s. It should be greater than or equal to 1.", { _height }));
|
|
35504
|
+
}
|
|
34804
35505
|
offsetHeight = _height;
|
|
34805
35506
|
}
|
|
34806
35507
|
if (width) {
|
|
34807
35508
|
const _width = toNumber(width, this.locale);
|
|
34808
|
-
|
|
35509
|
+
if (_width < 1) {
|
|
35510
|
+
return new EvaluationError(_t("Width value is %(_width)s. It should be greater than or equal to 1.", { _width }));
|
|
35511
|
+
}
|
|
34809
35512
|
offsetWidth = _width;
|
|
34810
35513
|
}
|
|
34811
35514
|
const { sheetName } = splitReference(_cellReference);
|
|
@@ -34903,7 +35606,9 @@ const DIVIDE = {
|
|
|
34903
35606
|
],
|
|
34904
35607
|
compute: function (dividend, divisor) {
|
|
34905
35608
|
const _divisor = toNumber(divisor, this.locale);
|
|
34906
|
-
|
|
35609
|
+
if (_divisor === 0) {
|
|
35610
|
+
return new DivisionByZeroError(_t("The divisor must be different from zero."));
|
|
35611
|
+
}
|
|
34907
35612
|
return {
|
|
34908
35613
|
value: toNumber(dividend, this.locale) / _divisor,
|
|
34909
35614
|
format: dividend?.format || divisor?.format,
|
|
@@ -35474,7 +36179,9 @@ const CHAR = {
|
|
|
35474
36179
|
],
|
|
35475
36180
|
compute: function (tableNumber) {
|
|
35476
36181
|
const _tableNumber = Math.trunc(toNumber(tableNumber, this.locale));
|
|
35477
|
-
|
|
36182
|
+
if (_tableNumber < 1) {
|
|
36183
|
+
return new EvaluationError(_t("The table_number (%s) is out of range.", _tableNumber));
|
|
36184
|
+
}
|
|
35478
36185
|
return String.fromCharCode(_tableNumber);
|
|
35479
36186
|
},
|
|
35480
36187
|
isExported: true,
|
|
@@ -35539,10 +36246,16 @@ const FIND = {
|
|
|
35539
36246
|
const _searchFor = toString(searchFor);
|
|
35540
36247
|
const _textToSearch = toString(textToSearch);
|
|
35541
36248
|
const _startingAt = toNumber(startingAt, this.locale);
|
|
35542
|
-
|
|
35543
|
-
|
|
36249
|
+
if (_textToSearch === "") {
|
|
36250
|
+
return new EvaluationError(_t("The text_to_search must be non-empty."));
|
|
36251
|
+
}
|
|
36252
|
+
if (_startingAt < 1) {
|
|
36253
|
+
return new EvaluationError(_t("The starting_at (%s) must be greater than or equal to 1.", _startingAt));
|
|
36254
|
+
}
|
|
35544
36255
|
const result = _textToSearch.indexOf(_searchFor, _startingAt - 1);
|
|
35545
|
-
|
|
36256
|
+
if (result === -1) {
|
|
36257
|
+
return new EvaluationError(_t("In [[FUNCTION_NAME]] evaluation, cannot find '%s' within '%s'.", _searchFor, _textToSearch));
|
|
36258
|
+
}
|
|
35546
36259
|
return result + 1;
|
|
35547
36260
|
},
|
|
35548
36261
|
isExported: true,
|
|
@@ -35573,7 +36286,9 @@ const LEFT = {
|
|
|
35573
36286
|
],
|
|
35574
36287
|
compute: function (text, ...args) {
|
|
35575
36288
|
const _numberOfCharacters = args.length ? toNumber(args[0], this.locale) : 1;
|
|
35576
|
-
|
|
36289
|
+
if (_numberOfCharacters < 0) {
|
|
36290
|
+
return new EvaluationError(_t("The number_of_characters (%s) must be positive or null.", _numberOfCharacters));
|
|
36291
|
+
}
|
|
35577
36292
|
return toString(text).substring(0, _numberOfCharacters);
|
|
35578
36293
|
},
|
|
35579
36294
|
isExported: true,
|
|
@@ -35614,8 +36329,12 @@ const MID = {
|
|
|
35614
36329
|
const _text = toString(text);
|
|
35615
36330
|
const _starting_at = toNumber(starting_at, this.locale);
|
|
35616
36331
|
const _extract_length = toNumber(extract_length, this.locale);
|
|
35617
|
-
|
|
35618
|
-
|
|
36332
|
+
if (_starting_at < 1) {
|
|
36333
|
+
return new EvaluationError(_t("The starting_at argument (%s) must be positive greater than one.", _starting_at.toString()));
|
|
36334
|
+
}
|
|
36335
|
+
if (_extract_length < 0) {
|
|
36336
|
+
return new EvaluationError(_t("The extract_length argument (%s) must be positive or null.", _extract_length));
|
|
36337
|
+
}
|
|
35619
36338
|
return _text.slice(_starting_at - 1, _starting_at + _extract_length - 1);
|
|
35620
36339
|
},
|
|
35621
36340
|
isExported: true,
|
|
@@ -35649,7 +36368,9 @@ const REPLACE = {
|
|
|
35649
36368
|
],
|
|
35650
36369
|
compute: function (text, position, length, newText) {
|
|
35651
36370
|
const _position = toNumber(position, this.locale);
|
|
35652
|
-
|
|
36371
|
+
if (_position < 1) {
|
|
36372
|
+
return new EvaluationError(_t("The position (%s) must be greater than or equal to 1.", _position));
|
|
36373
|
+
}
|
|
35653
36374
|
const _text = toString(text);
|
|
35654
36375
|
const _length = toNumber(length, this.locale);
|
|
35655
36376
|
const _newText = toString(newText);
|
|
@@ -35668,7 +36389,9 @@ const RIGHT = {
|
|
|
35668
36389
|
],
|
|
35669
36390
|
compute: function (text, ...args) {
|
|
35670
36391
|
const _numberOfCharacters = args.length ? toNumber(args[0], this.locale) : 1;
|
|
35671
|
-
|
|
36392
|
+
if (_numberOfCharacters < 0) {
|
|
36393
|
+
return new EvaluationError(_t("The number_of_characters (%s) must be positive or null.", _numberOfCharacters));
|
|
36394
|
+
}
|
|
35672
36395
|
const _text = toString(text);
|
|
35673
36396
|
const stringLength = _text.length;
|
|
35674
36397
|
return _text.substring(stringLength - _numberOfCharacters, stringLength);
|
|
@@ -35731,7 +36454,9 @@ const SPLIT = {
|
|
|
35731
36454
|
const _delimiter = escapeRegExp(toString(delimiter));
|
|
35732
36455
|
const _splitByEach = toBoolean(splitByEach);
|
|
35733
36456
|
const _removeEmptyText = toBoolean(removeEmptyText);
|
|
35734
|
-
|
|
36457
|
+
if (_delimiter.length <= 0) {
|
|
36458
|
+
return new EvaluationError(_t("The delimiter (%s) must be not be empty.", _delimiter));
|
|
36459
|
+
}
|
|
35735
36460
|
const regex = _splitByEach ? new RegExp(`[${_delimiter}]`, "g") : new RegExp(_delimiter, "g");
|
|
35736
36461
|
let result = _text.split(regex);
|
|
35737
36462
|
if (_removeEmptyText) {
|
|
@@ -35754,7 +36479,9 @@ const SUBSTITUTE = {
|
|
|
35754
36479
|
],
|
|
35755
36480
|
compute: function (textToSearch, searchFor, replaceWith, occurrenceNumber) {
|
|
35756
36481
|
const _occurrenceNumber = toNumber(occurrenceNumber, this.locale);
|
|
35757
|
-
|
|
36482
|
+
if (_occurrenceNumber < 0) {
|
|
36483
|
+
return new EvaluationError(_t("The occurrenceNumber (%s) must be positive or null.", _occurrenceNumber));
|
|
36484
|
+
}
|
|
35758
36485
|
const _textToSearch = toString(textToSearch);
|
|
35759
36486
|
const _searchFor = toString(searchFor);
|
|
35760
36487
|
if (_searchFor === "") {
|
|
@@ -35914,6 +36641,13 @@ const functionNameRegex = /^[A-Z0-9\_\.]+$/;
|
|
|
35914
36641
|
class FunctionRegistry extends Registry {
|
|
35915
36642
|
mapping = {};
|
|
35916
36643
|
add(name, addDescr) {
|
|
36644
|
+
name = name.toUpperCase();
|
|
36645
|
+
if (name in this.content) {
|
|
36646
|
+
throw new Error(`${name} is already present in this registry!`);
|
|
36647
|
+
}
|
|
36648
|
+
return this.replace(name, addDescr);
|
|
36649
|
+
}
|
|
36650
|
+
replace(name, addDescr) {
|
|
35917
36651
|
name = name.toUpperCase();
|
|
35918
36652
|
if (!functionNameRegex.test(name)) {
|
|
35919
36653
|
throw new Error(_t("Invalid function name %s. Function names can exclusively contain alphanumerical values separated by dots (.) or underscore (_)", name));
|
|
@@ -35921,7 +36655,7 @@ class FunctionRegistry extends Registry {
|
|
|
35921
36655
|
const descr = addMetaInfoFromArg(name, addDescr);
|
|
35922
36656
|
validateArguments(descr);
|
|
35923
36657
|
this.mapping[name] = createComputeFunction(descr);
|
|
35924
|
-
super.
|
|
36658
|
+
super.replace(name, descr);
|
|
35925
36659
|
return this;
|
|
35926
36660
|
}
|
|
35927
36661
|
}
|
|
@@ -40435,6 +41169,13 @@ class Composer extends Component {
|
|
|
40435
41169
|
openAssistant() {
|
|
40436
41170
|
this.assistant.forcedClosed = false;
|
|
40437
41171
|
}
|
|
41172
|
+
onWheel(event) {
|
|
41173
|
+
// detect if scrollbar is available
|
|
41174
|
+
if (this.composerRef.el &&
|
|
41175
|
+
this.composerRef.el.scrollHeight > this.composerRef.el.clientHeight) {
|
|
41176
|
+
event.stopPropagation();
|
|
41177
|
+
}
|
|
41178
|
+
}
|
|
40438
41179
|
// ---------------------------------------------------------------------------
|
|
40439
41180
|
// Private
|
|
40440
41181
|
// ---------------------------------------------------------------------------
|
|
@@ -40714,6 +41455,8 @@ class GridComposer extends Component {
|
|
|
40714
41455
|
}
|
|
40715
41456
|
get composerProps() {
|
|
40716
41457
|
const { width, height } = this.env.model.getters.getSheetViewDimensionWithHeaders();
|
|
41458
|
+
// Remove the wrapper border width
|
|
41459
|
+
const maxHeight = this.props.gridDims.height - this.rect.y - 2 * COMPOSER_BORDER_WIDTH;
|
|
40717
41460
|
return {
|
|
40718
41461
|
rect: { ...this.rect },
|
|
40719
41462
|
delimitation: {
|
|
@@ -40731,6 +41474,7 @@ class GridComposer extends Component {
|
|
|
40731
41474
|
}),
|
|
40732
41475
|
onInputContextMenu: this.props.onInputContextMenu,
|
|
40733
41476
|
composerStore: this.composerStore,
|
|
41477
|
+
inputStyle: `max-height: ${maxHeight}px;`,
|
|
40734
41478
|
};
|
|
40735
41479
|
}
|
|
40736
41480
|
get containerStyle() {
|
|
@@ -44310,6 +45054,7 @@ class SelectionInputStore extends SpreadsheetStore {
|
|
|
44310
45054
|
initialRanges;
|
|
44311
45055
|
inputHasSingleRange;
|
|
44312
45056
|
colors;
|
|
45057
|
+
disabledRanges;
|
|
44313
45058
|
mutators = [
|
|
44314
45059
|
"resetWithRanges",
|
|
44315
45060
|
"focusById",
|
|
@@ -44320,17 +45065,19 @@ class SelectionInputStore extends SpreadsheetStore {
|
|
|
44320
45065
|
"reset",
|
|
44321
45066
|
"confirm",
|
|
44322
45067
|
"updateColors",
|
|
45068
|
+
"updateDisabledRanges",
|
|
44323
45069
|
];
|
|
44324
45070
|
ranges = [];
|
|
44325
45071
|
focusedRangeIndex = null;
|
|
44326
45072
|
inputSheetId;
|
|
44327
45073
|
focusStore = this.get(FocusStore);
|
|
44328
45074
|
highlightStore = this.get(HighlightStore);
|
|
44329
|
-
constructor(get, initialRanges = [], inputHasSingleRange = false, colors = []) {
|
|
45075
|
+
constructor(get, initialRanges = [], inputHasSingleRange = false, colors = [], disabledRanges = []) {
|
|
44330
45076
|
super(get);
|
|
44331
45077
|
this.initialRanges = initialRanges;
|
|
44332
45078
|
this.inputHasSingleRange = inputHasSingleRange;
|
|
44333
45079
|
this.colors = colors;
|
|
45080
|
+
this.disabledRanges = disabledRanges;
|
|
44334
45081
|
if (inputHasSingleRange && initialRanges.length > 1) {
|
|
44335
45082
|
throw new Error("Input with a single range cannot be instantiated with several range references.");
|
|
44336
45083
|
}
|
|
@@ -44447,6 +45194,9 @@ class SelectionInputStore extends SpreadsheetStore {
|
|
|
44447
45194
|
color: colorGenerator.next(),
|
|
44448
45195
|
}));
|
|
44449
45196
|
}
|
|
45197
|
+
updateDisabledRanges(disabledRanges) {
|
|
45198
|
+
this.disabledRanges = disabledRanges;
|
|
45199
|
+
}
|
|
44450
45200
|
confirm() {
|
|
44451
45201
|
for (const range of this.selectionInputs) {
|
|
44452
45202
|
if (range.xc === "") {
|
|
@@ -44488,6 +45238,7 @@ class SelectionInputStore extends SpreadsheetStore {
|
|
|
44488
45238
|
: null,
|
|
44489
45239
|
isFocused: this.hasMainFocus && this.focusedRangeIndex === index,
|
|
44490
45240
|
isValidRange: input.xc === "" || this.getters.isRangeValid(input.xc),
|
|
45241
|
+
disabled: this.disabledRanges?.[index],
|
|
44491
45242
|
}));
|
|
44492
45243
|
}
|
|
44493
45244
|
get isResettable() {
|
|
@@ -44652,7 +45403,7 @@ css /* scss */ `
|
|
|
44652
45403
|
input.o-invalid {
|
|
44653
45404
|
background-color: ${ALERT_DANGER_BG};
|
|
44654
45405
|
}
|
|
44655
|
-
.
|
|
45406
|
+
.input-icon {
|
|
44656
45407
|
right: 7px;
|
|
44657
45408
|
top: 4px;
|
|
44658
45409
|
}
|
|
@@ -44660,6 +45411,9 @@ css /* scss */ `
|
|
|
44660
45411
|
cursor: move;
|
|
44661
45412
|
}
|
|
44662
45413
|
}
|
|
45414
|
+
.o-disabled-ranges {
|
|
45415
|
+
color: #888 !important;
|
|
45416
|
+
}
|
|
44663
45417
|
.o-button {
|
|
44664
45418
|
flex-grow: 0;
|
|
44665
45419
|
}
|
|
@@ -44692,6 +45446,8 @@ class SelectionInput extends Component {
|
|
|
44692
45446
|
onSelectionReordered: { type: Function, optional: true },
|
|
44693
45447
|
onSelectionRemoved: { type: Function, optional: true },
|
|
44694
45448
|
colors: { type: Array, optional: true, default: [] },
|
|
45449
|
+
disabledRanges: { type: Array, optional: true, default: [] },
|
|
45450
|
+
disabledRangeTitle: { type: String, optional: true },
|
|
44695
45451
|
};
|
|
44696
45452
|
state = useState({
|
|
44697
45453
|
isMissing: false,
|
|
@@ -44716,9 +45472,12 @@ class SelectionInput extends Component {
|
|
|
44716
45472
|
get isResettable() {
|
|
44717
45473
|
return this.store.isResettable;
|
|
44718
45474
|
}
|
|
45475
|
+
get hasDisabledRanges() {
|
|
45476
|
+
return this.store.disabledRanges.some(Boolean);
|
|
45477
|
+
}
|
|
44719
45478
|
setup() {
|
|
44720
45479
|
useEffect(() => this.focusedInput.el?.focus(), () => [this.focusedInput.el]);
|
|
44721
|
-
this.store = useLocalStore(SelectionInputStore, this.props.ranges, this.props.hasSingleRange || false, this.props.colors);
|
|
45480
|
+
this.store = useLocalStore(SelectionInputStore, this.props.ranges, this.props.hasSingleRange || false, this.props.colors, this.props.disabledRanges);
|
|
44722
45481
|
onWillUpdateProps((nextProps) => {
|
|
44723
45482
|
if (nextProps.ranges.join() !== this.store.selectionInputValues.join()) {
|
|
44724
45483
|
this.triggerChange();
|
|
@@ -44731,6 +45490,10 @@ class SelectionInput extends Component {
|
|
|
44731
45490
|
nextProps.colors?.join() !== this.store.colors.join()) {
|
|
44732
45491
|
this.store.updateColors(nextProps.colors || []);
|
|
44733
45492
|
}
|
|
45493
|
+
if (!deepEquals(nextProps.disabledRanges, this.props.disabledRanges) &&
|
|
45494
|
+
!deepEquals(nextProps.disabledRanges, this.store.disabledRanges)) {
|
|
45495
|
+
this.store.updateDisabledRanges(nextProps.disabledRanges || []);
|
|
45496
|
+
}
|
|
44734
45497
|
});
|
|
44735
45498
|
}
|
|
44736
45499
|
startDragAndDrop(rangeId, event) {
|
|
@@ -44846,10 +45609,14 @@ class ChartDataSeries extends Component {
|
|
|
44846
45609
|
onSelectionRemoved: { type: Function, optional: true },
|
|
44847
45610
|
onSelectionConfirmed: Function,
|
|
44848
45611
|
title: { type: String, optional: true },
|
|
45612
|
+
maxNumberOfUsedRanges: { type: Number, optional: true },
|
|
44849
45613
|
};
|
|
44850
45614
|
get ranges() {
|
|
44851
45615
|
return this.props.ranges.map((r) => r.dataRange);
|
|
44852
45616
|
}
|
|
45617
|
+
get disabledRanges() {
|
|
45618
|
+
return this.props.ranges.map((r, i) => this.props.maxNumberOfUsedRanges ? i >= this.props.maxNumberOfUsedRanges : false);
|
|
45619
|
+
}
|
|
44853
45620
|
get colors() {
|
|
44854
45621
|
return this.props.ranges.map((r) => r.backgroundColor);
|
|
44855
45622
|
}
|
|
@@ -44914,7 +45681,7 @@ class GenericChartConfigPanel extends Component {
|
|
|
44914
45681
|
const cancelledReasons = [
|
|
44915
45682
|
...(this.state.datasetDispatchResult?.reasons || []),
|
|
44916
45683
|
...(this.state.labelsDispatchResult?.reasons || []),
|
|
44917
|
-
];
|
|
45684
|
+
].filter((reason) => reason !== "NoChanges" /* CommandResult.NoChanges */);
|
|
44918
45685
|
return cancelledReasons.map((error) => ChartTerms.Errors[error] || ChartTerms.Errors.Unexpected);
|
|
44919
45686
|
}
|
|
44920
45687
|
get isDatasetInvalid() {
|
|
@@ -45036,6 +45803,9 @@ class GenericChartConfigPanel extends Component {
|
|
|
45036
45803
|
}
|
|
45037
45804
|
return undefined;
|
|
45038
45805
|
}
|
|
45806
|
+
get maxNumberOfUsedRanges() {
|
|
45807
|
+
return chartRegistry.get(this.props.definition.type).dataSeriesLimit;
|
|
45808
|
+
}
|
|
45039
45809
|
}
|
|
45040
45810
|
|
|
45041
45811
|
class BarConfigPanel extends GenericChartConfigPanel {
|
|
@@ -47194,13 +47964,10 @@ class GeoChartConfigPanel extends GenericChartConfigPanel {
|
|
|
47194
47964
|
static template = "o-spreadsheet-GeoChartConfigPanel";
|
|
47195
47965
|
static components = { ...GenericChartConfigPanel.components, GeoChartRegionSelectSection };
|
|
47196
47966
|
get dataRanges() {
|
|
47197
|
-
return this.getDataSeriesRanges()
|
|
47967
|
+
return this.getDataSeriesRanges();
|
|
47198
47968
|
}
|
|
47199
|
-
|
|
47200
|
-
|
|
47201
|
-
this.state.datasetDispatchResult = this.props.updateChart(this.props.figureId, {
|
|
47202
|
-
dataSets: this.dataSets,
|
|
47203
|
-
});
|
|
47969
|
+
get disabledRanges() {
|
|
47970
|
+
return this.props.definition.dataSets.map((ds, i) => i > 0);
|
|
47204
47971
|
}
|
|
47205
47972
|
getLabelRangeOptions() {
|
|
47206
47973
|
return [
|
|
@@ -47327,6 +48094,19 @@ class LineConfigPanel extends GenericChartConfigPanel {
|
|
|
47327
48094
|
}
|
|
47328
48095
|
}
|
|
47329
48096
|
|
|
48097
|
+
class PieHoleSize extends Component {
|
|
48098
|
+
static template = "o-spreadsheet.PieHoleSize";
|
|
48099
|
+
static components = { Section };
|
|
48100
|
+
static props = { onValueChange: Function, value: Number };
|
|
48101
|
+
// Very short debounce to prevent up/down arrow on number input to spam the onChange
|
|
48102
|
+
debouncedOnChange = debounce(this.onChange.bind(this), 100);
|
|
48103
|
+
onChange(value) {
|
|
48104
|
+
if (!isNaN(Number(value))) {
|
|
48105
|
+
this.props.onValueChange(clip(Number(value), 0, 95));
|
|
48106
|
+
}
|
|
48107
|
+
}
|
|
48108
|
+
}
|
|
48109
|
+
|
|
47330
48110
|
class PieChartDesignPanel extends Component {
|
|
47331
48111
|
static template = "o-spreadsheet-PieChartDesignPanel";
|
|
47332
48112
|
static components = {
|
|
@@ -47334,6 +48114,7 @@ class PieChartDesignPanel extends Component {
|
|
|
47334
48114
|
Section,
|
|
47335
48115
|
Checkbox,
|
|
47336
48116
|
ChartLegend,
|
|
48117
|
+
PieHoleSize,
|
|
47337
48118
|
};
|
|
47338
48119
|
static props = {
|
|
47339
48120
|
figureId: String,
|
|
@@ -47341,6 +48122,12 @@ class PieChartDesignPanel extends Component {
|
|
|
47341
48122
|
updateChart: Function,
|
|
47342
48123
|
canUpdateChart: { type: Function, optional: true },
|
|
47343
48124
|
};
|
|
48125
|
+
onPieHoleSizeChange(pieHolePercentage) {
|
|
48126
|
+
this.props.updateChart(this.props.figureId, {
|
|
48127
|
+
...this.props.definition,
|
|
48128
|
+
pieHolePercentage,
|
|
48129
|
+
});
|
|
48130
|
+
}
|
|
47344
48131
|
}
|
|
47345
48132
|
|
|
47346
48133
|
class RadarChartDesignPanel extends Component {
|
|
@@ -47509,6 +48296,7 @@ class SunburstChartDesignPanel extends Component {
|
|
|
47509
48296
|
TextStyler,
|
|
47510
48297
|
RoundColorPicker,
|
|
47511
48298
|
ChartLegend,
|
|
48299
|
+
PieHoleSize,
|
|
47512
48300
|
};
|
|
47513
48301
|
static props = {
|
|
47514
48302
|
figureId: String,
|
|
@@ -47534,6 +48322,12 @@ class SunburstChartDesignPanel extends Component {
|
|
|
47534
48322
|
colors[index] = color;
|
|
47535
48323
|
this.props.updateChart(this.props.figureId, { groupColors: colors });
|
|
47536
48324
|
}
|
|
48325
|
+
onPieHoleSizeChange(pieHolePercentage) {
|
|
48326
|
+
this.props.updateChart(this.props.figureId, {
|
|
48327
|
+
...this.props.definition,
|
|
48328
|
+
pieHolePercentage,
|
|
48329
|
+
});
|
|
48330
|
+
}
|
|
47537
48331
|
}
|
|
47538
48332
|
|
|
47539
48333
|
class TreeMapCategoryColors extends Component {
|
|
@@ -51958,8 +52752,8 @@ function compareDimensionValues(dimension, a, b) {
|
|
|
51958
52752
|
|
|
51959
52753
|
const NULL_SYMBOL = Symbol("NULL");
|
|
51960
52754
|
function createDate(dimension, value, locale) {
|
|
51961
|
-
const granularity = dimension.granularity;
|
|
51962
|
-
if (!
|
|
52755
|
+
const granularity = dimension.granularity || "month";
|
|
52756
|
+
if (!(granularity in MAP_VALUE_DIMENSION_DATE)) {
|
|
51963
52757
|
throw new Error(`Unknown date granularity: ${granularity}`);
|
|
51964
52758
|
}
|
|
51965
52759
|
const keyInMap = typeof value === "number" || typeof value === "string" ? value : NULL_SYMBOL;
|
|
@@ -51978,6 +52772,9 @@ function createDate(dimension, value, locale) {
|
|
|
51978
52772
|
case "month_number":
|
|
51979
52773
|
number = date.getMonth() + 1;
|
|
51980
52774
|
break;
|
|
52775
|
+
case "month":
|
|
52776
|
+
number = Math.floor(toNumber(value, locale));
|
|
52777
|
+
break;
|
|
51981
52778
|
case "iso_week_number":
|
|
51982
52779
|
number = date.getIsoWeek();
|
|
51983
52780
|
break;
|
|
@@ -52071,6 +52868,10 @@ const MAP_VALUE_DIMENSION_DATE = {
|
|
|
52071
52868
|
set: new Set(),
|
|
52072
52869
|
values: {},
|
|
52073
52870
|
},
|
|
52871
|
+
month: {
|
|
52872
|
+
set: new Set(),
|
|
52873
|
+
values: {},
|
|
52874
|
+
},
|
|
52074
52875
|
iso_week_number: {
|
|
52075
52876
|
set: new Set(),
|
|
52076
52877
|
values: {},
|
|
@@ -52281,7 +53082,7 @@ class SpreadsheetPivot {
|
|
|
52281
53082
|
const cells = this.filterDataEntriesFromDomain(this.dataEntries, domain);
|
|
52282
53083
|
const finalCell = cells[0]?.[dimension.nameWithGranularity];
|
|
52283
53084
|
if (dimension.type === "datetime") {
|
|
52284
|
-
const adapter = pivotTimeAdapter(dimension.granularity);
|
|
53085
|
+
const adapter = pivotTimeAdapter((dimension.granularity || "month"));
|
|
52285
53086
|
return adapter.toValueAndFormat(lastNode.value, this.getters.getLocale());
|
|
52286
53087
|
}
|
|
52287
53088
|
if (!finalCell) {
|
|
@@ -52399,7 +53200,7 @@ class SpreadsheetPivot {
|
|
|
52399
53200
|
if (nonEmptyCells.length === 0) {
|
|
52400
53201
|
return "integer";
|
|
52401
53202
|
}
|
|
52402
|
-
if (nonEmptyCells.every((cell) => cell.format && isDateTimeFormat(cell.format))) {
|
|
53203
|
+
if (nonEmptyCells.every((cell) => cell.type === CellValueType.number && cell.format && isDateTimeFormat(cell.format))) {
|
|
52403
53204
|
return "datetime";
|
|
52404
53205
|
}
|
|
52405
53206
|
if (nonEmptyCells.every((cell) => cell.type === CellValueType.boolean)) {
|
|
@@ -52492,7 +53293,7 @@ class SpreadsheetPivot {
|
|
|
52492
53293
|
for (const entry of dataEntries) {
|
|
52493
53294
|
for (const dimension of dateDimensions) {
|
|
52494
53295
|
const value = createDate(dimension, entry[dimension.fieldName]?.value || null, this.getters.getLocale());
|
|
52495
|
-
const adapter = pivotTimeAdapter(dimension.granularity);
|
|
53296
|
+
const adapter = pivotTimeAdapter((dimension.granularity || "month"));
|
|
52496
53297
|
const { format, value: valueToFormat } = adapter.toValueAndFormat(value, locale);
|
|
52497
53298
|
entry[dimension.nameWithGranularity] = {
|
|
52498
53299
|
value,
|
|
@@ -52512,6 +53313,7 @@ const dateGranularities = [
|
|
|
52512
53313
|
"year",
|
|
52513
53314
|
"quarter_number",
|
|
52514
53315
|
"month_number",
|
|
53316
|
+
"month",
|
|
52515
53317
|
"iso_week_number",
|
|
52516
53318
|
"day_of_month",
|
|
52517
53319
|
"day",
|
|
@@ -52762,7 +53564,7 @@ class PivotSidePanelStore extends SpreadsheetStore {
|
|
|
52762
53564
|
: this.datetimeGranularities);
|
|
52763
53565
|
}
|
|
52764
53566
|
for (const field of dateFields) {
|
|
52765
|
-
granularitiesPerFields[field.fieldName].delete(field.granularity);
|
|
53567
|
+
granularitiesPerFields[field.fieldName].delete(field.granularity || "month");
|
|
52766
53568
|
}
|
|
52767
53569
|
return granularitiesPerFields;
|
|
52768
53570
|
}
|
|
@@ -55998,7 +56800,7 @@ class ChartPlugin extends CorePlugin {
|
|
|
55998
56800
|
case "CREATE_CHART":
|
|
55999
56801
|
return this.checkValidations(cmd, this.chainValidations(this.validateChartDefinition, this.checkChartDuplicate));
|
|
56000
56802
|
case "UPDATE_CHART":
|
|
56001
|
-
return this.checkValidations(cmd, this.chainValidations(this.validateChartDefinition, this.checkChartExists));
|
|
56803
|
+
return this.checkValidations(cmd, this.chainValidations(this.validateChartDefinition, this.checkChartExists, this.checkChartChanged));
|
|
56002
56804
|
default:
|
|
56003
56805
|
return "Success" /* CommandResult.Success */;
|
|
56004
56806
|
}
|
|
@@ -56152,10 +56954,15 @@ class ChartPlugin extends CorePlugin {
|
|
|
56152
56954
|
: "Success" /* CommandResult.Success */;
|
|
56153
56955
|
}
|
|
56154
56956
|
checkChartExists(cmd) {
|
|
56155
|
-
return this.
|
|
56957
|
+
return this.isChartDefined(cmd.figureId)
|
|
56156
56958
|
? "Success" /* CommandResult.Success */
|
|
56157
56959
|
: "ChartDoesNotExist" /* CommandResult.ChartDoesNotExist */;
|
|
56158
56960
|
}
|
|
56961
|
+
checkChartChanged(cmd) {
|
|
56962
|
+
return deepEquals(this.getChartDefinition(cmd.figureId), cmd.definition)
|
|
56963
|
+
? "NoChanges" /* CommandResult.NoChanges */
|
|
56964
|
+
: "Success" /* CommandResult.Success */;
|
|
56965
|
+
}
|
|
56159
56966
|
}
|
|
56160
56967
|
|
|
56161
56968
|
// -----------------------------------------------------------------------------
|
|
@@ -58414,6 +59221,7 @@ class SheetPlugin extends CorePlugin {
|
|
|
58414
59221
|
"getCommandZones",
|
|
58415
59222
|
"getUnboundedZone",
|
|
58416
59223
|
"checkElementsIncludeAllNonFrozenHeaders",
|
|
59224
|
+
"getDuplicateSheetName",
|
|
58417
59225
|
];
|
|
58418
59226
|
sheetIdsMapName = {};
|
|
58419
59227
|
orderedSheetIds = [];
|
|
@@ -58438,7 +59246,11 @@ class SheetPlugin extends CorePlugin {
|
|
|
58438
59246
|
return this.checkValidations(cmd, this.checkSheetName, this.checkSheetPosition);
|
|
58439
59247
|
}
|
|
58440
59248
|
case "DUPLICATE_SHEET": {
|
|
58441
|
-
|
|
59249
|
+
if (this.sheets[cmd.sheetIdTo])
|
|
59250
|
+
return "DuplicatedSheetId" /* CommandResult.DuplicatedSheetId */;
|
|
59251
|
+
if (this.orderedSheetIds.map(this.getSheetName.bind(this)).includes(cmd.sheetNameTo))
|
|
59252
|
+
return "DuplicatedSheetName" /* CommandResult.DuplicatedSheetName */;
|
|
59253
|
+
return "Success" /* CommandResult.Success */;
|
|
58442
59254
|
}
|
|
58443
59255
|
case "MOVE_SHEET":
|
|
58444
59256
|
try {
|
|
@@ -58515,7 +59327,7 @@ class SheetPlugin extends CorePlugin {
|
|
|
58515
59327
|
this.showSheet(cmd.sheetId);
|
|
58516
59328
|
break;
|
|
58517
59329
|
case "DUPLICATE_SHEET":
|
|
58518
|
-
this.duplicateSheet(cmd.sheetId, cmd.sheetIdTo);
|
|
59330
|
+
this.duplicateSheet(cmd.sheetId, cmd.sheetIdTo, cmd.sheetNameTo);
|
|
58519
59331
|
break;
|
|
58520
59332
|
case "DELETE_SHEET":
|
|
58521
59333
|
this.deleteSheet(this.sheets[cmd.sheetId]);
|
|
@@ -58722,10 +59534,7 @@ class SheetPlugin extends CorePlugin {
|
|
|
58722
59534
|
}
|
|
58723
59535
|
getNextSheetName(baseName = "Sheet") {
|
|
58724
59536
|
const names = this.orderedSheetIds.map(this.getSheetName.bind(this));
|
|
58725
|
-
return
|
|
58726
|
-
compute: (name, i) => `${name}${i}`,
|
|
58727
|
-
computeFirstOne: true,
|
|
58728
|
-
});
|
|
59537
|
+
return getNextSheetName(names, baseName);
|
|
58729
59538
|
}
|
|
58730
59539
|
getSheetSize(sheetId) {
|
|
58731
59540
|
return {
|
|
@@ -58972,9 +59781,8 @@ class SheetPlugin extends CorePlugin {
|
|
|
58972
59781
|
showSheet(sheetId) {
|
|
58973
59782
|
this.history.update("sheets", sheetId, "isVisible", true);
|
|
58974
59783
|
}
|
|
58975
|
-
duplicateSheet(fromId, toId) {
|
|
59784
|
+
duplicateSheet(fromId, toId, toName) {
|
|
58976
59785
|
const sheet = this.getSheet(fromId);
|
|
58977
|
-
const toName = this.getDuplicateSheetName(sheet.name);
|
|
58978
59786
|
const newSheet = deepCopy(sheet);
|
|
58979
59787
|
newSheet.id = toId;
|
|
58980
59788
|
newSheet.name = toName;
|
|
@@ -59007,8 +59815,7 @@ class SheetPlugin extends CorePlugin {
|
|
|
59007
59815
|
}
|
|
59008
59816
|
getDuplicateSheetName(sheetName) {
|
|
59009
59817
|
const names = this.orderedSheetIds.map(this.getSheetName.bind(this));
|
|
59010
|
-
|
|
59011
|
-
return getUniqueText(baseName.toString(), names);
|
|
59818
|
+
return getDuplicateSheetName(sheetName, names);
|
|
59012
59819
|
}
|
|
59013
59820
|
deleteSheet(sheet) {
|
|
59014
59821
|
const name = sheet.name;
|
|
@@ -61812,8 +62619,8 @@ class SpreadingRelation {
|
|
|
61812
62619
|
const EMPTY_ARRAY = [];
|
|
61813
62620
|
|
|
61814
62621
|
const MAX_ITERATION = 30;
|
|
61815
|
-
const ERROR_CYCLE_CELL = createEvaluatedCell(new CircularDependencyError());
|
|
61816
|
-
const EMPTY_CELL = createEvaluatedCell({ value: null });
|
|
62622
|
+
const ERROR_CYCLE_CELL = Object.freeze(createEvaluatedCell(new CircularDependencyError()));
|
|
62623
|
+
const EMPTY_CELL = Object.freeze(createEvaluatedCell({ value: null }));
|
|
61817
62624
|
class Evaluator {
|
|
61818
62625
|
context;
|
|
61819
62626
|
getters;
|
|
@@ -65661,6 +66468,10 @@ class SpecificRangeTransformRegistry extends Registry {
|
|
|
65661
66468
|
super.add(cmdType, fn);
|
|
65662
66469
|
return this;
|
|
65663
66470
|
}
|
|
66471
|
+
replace(cmdType, fn) {
|
|
66472
|
+
super.replace(cmdType, fn);
|
|
66473
|
+
return this;
|
|
66474
|
+
}
|
|
65664
66475
|
get(cmdType) {
|
|
65665
66476
|
return this.content[cmdType];
|
|
65666
66477
|
}
|
|
@@ -68240,7 +69051,6 @@ repeatCommandTransformRegistry.add("CREATE_CHART", repeatCreateChartCommand);
|
|
|
68240
69051
|
repeatCommandTransformRegistry.add("CREATE_IMAGE", repeatCreateImageCommand);
|
|
68241
69052
|
repeatCommandTransformRegistry.add("GROUP_HEADERS", repeatGroupHeadersCommand);
|
|
68242
69053
|
repeatCommandTransformRegistry.add("UNGROUP_HEADERS", repeatGroupHeadersCommand);
|
|
68243
|
-
repeatCommandTransformRegistry.add("UNGROUP_HEADERS", repeatGroupHeadersCommand);
|
|
68244
69054
|
repeatCommandTransformRegistry.add("UNFOLD_HEADER_GROUPS_IN_ZONE", repeatZoneDependantCommand);
|
|
68245
69055
|
repeatCommandTransformRegistry.add("FOLD_HEADER_GROUPS_IN_ZONE", repeatZoneDependantCommand);
|
|
68246
69056
|
const repeatLocalCommandTransformRegistry = new Registry();
|
|
@@ -72543,9 +73353,9 @@ topbarMenuRegistry
|
|
|
72543
73353
|
class TopBarComponentRegistry extends Registry {
|
|
72544
73354
|
mapping = {};
|
|
72545
73355
|
uuidGenerator = new UuidGenerator();
|
|
72546
|
-
|
|
73356
|
+
replace(name, value) {
|
|
72547
73357
|
const component = { ...value, id: this.uuidGenerator.uuidv4() };
|
|
72548
|
-
return super.
|
|
73358
|
+
return super.replace(name, component);
|
|
72549
73359
|
}
|
|
72550
73360
|
getAllOrdered() {
|
|
72551
73361
|
return this.getAll().sort((a, b) => a.sequence - b.sequence);
|
|
@@ -74109,6 +74919,9 @@ class TopBarToolStore {
|
|
|
74109
74919
|
class ToolBarRegistry {
|
|
74110
74920
|
content = {};
|
|
74111
74921
|
add(key) {
|
|
74922
|
+
if (key in this.content) {
|
|
74923
|
+
throw new Error(`${key} is already present in this registry!`);
|
|
74924
|
+
}
|
|
74112
74925
|
this.content[key] = [];
|
|
74113
74926
|
return this;
|
|
74114
74927
|
}
|
|
@@ -75469,11 +76282,13 @@ class Spreadsheet extends Component {
|
|
|
75469
76282
|
this.checkViewportSize();
|
|
75470
76283
|
stores.on("store-updated", this, render);
|
|
75471
76284
|
resizeObserver.observe(this.spreadsheetRef.el);
|
|
76285
|
+
registerChartJSExtensions();
|
|
75472
76286
|
});
|
|
75473
76287
|
onWillUnmount(() => {
|
|
75474
76288
|
this.unbindModelEvents();
|
|
75475
76289
|
stores.off("store-updated", this);
|
|
75476
76290
|
resizeObserver.disconnect();
|
|
76291
|
+
unregisterChartJsExtensions();
|
|
75477
76292
|
});
|
|
75478
76293
|
onPatched(() => {
|
|
75479
76294
|
this.checkViewportSize();
|
|
@@ -79976,6 +80791,7 @@ const components = {
|
|
|
79976
80791
|
SidePanelCollapsible,
|
|
79977
80792
|
RadioSelection,
|
|
79978
80793
|
GeoChartRegionSelectSection,
|
|
80794
|
+
ChartDashboardMenu,
|
|
79979
80795
|
};
|
|
79980
80796
|
const hooks = {
|
|
79981
80797
|
useDragAndDropListItems,
|
|
@@ -80020,6 +80836,6 @@ const chartHelpers = { ...CHART_HELPERS, ...CHART_RUNTIME_HELPERS };
|
|
|
80020
80836
|
export { AbstractCellClipboardHandler, AbstractChart, AbstractFigureClipboardHandler, CellErrorType, CommandResult, CorePlugin, CoreViewPlugin, DispatchResult, EvaluationError, Model, PivotRuntimeDefinition, Registry, Revision, SPREADSHEET_DIMENSIONS, Spreadsheet, SpreadsheetPivotTable, UIPlugin, __info__, addFunction, addRenderingLayer, astToFormula, chartHelpers, compile, compileTokens, components, constants, convertAstNodes, coreTypes, findCellInNewZone, functionCache, helpers, hooks, invalidateCFEvaluationCommands, invalidateChartEvaluationCommands, invalidateDependenciesCommands, invalidateEvaluationCommands, iterateAstNodes, links, load, parse, parseTokens, readonlyAllowedCommands, registries, setDefaultSheetViewSize, setTranslationMethod, stores, tokenColors, tokenize };
|
|
80021
80837
|
|
|
80022
80838
|
|
|
80023
|
-
__info__.version = "18.4.0-alpha.
|
|
80024
|
-
__info__.date = "2025-05-
|
|
80025
|
-
__info__.hash = "
|
|
80839
|
+
__info__.version = "18.4.0-alpha.2";
|
|
80840
|
+
__info__.date = "2025-05-12T05:28:06.109Z";
|
|
80841
|
+
__info__.hash = "a11158e";
|