@odoo/o-spreadsheet 19.5.0-alpha.12 → 19.5.0-alpha.13

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 19.5.0-alpha.12
6
- * @date 2026-08-19T09:47:03.774Z
7
- * @hash 68ca68e
5
+ * @version 19.5.0-alpha.13
6
+ * @date 2026-08-21T15:33:44.046Z
7
+ * @hash a73559e
8
8
  */
9
9
 
10
10
  Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });
@@ -402,7 +402,13 @@ function createAction(item) {
402
402
  if (isEnabled(env)) return item.execute(env, isMiddleClick);
403
403
  } : void 0,
404
404
  children: children ? (env) => {
405
- return children.map((child) => typeof child === "function" ? child(env) : child).flat().map(createAction).sort((a, b) => a.sequence - b.sequence);
405
+ const uniqueChildren = {};
406
+ children.flatMap((child) => typeof child === "function" ? child(env) : child).forEach((childSpec) => {
407
+ const childAction = createAction(childSpec);
408
+ if (uniqueChildren[childAction.id]) throw new Error(`Duplicate child action id "${childAction.id}" in action "${itemId}".`);
409
+ uniqueChildren[childAction.id] = childAction;
410
+ });
411
+ return Object.values(uniqueChildren).sort((a, b) => a.sequence - b.sequence);
406
412
  } : () => [],
407
413
  isReadonlyAllowed: item.isReadonlyAllowed || false,
408
414
  isEnabledOnLockedSheet: item.isEnabledOnLockedSheet || false,
@@ -5408,7 +5414,7 @@ function _roundFormat(internalFormat) {
5408
5414
  }
5409
5415
  function humanizeNumber({ value, format }, locale) {
5410
5416
  const numberValue = tryToNumber(value, locale);
5411
- if (numberValue === void 0) return "";
5417
+ if (numberValue === void 0) return value?.toString() || "";
5412
5418
  let numberFormat = format;
5413
5419
  if (Math.abs(numberValue) < 1e3) {
5414
5420
  const hasDecimal = numberValue % 1 !== 0;
@@ -8123,8 +8129,6 @@ let CommandResult = /* @__PURE__ */ function(CommandResult) {
8123
8129
  CommandResult["InvalidXRange"] = "InvalidXRange";
8124
8130
  CommandResult["InvalidLabelRange"] = "InvalidLabelRange";
8125
8131
  CommandResult["InvalidBubbleSizeRange"] = "InvalidBubbleSizeRange";
8126
- CommandResult["InvalidScorecardKeyValue"] = "InvalidScorecardKeyValue";
8127
- CommandResult["InvalidScorecardBaseline"] = "InvalidScorecardBaseline";
8128
8132
  CommandResult["InvalidGaugeDataRange"] = "InvalidGaugeDataRange";
8129
8133
  CommandResult["EmptyGaugeRangeMin"] = "EmptyGaugeRangeMin";
8130
8134
  CommandResult["GaugeRangeMinNaN"] = "GaugeRangeMinNaN";
@@ -12011,6 +12015,110 @@ var ChartJsComponent = class extends Component {
12011
12015
  }
12012
12016
  };
12013
12017
 
12018
+ //#endregion
12019
+ //#region src/functions/helper_matrices.ts
12020
+ function getUnitMatrix(n) {
12021
+ const matrix = Array(n);
12022
+ for (let i = 0; i < n; i++) {
12023
+ matrix[i] = Array(n).fill(0);
12024
+ matrix[i][i] = 1;
12025
+ }
12026
+ return matrix;
12027
+ }
12028
+ /**
12029
+ * Invert a matrix and compute its determinant using Gaussian Elimination.
12030
+ *
12031
+ * The Matrix should be a square matrix, and should be indexed [col][row] instead of the
12032
+ * standard mathematical indexing [row][col].
12033
+ */
12034
+ function invertMatrix(M) {
12035
+ if (M.length < 1 || M[0].length < 1) throw new Error("invertMatrix: an empty matrix cannot be inverted.");
12036
+ if (M.length !== M[0].length) throw new Error("invertMatrix: only square matrices are invertible");
12037
+ let determinant = 1;
12038
+ const dim = M.length;
12039
+ const I = getUnitMatrix(dim);
12040
+ const C = M.map((row) => row.slice());
12041
+ for (let pivot = 0; pivot < dim; pivot++) {
12042
+ let diagonalElement = C[pivot][pivot];
12043
+ if (diagonalElement === 0) {
12044
+ for (let row = pivot + 1; row < dim; row++) if (C[pivot][row] !== 0) {
12045
+ swapMatrixRows(C, pivot, row);
12046
+ swapMatrixRows(I, pivot, row);
12047
+ determinant *= -1;
12048
+ break;
12049
+ }
12050
+ diagonalElement = C[pivot][pivot];
12051
+ if (diagonalElement === 0) return { determinant: 0 };
12052
+ }
12053
+ for (let col = 0; col < dim; col++) {
12054
+ C[col][pivot] = C[col][pivot] / diagonalElement;
12055
+ I[col][pivot] = I[col][pivot] / diagonalElement;
12056
+ }
12057
+ determinant *= diagonalElement;
12058
+ for (let row = 0; row < dim; row++) {
12059
+ if (row === pivot) continue;
12060
+ const e = C[pivot][row];
12061
+ for (let col = 0; col < dim; col++) {
12062
+ C[col][row] -= e * C[col][pivot];
12063
+ I[col][row] -= e * I[col][pivot];
12064
+ }
12065
+ }
12066
+ }
12067
+ return {
12068
+ inverted: I,
12069
+ determinant
12070
+ };
12071
+ }
12072
+ function swapMatrixRows(matrix, row1, row2) {
12073
+ for (let i = 0; i < matrix.length; i++) {
12074
+ const tmp = matrix[i][row1];
12075
+ matrix[i][row1] = matrix[i][row2];
12076
+ matrix[i][row2] = tmp;
12077
+ }
12078
+ }
12079
+ /**
12080
+ * Matrix multiplication of 2 matrices.
12081
+ * ex: matrix1 : n x l, matrix2 : m x n => result : m x l
12082
+ *
12083
+ * Note: we use indexing [col][row] instead of the standard mathematical notation [row][col]
12084
+ */
12085
+ function multiplyMatrices(matrix1, matrix2) {
12086
+ if (matrix1.length < 1 || matrix2.length < 1) throw new Error("multiplyMatrices: empty matrices cannot be multiplied.");
12087
+ if (matrix1.length !== matrix2[0].length) throw new Error("multiplyMatrices: incompatible matrices size.");
12088
+ const rowsM1 = matrix1[0].length;
12089
+ const colsM2 = matrix2.length;
12090
+ const n = matrix1.length;
12091
+ const result = Array(colsM2);
12092
+ for (let col = 0; col < colsM2; col++) {
12093
+ result[col] = Array(rowsM1);
12094
+ for (let row = 0; row < rowsM1; row++) {
12095
+ let sum = 0;
12096
+ for (let k = 0; k < n; k++) sum += matrix1[k][row] * matrix2[col][k];
12097
+ result[col][row] = sum;
12098
+ }
12099
+ }
12100
+ return result;
12101
+ }
12102
+ /**
12103
+ * Return the input if it's a scalar or the first element of the input if it's a matrix.
12104
+ */
12105
+ function toScalar(arg) {
12106
+ if (!isMatrix(arg)) return arg;
12107
+ if (!isSingleElementMatrix(arg)) throw new Error("The value should be a scalar or a 1x1 matrix");
12108
+ return arg[0][0];
12109
+ }
12110
+ function isSingleElementMatrix(matrix) {
12111
+ return matrix.length === 1 && matrix[0].length === 1;
12112
+ }
12113
+ function isMultipleElementMatrix(arg) {
12114
+ return isMatrix(arg) && !isSingleElementMatrix(arg);
12115
+ }
12116
+ function getMatrixArgIndices(args) {
12117
+ const indices = [];
12118
+ for (let i = 0; i < args.length; i++) if (isMultipleElementMatrix(args[i])) indices.push(i);
12119
+ return indices;
12120
+ }
12121
+
12014
12122
  //#endregion
12015
12123
  //#region src/helpers/figures/charts/abstract_chart.ts
12016
12124
  var AbstractChart = class {
@@ -12026,11 +12134,42 @@ var AbstractChart = class {
12026
12134
 
12027
12135
  //#endregion
12028
12136
  //#region src/helpers/figures/charts/scorecard_chart.ts
12137
+ function getData$1(value, getters, sheetId) {
12138
+ if (!value) return {
12139
+ scalar: void 0,
12140
+ range: void 0
12141
+ };
12142
+ if (!isFormula(value)) return {
12143
+ scalar: { value },
12144
+ range: void 0
12145
+ };
12146
+ const result = getters.evaluateFormulaResult(sheetId, value);
12147
+ let scalar = isMultipleElementMatrix(result) ? result[0][0] : toScalar(result);
12148
+ let range = void 0;
12149
+ const xc = getFormulaRangeXc(value);
12150
+ if (xc) {
12151
+ range = createValidRange(getters, sheetId, xc);
12152
+ if (range) {
12153
+ if (getters.getEvaluatedCell({
12154
+ sheetId: range.sheetId,
12155
+ col: range.zone.left,
12156
+ row: range.zone.top
12157
+ }).type === "empty") scalar = void 0;
12158
+ }
12159
+ }
12160
+ return {
12161
+ scalar,
12162
+ range
12163
+ };
12164
+ }
12029
12165
  function getBaselineText(baseline, keyValue, baselineMode, humanizeNumbers, locale) {
12030
12166
  if (!baseline) return "";
12031
- else if (baselineMode === "text" || keyValue?.type !== "number" || baseline.type !== "number") {
12167
+ else if (baselineMode === "text" || typeof keyValue?.value !== "number" || typeof baseline.value !== "number") {
12032
12168
  if (humanizeNumbers) return humanizeNumber(baseline, locale);
12033
- return baseline.formattedValue;
12169
+ return formatValue(baseline.value, {
12170
+ format: baseline.format,
12171
+ locale
12172
+ });
12034
12173
  }
12035
12174
  let { value, format } = baseline;
12036
12175
  if (baselineMode === "progress") {
@@ -12051,29 +12190,31 @@ function getBaselineText(baseline, keyValue, baselineMode, humanizeNumbers, loca
12051
12190
  locale
12052
12191
  });
12053
12192
  }
12054
- function getKeyValueText(keyValueCell, humanizeNumbers, locale) {
12055
- if (!keyValueCell) return "";
12056
- if (humanizeNumbers) return humanizeNumber(keyValueCell, locale);
12057
- return keyValueCell.formattedValue ?? String(keyValueCell.value ?? "");
12193
+ function getKeyValueText(keyValue, humanizeNumbers, locale) {
12194
+ if (keyValue?.value === void 0 || keyValue?.value === null) return "";
12195
+ if (humanizeNumbers) return humanizeNumber(keyValue, locale);
12196
+ return keyValue.format ? formatValue(keyValue.value, {
12197
+ format: keyValue.format,
12198
+ locale
12199
+ }) : String(keyValue.value ?? "");
12058
12200
  }
12059
12201
  function getBaselineColor(baseline, baselineMode, keyValue, colorUp, colorDown) {
12060
- if (baselineMode === "text" || baselineMode === "progress" || baseline?.type !== "number" || keyValue?.type !== "number") return;
12202
+ if (baselineMode === "text" || baselineMode === "progress" || typeof baseline?.value !== "number" || typeof keyValue?.value !== "number") return;
12061
12203
  const diff = keyValue.value - baseline.value;
12062
12204
  if (diff > 0) return colorUp;
12063
12205
  else if (diff < 0) return colorDown;
12064
12206
  }
12065
12207
  function getBaselineArrowDirection(baseline, keyValue, baselineMode) {
12066
- if (baselineMode === "text" || baseline?.type !== "number" || keyValue?.type !== "number") return "neutral";
12208
+ if (baselineMode === "text" || typeof baseline?.value !== "number" || typeof keyValue?.value !== "number") return "neutral";
12067
12209
  const diff = keyValue.value - baseline.value;
12068
12210
  if (diff > 0) return "up";
12069
12211
  else if (diff < 0) return "down";
12070
12212
  return "neutral";
12071
12213
  }
12072
- function checkKeyValue(definition) {
12073
- return definition.keyValue && !rangeReference.test(definition.keyValue) ? "InvalidScorecardKeyValue" : "Success";
12074
- }
12075
- function checkBaseline(definition) {
12076
- return definition.baseline && !rangeReference.test(definition.baseline) ? "InvalidScorecardBaseline" : "Success";
12214
+ function getFormulaRangeXc(formula) {
12215
+ if (!formula || !isFormula(formula)) return;
12216
+ const content = formula.slice(1);
12217
+ return rangeReference.test(content) ? content : void 0;
12077
12218
  }
12078
12219
  const Path2DConstructor = globalThis.Path2D;
12079
12220
  const arrowDownPath = Path2DConstructor && new Path2DConstructor("M8.6 4.8a.5.5 0 0 1 0 .75l-3.9 3.9a.5 .5 0 0 1 -.75 0l-3.8 -3.9a.5 .5 0 0 1 0 -.75l.4-.4a.5.5 0 0 1 .75 0l2.3 2.4v-5.7c0-.25.25-.5.5-.5h.6c.25 0 .5.25.5.5v5.8l2.3 -2.4a.5.5 0 0 1 .75 0z");
@@ -12090,84 +12231,76 @@ const ScorecardChart$1 = {
12090
12231
  "baselineColorUp",
12091
12232
  "baselineColorDown"
12092
12233
  ],
12093
- fromStrDefinition(definition, sheetId, getters) {
12094
- const baseline = createValidRange(getters, sheetId, definition.baseline);
12095
- const keyValue = createValidRange(getters, sheetId, definition.keyValue);
12234
+ fromStrDefinition: (definition) => definition,
12235
+ validateDefinition(validator, definition) {
12236
+ return "Success";
12237
+ },
12238
+ copyInSheetId: (definition, sheetIdFrom, sheetIdTo, getters) => {
12239
+ const adaptFormula = (formula) => getters.copyFormulaStringForSheet(sheetIdFrom, sheetIdTo, formula, "keepSameReference");
12096
12240
  return {
12097
12241
  ...definition,
12098
- baseline,
12099
- keyValue
12242
+ keyValue: definition.keyValue ? adaptFormula(definition.keyValue) : definition.keyValue,
12243
+ baseline: definition.baseline ? adaptFormula(definition.baseline) : definition.baseline
12100
12244
  };
12101
12245
  },
12102
- validateDefinition(validator, definition) {
12103
- return validator.checkValidations(definition, checkKeyValue, checkBaseline);
12104
- },
12105
- copyInSheetId: (definition) => definition,
12106
12246
  getDefinitionFromContextCreation(context, dataSourceBuilder) {
12247
+ const dataRange = context.dataSource?.type === "range" ? context.dataSource?.dataSets?.[0]?.dataRange : void 0;
12248
+ const keyValue = (context.scorecardKeyValueFormula === void 0 || getFormulaRangeXc(context.scorecardKeyValueFormula)) && dataRange ? `=${dataRange}` : context.scorecardKeyValueFormula;
12249
+ const baseline = (context.scorecardBaselineFormula === void 0 || getFormulaRangeXc(context.scorecardBaselineFormula)) && context.auxiliaryRange ? `=${context.auxiliaryRange}` : context.scorecardBaselineFormula;
12107
12250
  return {
12108
12251
  background: context.background,
12109
12252
  type: "scorecard",
12110
- keyValue: context.dataSource?.type === "range" ? context.dataSource?.dataSets?.[0]?.dataRange : void 0,
12253
+ keyValue,
12111
12254
  title: context.title || { text: "" },
12112
12255
  baselineMode: DEFAULT_SCORECARD_BASELINE_MODE,
12113
12256
  baselineColorUp: DEFAULT_SCORECARD_BASELINE_COLOR_UP,
12114
12257
  baselineColorDown: DEFAULT_SCORECARD_BASELINE_COLOR_DOWN,
12115
- baseline: context.auxiliaryRange || "",
12258
+ baseline,
12116
12259
  humanize: context.humanize,
12117
12260
  annotationLink: context.annotationLink,
12118
12261
  annotationText: context.annotationText
12119
12262
  };
12120
12263
  },
12121
- transformDefinition(definition, chartSheetId, { adaptRangeString }) {
12264
+ transformDefinition(definition, chartSheetId, { adaptFormulaString }) {
12122
12265
  let baseline;
12123
12266
  let keyValue;
12124
- if (definition.baseline) {
12125
- const { changeType, range: adaptedRange } = adaptRangeString(chartSheetId, definition.baseline);
12126
- if (changeType !== "REMOVE") baseline = adaptedRange;
12127
- }
12128
- if (definition.keyValue) {
12129
- const { changeType, range: adaptedRange } = adaptRangeString(chartSheetId, definition.keyValue);
12130
- if (changeType !== "REMOVE") keyValue = adaptedRange;
12131
- }
12132
- return {
12133
- ...definition,
12134
- baseline,
12135
- keyValue
12136
- };
12137
- },
12138
- duplicateInDuplicatedSheet(definition, sheetIdFrom, sheetIdTo) {
12139
- const baseline = duplicateLabelRangeInDuplicatedSheet(sheetIdFrom, sheetIdTo, definition.baseline);
12140
- const keyValue = duplicateLabelRangeInDuplicatedSheet(sheetIdFrom, sheetIdTo, definition.keyValue);
12267
+ if (definition.baseline) baseline = adaptFormulaString(chartSheetId, definition.baseline);
12268
+ if (definition.keyValue) keyValue = adaptFormulaString(chartSheetId, definition.keyValue);
12141
12269
  return {
12142
12270
  ...definition,
12143
12271
  baseline,
12144
12272
  keyValue
12145
12273
  };
12146
12274
  },
12147
- toStrDefinition(definition, sheetId, getters) {
12275
+ duplicateInDuplicatedSheet(definition, sheetIdFrom, sheetIdTo, getters) {
12276
+ const adaptFormula = (formula) => getters.copyFormulaStringForSheet(sheetIdFrom, sheetIdTo, formula, "moveReference");
12148
12277
  return {
12149
12278
  ...definition,
12150
- keyValue: definition.keyValue ? getters.getRangeString(definition.keyValue, sheetId) : void 0,
12151
- baseline: definition.baseline ? getters.getRangeString(definition.baseline, sheetId) : void 0
12279
+ keyValue: definition.keyValue ? adaptFormula(definition.keyValue) : definition.keyValue,
12280
+ baseline: definition.baseline ? adaptFormula(definition.baseline) : definition.baseline
12152
12281
  };
12153
12282
  },
12283
+ toStrDefinition: (definition) => definition,
12154
12284
  getContextCreation(definition, dataSource) {
12285
+ const keyValueXc = getFormulaRangeXc(definition.keyValue);
12155
12286
  return {
12156
12287
  ...definition,
12157
12288
  dataSource: {
12158
12289
  type: "range",
12159
- dataSets: definition.keyValue ? [{
12160
- dataRange: definition.keyValue,
12290
+ dataSets: keyValueXc ? [{
12291
+ dataRange: keyValueXc,
12161
12292
  dataSetId: "0"
12162
12293
  }] : []
12163
12294
  },
12164
- auxiliaryRange: definition.baseline
12295
+ auxiliaryRange: getFormulaRangeXc(definition.baseline),
12296
+ scorecardKeyValueFormula: definition.keyValue,
12297
+ scorecardBaselineFormula: definition.baseline
12165
12298
  };
12166
12299
  },
12167
12300
  getDefinitionForExcel: () => void 0,
12168
- updateRanges(definition, adapterFunctions) {
12169
- const baseline = adaptChartRange(definition.baseline, adapterFunctions);
12170
- const keyValue = adaptChartRange(definition.keyValue, adapterFunctions);
12301
+ updateRanges(definition, adapterFunctions, sheetId) {
12302
+ const baseline = definition.baseline ? adapterFunctions.adaptFormulaString(sheetId, definition.baseline) : definition.baseline;
12303
+ const keyValue = definition.keyValue ? adapterFunctions.adaptFormulaString(sheetId, definition.keyValue) : definition.keyValue;
12171
12304
  if (definition.baseline === baseline && definition.keyValue === keyValue) return definition;
12172
12305
  return {
12173
12306
  ...definition,
@@ -12175,32 +12308,21 @@ const ScorecardChart$1 = {
12175
12308
  keyValue
12176
12309
  };
12177
12310
  },
12178
- getFormulas: () => [],
12179
- getRuntime(getters, definition) {
12311
+ getFormulas(getters, sheetId, definition) {
12312
+ const formulas = [];
12313
+ if (definition.keyValue && isFormula(definition.keyValue)) formulas.push(CompiledFormula.Compile(definition.keyValue, sheetId, getters));
12314
+ if (definition.baseline && isFormula(definition.baseline)) formulas.push(CompiledFormula.Compile(definition.baseline, sheetId, getters));
12315
+ return formulas;
12316
+ },
12317
+ getRuntime(getters, definition, _dataExtractor, sheetId) {
12180
12318
  let formattedKeyValue = "";
12181
- let keyValueCell;
12319
+ const { scalar: keyValue, range: keyValueRange } = getData$1(definition.keyValue, getters, sheetId);
12182
12320
  const locale = getters.getLocale();
12183
- if (definition.keyValue) {
12184
- const keyValuePosition = {
12185
- sheetId: definition.keyValue.sheetId,
12186
- col: definition.keyValue.zone.left,
12187
- row: definition.keyValue.zone.top
12188
- };
12189
- keyValueCell = getters.getEvaluatedCell(keyValuePosition);
12190
- formattedKeyValue = getKeyValueText(keyValueCell, definition.humanize ?? true, locale);
12191
- }
12192
- let baselineCell;
12193
- const baseline = definition.baseline;
12194
- if (baseline) {
12195
- const baselinePosition = {
12196
- sheetId: baseline.sheetId,
12197
- col: baseline.zone.left,
12198
- row: baseline.zone.top
12199
- };
12200
- baselineCell = getters.getEvaluatedCell(baselinePosition);
12201
- }
12202
- const { background, fontColor } = getters.getStyleOfSingleCellChart(definition.background, definition.keyValue);
12203
- const baselineDisplay = getBaselineText(baselineCell, keyValueCell, definition.baselineMode, definition.humanize ?? true, locale);
12321
+ if (keyValue !== null && keyValue !== void 0) formattedKeyValue = getKeyValueText(keyValue, definition.humanize ?? true, locale);
12322
+ else formattedKeyValue = "";
12323
+ const { scalar: baseline, range: baselineRange } = getData$1(definition.baseline, getters, sheetId);
12324
+ const { background, fontColor } = getters.getStyleOfSingleCellChart(definition.background, keyValueRange);
12325
+ const baselineDisplay = getBaselineText(baseline, keyValue, definition.baselineMode, definition.humanize ?? true, locale);
12204
12326
  const baselineValue = definition.baselineMode === "progress" && isNumber(baselineDisplay, locale) ? toNumber(baselineDisplay, locale) : 0;
12205
12327
  const title = definition.title;
12206
12328
  return {
@@ -12211,16 +12333,16 @@ const ScorecardChart$1 = {
12211
12333
  keyValue: formattedKeyValue,
12212
12334
  keyDescr: definition.keyDescr?.text ? getters.dynamicTranslate(definition.keyDescr.text) : "",
12213
12335
  baselineDisplay,
12214
- baselineArrow: getBaselineArrowDirection(baselineCell, keyValueCell, definition.baselineMode),
12215
- baselineColor: getBaselineColor(baselineCell, definition.baselineMode, keyValueCell, definition.baselineColorUp, definition.baselineColorDown),
12336
+ baselineArrow: getBaselineArrowDirection(baseline, keyValue, definition.baselineMode),
12337
+ baselineColor: getBaselineColor(baseline, definition.baselineMode, keyValue, definition.baselineColorUp, definition.baselineColorDown),
12216
12338
  baselineDescr: definition.baselineMode !== "progress" && definition.baselineDescr?.text ? getters.dynamicTranslate(definition.baselineDescr.text) : "",
12217
12339
  fontColor,
12218
12340
  background,
12219
12341
  baselineStyle: {
12220
- ...definition.baselineMode !== "percentage" && definition.baselineMode !== "progress" && baseline ? getters.getCellComputedStyle({
12221
- sheetId: baseline.sheetId,
12222
- col: baseline.zone.left,
12223
- row: baseline.zone.top
12342
+ ...definition.baselineMode !== "percentage" && definition.baselineMode !== "progress" && baselineRange ? getters.getCellComputedStyle({
12343
+ sheetId: baselineRange.sheetId,
12344
+ col: baselineRange.zone.left,
12345
+ row: baselineRange.zone.top
12224
12346
  }) : void 0,
12225
12347
  fontSize: definition.baselineDescr?.fontSize,
12226
12348
  align: definition.baselineDescr?.align
@@ -12230,10 +12352,10 @@ const ScorecardChart$1 = {
12230
12352
  ...definition.baselineDescr
12231
12353
  },
12232
12354
  keyValueStyle: {
12233
- ...definition.keyValue ? getters.getCellComputedStyle({
12234
- sheetId: definition.keyValue.sheetId,
12235
- col: definition.keyValue.zone.left,
12236
- row: definition.keyValue.zone.top
12355
+ ...keyValueRange ? getters.getCellComputedStyle({
12356
+ sheetId: keyValueRange.sheetId,
12357
+ col: keyValueRange.zone.left,
12358
+ row: keyValueRange.zone.top
12237
12359
  }) : void 0,
12238
12360
  fontSize: definition.keyDescr?.fontSize,
12239
12361
  align: definition.keyDescr?.align
@@ -13735,33 +13857,8 @@ var ViewportsStore = class extends SpreadsheetStore {
13735
13857
  displayedSheetId = this.model.getters.getActiveSheetId();
13736
13858
  constructor(get) {
13737
13859
  super(get);
13738
- this.model.selection.observe(this, { handleEvent: this.handleEvent.bind(this) });
13739
- this.onDispose(() => {
13740
- this.model.selection.unobserve(this);
13741
- });
13742
13860
  this.viewports.resetViewports(this.displayedSheetId);
13743
13861
  }
13744
- handleEvent(event) {
13745
- const eventSheetId = this.getters.getActiveSheetId();
13746
- if (event.options.scrollIntoView) {
13747
- const oldZone = event.previousAnchor.zone;
13748
- const newZone = event.anchor.zone;
13749
- const isUpdateAnchorEvent = event.mode === "updateAnchor";
13750
- const sameZone = isEqual(oldZone, newZone);
13751
- let { col, row } = isUpdateAnchorEvent && sameZone ? event.anchor.cell : findCellInNewZone(oldZone, newZone);
13752
- if (isUpdateAnchorEvent && !sameZone) {
13753
- const { top, bottom, left, right } = this.viewports.getMainInternalViewport(eventSheetId);
13754
- if (oldZone.left === newZone.left && oldZone.right === newZone.right) col = left > col || col > right ? left : col;
13755
- if (oldZone.top === newZone.top && oldZone.bottom === newZone.bottom) row = top > row || row > bottom ? top : row;
13756
- }
13757
- col = Math.min(col, this.getters.getNumberCols(eventSheetId) - 1);
13758
- row = Math.min(row, this.getters.getNumberRows(eventSheetId) - 1);
13759
- if (!this.sheetsWithDirtyViewports.has(eventSheetId)) this.viewports.refreshViewport(eventSheetId, {
13760
- col,
13761
- row
13762
- });
13763
- }
13764
- }
13765
13862
  handle(cmd) {
13766
13863
  if (invalidateEvaluationCommands.has(cmd.type)) for (const sheetId of this.getters.getSheetIds()) this.sheetsWithDirtyViewports.add(sheetId);
13767
13864
  switch (cmd.type) {
@@ -13861,6 +13958,7 @@ var ViewportsStore = class extends SpreadsheetStore {
13861
13958
  this.shiftVertically(topRowDims.end - boundaryTopY - viewportHeight);
13862
13959
  }
13863
13960
  scrollToCell(sheetId, col, row) {
13961
+ if (this.sheetsWithDirtyViewports.has(sheetId)) return;
13864
13962
  this.viewports.refreshViewport(sheetId, {
13865
13963
  col,
13866
13964
  row
@@ -16953,110 +17051,6 @@ function isSquareMatrix(arg) {
16953
17051
  }
16954
17052
  const expectNumberGreaterThanOrEqualToOne = (value) => _t("The function [[FUNCTION_NAME]] expects a number value to be greater than or equal to 1, but receives %s.", value);
16955
17053
 
16956
- //#endregion
16957
- //#region src/functions/helper_matrices.ts
16958
- function getUnitMatrix(n) {
16959
- const matrix = Array(n);
16960
- for (let i = 0; i < n; i++) {
16961
- matrix[i] = Array(n).fill(0);
16962
- matrix[i][i] = 1;
16963
- }
16964
- return matrix;
16965
- }
16966
- /**
16967
- * Invert a matrix and compute its determinant using Gaussian Elimination.
16968
- *
16969
- * The Matrix should be a square matrix, and should be indexed [col][row] instead of the
16970
- * standard mathematical indexing [row][col].
16971
- */
16972
- function invertMatrix(M) {
16973
- if (M.length < 1 || M[0].length < 1) throw new Error("invertMatrix: an empty matrix cannot be inverted.");
16974
- if (M.length !== M[0].length) throw new Error("invertMatrix: only square matrices are invertible");
16975
- let determinant = 1;
16976
- const dim = M.length;
16977
- const I = getUnitMatrix(dim);
16978
- const C = M.map((row) => row.slice());
16979
- for (let pivot = 0; pivot < dim; pivot++) {
16980
- let diagonalElement = C[pivot][pivot];
16981
- if (diagonalElement === 0) {
16982
- for (let row = pivot + 1; row < dim; row++) if (C[pivot][row] !== 0) {
16983
- swapMatrixRows(C, pivot, row);
16984
- swapMatrixRows(I, pivot, row);
16985
- determinant *= -1;
16986
- break;
16987
- }
16988
- diagonalElement = C[pivot][pivot];
16989
- if (diagonalElement === 0) return { determinant: 0 };
16990
- }
16991
- for (let col = 0; col < dim; col++) {
16992
- C[col][pivot] = C[col][pivot] / diagonalElement;
16993
- I[col][pivot] = I[col][pivot] / diagonalElement;
16994
- }
16995
- determinant *= diagonalElement;
16996
- for (let row = 0; row < dim; row++) {
16997
- if (row === pivot) continue;
16998
- const e = C[pivot][row];
16999
- for (let col = 0; col < dim; col++) {
17000
- C[col][row] -= e * C[col][pivot];
17001
- I[col][row] -= e * I[col][pivot];
17002
- }
17003
- }
17004
- }
17005
- return {
17006
- inverted: I,
17007
- determinant
17008
- };
17009
- }
17010
- function swapMatrixRows(matrix, row1, row2) {
17011
- for (let i = 0; i < matrix.length; i++) {
17012
- const tmp = matrix[i][row1];
17013
- matrix[i][row1] = matrix[i][row2];
17014
- matrix[i][row2] = tmp;
17015
- }
17016
- }
17017
- /**
17018
- * Matrix multiplication of 2 matrices.
17019
- * ex: matrix1 : n x l, matrix2 : m x n => result : m x l
17020
- *
17021
- * Note: we use indexing [col][row] instead of the standard mathematical notation [row][col]
17022
- */
17023
- function multiplyMatrices(matrix1, matrix2) {
17024
- if (matrix1.length < 1 || matrix2.length < 1) throw new Error("multiplyMatrices: empty matrices cannot be multiplied.");
17025
- if (matrix1.length !== matrix2[0].length) throw new Error("multiplyMatrices: incompatible matrices size.");
17026
- const rowsM1 = matrix1[0].length;
17027
- const colsM2 = matrix2.length;
17028
- const n = matrix1.length;
17029
- const result = Array(colsM2);
17030
- for (let col = 0; col < colsM2; col++) {
17031
- result[col] = Array(rowsM1);
17032
- for (let row = 0; row < rowsM1; row++) {
17033
- let sum = 0;
17034
- for (let k = 0; k < n; k++) sum += matrix1[k][row] * matrix2[col][k];
17035
- result[col][row] = sum;
17036
- }
17037
- }
17038
- return result;
17039
- }
17040
- /**
17041
- * Return the input if it's a scalar or the first element of the input if it's a matrix.
17042
- */
17043
- function toScalar(arg) {
17044
- if (!isMatrix(arg)) return arg;
17045
- if (!isSingleElementMatrix(arg)) throw new Error("The value should be a scalar or a 1x1 matrix");
17046
- return arg[0][0];
17047
- }
17048
- function isSingleElementMatrix(matrix) {
17049
- return matrix.length === 1 && matrix[0].length === 1;
17050
- }
17051
- function isMultipleElementMatrix(arg) {
17052
- return isMatrix(arg) && !isSingleElementMatrix(arg);
17053
- }
17054
- function getMatrixArgIndices(args) {
17055
- const indices = [];
17056
- for (let i = 0; i < args.length; i++) if (isMultipleElementMatrix(args[i])) indices.push(i);
17057
- return indices;
17058
- }
17059
-
17060
17054
  //#endregion
17061
17055
  //#region src/functions/helper_statistical.ts
17062
17056
  function assertSameNumberOfElements(...args) {
@@ -18207,8 +18201,6 @@ const ChartTerms = {
18207
18201
  Unexpected: _t("The chart definition is invalid for an unknown reason"),
18208
18202
  ["InvalidDataSet"]: _t("The dataset is invalid"),
18209
18203
  ["InvalidLabelRange"]: _t("Labels are invalid"),
18210
- ["InvalidScorecardKeyValue"]: _t("The key value is invalid"),
18211
- ["InvalidScorecardBaseline"]: _t("The baseline value is invalid"),
18212
18204
  ["InvalidGaugeDataRange"]: _t("The data range is invalid"),
18213
18205
  ["EmptyGaugeRangeMin"]: _t("A minimum range limit value is needed"),
18214
18206
  ["GaugeRangeMinNaN"]: _t("The minimum range limit value must be a number"),
@@ -21885,7 +21877,7 @@ function getPivotIconSvg(isCollapsed, isHovered) {
21885
21877
  };
21886
21878
  }
21887
21879
  function getDataFilterIcon(isActive, isHighContrast, isHovered) {
21888
- const symbolPath = isActive ? "M18.6 3.5H4.29c-.7 0-1.06.85-.56 1.35l6.1 6.1v6.8c0 .26.13.5.34.65l2.64 1.85a.79.79 0 0 0 1.25-.65v-8.64l6.1-6.1a.79.79 0 0 0-.56-1.35" : "M 339.667 681 L 510.333 681 L 510.333 595.667 L 339.667 595.667 L 339.667 681 Z M 41 169 L 41 254.333 L 809 254.333 L 809 169 L 41 169 Z M 169 467.667 L 681 467.667 L 681 382.333 L 169 382.333 L 169 467.667 Z";
21880
+ const symbolPath = isActive ? "M 11 20 Q 10.575 20 10.2875 19.7125 T 10 19 L 10 13 L 4.2 5.6 Q 3.825 5.1 4.0875 4.55 T 5 4 L 19 4 Q 19.65 4 19.9125 4.55 T 19.8 5.6 L 14 13 L 14 19 Q 14 19.425 13.7125 19.7125 T 13 20 L 11 20 Z" : "M 339.667 681 L 510.333 681 L 510.333 595.667 L 339.667 595.667 L 339.667 681 Z M 41 169 L 41 254.333 L 809 254.333 L 809 169 L 41 169 Z M 169 467.667 L 681 467.667 L 681 382.333 L 169 382.333 L 169 467.667 Z";
21889
21881
  const hoverBackgroundPath = isActive ? "M0,0 h24 v24 h-24" : "M0,0 h850 v850 h-850";
21890
21882
  const colors = {
21891
21883
  iconColor: FILTERS_COLOR,
@@ -29278,6 +29270,20 @@ migrationStepRegistry.add("0.1", { migrate(data) {
29278
29270
  figure.data.chartDefinitions[chartId] = upgrade(definition);
29279
29271
  }
29280
29272
  return data;
29273
+ } }).add("19.5.1", { migrate(data) {
29274
+ function upgrade(definition) {
29275
+ if (definition.type !== "scorecard") return definition;
29276
+ definition = { ...definition };
29277
+ if (definition.keyValue) definition.keyValue = `=${definition.keyValue}`;
29278
+ if (definition.baseline) definition.baseline = `=${definition.baseline}`;
29279
+ return definition;
29280
+ }
29281
+ for (const sheet of data.sheets || []) for (const figure of sheet.figures || []) if (figure.tag === "chart") figure.data = upgrade(figure.data);
29282
+ else if (figure.tag === "carousel") for (const chartId in figure.data.chartDefinitions) {
29283
+ const definition = figure.data.chartDefinitions[chartId];
29284
+ figure.data.chartDefinitions[chartId] = upgrade(definition);
29285
+ }
29286
+ return data;
29281
29287
  } });
29282
29288
  function fixOverlappingFilters(data) {
29283
29289
  for (const sheet of data.sheets || []) {
@@ -29950,7 +29956,7 @@ function buildScorecard(zone, getters) {
29950
29956
  return {
29951
29957
  type: "scorecard",
29952
29958
  title: {},
29953
- keyValue: getUnboundRange(getters, zone),
29959
+ keyValue: `=${getUnboundRange(getters, zone)}`,
29954
29960
  background: cell?.style?.fillColor,
29955
29961
  baselineMode: DEFAULT_SCORECARD_BASELINE_MODE,
29956
29962
  baselineColorUp: DEFAULT_SCORECARD_BASELINE_COLOR_UP,
@@ -39648,10 +39654,10 @@ var ScatterConfigPanel = class extends GenericChartConfigPanel {
39648
39654
  var ScorecardChartConfigPanel = class extends Component {
39649
39655
  static template = "o-spreadsheet-ScorecardChartConfigPanel";
39650
39656
  static components = {
39651
- SelectionInput,
39652
39657
  ChartErrorSection,
39653
39658
  Section,
39654
- Select
39659
+ Select,
39660
+ StandaloneComposer
39655
39661
  };
39656
39662
  props = (0, _odoo_owl.useProps)(chartSidePanelPropsDefinition);
39657
39663
  state = (0, _odoo_owl.proxy)({
@@ -39663,30 +39669,18 @@ var ScorecardChartConfigPanel = class extends Component {
39663
39669
  get errorMessages() {
39664
39670
  return [...this.state.keyValueDispatchResult?.reasons || [], ...this.state.baselineDispatchResult?.reasons || []].filter((reason) => reason !== "NoChanges").map((error) => ChartTerms.Errors[error] || ChartTerms.Errors.Unexpected);
39665
39671
  }
39666
- get isKeyValueInvalid() {
39667
- return !!this.state.keyValueDispatchResult?.isCancelledBecause("InvalidScorecardKeyValue");
39668
- }
39669
- get isBaselineInvalid() {
39670
- return !!this.state.baselineDispatchResult?.isCancelledBecause("InvalidScorecardBaseline");
39671
- }
39672
- onKeyValueRangeChanged(ranges) {
39673
- this.keyValue = ranges[0];
39674
- this.state.keyValueDispatchResult = this.props.canUpdateChart(this.props.chartId, { keyValue: this.keyValue });
39675
- }
39676
- updateKeyValueRange() {
39672
+ onConfirmKeyValue(keyValue) {
39673
+ this.keyValue = keyValue;
39677
39674
  this.state.keyValueDispatchResult = this.props.updateChart(this.props.chartId, { keyValue: this.keyValue });
39678
39675
  }
39679
- getKeyValueRange() {
39676
+ getKeyValue() {
39680
39677
  return this.keyValue || "";
39681
39678
  }
39682
- onBaselineRangeChanged(ranges) {
39683
- this.baseline = ranges[0];
39684
- this.state.baselineDispatchResult = this.props.canUpdateChart(this.props.chartId, { baseline: this.baseline });
39685
- }
39686
- updateBaselineRange() {
39679
+ onConfirmBaseline(baseline) {
39680
+ this.baseline = baseline;
39687
39681
  this.state.baselineDispatchResult = this.props.updateChart(this.props.chartId, { baseline: this.baseline });
39688
39682
  }
39689
- getBaselineRange() {
39683
+ getBaseline() {
39690
39684
  return this.baseline || "";
39691
39685
  }
39692
39686
  updateBaselineMode(baselineMode) {
@@ -40143,6 +40137,7 @@ var MainChartPanelStore = class extends SpreadsheetStore {
40143
40137
  changeChartType(chartId, newDisplayType) {
40144
40138
  const currentCreationContext = this.getters.getContextCreationChart(chartId);
40145
40139
  const savedCreationContext = this.creationContexts[chartId] || {};
40140
+ const auxiliaryRange = currentCreationContext && "auxiliaryRange" in currentCreationContext ? currentCreationContext.auxiliaryRange : savedCreationContext.auxiliaryRange;
40146
40141
  let dataSetStyles = savedCreationContext.dataSetStyles ?? currentCreationContext?.dataSetStyles;
40147
40142
  let dataSource = {
40148
40143
  ...savedCreationContext.dataSource,
@@ -40164,7 +40159,8 @@ var MainChartPanelStore = class extends SpreadsheetStore {
40164
40159
  dataSetsHaveTitle: false,
40165
40160
  ...savedCreationContext.dataSource,
40166
40161
  ...currentCreationContext?.dataSource,
40167
- dataSets: newRanges ?? []
40162
+ dataSets: newRanges ?? [],
40163
+ labelRange: auxiliaryRange
40168
40164
  };
40169
40165
  }
40170
40166
  this.creationContexts[chartId] = {
@@ -40193,7 +40189,7 @@ var MainChartPanelStore = class extends SpreadsheetStore {
40193
40189
  dataSetsStartWithSameRanges(currentDataSets, savedDataSets, currentStyles, savedStyles) {
40194
40190
  return currentDataSets.every((ds, i) => {
40195
40191
  const savedDs = savedDataSets[i];
40196
- return deepEquals(ds.dataRange, savedDs.dataRange) && deepEquals(currentStyles?.[ds.dataSetId], savedStyles?.[savedDs.dataSetId]);
40192
+ return deepEquals(ds.dataRange, savedDs?.dataRange) && deepEquals(currentStyles?.[ds.dataSetId], savedStyles?.[savedDs?.dataSetId]);
40197
40193
  });
40198
40194
  }
40199
40195
  /**
@@ -42713,8 +42709,8 @@ const SINGLE_NUMBER_COLUMN_SUGGESTIONS = [
42713
42709
  {
42714
42710
  description: _t("Highlights the most recent value compared to the previous one."),
42715
42711
  isApplicable: ({ rowCount }) => rowCount < 3,
42716
- build: (ctx) => scorecardChart(ctx.title, ctx.lastCellXC, {
42717
- baseline: ctx.prevCellXC,
42712
+ build: (ctx) => scorecardChart(ctx.title, `=${ctx.lastCellXC}`, {
42713
+ baseline: ctx.prevCellXC ? `=${ctx.prevCellXC}` : void 0,
42718
42714
  baselineMode: "difference"
42719
42715
  })
42720
42716
  },
@@ -42747,8 +42743,8 @@ const SINGLE_PERCENTAGE_COLUMN_SUGGESTIONS = [
42747
42743
  {
42748
42744
  description: _t("Shows the last percentage value with its baseline."),
42749
42745
  isApplicable: ({ rowCount }) => rowCount < 3,
42750
- build: (ctx) => scorecardChart(ctx.title, ctx.lastCellXC, {
42751
- baseline: ctx.prevCellXC,
42746
+ build: (ctx) => scorecardChart(ctx.title, `=${ctx.lastCellXC}`, {
42747
+ baseline: ctx.prevCellXC ? `=${ctx.prevCellXC}` : void 0,
42752
42748
  baselineMode: "percentage"
42753
42749
  })
42754
42750
  },
@@ -42777,7 +42773,7 @@ const SINGLE_PERCENTAGE_COLUMN_SUGGESTIONS = [
42777
42773
  const SINGLE_DATE_COLUMN_SUGGESTIONS = [{
42778
42774
  description: _t("Shows the last date value."),
42779
42775
  isApplicable: ({ rowCount }) => rowCount === 1,
42780
- build: (ctx) => scorecardChart(ctx.title, ctx.lastCellXC)
42776
+ build: (ctx) => scorecardChart(ctx.title, `=${ctx.lastCellXC}`)
42781
42777
  }];
42782
42778
  /** Pattern D — Single categorical column */
42783
42779
  const SINGLE_CATEGORICAL_COLUMN_SUGGESTIONS = [
@@ -42811,7 +42807,7 @@ const SINGLE_CATEGORICAL_COLUMN_SUGGESTIONS = [
42811
42807
  const SINGLE_LABEL_COLUMN_SUGGESTIONS = [{
42812
42808
  description: _t("Displays a key performance indicator."),
42813
42809
  isApplicable: ({ rowCount }) => rowCount === 1,
42814
- build: (ctx) => scorecardChart(ctx.title, ctx.lastCellXC, {
42810
+ build: (ctx) => scorecardChart(ctx.title, `=${ctx.lastCellXC}`, {
42815
42811
  baselineMode: "text",
42816
42812
  humanize: false
42817
42813
  })
@@ -42869,8 +42865,8 @@ const NUMBER_VS_NUMBER_SUGGESTIONS = [
42869
42865
  {
42870
42866
  description: _t("Highlights the second metric compared to the first one."),
42871
42867
  isApplicable: ({ rowCount1, rowCount2 }) => rowCount1 === 1 && rowCount2 === 1,
42872
- build: (ctx) => scorecardChart(ctx.title, ctx.lastCellXC, {
42873
- baseline: ctx.prevCellXC,
42868
+ build: (ctx) => scorecardChart(ctx.title, `=${ctx.lastCellXC}`, {
42869
+ baseline: ctx.prevCellXC ? `=${ctx.prevCellXC}` : void 0,
42874
42870
  baselineMode: "difference"
42875
42871
  })
42876
42872
  },
@@ -42928,10 +42924,10 @@ const LABEL_VS_NUMBER_SUGGESTIONS = [
42928
42924
  {
42929
42925
  description: _t("Highlights the most recent value for the named entity."),
42930
42926
  isApplicable: ({ rowCount }) => rowCount === 1,
42931
- build: (ctx) => scorecardChart("", ctx.lastCellXC, {
42927
+ build: (ctx) => scorecardChart("", `=${ctx.lastCellXC}`, {
42932
42928
  humanize: false,
42933
42929
  baselineMode: "text",
42934
- baseline: ctx.prevCellXC
42930
+ baseline: ctx.prevCellXC ? `=${ctx.prevCellXC}` : void 0
42935
42931
  })
42936
42932
  },
42937
42933
  {
@@ -71680,6 +71676,31 @@ var ImageProvider = class {
71680
71676
  var MainViewportStore = class extends SpreadsheetStore {
71681
71677
  viewStore = this.get(ViewportsStore);
71682
71678
  sheetIdAtFinalize = void 0;
71679
+ constructor(get) {
71680
+ super(get);
71681
+ this.model.selection.observe(this, { handleEvent: this.handleEvent.bind(this) });
71682
+ this.onDispose(() => {
71683
+ this.model.selection.unobserve(this);
71684
+ });
71685
+ }
71686
+ handleEvent(event) {
71687
+ const eventSheetId = this.getters.getActiveSheetId();
71688
+ if (event.options.scrollIntoView) {
71689
+ const oldZone = event.previousAnchor.zone;
71690
+ const newZone = event.anchor.zone;
71691
+ const isUpdateAnchorEvent = event.mode === "updateAnchor";
71692
+ const sameZone = isEqual(oldZone, newZone);
71693
+ let { col, row } = isUpdateAnchorEvent && sameZone ? event.anchor.cell : findCellInNewZone(oldZone, newZone);
71694
+ if (isUpdateAnchorEvent && !sameZone) {
71695
+ const { top, bottom, left, right } = this.viewStore.viewports.getMainInternalViewport(eventSheetId);
71696
+ if (oldZone.left === newZone.left && oldZone.right === newZone.right) col = left > col || col > right ? left : col;
71697
+ if (oldZone.top === newZone.top && oldZone.bottom === newZone.bottom) row = top > row || row > bottom ? top : row;
71698
+ }
71699
+ col = Math.min(col, this.getters.getNumberCols(eventSheetId) - 1);
71700
+ row = Math.min(row, this.getters.getNumberRows(eventSheetId) - 1);
71701
+ this.viewStore.scrollToCell(eventSheetId, col, row);
71702
+ }
71703
+ }
71683
71704
  handle(cmd) {
71684
71705
  switch (cmd.type) {
71685
71706
  case "UNDO":
@@ -91193,6 +91214,6 @@ exports.stores = stores;
91193
91214
  exports.tokenColors = tokenColors;
91194
91215
  exports.tokenize = tokenize;
91195
91216
 
91196
- __info__.version = "19.5.0-alpha.12";
91197
- __info__.date = "2026-08-19T09:47:03.774Z";
91198
- __info__.hash = "68ca68e";
91217
+ __info__.version = "19.5.0-alpha.13";
91218
+ __info__.date = "2026-08-21T15:33:44.046Z";
91219
+ __info__.hash = "a73559e";