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