@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.
@@ -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.1
6
- * @date 2025-05-02T12:34:49.374Z
7
- * @hash 50d42e1
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(() => arg.length === dims, _t("[[FUNCTION_NAME]] has mismatched dimensions for argument %s (%s vs %s).", i.toString(), dims.toString(), arg.length.toString())));
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(() => order >= 1, _t("Function [[FUNCTION_NAME]] A regression of order less than 1 cannot be possible."));
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
- if (!granularity) {
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
- if (!granularity) {
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
- /** Return window.Chart, making sure all our extensions are loaded in ChartJS */
10085
- function getChartJSConstructor() {
10086
- if (window.Chart && !window.Chart?.registry.plugins.get("chartShowValuesPlugin")) {
10087
- const extensions = chartJsExtensionRegistry.getAll();
10088
- for (const extension of extensions) {
10089
- if (typeof extension === "function") {
10090
- extension(window.Chart);
10091
- }
10092
- else {
10093
- window.Chart.register(extension);
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", chartShowValuesPlugin);
11721
- chartJsExtensionRegistry.add("waterfallLinesPlugin", waterfallLinesPlugin);
11722
- chartJsExtensionRegistry.add("funnelController", (Chart) => Chart.register(getFunnelChartController()));
11723
- chartJsExtensionRegistry.add("funnelElement", (Chart) => Chart.register(getFunnelChartElement()));
11724
- chartJsExtensionRegistry.add("funnelTooltipPositioner", (Chart) => (Chart.Tooltip.positioners.funnelTooltipPositioner = funnelTooltipPositioner));
11725
- chartJsExtensionRegistry.add("sunburstLabelsPlugin", sunburstLabelsPlugin);
11726
- chartJsExtensionRegistry.add("sunburstHoverPlugin", sunburstHoverPlugin);
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
- const Chart = getChartJSConstructor();
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 Chart = getChartJSConstructor();
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, dataSets, labelRange, getters) {
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
- const Chart = getChartJSConstructor();
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
- layout: getChartLayout(),
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).slice(0, 2);
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
- const figureRegistry = new Registry();
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 getImageMenuRegistry(figureId, onFigureDeleted, env) {
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
- add(key, value) {
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, options = { force: false }) {
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
- assertPositive(_t("The rows argument (%s) must be strictly positive.", _rowsArg.toString()), _rowsArg);
26914
- assertPositive(_t("The columns argument (%s) must be strictly positive.", _rowsArg.toString()), _columnsArg);
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
- assert(() => argOutOfRange.length === 0, _t("The columns arguments must be between -%s and %s (got %s), excluding 0.", _array.length.toString(), _array.length.toString(), argOutOfRange.join(",")));
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
- assert(() => argOutOfRange.length === 0, _t("The rows arguments must be between -%s and %s (got %s), excluding 0.", _array[0].length.toString(), _array[0].length.toString(), argOutOfRange.join(",")));
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
- assert(() => _nbRows >= _array[0].length, _t("The rows arguments (%s) must be greater or equal than the number of rows of the array.", _nbRows.toString()));
26991
- assert(() => _nbColumns >= _array.length, _t("The columns arguments (%s) must be greater or equal than the number of columns of the array.", _nbColumns.toString()));
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
- assertSquareMatrix(_t("The argument square_matrix must have the same number of columns and rows."), _matrix);
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
- assertSquareMatrix(_t("The argument square_matrix must have the same number of columns and rows."), _matrix);
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
- assert(() => _matrix1.length === _matrix2[0].length, _t("In [[FUNCTION_NAME]], the number of columns of the first matrix (%s) must be equal to the \
27131
- number of rows of the second matrix (%s).", _matrix1.length.toString(), _matrix2[0].length.toString()));
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
- assertSameDimensions(_t("All the ranges must have the same dimensions."), ...args);
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
- assertSameDimensions("The arguments array_x and array_y must have the same dimensions.", arrayX, arrayY);
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
- assertSingleColOrRow(_t("Argument range must be a single row or column."), _array);
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
- assertSingleColOrRow(_t("Argument range must be a single row or column."), _array);
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
- assert(() => Math.abs(_value) <= 1, _t("The value (%s) must be between -1 and 1 inclusive.", _value.toString()));
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
- assert(() => _value >= 1, _t("The value (%s) must be greater than or equal to 1.", _value.toString()));
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
- assert(() => Math.abs(_value) > 1, _t("The value (%s) cannot be between -1 and 1 inclusive.", _value.toString()));
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
- assert(() => Math.abs(_value) <= 1, _t("The value (%s) must be between -1 and 1 inclusive.", _value.toString()));
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
- assert(() => _x !== 0 || _y !== 0, _t("Function [[FUNCTION_NAME]] caused a divide by zero error."), CellErrorType.DivisionByZero);
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
- assert(() => Math.abs(_value) < 1, _t("The value (%s) must be between -1 and 1 exclusive.", _value.toString()));
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
- assert(() => _factor >= 0 || _value <= 0, _t("The factor (%s) must be positive when the value (%s) is positive.", _factor.toString(), _value.toString()));
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
- assertNotZero(_angle);
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
- assertNotZero(_value);
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
- assertNotZero(_angle);
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
- assertNotZero(_value);
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
- assert(() => 2 <= _base && _base <= 36, _t("The base (%s) must be between 2 and 36 inclusive.", _base.toString()));
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
- assert(() => DECIMAL_REPRESENTATION.test(_value), _t("The value (%s) must be a valid base %s representation.", _value, _base.toString()));
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
- assert(() => !isNaN(deci), _t("The value (%s) must be a valid base %s representation.", _value, _base.toString()));
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
- assert(() => _factor >= 0 || _value <= 0, _t("The factor (%s) must be positive when the value (%s) is positive.", _factor.toString(), _value.toString()));
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
- assert(() => _value > 0, _t("The value (%s) must be strictly positive.", _value.toString()));
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
- assert(() => _value > 0, _t("The value (%s) must be strictly positive.", _value.toString()));
28024
- assert(() => _base > 0, _t("The base (%s) must be strictly positive.", _base.toString()));
28025
- assert(() => _base !== 1, _t("The base must be different from 1."));
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
- assert(() => divisor !== 0, _t("The divisor must be different from 0."), CellErrorType.DivisionByZero);
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
- assertPositive(_t("The argument dimension must be positive"), _n);
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
- assert(() => _base >= 0 || Number.isInteger(_exponent), _t("The exponent (%s) must be an integer when the base is negative.", _exponent.toString()));
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
- assertPositive(_t("The number of columns (%s) must be positive.", _cols.toString()), _cols);
28187
- assertPositive(_t("The number of rows (%s) must be positive.", _rows.toString()), _rows);
28188
- assert(() => _min <= _max, _t("The maximum (%s) must be greater than or equal to the minimum (%s).", _max.toString(), _min.toString()));
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
- assert(() => Number.isInteger(_min) && Number.isInteger(_max), _t("The maximum (%s) and minimum (%s) must be integers when whole_number is TRUE.", _max.toString(), _min.toString()));
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
- assert(() => _low <= _high, _t("The high (%s) must be greater than or equal to the low (%s).", _high.toString(), _low.toString()));
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
- assertPositive(_t("The number of columns (%s) must be positive.", _columns), _columns);
28363
- assertPositive(_t("The number of rows (%s) must be positive.", _rows), _rows);
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
- assert(() => _value >= 0, _t("The value (%s) must be positive or null.", _value.toString()));
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(() => lenY === lenX, _t("[[FUNCTION_NAME]] has mismatched argument count %s vs %s.", lenY, lenX));
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
- assert(() => count !== 0 && (!isSample || count !== 1), _t("Evaluation of function [[FUNCTION_NAME]] caused a divide by zero error."), CellErrorType.DivisionByZero);
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
- assert(() => count !== 0 && (!isSample || count !== 1), _t("Evaluation of function [[FUNCTION_NAME]] caused a divide by zero error."), CellErrorType.DivisionByZero);
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(() => (isInclusive ? 0 <= _percent && _percent <= 1 : 0 < _percent && _percent < 1), _t("Function [[FUNCTION_NAME]] parameter 2 value is out of range."));
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(() => count !== 0, _t("[[FUNCTION_NAME]] has no valid input data."));
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(() => 1 / (count + 1) <= _percent && _percent <= count / (count + 1), _t("Function [[FUNCTION_NAME]] parameter 2 value is out of range."));
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
- assertNotZero(count);
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
- assertSameDimensions(rangeError, argN, argN1);
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
- assert(() => weight >= 0, negativeWeightError);
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
- assert(() => valueIsNumber === weightIsNumber, _t("[[FUNCTION_NAME]] expects number values."));
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
- assert(() => weight >= 0, negativeWeightError);
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
- assertNotZero(count);
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
- assertNotZero(count);
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
- assertNotZero(count);
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
- assertNotZero(count);
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
- assert(() => result !== undefined, _t("[[FUNCTION_NAME]] has no valid input data."));
28987
- assert(() => count >= _n, _t("Function [[FUNCTION_NAME]] parameter 2 value (%s) is out of range.", _n));
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
- assert(() => result !== undefined, _t("[[FUNCTION_NAME]] has no valid input data."));
29468
- assert(() => count >= _n, _t("Function [[FUNCTION_NAME]] parameter 2 value (%s) is out of range.", _n));
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
- assert(() => cells.length === 1, _t("More than one match found in DGET evaluation."));
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
- assert(() => 0 <= _year && _year <= 9999, _t("The year (%s) must be between 0 and 9999 inclusive.", _year.toString()));
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
- assert(() => result >= 0, _t("The function [[FUNCTION_NAME]] result must be greater than or equal 01/01/1900."));
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
- assert(() => Object.values(TIME_UNIT).includes(_unit), expectStringSetError(Object.values(TIME_UNIT), toString(unit)));
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
- assert(() => _endDate >= _startDate, _t("start_date (%s) should be on or before end_date (%s).", jsStartDate.toLocaleDateString(), jsEndDate.toLocaleDateString()));
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
- assert(() => internalDate !== null, _t("The date_string (%s) cannot be parsed to date/time.", _dateString.toString()));
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(() => (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()));
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
- assert(() => _hour >= 0, _t("The function [[FUNCTION_NAME]] result cannot be negative"));
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
- assert(() => internalDate !== null, _t("The time_string (%s) cannot be parsed to date/time.", _timeString));
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
- assert(() => [1, 2, 3].includes(_type), _t("The type (%s) must be 1, 2 or 3.", _type.toString()));
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
- assert(() => _type === 1 || _type === 2 || (11 <= _type && _type <= 17) || _type === 21, _t("The type (%s) is out of range.", _type.toString()));
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 (typeof weekend.value === "string") {
30615
- assert(() => weekend.value !== "1111111", _t("The weekend must be different from '1111111'."));
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
- assert(() => _startDate >= 0, _t("The start_date (%s) must be positive or null.", _startDate.toString()));
30671
- assert(() => _endDate >= 0, _t("The end_date (%s) must be positive or null.", _endDate.toString()));
30672
- assert(() => 0 <= _dayCountConvention && _dayCountConvention <= 4, _t("The day_count_convention (%s) must be between 0 and 4 inclusive.", _dayCountConvention.toString()));
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(() => value !== undefined, _t("Value for parameter %d is missing, while the function [[FUNCTION_NAME]] expect a number or a range.", i + 1));
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(() => 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));
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
- _conditionsMatrices.map((c) => assertSingleColOrRow(_t("The arguments condition must be a single column or row."), c));
30912
- assertSameDimensions(_t("The arguments conditions must have the same dimensions."), ...conditions);
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
- assert(() => _conditions.every((cond) => cond.length === _array.length), _t("FILTER has mismatched sizes on the range and conditions."));
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
- assert(() => _n >= 0, _t("Wrong value of 'n'. Expected a positive number. Got %s.", _n));
30968
- assert(() => _displayTiesMode >= 0 && _displayTiesMode <= 3, _t("Wrong value of 'display_ties_mode'. Expected a positive number between 0 and 3. Got %s.", _displayTiesMode));
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
- /** Assert maturity date > settlement date */
31074
- function assertMaturityAndSettlementDatesAreValid(settlement, maturity) {
31075
- assert(() => settlement < maturity, _t("The maturity (%s) must be strictly greater than the settlement (%s).", maturity.toString(), settlement.toString()));
31076
- }
31077
- /** Assert settlement date > issue date */
31078
- function assertSettlementAndIssueDatesAreValid(settlement, issue) {
31079
- assert(() => issue < settlement, _t("The settlement date (%s) must be strictly greater than the issue date (%s).", settlement.toString(), issue.toString()));
31080
- }
31081
- /** Assert coupon frequency is in [1, 2, 4] */
31082
- function assertCouponFrequencyIsValid(frequency) {
31083
- assert(() => [1, 2, 4].includes(frequency), _t("The frequency (%s) must be one of %s", frequency.toString(), [1, 2, 4].toString()));
31084
- }
31085
- /** Assert dayCountConvention is between 0 and 4 */
31086
- function assertDayCountConventionIsValid(dayCountConvention) {
31087
- assert(() => 0 <= dayCountConvention && dayCountConvention <= 4, _t("The day_count_convention (%s) must be between 0 and 4 inclusive.", dayCountConvention.toString()));
31088
- }
31089
- function assertRedemptionStrictlyPositive(redemption) {
31090
- assert(() => redemption > 0, _t("The redemption (%s) must be strictly positive.", redemption.toString()));
31091
- }
31092
- function assertPriceStrictlyPositive(price) {
31093
- assert(() => price > 0, _t("The price (%s) must be strictly positive.", price.toString()));
31094
- }
31095
- function assertNumberOfPeriodsStrictlyPositive(nPeriods) {
31096
- assert(() => nPeriods > 0, _t("The number_of_periods (%s) must be greater than 0.", nPeriods.toString()));
31097
- }
31098
- function assertRateStrictlyPositive(rate) {
31099
- assert(() => rate > 0, _t("The rate (%s) must be strictly positive.", rate.toString()));
31100
- }
31101
- function assertLifeStrictlyPositive(life) {
31102
- assert(() => life > 0, _t("The life (%s) must be strictly positive.", life.toString()));
31103
- }
31104
- function assertCostStrictlyPositive(cost) {
31105
- assert(() => cost > 0, _t("The cost (%s) must be strictly positive.", cost.toString()));
31106
- }
31107
- function assertPurchaseDatePositiveOrZero(purchaseDate) {
31108
- assert(() => purchaseDate >= 0, _t("The purchase_date (%s) must be positive or null.", purchaseDate.toString()));
31109
- }
31110
- function assertIssuePositiveOrZero(issue) {
31111
- assert(() => issue >= 0, _t("The issue (%s) must be positive or null.", issue.toString()));
31112
- }
31113
- function assertCostPositiveOrZero(cost) {
31114
- assert(() => cost >= 0, _t("The cost (%s) must be positive or null.", cost.toString()));
31115
- }
31116
- function assertPeriodStrictlyPositive(period) {
31117
- assert(() => period > 0, _t("The period (%s) must be strictly positive.", period.toString()));
31118
- }
31119
- function assertPeriodPositiveOrZero(period) {
31120
- assert(() => period >= 0, _t("The period (%s) must be positive or null.", period.toString()));
31121
- }
31122
- function assertSalvagePositiveOrZero(salvage) {
31123
- assert(() => salvage >= 0, _t("The salvage (%s) must be positive or null.", salvage.toString()));
31124
- }
31125
- function assertSalvageSmallerOrEqualThanCost(salvage, cost) {
31126
- assert(() => salvage <= cost, _t("The salvage (%s) must be smaller or equal than the cost (%s).", salvage.toString(), cost.toString()));
31127
- }
31128
- function assertPresentValueStrictlyPositive(pv) {
31129
- assert(() => pv > 0, _t("The present value (%s) must be strictly positive.", pv.toString()));
31130
- }
31131
- function assertPeriodSmallerOrEqualToLife(period, life) {
31132
- assert(() => period <= life, _t("The period (%s) must be less than or equal life (%s).", period.toString(), life.toString()));
31133
- }
31134
- function assertInvestmentStrictlyPositive(investment) {
31135
- assert(() => investment > 0, _t("The investment (%s) must be strictly positive.", investment.toString()));
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 assertDiscountStrictlySmallerThanOne(discount) {
31141
- assert(() => discount < 1, _t("The discount (%s) must be smaller than 1.", discount.toString()));
31513
+ function isInvalidDayCountConvention(dayCountConvention) {
31514
+ return ![0, 1, 2, 3, 4].includes(dayCountConvention);
31142
31515
  }
31143
- function assertDeprecationFactorStrictlyPositive(factor) {
31144
- assert(() => factor > 0, _t("The depreciation factor (%s) must be strictly positive.", factor.toString()));
31516
+ function isInvalidFrequency(frequency) {
31517
+ return ![1, 2, 4].includes(frequency);
31145
31518
  }
31146
- function assertSettlementLessThanOneYearBeforeMaturity(settlement, maturity, locale) {
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
- assert(() => endDate.getTime() <= startDatePlusOneYear.getTime(), _t("The settlement date (%s) must at most one year after the maturity date (%s).", settlement.toString(), maturity.toString()));
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(() => count < maxIterations && nanFallback !== undefined, _t("Function [[FUNCTION_NAME]] didn't find any result."));
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(() => count < maxIterations, _t("Function [[FUNCTION_NAME]] didn't find any result."));
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
- assertIssuePositiveOrZero(start);
31262
- assertSettlementAndIssueDatesAreValid(end, start);
31263
- assertDayCountConventionIsValid(_dayCountConvention);
31264
- assertRedemptionStrictlyPositive(_redemption);
31265
- assertRateStrictlyPositive(_rate);
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
- assertCostStrictlyPositive(_cost);
31295
- assertPurchaseDatePositiveOrZero(_purchaseDate);
31296
- assertSalvagePositiveOrZero(_salvage);
31297
- assertSalvageSmallerOrEqualThanCost(_salvage, _cost);
31298
- assertPeriodPositiveOrZero(_period);
31299
- assertRateStrictlyPositive(_rate);
31300
- assertDayCountConventionIsValid(_dayCountConvention);
31301
- assert(() => _purchaseDate <= _firstPeriodEnd, _t("The purchase_date (%s) must be before the first_period_end (%s).", _purchaseDate.toString(), _firstPeriodEnd.toString()));
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
- assertMaturityAndSettlementDatesAreValid(start, end);
31340
- assertCouponFrequencyIsValid(_frequency);
31341
- assertDayCountConventionIsValid(_dayCountConvention);
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
- assertMaturityAndSettlementDatesAreValid(start, end);
31366
- assertCouponFrequencyIsValid(_frequency);
31367
- assertDayCountConventionIsValid(_dayCountConvention);
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 - couponBeforeStart;
31740
+ return start - _couponBeforeStart;
31371
31741
  }
31372
31742
  if (_dayCountConvention === 4) {
31373
- const yearFrac = getYearFrac(couponBeforeStart, start, _dayCountConvention);
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(couponBeforeStart, this.locale);
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
- assertMaturityAndSettlementDatesAreValid(start, end);
31422
- assertCouponFrequencyIsValid(_frequency);
31423
- assertDayCountConventionIsValid(_dayCountConvention);
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 couponAfterStart - start;
31803
+ return _couponAfterStart - start;
31427
31804
  }
31428
31805
  if (_dayCountConvention === 4) {
31429
- const yearFrac = getYearFrac(start, couponAfterStart, _dayCountConvention);
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
- assertMaturityAndSettlementDatesAreValid(start, end);
31451
- assertCouponFrequencyIsValid(_frequency);
31452
- assertDayCountConventionIsValid(_dayCountConvention);
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
- assertMaturityAndSettlementDatesAreValid(start, end);
31476
- assertCouponFrequencyIsValid(_frequency);
31477
- assertDayCountConventionIsValid(_dayCountConvention);
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
- assertMaturityAndSettlementDatesAreValid(start, end);
31502
- assertCouponFrequencyIsValid(_frequency);
31503
- assertDayCountConventionIsValid(_dayCountConvention);
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
- assertFirstAndLastPeriodsAreValid(first, last, n);
31535
- assertRateStrictlyPositive(r);
31536
- assertPresentValueStrictlyPositive(pv);
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
- assertFirstAndLastPeriodsAreValid(first, last, n);
31566
- assertRateStrictlyPositive(r);
31567
- assertPresentValueStrictlyPositive(pv);
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
- assertCostPositiveOrZero(_cost);
31597
- assertSalvagePositiveOrZero(_salvage);
31598
- assertPeriodStrictlyPositive(_period);
31599
- assertLifeStrictlyPositive(_life);
31600
- assert(() => 1 <= _month && _month <= 12, _t("The month (%s) must be between 1 and 12 inclusive.", _month.toString()));
31601
- assert(() => _period <= lifeLimit, _t("The period (%s) must be less than or equal to %s.", _period.toString(), lifeLimit.toString()));
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
- function ddb(cost, salvage, life, period, factor) {
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: ddb(_cost, _salvage, _life, _period, _factor),
31664
- format: "#,##0.00",
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
- assertMaturityAndSettlementDatesAreValid(_settlement, _maturity);
31689
- assertDayCountConventionIsValid(_dayCountConvention);
31690
- assertPriceStrictlyPositive(_price);
31691
- assertRedemptionStrictlyPositive(_redemption);
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
- assert(() => _unit > 0, _t("The unit (%s) must be strictly positive.", _unit.toString()));
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
- assert(() => _unit > 0, _t("The unit (%s) must be strictly positive.", _unit.toString()));
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
- assertMaturityAndSettlementDatesAreValid(start, end);
31768
- assertCouponFrequencyIsValid(_frequency);
31769
- assertDayCountConventionIsValid(_dayCountConvention);
31770
- assert(() => _rate >= 0, _t("The rate (%s) must be positive or null.", _rate.toString()));
31771
- assert(() => _yield >= 0, _t("The yield (%s) must be positive or null.", _yield.toString()));
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
- assert(() => nominal > 0, _t("The nominal rate (%s) must be strictly greater than 0.", nominal.toString()));
31804
- assert(() => periods > 0, _t("The number of periods by year (%s) must strictly greater than 0.", periods.toString()));
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
- assertMaturityAndSettlementDatesAreValid(_settlement, _maturity);
31879
- assertInvestmentStrictlyPositive(_investment);
31880
- assertRedemptionStrictlyPositive(_redemption);
31881
- assertDayCountConventionIsValid(_dayCountConvention);
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
- assertRateGuessStrictlyGreaterThanMinusOne(_rateGuess);
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
- assert(() => positive && negative, _t("The cashflow_amounts must include negative and positive values."));
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
- assert(() => nOfPeriods !== 0, _t("The number of periods must be different than 0.", nOfPeriods.toString()));
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
- assert(() => pv !== 0 && fv !== 0, _t("There must be both positive and negative values in cashflow_amounts."));
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
- assert(() => effective > 0, _t("The effective rate (%s) must must strictly greater than 0.", effective.toString()));
32092
- assert(() => periods > 0, _t("The number of periods by year (%s) must strictly greater than 0.", periods.toString()));
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
- assert(() => _discount !== -1, _t("The discount (%s) must be different from -1.", _discount.toString()));
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
- assertRateStrictlyPositive(_rate);
32183
- assert(() => _presentValue > 0, _t("The present_value (%s) must be strictly positive.", _presentValue.toString()));
32184
- assert(() => _futureValue > 0, _t("The future_value (%s) must be strictly positive.", _futureValue.toString()));
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
- assertNumberOfPeriodsStrictlyPositive(n);
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
- assertNumberOfPeriodsStrictlyPositive(n);
32232
- assert(() => per > 0 && per <= n, _t("The period must be between 1 and number_of_periods (%s)", n));
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
- assertMaturityAndSettlementDatesAreValid(_settlement, _maturity);
32321
- assertCouponFrequencyIsValid(_frequency);
32322
- assertDayCountConventionIsValid(_dayCountConvention);
32323
- assert(() => _rate >= 0, _t("The rate (%s) must be positive or null.", _rate.toString()));
32324
- assert(() => _yield >= 0, _t("The yield (%s) must be positive or null.", _yield.toString()));
32325
- assertRedemptionStrictlyPositive(_redemption);
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
- assertMaturityAndSettlementDatesAreValid(_settlement, _maturity);
32366
- assertDayCountConventionIsValid(_dayCountConvention);
32367
- assertDiscountStrictlyPositive(_discount);
32368
- assertRedemptionStrictlyPositive(_redemption);
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
- assertSettlementAndIssueDatesAreValid(_settlement, _issue);
32404
- assertMaturityAndSettlementDatesAreValid(_settlement, _maturity);
32405
- assertDayCountConventionIsValid(_dayCount);
32406
- assert(() => _rate >= 0, _t("The rate (%s) must be positive or null.", _rate.toString()));
32407
- assert(() => _yield >= 0, _t("The yield (%s) must be positive or null.", _yield.toString()));
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
- assertNumberOfPeriodsStrictlyPositive(n);
32466
- assert(() => [payment, pv, fv].some((val) => val > 0) && [payment, pv, fv].some((val) => val < 0), _t("There must be both positive and negative values in [payment_amount, present_value, future_value].", n.toString()));
32467
- assertRateGuessStrictlyGreaterThanMinusOne(guess);
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
- assertMaturityAndSettlementDatesAreValid(_settlement, _maturity);
32511
- assertDayCountConventionIsValid(_dayCountConvention);
32512
- assertInvestmentStrictlyPositive(_investment);
32513
- assertDiscountStrictlyPositive(_discount);
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
- assertNumberOfPeriodsStrictlyPositive(n);
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
- assertPeriodStrictlyPositive(_period);
32594
- assertLifeStrictlyPositive(_life);
32595
- assertPeriodSmallerOrEqualToLife(_period, _life);
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
- assertMaturityAndSettlementDatesAreValid(start, end);
32641
- assertSettlementLessThanOneYearBeforeMaturity(start, end, this.locale);
32642
- assertDiscountStrictlyPositive(disc);
32643
- assertDiscountStrictlySmallerThanOne(disc);
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
- assertMaturityAndSettlementDatesAreValid(start, end);
32663
- assertSettlementLessThanOneYearBeforeMaturity(start, end, this.locale);
32664
- assertDiscountStrictlyPositive(disc);
32665
- assertDiscountStrictlySmallerThanOne(disc);
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
- assertMaturityAndSettlementDatesAreValid(start, end);
32720
- assertSettlementLessThanOneYearBeforeMaturity(start, end, this.locale);
32721
- assertPriceStrictlyPositive(p);
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
- assertCostPositiveOrZero(_cost);
32768
- assertSalvagePositiveOrZero(_salvage);
32769
- assertStartAndEndPeriodAreValid(_startPeriod, _endPeriod, _life);
32770
- assertDeprecationFactorStrictlyPositive(_factor);
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
- assertCashFlowsAndDatesHaveSameDimension(cashflowAmounts, cashflowDates);
32823
- assertCashFlowsHavePositiveAndNegativesValues(_cashFlows);
32824
- assertEveryDateGreaterThanFirstDateOfCashFlowDates(_dates);
32825
- assertRateGuessStrictlyGreaterThanMinusOne(guess);
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
- const _cashFlows = isMatrix(cashflowAmounts)
32891
- ? cashflowAmounts.flat().map((data) => strictToNumber(data, this.locale))
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
- else {
32900
- assert(() => _cashFlows.length === _dates.length, _t("There must be the same number of values in cashflow_amounts and cashflow_dates."));
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
- assertMaturityAndSettlementDatesAreValid(_settlement, _maturity);
32964
- assertCouponFrequencyIsValid(_frequency);
32965
- assertDayCountConventionIsValid(_dayCountConvention);
32966
- assert(() => _rate >= 0, _t("The rate (%s) must be positive or null.", _rate.toString()));
32967
- assertPriceStrictlyPositive(_price);
32968
- assertRedemptionStrictlyPositive(_redemption);
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
- assertMaturityAndSettlementDatesAreValid(_settlement, _maturity);
33036
- assertDayCountConventionIsValid(_dayCountConvention);
33037
- assertPriceStrictlyPositive(_price);
33038
- assertRedemptionStrictlyPositive(_redemption);
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
- assertMaturityAndSettlementDatesAreValid(_settlement, _maturity);
33073
- assertDayCountConventionIsValid(_dayCountConvention);
33074
- assert(() => _settlement >= _issue, _t("The settlement (%s) must be greater than or equal to the issue (%s).", _settlement.toString(), _issue.toString()));
33075
- assert(() => _rate >= 0, _t("The rate (%s) must be positive or null.", _rate.toString()));
33076
- assertPriceStrictlyPositive(_price);
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
- assert(() => CELL_INFO_TYPES.includes(_info), _t("The info_type should be one of %s.", CELL_INFO_TYPES.join(", ")));
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
- assert(() => foundBoolean, _t("[[FUNCTION_NAME]] has no valid input data."));
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.value = "";
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.value = "";
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.value = "";
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
- assert(() => values.length % 2 === 0, _t("Wrong number of arguments. Expected an even number of arguments."));
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.value = "";
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
- assert(() => foundBoolean, _t("[[FUNCTION_NAME]] has no valid input data."));
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
- assert(() => foundBoolean, _t("[[FUNCTION_NAME]] has no valid input data."));
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
- assertNumberGreaterThanOrEqualToOne(rowNumber);
34264
- assertNumberGreaterThanOrEqualToOne(colNumber);
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
- assert(() => [1, 2, 3, 4].includes(_absoluteRelativeMode), expectNumberRangeError(1, 4, _absoluteRelativeMode));
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
- assert(() => column !== undefined, "In this context, the function [[FUNCTION_NAME]] needs to have a cell or range in parameter.");
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
- assert(() => 1 <= _index && _index <= _range[0].length, _t("[[FUNCTION_NAME]] evaluates to an out of bounds range."));
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
- assert(() => _column >= 0 &&
34366
- _column - 1 < _reference.length &&
34367
- _row >= 0 &&
34368
- _row - 1 < _reference[0].length, _t("Index out of range."));
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
- assert(() => nbCol === 1 || nbRow === 1, _t("The result_range must be a single row or a single column."));
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
- assert(() => index <= nbCol - 1, _t("[[FUNCTION_NAME]] evaluates to an out of range row value %s.", (index + 1).toString()));
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
- assert(() => index <= nbRow - 1, _t("[[FUNCTION_NAME]] evaluates to an out of range column value %s.", (index + 1).toString()));
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
- assert(() => nbCol === 1 || nbRow === 1, _t("The range must be a single row or a single column."));
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
- assert(() => row !== undefined, "In this context, the function [[FUNCTION_NAME]] needs to have a cell or range in parameter.");
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
- assert(() => 1 <= _index && _index <= _range.length, _t("[[FUNCTION_NAME]] evaluates to an out of bounds range."));
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
- assert(() => _lookupRange.length === 1 || _lookupRange[0].length === 1, _t("lookup_range should be either a single row or single column."));
34606
- assert(() => [-1, 1, -2, 2].includes(_searchMode), _t("search_mode should be a value in [-1, 1, -2, 2]."));
34607
- assert(() => [-1, 0, 1, 2].includes(_matchMode), _t("match_mode should be a value in [-1, 0, 1, 2]."));
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
- assert(() => !(_matchMode === 2 && [-2, 2].includes(_searchMode)), _t("the search and match mode combination is not supported for XLOOKUP evaluation."));
34610
- assert(() => lookupDirection === "col"
34611
- ? _returnRange[0].length === _lookupRange[0].length
34612
- : _returnRange.length === _lookupRange.length, _t("return_range should have the same dimensions as lookup_range."));
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
- assertPositive(_t("Height value is %(_height)s. It should be greater than or equal to 1.", { _height }), _height);
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
- assertPositive(_t("Width value is %(_width)s. It should be greater than or equal to 1.", { _width }), _width);
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
- assert(() => _divisor !== 0, _t("The divisor must be different from zero."), CellErrorType.DivisionByZero);
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
- assert(() => _tableNumber >= 1, _t("The table_number (%s) is out of range.", _tableNumber.toString()));
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
- assert(() => _textToSearch !== "", _t("The text_to_search must be non-empty."));
35544
- assert(() => _startingAt >= 1, _t("The starting_at (%s) must be greater than or equal to 1.", _startingAt.toString()));
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
- assert(() => result >= 0, _t("In [[FUNCTION_NAME]] evaluation, cannot find '%s' within '%s'.", _searchFor.toString(), _textToSearch));
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
- assert(() => _numberOfCharacters >= 0, _t("The number_of_characters (%s) must be positive or null.", _numberOfCharacters.toString()));
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
- assert(() => _starting_at >= 1, _t("The starting_at argument (%s) must be positive greater than one.", _starting_at.toString()));
35619
- assert(() => _extract_length >= 0, _t("The extract_length argument (%s) must be positive or null.", _extract_length.toString()));
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
- assert(() => _position >= 1, _t("The position (%s) must be greater than or equal to 1.", _position.toString()));
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
- assert(() => _numberOfCharacters >= 0, _t("The number_of_characters (%s) must be positive or null.", _numberOfCharacters.toString()));
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
- assert(() => _delimiter.length > 0, _t("The _delimiter (%s) must be not be empty.", _delimiter));
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
- assert(() => _occurrenceNumber >= 0, _t("The occurrenceNumber (%s) must be positive or null.", _occurrenceNumber.toString()));
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.add(name, descr);
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
- .error-icon {
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().slice(0, 1);
47968
+ return this.getDataSeriesRanges();
47199
47969
  }
47200
- onDataSeriesConfirmed() {
47201
- this.dataSets = spreadRange(this.env.model.getters, this.dataSets).slice(0, 1);
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 (!granularity || !(granularity in MAP_VALUE_DIMENSION_DATE)) {
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.getters.getFigureSheetId(cmd.figureId)
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
- return this.sheets[cmd.sheetIdTo] ? "DuplicatedSheetId" /* CommandResult.DuplicatedSheetId */ : "Success" /* CommandResult.Success */;
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 getUniqueText(baseName, names, {
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
- const baseName = _t("Copy of %s", sheetName);
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
- add(name, value) {
73357
+ replace(name, value) {
72548
73358
  const component = { ...value, id: this.uuidGenerator.uuidv4() };
72549
- return super.add(name, component);
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.1";
80071
- __info__.date = "2025-05-02T12:34:49.374Z";
80072
- __info__.hash = "50d42e1";
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);