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