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