@odoo/o-spreadsheet 16.1.6 → 16.1.7
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/o-spreadsheet.cjs.js +269 -179
- package/dist/o-spreadsheet.d.ts +79 -49
- package/dist/o-spreadsheet.esm.js +269 -179
- package/dist/o-spreadsheet.iife.js +269 -179
- package/dist/o-spreadsheet.iife.min.js +239 -237
- package/package.json +2 -2
|
@@ -115,88 +115,88 @@ const LINK_COLOR = "#01666b";
|
|
|
115
115
|
const FILTERS_COLOR = "#188038";
|
|
116
116
|
const BACKGROUND_HEADER_FILTER_COLOR = "#E6F4EA";
|
|
117
117
|
const BACKGROUND_HEADER_SELECTED_FILTER_COLOR = "#CEEAD6";
|
|
118
|
-
// Color picker
|
|
118
|
+
// Color picker defaults as upper case HEX to match `toHex`helper
|
|
119
119
|
const COLOR_PICKER_DEFAULTS = [
|
|
120
120
|
"#000000",
|
|
121
121
|
"#434343",
|
|
122
122
|
"#666666",
|
|
123
123
|
"#999999",
|
|
124
|
-
"#
|
|
125
|
-
"#
|
|
126
|
-
"#
|
|
127
|
-
"#
|
|
128
|
-
"#
|
|
129
|
-
"#
|
|
124
|
+
"#B7B7B7",
|
|
125
|
+
"#CCCCCC",
|
|
126
|
+
"#D9D9D9",
|
|
127
|
+
"#EFEFEF",
|
|
128
|
+
"#F3F3F3",
|
|
129
|
+
"#FFFFFF",
|
|
130
130
|
"#980000",
|
|
131
|
-
"#
|
|
132
|
-
"#
|
|
133
|
-
"#
|
|
134
|
-
"#
|
|
135
|
-
"#
|
|
136
|
-
"#
|
|
137
|
-
"#
|
|
138
|
-
"#
|
|
139
|
-
"#
|
|
140
|
-
"#
|
|
141
|
-
"#
|
|
142
|
-
"#
|
|
143
|
-
"#
|
|
144
|
-
"#
|
|
145
|
-
"#
|
|
146
|
-
"#
|
|
147
|
-
"#
|
|
148
|
-
"#
|
|
149
|
-
"#
|
|
150
|
-
"#
|
|
151
|
-
"#
|
|
152
|
-
"#
|
|
153
|
-
"#
|
|
154
|
-
"#
|
|
155
|
-
"#
|
|
156
|
-
"#
|
|
157
|
-
"#
|
|
158
|
-
"#
|
|
159
|
-
"#
|
|
160
|
-
"#
|
|
161
|
-
"#
|
|
162
|
-
"#
|
|
163
|
-
"#
|
|
164
|
-
"#
|
|
165
|
-
"#
|
|
166
|
-
"#
|
|
167
|
-
"#
|
|
168
|
-
"#
|
|
169
|
-
"#
|
|
170
|
-
"#
|
|
171
|
-
"#
|
|
172
|
-
"#
|
|
173
|
-
"#
|
|
174
|
-
"#
|
|
175
|
-
"#
|
|
176
|
-
"#
|
|
177
|
-
"#
|
|
178
|
-
"#
|
|
179
|
-
"#
|
|
180
|
-
"#
|
|
131
|
+
"#FF0000",
|
|
132
|
+
"#FF9900",
|
|
133
|
+
"#FFFF00",
|
|
134
|
+
"#00FF00",
|
|
135
|
+
"#00FFFF",
|
|
136
|
+
"#4A86E8",
|
|
137
|
+
"#0000FF",
|
|
138
|
+
"#9900FF",
|
|
139
|
+
"#FF00FF",
|
|
140
|
+
"#E6B8AF",
|
|
141
|
+
"#F4CCCC",
|
|
142
|
+
"#FCE5CD",
|
|
143
|
+
"#FFF2CC",
|
|
144
|
+
"#D9EAD3",
|
|
145
|
+
"#D0E0E3",
|
|
146
|
+
"#C9DAF8",
|
|
147
|
+
"#CFE2F3",
|
|
148
|
+
"#D9D2E9",
|
|
149
|
+
"#EAD1DC",
|
|
150
|
+
"#DD7E6B",
|
|
151
|
+
"#EA9999",
|
|
152
|
+
"#F9CB9C",
|
|
153
|
+
"#FFE599",
|
|
154
|
+
"#B6D7A8",
|
|
155
|
+
"#A2C4C9",
|
|
156
|
+
"#A4C2F4",
|
|
157
|
+
"#9FC5E8",
|
|
158
|
+
"#B4A7D6",
|
|
159
|
+
"#D5A6BD",
|
|
160
|
+
"#CC4125",
|
|
161
|
+
"#E06666",
|
|
162
|
+
"#F6B26B",
|
|
163
|
+
"#FFD966",
|
|
164
|
+
"#93C47D",
|
|
165
|
+
"#76A5AF",
|
|
166
|
+
"#6D9EEB",
|
|
167
|
+
"#6FA8DC",
|
|
168
|
+
"#8E7CC3",
|
|
169
|
+
"#C27BA0",
|
|
170
|
+
"#A61C00",
|
|
171
|
+
"#CC0000",
|
|
172
|
+
"#E69138",
|
|
173
|
+
"#F1C232",
|
|
174
|
+
"#6AA84F",
|
|
175
|
+
"#45818E",
|
|
176
|
+
"#3C78D8",
|
|
177
|
+
"#3D85C6",
|
|
178
|
+
"#674EA7",
|
|
179
|
+
"#A64D79",
|
|
180
|
+
"#85200C",
|
|
181
181
|
"#990000",
|
|
182
|
-
"#
|
|
183
|
-
"#
|
|
184
|
-
"#
|
|
185
|
-
"#
|
|
186
|
-
"#
|
|
187
|
-
"#
|
|
188
|
-
"#
|
|
189
|
-
"#
|
|
190
|
-
"#
|
|
182
|
+
"#B45F06",
|
|
183
|
+
"#BF9000",
|
|
184
|
+
"#38761D",
|
|
185
|
+
"#134F5C",
|
|
186
|
+
"#1155CC",
|
|
187
|
+
"#0B5394",
|
|
188
|
+
"#351C75",
|
|
189
|
+
"#741B47",
|
|
190
|
+
"#5B0F00",
|
|
191
191
|
"#660000",
|
|
192
|
-
"#
|
|
193
|
-
"#
|
|
194
|
-
"#
|
|
195
|
-
"#
|
|
196
|
-
"#
|
|
192
|
+
"#783F04",
|
|
193
|
+
"#7F6000",
|
|
194
|
+
"#274E13",
|
|
195
|
+
"#0C343D",
|
|
196
|
+
"#1C4587",
|
|
197
197
|
"#073763",
|
|
198
|
-
"#
|
|
199
|
-
"#
|
|
198
|
+
"#20124D",
|
|
199
|
+
"#4C1130",
|
|
200
200
|
];
|
|
201
201
|
// Dimensions
|
|
202
202
|
const MIN_ROW_HEIGHT = 10;
|
|
@@ -235,7 +235,7 @@ const DEFAULT_FONT = "'Roboto', arial";
|
|
|
235
235
|
const DEFAULT_BORDER_DESC = ["thin", "#000"];
|
|
236
236
|
const DEFAULT_FILTER_BORDER_DESC = ["thin", FILTERS_COLOR];
|
|
237
237
|
// DateTimeRegex
|
|
238
|
-
const DATETIME_FORMAT = /[
|
|
238
|
+
const DATETIME_FORMAT = /[ymdhs:]/;
|
|
239
239
|
// Ranges
|
|
240
240
|
const INCORRECT_RANGE_STRING = CellErrorType.InvalidReference;
|
|
241
241
|
// Max Number of history steps kept in memory
|
|
@@ -272,11 +272,13 @@ var ComponentsImportance;
|
|
|
272
272
|
ComponentsImportance[ComponentsImportance["Highlight"] = 5] = "Highlight";
|
|
273
273
|
ComponentsImportance[ComponentsImportance["Figure"] = 10] = "Figure";
|
|
274
274
|
ComponentsImportance[ComponentsImportance["ScrollBar"] = 15] = "ScrollBar";
|
|
275
|
-
ComponentsImportance[ComponentsImportance["
|
|
276
|
-
ComponentsImportance[ComponentsImportance["
|
|
275
|
+
ComponentsImportance[ComponentsImportance["GridPopover"] = 19] = "GridPopover";
|
|
276
|
+
ComponentsImportance[ComponentsImportance["GridComposer"] = 20] = "GridComposer";
|
|
277
|
+
ComponentsImportance[ComponentsImportance["Dropdown"] = 21] = "Dropdown";
|
|
277
278
|
ComponentsImportance[ComponentsImportance["ColorPicker"] = 25] = "ColorPicker";
|
|
278
279
|
ComponentsImportance[ComponentsImportance["IconPicker"] = 25] = "IconPicker";
|
|
279
|
-
ComponentsImportance[ComponentsImportance["
|
|
280
|
+
ComponentsImportance[ComponentsImportance["TopBarComposer"] = 30] = "TopBarComposer";
|
|
281
|
+
ComponentsImportance[ComponentsImportance["Popover"] = 35] = "Popover";
|
|
280
282
|
ComponentsImportance[ComponentsImportance["ChartAnchor"] = 1000] = "ChartAnchor";
|
|
281
283
|
})(ComponentsImportance || (ComponentsImportance = {}));
|
|
282
284
|
const DEFAULT_SHEETVIEW_SIZE = 1000;
|
|
@@ -1039,6 +1041,8 @@ function transpose2dArray(matrix) {
|
|
|
1039
1041
|
return matrix[0].map((_, i) => matrix.map((row) => row[i]));
|
|
1040
1042
|
}
|
|
1041
1043
|
|
|
1044
|
+
const RBA_REGEX = /rgba?\(|\s+|\)/gi;
|
|
1045
|
+
const HEX_MATCH = /^#([A-F\d]{2}){3,4}$/g;
|
|
1042
1046
|
const colors$1 = [
|
|
1043
1047
|
"#eb6d00",
|
|
1044
1048
|
"#0074d9",
|
|
@@ -1064,7 +1068,10 @@ function colorNumberString(color) {
|
|
|
1064
1068
|
}
|
|
1065
1069
|
/**
|
|
1066
1070
|
* Converts any CSS color value to a standardized hex6 value.
|
|
1067
|
-
* Accepts: hex3, hex6, hex8 and
|
|
1071
|
+
* Accepts: hex3, hex6, hex8, rgb[1] and rgba[1].
|
|
1072
|
+
*
|
|
1073
|
+
* [1] under the form rgb(r, g, b, a?) or rgba(r, g, b, a?)
|
|
1074
|
+
* with r,g,b ∈ [0, 255] and a ∈ [0, 1]
|
|
1068
1075
|
*
|
|
1069
1076
|
* toHex("#ABC")
|
|
1070
1077
|
* >> "#AABBCC"
|
|
@@ -1075,27 +1082,31 @@ function colorNumberString(color) {
|
|
|
1075
1082
|
* toHex("rgb(30, 80, 16)")
|
|
1076
1083
|
* >> "#1E5010"
|
|
1077
1084
|
*
|
|
1085
|
+
* * toHex("rgb(30, 80, 16, 0.5)")
|
|
1086
|
+
* >> "#1E501080"
|
|
1087
|
+
*
|
|
1078
1088
|
*/
|
|
1079
1089
|
function toHex(color) {
|
|
1080
|
-
|
|
1081
|
-
|
|
1090
|
+
let hexColor = color;
|
|
1091
|
+
if (color.startsWith("rgb")) {
|
|
1092
|
+
hexColor = rgbaStringToHex(color);
|
|
1082
1093
|
}
|
|
1083
|
-
|
|
1084
|
-
|
|
1085
|
-
|
|
1086
|
-
|
|
1087
|
-
|
|
1088
|
-
|
|
1094
|
+
else {
|
|
1095
|
+
hexColor = color.replace("#", "").toUpperCase();
|
|
1096
|
+
if (hexColor.length === 3 || hexColor.length === 4) {
|
|
1097
|
+
hexColor = hexColor.split("").reduce((acc, h) => acc + h + h, "");
|
|
1098
|
+
}
|
|
1099
|
+
hexColor = `#${hexColor}`;
|
|
1089
1100
|
}
|
|
1090
|
-
if (
|
|
1091
|
-
throw new Error(
|
|
1101
|
+
if (!hexColor.match(HEX_MATCH)) {
|
|
1102
|
+
throw new Error(`invalid color input: ${color}`);
|
|
1092
1103
|
}
|
|
1093
|
-
return
|
|
1104
|
+
return hexColor;
|
|
1094
1105
|
}
|
|
1095
1106
|
function isColorValid(color) {
|
|
1096
1107
|
try {
|
|
1097
|
-
|
|
1098
|
-
return
|
|
1108
|
+
toHex(color);
|
|
1109
|
+
return true;
|
|
1099
1110
|
}
|
|
1100
1111
|
catch (error) {
|
|
1101
1112
|
return false;
|
|
@@ -1128,15 +1139,29 @@ function relativeLuminance(color) {
|
|
|
1128
1139
|
/**
|
|
1129
1140
|
* Convert a CSS rgb color string to a standardized hex6 color value.
|
|
1130
1141
|
*
|
|
1131
|
-
*
|
|
1142
|
+
* rgbaStringToHex("rgb(30, 80, 16)")
|
|
1132
1143
|
* >> "#1E5010"
|
|
1144
|
+
*
|
|
1145
|
+
* rgbaStringToHex("rgba(30, 80, 16, 0.5)")
|
|
1146
|
+
* >> "#1E501080"
|
|
1147
|
+
*
|
|
1148
|
+
* DOES NOT SUPPORT NON INTEGER RGB VALUES
|
|
1133
1149
|
*/
|
|
1134
|
-
function
|
|
1135
|
-
|
|
1136
|
-
|
|
1137
|
-
|
|
1138
|
-
|
|
1139
|
-
|
|
1150
|
+
function rgbaStringToHex(color) {
|
|
1151
|
+
const stringVals = color.replace(RBA_REGEX, "").split(",");
|
|
1152
|
+
let alphaHex = 255;
|
|
1153
|
+
if (stringVals.length !== 3 && stringVals.length !== 4) {
|
|
1154
|
+
throw new Error("invalid color");
|
|
1155
|
+
}
|
|
1156
|
+
else if (stringVals.length === 4) {
|
|
1157
|
+
const alpha = parseFloat(stringVals.pop() || "1");
|
|
1158
|
+
alphaHex = Math.round((alpha || 1) * 255);
|
|
1159
|
+
}
|
|
1160
|
+
const vals = stringVals.map((val) => parseInt(val, 10));
|
|
1161
|
+
if (alphaHex !== 255) {
|
|
1162
|
+
vals.push(alphaHex);
|
|
1163
|
+
}
|
|
1164
|
+
return "#" + concat(vals.map((value) => value.toString(16).padStart(2, "0"))).toUpperCase();
|
|
1140
1165
|
}
|
|
1141
1166
|
/**
|
|
1142
1167
|
* RGBA to HEX representation (#RRGGBBAA).
|
|
@@ -1158,7 +1183,7 @@ function rgbaToHex(rgba) {
|
|
|
1158
1183
|
a = "0" + a;
|
|
1159
1184
|
if (a === "ff")
|
|
1160
1185
|
a = "";
|
|
1161
|
-
return "#" + r + g + b + a;
|
|
1186
|
+
return ("#" + r + g + b + a).toUpperCase();
|
|
1162
1187
|
}
|
|
1163
1188
|
/**
|
|
1164
1189
|
* Color string to RGBA representation
|
|
@@ -1279,6 +1304,9 @@ function rgbaToHSLA(rgba) {
|
|
|
1279
1304
|
s = +(s * 100).toFixed(1);
|
|
1280
1305
|
l = +(l * 100).toFixed(1);
|
|
1281
1306
|
return { a: rgba.a, h, s, l };
|
|
1307
|
+
}
|
|
1308
|
+
function isSameColor(color1, color2) {
|
|
1309
|
+
return isColorValid(color1) && isColorValid(color2) && toHex(color1) === toHex(color2);
|
|
1282
1310
|
}
|
|
1283
1311
|
|
|
1284
1312
|
/** Reference of a cell (eg. A1, $B$5) */
|
|
@@ -1430,7 +1458,7 @@ function scrollDelay(value) {
|
|
|
1430
1458
|
* formula, commas are used to separate arguments
|
|
1431
1459
|
* - it does not support % symbol, in formulas % is an operator
|
|
1432
1460
|
*/
|
|
1433
|
-
const formulaNumberRegexp =
|
|
1461
|
+
const formulaNumberRegexp = /(^-?\d+(\.?\d*(e\d+)?)?|^-?\.\d+)(?!\w|!)/;
|
|
1434
1462
|
const pIntegerAndDecimals = "(\\d+(,\\d{3,})*(\\.\\d*)?)"; // pattern that match integer number with or without decimal digits
|
|
1435
1463
|
const pOnlyDecimals = "(\\.\\d+)"; // pattern that match only expression with decimal digits
|
|
1436
1464
|
const pScientificFormat = "(e(\\+|-)?\\d+)?"; // pattern that match scientific format between zero and one time (should be placed before pPercentFormat)
|
|
@@ -3025,6 +3053,7 @@ const CHART_PADDING_RATIO = 0.02;
|
|
|
3025
3053
|
const LINE_HEIGHT = 1.2;
|
|
3026
3054
|
css /* scss */ `
|
|
3027
3055
|
div.o-scorecard {
|
|
3056
|
+
font-family: ${DEFAULT_FONT};
|
|
3028
3057
|
user-select: none;
|
|
3029
3058
|
background-color: white;
|
|
3030
3059
|
display: flex;
|
|
@@ -5649,6 +5678,11 @@ class Popover extends owl.Component {
|
|
|
5649
5678
|
this.currentPosition = newPosition;
|
|
5650
5679
|
});
|
|
5651
5680
|
}
|
|
5681
|
+
get popoverStyle() {
|
|
5682
|
+
return cssPropertiesToCss({
|
|
5683
|
+
"z-index": `${this.props.zIndex}`,
|
|
5684
|
+
});
|
|
5685
|
+
}
|
|
5652
5686
|
}
|
|
5653
5687
|
Popover.template = "o-spreadsheet-Popover";
|
|
5654
5688
|
Popover.defaultProps = {
|
|
@@ -5657,6 +5691,7 @@ Popover.defaultProps = {
|
|
|
5657
5691
|
onMouseWheel: () => { },
|
|
5658
5692
|
onPopoverMoved: () => { },
|
|
5659
5693
|
onPopoverHidden: () => { },
|
|
5694
|
+
zIndex: ComponentsImportance.Popover,
|
|
5660
5695
|
};
|
|
5661
5696
|
Popover.props = {
|
|
5662
5697
|
anchorRect: Object,
|
|
@@ -5668,6 +5703,7 @@ Popover.props = {
|
|
|
5668
5703
|
onMouseWheel: { type: Function, optional: true },
|
|
5669
5704
|
onPopoverHidden: { type: Function, optional: true },
|
|
5670
5705
|
onPopoverMoved: { type: Function, optional: true },
|
|
5706
|
+
zIndex: { type: Number, optional: true },
|
|
5671
5707
|
slots: Object,
|
|
5672
5708
|
};
|
|
5673
5709
|
class PopoverPositionContext {
|
|
@@ -17038,10 +17074,10 @@ css /* scss */ `
|
|
|
17038
17074
|
padding: 0;
|
|
17039
17075
|
margin: 0;
|
|
17040
17076
|
border: 0;
|
|
17041
|
-
z-index: ${ComponentsImportance.Composer};
|
|
17042
17077
|
flex-grow: 1;
|
|
17043
17078
|
max-height: inherit;
|
|
17044
17079
|
.o-composer {
|
|
17080
|
+
font-family: ${DEFAULT_FONT};
|
|
17045
17081
|
caret-color: black;
|
|
17046
17082
|
padding-left: 3px;
|
|
17047
17083
|
padding-right: 3px;
|
|
@@ -17076,6 +17112,10 @@ css /* scss */ `
|
|
|
17076
17112
|
.o-topbar-toolbar .o-composer-container:focus-within {
|
|
17077
17113
|
border: 1px solid ${SELECTION_BORDER_COLOR};
|
|
17078
17114
|
}
|
|
17115
|
+
|
|
17116
|
+
.o-topbar-toolbar .o-composer-container {
|
|
17117
|
+
z-index: ${ComponentsImportance.TopBarComposer};
|
|
17118
|
+
}
|
|
17079
17119
|
`;
|
|
17080
17120
|
class Composer extends owl.Component {
|
|
17081
17121
|
constructor() {
|
|
@@ -17524,7 +17564,7 @@ const SCROLLBAR_HIGHT = 15;
|
|
|
17524
17564
|
const COMPOSER_BORDER_WIDTH = 3 * 0.4 * window.devicePixelRatio || 1;
|
|
17525
17565
|
css /* scss */ `
|
|
17526
17566
|
div.o-grid-composer {
|
|
17527
|
-
z-index: ${ComponentsImportance.
|
|
17567
|
+
z-index: ${ComponentsImportance.GridComposer};
|
|
17528
17568
|
box-sizing: border-box;
|
|
17529
17569
|
position: absolute;
|
|
17530
17570
|
border: ${COMPOSER_BORDER_WIDTH}px solid ${SELECTION_BORDER_COLOR};
|
|
@@ -20060,6 +20100,9 @@ class ColorPicker extends owl.Component {
|
|
|
20060
20100
|
const { display, background, left, top } = this.state.style;
|
|
20061
20101
|
return `display:${display};${display === "block" ? `background-color:${background};left:${left};top:${top};` : ""}`;
|
|
20062
20102
|
}
|
|
20103
|
+
isSameColor(color1, color2) {
|
|
20104
|
+
return isSameColor(color1, color2);
|
|
20105
|
+
}
|
|
20063
20106
|
}
|
|
20064
20107
|
ColorPicker.template = "o-spreadsheet-ColorPicker";
|
|
20065
20108
|
ColorPicker.defaultProps = {
|
|
@@ -20204,6 +20247,12 @@ class GaugeChartDesignPanel extends owl.Component {
|
|
|
20204
20247
|
const cancelledReasons = [...(((_a = this.state.sectionRuleDispatchResult) === null || _a === void 0 ? void 0 : _a.reasons) || [])];
|
|
20205
20248
|
return cancelledReasons.map((error) => ChartTerms.Errors[error] || ChartTerms.Errors.Unexpected);
|
|
20206
20249
|
}
|
|
20250
|
+
onClick(ev) {
|
|
20251
|
+
this.state.openedMenu = undefined;
|
|
20252
|
+
}
|
|
20253
|
+
setup() {
|
|
20254
|
+
owl.useExternalListener(window, "click", this.onClick);
|
|
20255
|
+
}
|
|
20207
20256
|
updateBackgroundColor(color) {
|
|
20208
20257
|
this.state.openedMenu = undefined;
|
|
20209
20258
|
this.props.updateChart({
|
|
@@ -20387,6 +20436,12 @@ class ScorecardChartDesignPanel extends owl.Component {
|
|
|
20387
20436
|
openedColorPicker: undefined,
|
|
20388
20437
|
});
|
|
20389
20438
|
}
|
|
20439
|
+
onClick(ev) {
|
|
20440
|
+
this.state.openedColorPicker = undefined;
|
|
20441
|
+
}
|
|
20442
|
+
setup() {
|
|
20443
|
+
owl.useExternalListener(window, "click", this.onClick);
|
|
20444
|
+
}
|
|
20390
20445
|
updateTitle(ev) {
|
|
20391
20446
|
this.props.updateChart({
|
|
20392
20447
|
title: ev.target.value,
|
|
@@ -22349,6 +22404,10 @@ GridOverlay.props = {
|
|
|
22349
22404
|
};
|
|
22350
22405
|
|
|
22351
22406
|
class GridPopover extends owl.Component {
|
|
22407
|
+
constructor() {
|
|
22408
|
+
super(...arguments);
|
|
22409
|
+
this.zIndex = ComponentsImportance.GridPopover;
|
|
22410
|
+
}
|
|
22352
22411
|
get cellPopover() {
|
|
22353
22412
|
const popover = this.env.model.getters.getCellPopover(this.props.hoveredCell);
|
|
22354
22413
|
if (!popover.isOpen) {
|
|
@@ -24566,6 +24625,70 @@ function fixXlsxUnicode(str) {
|
|
|
24566
24625
|
});
|
|
24567
24626
|
}
|
|
24568
24627
|
|
|
24628
|
+
const XLSX_DATE_FORMAT_REGEX = /^(yy|yyyy|m{1,5}|d{1,4}|h{1,2}|s{1,2}|am\/pm|a\/m|\s|-|\/|\.|:)+$/i;
|
|
24629
|
+
/**
|
|
24630
|
+
* Convert excel format to o_spreadsheet format
|
|
24631
|
+
*
|
|
24632
|
+
* Excel format are defined in openXML §18.8.31
|
|
24633
|
+
*/
|
|
24634
|
+
function convertXlsxFormat(numFmtId, formats, warningManager) {
|
|
24635
|
+
var _a, _b, _c;
|
|
24636
|
+
if (numFmtId === 0) {
|
|
24637
|
+
return undefined;
|
|
24638
|
+
}
|
|
24639
|
+
// Format is either defined in the imported data, or the formatId is defined in openXML §18.8.30
|
|
24640
|
+
let format = XLSX_FORMATS_CONVERSION_MAP[numFmtId] || ((_a = formats.find((f) => f.id === numFmtId)) === null || _a === void 0 ? void 0 : _a.format);
|
|
24641
|
+
if (format) {
|
|
24642
|
+
try {
|
|
24643
|
+
let convertedFormat = format.replace(/(.*?);.*/, "$1"); // only take first part of multi-part format
|
|
24644
|
+
convertedFormat = convertedFormat.replace(/\[(.*)-[A-Z0-9]{3}\]/g, "[$1]"); // remove currency and locale/date system/number system info (ECMA §18.8.31)
|
|
24645
|
+
convertedFormat = convertedFormat.replace(/\[\$\]/g, ""); // remove empty bocks
|
|
24646
|
+
// Quotes in format escape sequences of characters. ATM we only support [$...] blocks to escape characters, and only one of them per format
|
|
24647
|
+
const numberOfQuotes = ((_b = convertedFormat.match(/"/g)) === null || _b === void 0 ? void 0 : _b.length) || 0;
|
|
24648
|
+
const numberOfOpenBrackets = ((_c = convertedFormat.match(/\[/g)) === null || _c === void 0 ? void 0 : _c.length) || 0;
|
|
24649
|
+
if (numberOfQuotes / 2 + numberOfOpenBrackets > 1) {
|
|
24650
|
+
throw new Error("Multiple escaped blocks in format");
|
|
24651
|
+
}
|
|
24652
|
+
convertedFormat = convertedFormat.replace(/"(.*)"/g, "[$$$1]"); // replace '"..."' by '[$...]'
|
|
24653
|
+
convertedFormat = convertedFormat.replace(/_.{1}/g, ""); // _ == ignore width of next char for align purposes. Not supported ATM
|
|
24654
|
+
convertedFormat = convertedFormat.replace(/\*.{1}/g, ""); // * == repeat next character enough to fill the line. Not supported ATM
|
|
24655
|
+
convertedFormat = convertedFormat.replace(/\\ /g, " "); // unescape spaces
|
|
24656
|
+
convertedFormat = convertedFormat.replace(/\\./g, (match) => match[1]); // unescape other characters
|
|
24657
|
+
if (isXlsxDateFormat(convertedFormat)) {
|
|
24658
|
+
convertedFormat = convertDateFormat$1(convertedFormat);
|
|
24659
|
+
}
|
|
24660
|
+
if (isFormatSupported(convertedFormat)) {
|
|
24661
|
+
return convertedFormat;
|
|
24662
|
+
}
|
|
24663
|
+
}
|
|
24664
|
+
catch (e) { }
|
|
24665
|
+
}
|
|
24666
|
+
warningManager.generateNotSupportedWarning(WarningTypes.NumFmtIdNotSupported, format || `nmFmtId ${numFmtId}`);
|
|
24667
|
+
return undefined;
|
|
24668
|
+
}
|
|
24669
|
+
function isFormatSupported(format) {
|
|
24670
|
+
try {
|
|
24671
|
+
formatValue(0, format);
|
|
24672
|
+
return true;
|
|
24673
|
+
}
|
|
24674
|
+
catch (e) {
|
|
24675
|
+
return false;
|
|
24676
|
+
}
|
|
24677
|
+
}
|
|
24678
|
+
function isXlsxDateFormat(format) {
|
|
24679
|
+
return format.match(XLSX_DATE_FORMAT_REGEX) !== null;
|
|
24680
|
+
}
|
|
24681
|
+
function convertDateFormat$1(format) {
|
|
24682
|
+
// Some of these aren't defined neither in the OpenXML spec not the Xlsx extension of OpenXML,
|
|
24683
|
+
// but can still occur and are supported by Excel/Google sheets
|
|
24684
|
+
format = format.toLowerCase();
|
|
24685
|
+
format = format.replace(/mmmmm/g, "mmm");
|
|
24686
|
+
format = format.replace(/am\/pm|a\/m/g, "a");
|
|
24687
|
+
format = format.replace(/hhhh/g, "hh");
|
|
24688
|
+
format = format.replace(/\bh\b/g, "hh");
|
|
24689
|
+
return format;
|
|
24690
|
+
}
|
|
24691
|
+
|
|
24569
24692
|
function convertBorders(data, warningManager) {
|
|
24570
24693
|
const borderArray = data.borders.map((border) => {
|
|
24571
24694
|
addBorderWarnings(border, warningManager);
|
|
@@ -24630,41 +24753,6 @@ function convertFormats(data, warningManager) {
|
|
|
24630
24753
|
}
|
|
24631
24754
|
return arrayToObject(formats, 1);
|
|
24632
24755
|
}
|
|
24633
|
-
/**
|
|
24634
|
-
* Convert excel format to o_spreadsheet format
|
|
24635
|
-
*
|
|
24636
|
-
* Excel format are defined in openXML §18.8.31
|
|
24637
|
-
*/
|
|
24638
|
-
function convertXlsxFormat(numFmtId, formats, warningManager) {
|
|
24639
|
-
var _a, _b, _c;
|
|
24640
|
-
if (numFmtId === 0) {
|
|
24641
|
-
return undefined;
|
|
24642
|
-
}
|
|
24643
|
-
// Format is either defined in the imported data, or the formatId is defined in openXML §18.8.30
|
|
24644
|
-
let format = XLSX_FORMATS_CONVERSION_MAP[numFmtId] || ((_a = formats.find((f) => f.id === numFmtId)) === null || _a === void 0 ? void 0 : _a.format);
|
|
24645
|
-
if (format) {
|
|
24646
|
-
try {
|
|
24647
|
-
let convertedFormat = format.replace(/(.*?);.*/, "$1"); // only take first part of multi-part format
|
|
24648
|
-
convertedFormat = convertedFormat.replace(/\[(.*)-[A-Z0-9]{3}\]/g, "[$1]"); // remove currency and locale/date system/number system info (ECMA §18.8.31)
|
|
24649
|
-
convertedFormat = convertedFormat.replace(/\[\$\]/g, ""); // remove empty bocks
|
|
24650
|
-
// Quotes in format escape sequences of characters. ATM we only support [$...] blocks to escape characters, and only one of them per format
|
|
24651
|
-
const numberOfQuotes = ((_b = convertedFormat.match(/"/g)) === null || _b === void 0 ? void 0 : _b.length) || 0;
|
|
24652
|
-
const numberOfOpenBrackets = ((_c = convertedFormat.match(/\[/g)) === null || _c === void 0 ? void 0 : _c.length) || 0;
|
|
24653
|
-
if (numberOfQuotes / 2 + numberOfOpenBrackets > 1) {
|
|
24654
|
-
throw new Error("Multiple escaped blocks in format");
|
|
24655
|
-
}
|
|
24656
|
-
convertedFormat = convertedFormat.replace(/"(.*)"/g, "[$$$1]"); // replace '"..."' by '[$...]'
|
|
24657
|
-
convertedFormat = convertedFormat.replace(/_.{1}/g, ""); // _ == ignore with of next char for align purposes. Not supported ATM
|
|
24658
|
-
convertedFormat = convertedFormat.replace(/\*.{1}/g, ""); // * == repeat next character enough to fill the line. Not supported ATM
|
|
24659
|
-
convertedFormat = convertedFormat.replace(/\\ /g, " "); // unescape spaces
|
|
24660
|
-
formatValue(0, convertedFormat);
|
|
24661
|
-
return convertedFormat;
|
|
24662
|
-
}
|
|
24663
|
-
catch (e) { }
|
|
24664
|
-
}
|
|
24665
|
-
warningManager.generateNotSupportedWarning(WarningTypes.NumFmtIdNotSupported, format || `nmFmtId ${numFmtId}`);
|
|
24666
|
-
return undefined;
|
|
24667
|
-
}
|
|
24668
24756
|
/**
|
|
24669
24757
|
* We currently only support only a set of font sizes, we cannot define new font sizes.
|
|
24670
24758
|
* This function adapts an arbitrary font size to the closest supported font size.
|
|
@@ -31831,9 +31919,7 @@ class CustomColorsPlugin extends UIPlugin {
|
|
|
31831
31919
|
// remove duplicates first to check validity on a reduced
|
|
31832
31920
|
// set of colors, then normalize to HEX and remove duplicates
|
|
31833
31921
|
// again
|
|
31834
|
-
[...new Set([...usedColors, ...this.customColors])]
|
|
31835
|
-
.filter(isColorValid)
|
|
31836
|
-
.map((c) => toHex(c).toLowerCase())),
|
|
31922
|
+
[...new Set([...usedColors, ...this.customColors])].filter(isColorValid).map(toHex)),
|
|
31837
31923
|
]).filter((color) => !COLOR_PICKER_DEFAULTS.includes(color));
|
|
31838
31924
|
}
|
|
31839
31925
|
getColorsFromCells(cells) {
|
|
@@ -31894,7 +31980,7 @@ class CustomColorsPlugin extends UIPlugin {
|
|
|
31894
31980
|
return [...chartsColors];
|
|
31895
31981
|
}
|
|
31896
31982
|
tryToAddColor(color) {
|
|
31897
|
-
const formattedColor = toHex(color)
|
|
31983
|
+
const formattedColor = toHex(color);
|
|
31898
31984
|
if (color && !COLOR_PICKER_DEFAULTS.includes(formattedColor)) {
|
|
31899
31985
|
this.customColors.add(formattedColor);
|
|
31900
31986
|
}
|
|
@@ -32899,8 +32985,8 @@ class InternalViewport {
|
|
|
32899
32985
|
this.getters = getters;
|
|
32900
32986
|
this.sheetId = sheetId;
|
|
32901
32987
|
this.boundaries = boundaries;
|
|
32902
|
-
this.
|
|
32903
|
-
this.
|
|
32988
|
+
this.viewportWidth = sizeInGrid.width;
|
|
32989
|
+
this.viewportHeight = sizeInGrid.height;
|
|
32904
32990
|
this.offsetScrollbarX = offsets.x;
|
|
32905
32991
|
this.offsetScrollbarY = offsets.y;
|
|
32906
32992
|
this.canScrollVertically = options.canScrollVertically;
|
|
@@ -32911,6 +32997,10 @@ class InternalViewport {
|
|
|
32911
32997
|
this.adjustViewportOffsetY();
|
|
32912
32998
|
}
|
|
32913
32999
|
// PUBLIC
|
|
33000
|
+
/** Returns the maximum size (in Pixels) of the viewport relative to its allocated client size
|
|
33001
|
+
* When the viewport grid size is smaller than its client width (resp. height), it will return
|
|
33002
|
+
* the client width (resp. height).
|
|
33003
|
+
*/
|
|
32914
33004
|
getMaxSize() {
|
|
32915
33005
|
const lastCol = this.getters.findLastVisibleColRowIndex(this.sheetId, "COL", {
|
|
32916
33006
|
first: this.boundaries.left,
|
|
@@ -32922,20 +33012,24 @@ class InternalViewport {
|
|
|
32922
33012
|
});
|
|
32923
33013
|
const { end: lastColEnd, size: lastColSize } = this.getters.getColDimensions(this.sheetId, lastCol);
|
|
32924
33014
|
const { end: lastRowEnd, size: lastRowSize } = this.getters.getRowDimensions(this.sheetId, lastRow);
|
|
32925
|
-
const leftColIndex = this.searchHeaderIndex("COL", lastColEnd - this.
|
|
33015
|
+
const leftColIndex = this.searchHeaderIndex("COL", lastColEnd - this.viewportWidth, 0);
|
|
32926
33016
|
const leftColSize = this.getters.getColSize(this.sheetId, leftColIndex);
|
|
32927
|
-
const leftRowIndex = this.searchHeaderIndex("ROW", lastRowEnd - this.
|
|
33017
|
+
const leftRowIndex = this.searchHeaderIndex("ROW", lastRowEnd - this.viewportHeight, 0);
|
|
32928
33018
|
const topRowSize = this.getters.getRowSize(this.sheetId, leftRowIndex);
|
|
32929
|
-
|
|
32930
|
-
|
|
32931
|
-
(
|
|
32932
|
-
|
|
32933
|
-
|
|
32934
|
-
|
|
32935
|
-
|
|
32936
|
-
|
|
32937
|
-
|
|
32938
|
-
|
|
33019
|
+
let width = lastColEnd - this.offsetCorrectionX;
|
|
33020
|
+
if (this.canScrollHorizontally) {
|
|
33021
|
+
width += Math.max(DEFAULT_CELL_WIDTH, // leave some minimal space to let the user know they scrolled all the way
|
|
33022
|
+
Math.min(leftColSize, this.viewportWidth - lastColSize) // Add pixels that allows the snapping at maximum horizontal scroll
|
|
33023
|
+
);
|
|
33024
|
+
width = Math.max(width, this.viewportWidth); // if the viewport grid size is smaller than its client width, return client width
|
|
33025
|
+
}
|
|
33026
|
+
let height = lastRowEnd - this.offsetCorrectionY;
|
|
33027
|
+
if (this.canScrollVertically) {
|
|
33028
|
+
height += Math.max(DEFAULT_CELL_HEIGHT + 5, // leave some space to let the user know they scrolled all the way
|
|
33029
|
+
Math.min(topRowSize, this.viewportHeight - lastRowSize) // Add pixels that allows the snapping at maximum vertical scroll
|
|
33030
|
+
);
|
|
33031
|
+
height = Math.max(height, this.viewportHeight); // if the viewport grid size is smaller than its client height, return client height
|
|
33032
|
+
}
|
|
32939
33033
|
return { width, height };
|
|
32940
33034
|
}
|
|
32941
33035
|
/**
|
|
@@ -32944,7 +33038,7 @@ class InternalViewport {
|
|
|
32944
33038
|
* It returns -1 if no column is found.
|
|
32945
33039
|
*/
|
|
32946
33040
|
getColIndex(x, absolute = false) {
|
|
32947
|
-
if (x < this.offsetCorrectionX || x > this.offsetCorrectionX + this.
|
|
33041
|
+
if (x < this.offsetCorrectionX || x > this.offsetCorrectionX + this.viewportWidth) {
|
|
32948
33042
|
return -1;
|
|
32949
33043
|
}
|
|
32950
33044
|
return this.searchHeaderIndex("COL", x - this.offsetCorrectionX, this.left, absolute);
|
|
@@ -32955,7 +33049,7 @@ class InternalViewport {
|
|
|
32955
33049
|
* It returns -1 if no row is found.
|
|
32956
33050
|
*/
|
|
32957
33051
|
getRowIndex(y, absolute = false) {
|
|
32958
|
-
if (y < this.offsetCorrectionY || y > this.offsetCorrectionY + this.
|
|
33052
|
+
if (y < this.offsetCorrectionY || y > this.offsetCorrectionY + this.viewportHeight) {
|
|
32959
33053
|
return -1;
|
|
32960
33054
|
}
|
|
32961
33055
|
return this.searchHeaderIndex("ROW", y - this.offsetCorrectionY, this.top, absolute);
|
|
@@ -32982,7 +33076,7 @@ class InternalViewport {
|
|
|
32982
33076
|
adjustPositionX(col) {
|
|
32983
33077
|
const sheetId = this.sheetId;
|
|
32984
33078
|
const { start, end } = this.getters.getColDimensions(sheetId, col);
|
|
32985
|
-
while (end > this.offsetX + this.offsetCorrectionX + this.
|
|
33079
|
+
while (end > this.offsetX + this.offsetCorrectionX + this.viewportWidth &&
|
|
32986
33080
|
this.offsetX + this.offsetCorrectionX < start) {
|
|
32987
33081
|
this.offsetX = this.getters.getColDimensions(sheetId, this.left).end - this.offsetCorrectionX;
|
|
32988
33082
|
this.offsetScrollbarX = this.offsetX;
|
|
@@ -33004,7 +33098,7 @@ class InternalViewport {
|
|
|
33004
33098
|
adjustPositionY(row) {
|
|
33005
33099
|
const sheetId = this.sheetId;
|
|
33006
33100
|
while (this.getters.getRowDimensions(sheetId, row).end >
|
|
33007
|
-
this.offsetY + this.
|
|
33101
|
+
this.offsetY + this.viewportHeight + this.offsetCorrectionY &&
|
|
33008
33102
|
this.offsetY + this.offsetCorrectionY < this.getters.getRowDimensions(sheetId, row).start) {
|
|
33009
33103
|
this.offsetY = this.getters.getRowDimensions(sheetId, this.top).end - this.offsetCorrectionY;
|
|
33010
33104
|
this.offsetScrollbarY = this.offsetY;
|
|
@@ -33042,8 +33136,8 @@ class InternalViewport {
|
|
|
33042
33136
|
const x = this.getters.getColRowOffset("COL", this.zone.left, targetZone.left) +
|
|
33043
33137
|
this.offsetCorrectionX;
|
|
33044
33138
|
const y = this.getters.getColRowOffset("ROW", this.zone.top, targetZone.top) + this.offsetCorrectionY;
|
|
33045
|
-
const width = Math.min(this.getters.getColRowOffset("COL", targetZone.left, targetZone.right + 1), this.
|
|
33046
|
-
const height = Math.min(this.getters.getColRowOffset("ROW", targetZone.top, targetZone.bottom + 1), this.
|
|
33139
|
+
const width = Math.min(this.getters.getColRowOffset("COL", targetZone.left, targetZone.right + 1), this.viewportWidth);
|
|
33140
|
+
const height = Math.min(this.getters.getColRowOffset("ROW", targetZone.top, targetZone.bottom + 1), this.viewportHeight);
|
|
33047
33141
|
return {
|
|
33048
33142
|
x,
|
|
33049
33143
|
y,
|
|
@@ -33106,12 +33200,12 @@ class InternalViewport {
|
|
|
33106
33200
|
adjustViewportOffsetX() {
|
|
33107
33201
|
if (this.canScrollHorizontally) {
|
|
33108
33202
|
const { width: viewportWidth } = this.getMaxSize();
|
|
33109
|
-
if (this.
|
|
33110
|
-
this.offsetScrollbarX = Math.max(0, viewportWidth - this.
|
|
33203
|
+
if (this.viewportWidth + this.offsetScrollbarX > viewportWidth) {
|
|
33204
|
+
this.offsetScrollbarX = Math.max(0, viewportWidth - this.viewportWidth);
|
|
33111
33205
|
}
|
|
33112
33206
|
}
|
|
33113
33207
|
this.left = this.getColIndex(this.offsetScrollbarX, true);
|
|
33114
|
-
this.right = this.getColIndex(this.offsetScrollbarX + this.
|
|
33208
|
+
this.right = this.getColIndex(this.offsetScrollbarX + this.viewportWidth, true);
|
|
33115
33209
|
if (this.right === -1) {
|
|
33116
33210
|
this.right = this.boundaries.right;
|
|
33117
33211
|
}
|
|
@@ -33123,12 +33217,12 @@ class InternalViewport {
|
|
|
33123
33217
|
adjustViewportOffsetY() {
|
|
33124
33218
|
if (this.canScrollVertically) {
|
|
33125
33219
|
const { height: paneHeight } = this.getMaxSize();
|
|
33126
|
-
if (this.
|
|
33127
|
-
this.offsetScrollbarY = Math.max(0, paneHeight - this.
|
|
33220
|
+
if (this.viewportHeight + this.offsetScrollbarY > paneHeight) {
|
|
33221
|
+
this.offsetScrollbarY = Math.max(0, paneHeight - this.viewportHeight);
|
|
33128
33222
|
}
|
|
33129
33223
|
}
|
|
33130
33224
|
this.top = this.getRowIndex(this.offsetScrollbarY, true);
|
|
33131
|
-
this.bottom = this.getRowIndex(this.offsetScrollbarY + this.
|
|
33225
|
+
this.bottom = this.getRowIndex(this.offsetScrollbarY + this.viewportWidth, true);
|
|
33132
33226
|
if (this.bottom === -1) {
|
|
33133
33227
|
this.bottom = this.boundaries.bottom;
|
|
33134
33228
|
}
|
|
@@ -33139,7 +33233,7 @@ class InternalViewport {
|
|
|
33139
33233
|
adjustViewportZoneX() {
|
|
33140
33234
|
const sheetId = this.sheetId;
|
|
33141
33235
|
this.left = this.searchHeaderIndex("COL", this.offsetScrollbarX, this.boundaries.left);
|
|
33142
|
-
this.right = Math.min(this.boundaries.right, this.searchHeaderIndex("COL", this.
|
|
33236
|
+
this.right = Math.min(this.boundaries.right, this.searchHeaderIndex("COL", this.viewportWidth, this.left));
|
|
33143
33237
|
if (this.right === -1) {
|
|
33144
33238
|
this.right = this.getters.getNumberCols(sheetId) - 1;
|
|
33145
33239
|
}
|
|
@@ -33152,7 +33246,7 @@ class InternalViewport {
|
|
|
33152
33246
|
adjustViewportZoneY() {
|
|
33153
33247
|
const sheetId = this.sheetId;
|
|
33154
33248
|
this.top = this.searchHeaderIndex("ROW", this.offsetScrollbarY, this.boundaries.top);
|
|
33155
|
-
this.bottom = Math.min(this.boundaries.bottom, this.searchHeaderIndex("ROW", this.
|
|
33249
|
+
this.bottom = Math.min(this.boundaries.bottom, this.searchHeaderIndex("ROW", this.viewportHeight, this.top));
|
|
33156
33250
|
if (this.bottom === -1) {
|
|
33157
33251
|
this.bottom = this.getters.getNumberRows(sheetId) - 1;
|
|
33158
33252
|
}
|
|
@@ -33414,8 +33508,7 @@ class SheetViewPlugin extends UIPlugin {
|
|
|
33414
33508
|
return [...new Set(viewports.map((v) => range(v.top, v.bottom + 1)).flat())].filter((row) => !this.getters.isHeaderHidden(sheetId, "ROW", row));
|
|
33415
33509
|
}
|
|
33416
33510
|
/**
|
|
33417
|
-
* Return the main viewport maximum size
|
|
33418
|
-
* with some bottom and right padding.
|
|
33511
|
+
* Return the main viewport maximum size relative to the client size.
|
|
33419
33512
|
*/
|
|
33420
33513
|
getMainViewportRect() {
|
|
33421
33514
|
const sheetId = this.getters.getActiveSheetId();
|
|
@@ -33431,8 +33524,8 @@ class SheetViewPlugin extends UIPlugin {
|
|
|
33431
33524
|
const { width, height } = this.getMainViewportRect();
|
|
33432
33525
|
const viewport = this.getMainInternalViewport(sheetId);
|
|
33433
33526
|
return {
|
|
33434
|
-
maxOffsetX: Math.max(0, width - viewport.
|
|
33435
|
-
maxOffsetY: Math.max(0, height - viewport.
|
|
33527
|
+
maxOffsetX: Math.max(0, width - viewport.viewportWidth + 1),
|
|
33528
|
+
maxOffsetY: Math.max(0, height - viewport.viewportHeight + 1),
|
|
33436
33529
|
};
|
|
33437
33530
|
}
|
|
33438
33531
|
getColRowOffsetInViewport(dimension, referenceIndex, index) {
|
|
@@ -40136,9 +40229,6 @@ css /* scss */ `
|
|
|
40136
40229
|
color: #333;
|
|
40137
40230
|
}
|
|
40138
40231
|
|
|
40139
|
-
* {
|
|
40140
|
-
font-family: "Roboto", "RobotoDraft", Helvetica, Arial, sans-serif;
|
|
40141
|
-
}
|
|
40142
40232
|
&,
|
|
40143
40233
|
*,
|
|
40144
40234
|
*:before,
|
|
@@ -43934,6 +44024,6 @@ exports.setTranslationMethod = setTranslationMethod;
|
|
|
43934
44024
|
exports.tokenize = tokenize;
|
|
43935
44025
|
|
|
43936
44026
|
|
|
43937
|
-
__info__.version = '16.1.
|
|
43938
|
-
__info__.date = '2023-
|
|
43939
|
-
__info__.hash = '
|
|
44027
|
+
__info__.version = '16.1.7';
|
|
44028
|
+
__info__.date = '2023-04-13T08:29:52.140Z';
|
|
44029
|
+
__info__.hash = '3141a6d';
|