@odoo/o-spreadsheet 16.4.0-alpha.1 → 16.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.
- package/dist/o-spreadsheet.cjs.js +379 -277
- package/dist/o-spreadsheet.d.ts +26 -6
- package/dist/o-spreadsheet.esm.js +378 -278
- package/dist/o-spreadsheet.iife.js +379 -277
- package/dist/o-spreadsheet.iife.min.js +35 -34
- package/package.json +1 -1
|
@@ -2215,249 +2215,6 @@
|
|
|
2215
2215
|
return { sheetName, xc };
|
|
2216
2216
|
}
|
|
2217
2217
|
|
|
2218
|
-
class RangeImpl {
|
|
2219
|
-
getSheetSize;
|
|
2220
|
-
_zone;
|
|
2221
|
-
parts;
|
|
2222
|
-
invalidXc;
|
|
2223
|
-
prefixSheet = false;
|
|
2224
|
-
sheetId; // the sheet on which the range is defined
|
|
2225
|
-
invalidSheetName; // the name of any sheet that is invalid
|
|
2226
|
-
constructor(args, getSheetSize) {
|
|
2227
|
-
this.getSheetSize = getSheetSize;
|
|
2228
|
-
this._zone = args.zone;
|
|
2229
|
-
this.parts = args.parts;
|
|
2230
|
-
this.prefixSheet = args.prefixSheet;
|
|
2231
|
-
this.invalidXc = args.invalidXc;
|
|
2232
|
-
this.sheetId = args.sheetId;
|
|
2233
|
-
this.invalidSheetName = args.invalidSheetName;
|
|
2234
|
-
}
|
|
2235
|
-
static fromRange(range, getters) {
|
|
2236
|
-
if (range instanceof RangeImpl) {
|
|
2237
|
-
return range;
|
|
2238
|
-
}
|
|
2239
|
-
return new RangeImpl(range, getters.getSheetSize);
|
|
2240
|
-
}
|
|
2241
|
-
get unboundedZone() {
|
|
2242
|
-
return this._zone;
|
|
2243
|
-
}
|
|
2244
|
-
get zone() {
|
|
2245
|
-
const { left, top, bottom, right } = this._zone;
|
|
2246
|
-
if (right !== undefined && bottom !== undefined)
|
|
2247
|
-
return { left, top, right, bottom };
|
|
2248
|
-
else if (bottom === undefined && right !== undefined) {
|
|
2249
|
-
return { right, top, left, bottom: this.getSheetSize(this.sheetId).numberOfRows - 1 };
|
|
2250
|
-
}
|
|
2251
|
-
else if (right === undefined && bottom !== undefined) {
|
|
2252
|
-
return { bottom, left, top, right: this.getSheetSize(this.sheetId).numberOfCols - 1 };
|
|
2253
|
-
}
|
|
2254
|
-
throw new Error(_lt("Bad zone format"));
|
|
2255
|
-
}
|
|
2256
|
-
static getRangeParts(xc, zone) {
|
|
2257
|
-
const parts = xc.split(":").map((p) => {
|
|
2258
|
-
const isFullRow = isRowReference(p);
|
|
2259
|
-
return {
|
|
2260
|
-
colFixed: isFullRow ? false : p.startsWith("$"),
|
|
2261
|
-
rowFixed: isFullRow ? p.startsWith("$") : p.includes("$", 1),
|
|
2262
|
-
};
|
|
2263
|
-
});
|
|
2264
|
-
const isFullCol = zone.bottom === undefined;
|
|
2265
|
-
const isFullRow = zone.right === undefined;
|
|
2266
|
-
if (isFullCol) {
|
|
2267
|
-
parts[0].rowFixed = parts[0].rowFixed || parts[1].rowFixed;
|
|
2268
|
-
parts[1].rowFixed = parts[0].rowFixed || parts[1].rowFixed;
|
|
2269
|
-
if (zone.left === zone.right) {
|
|
2270
|
-
parts[0].colFixed = parts[0].colFixed || parts[1].colFixed;
|
|
2271
|
-
parts[1].colFixed = parts[0].colFixed || parts[1].colFixed;
|
|
2272
|
-
}
|
|
2273
|
-
}
|
|
2274
|
-
if (isFullRow) {
|
|
2275
|
-
parts[0].colFixed = parts[0].colFixed || parts[1].colFixed;
|
|
2276
|
-
parts[1].colFixed = parts[0].colFixed || parts[1].colFixed;
|
|
2277
|
-
if (zone.top === zone.bottom) {
|
|
2278
|
-
parts[0].rowFixed = parts[0].rowFixed || parts[1].rowFixed;
|
|
2279
|
-
parts[1].rowFixed = parts[0].rowFixed || parts[1].rowFixed;
|
|
2280
|
-
}
|
|
2281
|
-
}
|
|
2282
|
-
return parts;
|
|
2283
|
-
}
|
|
2284
|
-
get isFullCol() {
|
|
2285
|
-
return this._zone.bottom === undefined;
|
|
2286
|
-
}
|
|
2287
|
-
get isFullRow() {
|
|
2288
|
-
return this._zone.right === undefined;
|
|
2289
|
-
}
|
|
2290
|
-
get rangeData() {
|
|
2291
|
-
return {
|
|
2292
|
-
_zone: this._zone,
|
|
2293
|
-
_sheetId: this.sheetId,
|
|
2294
|
-
};
|
|
2295
|
-
}
|
|
2296
|
-
/**
|
|
2297
|
-
* Check that a zone is valid regarding the order of top-bottom and left-right.
|
|
2298
|
-
* Left should be smaller than right, top should be smaller than bottom.
|
|
2299
|
-
* If it's not the case, simply invert them, and invert the linked parts
|
|
2300
|
-
*/
|
|
2301
|
-
orderZone() {
|
|
2302
|
-
const zone = { ...this._zone };
|
|
2303
|
-
let parts = this.parts;
|
|
2304
|
-
if (zone.right !== undefined && zone.right < zone.left) {
|
|
2305
|
-
let right = zone.right;
|
|
2306
|
-
zone.right = zone.left;
|
|
2307
|
-
zone.left = right;
|
|
2308
|
-
parts = [
|
|
2309
|
-
{
|
|
2310
|
-
colFixed: parts[1]?.colFixed || false,
|
|
2311
|
-
rowFixed: parts[0]?.rowFixed || false,
|
|
2312
|
-
},
|
|
2313
|
-
{
|
|
2314
|
-
colFixed: parts[0]?.colFixed || false,
|
|
2315
|
-
rowFixed: parts[1]?.rowFixed || false,
|
|
2316
|
-
},
|
|
2317
|
-
];
|
|
2318
|
-
}
|
|
2319
|
-
if (zone.bottom !== undefined && zone.bottom < zone.top) {
|
|
2320
|
-
let bottom = zone.bottom;
|
|
2321
|
-
zone.bottom = zone.top;
|
|
2322
|
-
zone.top = bottom;
|
|
2323
|
-
parts = [
|
|
2324
|
-
{
|
|
2325
|
-
colFixed: parts[0]?.colFixed || false,
|
|
2326
|
-
rowFixed: parts[1]?.rowFixed || false,
|
|
2327
|
-
},
|
|
2328
|
-
{
|
|
2329
|
-
colFixed: parts[1]?.colFixed || false,
|
|
2330
|
-
rowFixed: parts[0]?.rowFixed || false,
|
|
2331
|
-
},
|
|
2332
|
-
];
|
|
2333
|
-
}
|
|
2334
|
-
return this.clone({ zone, parts });
|
|
2335
|
-
}
|
|
2336
|
-
/**
|
|
2337
|
-
*
|
|
2338
|
-
* @param rangeParams optional, values to put in the cloned range instead of the current values of the range
|
|
2339
|
-
*/
|
|
2340
|
-
clone(rangeParams) {
|
|
2341
|
-
return new RangeImpl({
|
|
2342
|
-
zone: rangeParams?.zone ? rangeParams.zone : { ...this._zone },
|
|
2343
|
-
sheetId: rangeParams?.sheetId ? rangeParams.sheetId : this.sheetId,
|
|
2344
|
-
invalidSheetName: rangeParams && "invalidSheetName" in rangeParams // 'attr in obj' instead of just 'obj.attr' because we accept undefined values
|
|
2345
|
-
? rangeParams.invalidSheetName
|
|
2346
|
-
: this.invalidSheetName,
|
|
2347
|
-
invalidXc: rangeParams && "invalidXc" in rangeParams ? rangeParams.invalidXc : this.invalidXc,
|
|
2348
|
-
parts: rangeParams?.parts
|
|
2349
|
-
? rangeParams.parts
|
|
2350
|
-
: this.parts.map((part) => {
|
|
2351
|
-
return { rowFixed: part.rowFixed, colFixed: part.colFixed };
|
|
2352
|
-
}),
|
|
2353
|
-
prefixSheet: rangeParams?.prefixSheet ? rangeParams.prefixSheet : this.prefixSheet,
|
|
2354
|
-
}, this.getSheetSize);
|
|
2355
|
-
}
|
|
2356
|
-
}
|
|
2357
|
-
/**
|
|
2358
|
-
* Copy a range. If the range is on the sheetIdFrom, the range will target
|
|
2359
|
-
* sheetIdTo.
|
|
2360
|
-
*/
|
|
2361
|
-
function copyRangeWithNewSheetId(sheetIdFrom, sheetIdTo, range) {
|
|
2362
|
-
const sheetId = range.sheetId === sheetIdFrom ? sheetIdTo : range.sheetId;
|
|
2363
|
-
return range.clone({ sheetId });
|
|
2364
|
-
}
|
|
2365
|
-
/**
|
|
2366
|
-
* Create a range from a xc. If the xc is empty, this function returns undefined.
|
|
2367
|
-
*/
|
|
2368
|
-
function createRange(getters, sheetId, range) {
|
|
2369
|
-
return range ? getters.getRangeFromSheetXC(sheetId, range) : undefined;
|
|
2370
|
-
}
|
|
2371
|
-
|
|
2372
|
-
/** Methods from Odoo Web Utils */
|
|
2373
|
-
/**
|
|
2374
|
-
* This function computes a score that represent the fact that the
|
|
2375
|
-
* string contains the pattern, or not
|
|
2376
|
-
*
|
|
2377
|
-
* - If the score is 0, the string does not contain the letters of the pattern in
|
|
2378
|
-
* the correct order.
|
|
2379
|
-
* - if the score is > 0, it actually contains the letters.
|
|
2380
|
-
*
|
|
2381
|
-
* Better matches will get a higher score: consecutive letters are better,
|
|
2382
|
-
* and a match closer to the beginning of the string is also scored higher.
|
|
2383
|
-
*/
|
|
2384
|
-
function fuzzyMatch(pattern, str) {
|
|
2385
|
-
pattern = pattern.toLocaleLowerCase();
|
|
2386
|
-
str = str.toLocaleLowerCase();
|
|
2387
|
-
let totalScore = 0;
|
|
2388
|
-
let currentScore = 0;
|
|
2389
|
-
let len = str.length;
|
|
2390
|
-
let patternIndex = 0;
|
|
2391
|
-
for (let i = 0; i < len; i++) {
|
|
2392
|
-
if (str[i] === pattern[patternIndex]) {
|
|
2393
|
-
patternIndex++;
|
|
2394
|
-
currentScore += 100 + currentScore - i / 200;
|
|
2395
|
-
}
|
|
2396
|
-
else {
|
|
2397
|
-
currentScore = 0;
|
|
2398
|
-
}
|
|
2399
|
-
totalScore = totalScore + currentScore;
|
|
2400
|
-
}
|
|
2401
|
-
return patternIndex === pattern.length ? totalScore : 0;
|
|
2402
|
-
}
|
|
2403
|
-
/**
|
|
2404
|
-
* Return a list of things that matches a pattern, ordered by their 'score' (
|
|
2405
|
-
* higher score first). An higher score means that the match is better. For
|
|
2406
|
-
* example, consecutive letters are considered a better match.
|
|
2407
|
-
*/
|
|
2408
|
-
function fuzzyLookup(pattern, list, fn) {
|
|
2409
|
-
const results = [];
|
|
2410
|
-
list.forEach((data) => {
|
|
2411
|
-
const score = fuzzyMatch(pattern, fn(data));
|
|
2412
|
-
if (score > 0) {
|
|
2413
|
-
results.push({ score, elem: data });
|
|
2414
|
-
}
|
|
2415
|
-
});
|
|
2416
|
-
// we want better matches first
|
|
2417
|
-
results.sort((a, b) => b.score - a.score);
|
|
2418
|
-
return results.map((r) => r.elem);
|
|
2419
|
-
}
|
|
2420
|
-
|
|
2421
|
-
function createDefaultRows(rowNumber) {
|
|
2422
|
-
const rows = [];
|
|
2423
|
-
for (let i = 0; i < rowNumber; i++) {
|
|
2424
|
-
const row = {
|
|
2425
|
-
cells: {},
|
|
2426
|
-
};
|
|
2427
|
-
rows.push(row);
|
|
2428
|
-
}
|
|
2429
|
-
return rows;
|
|
2430
|
-
}
|
|
2431
|
-
|
|
2432
|
-
/*
|
|
2433
|
-
* https://stackoverflow.com/questions/105034/create-guid-uuid-in-javascript
|
|
2434
|
-
* */
|
|
2435
|
-
class UuidGenerator {
|
|
2436
|
-
isFastIdStrategy = false;
|
|
2437
|
-
fastIdStart = 0;
|
|
2438
|
-
setIsFastStrategy(isFast) {
|
|
2439
|
-
this.isFastIdStrategy = isFast;
|
|
2440
|
-
}
|
|
2441
|
-
uuidv4() {
|
|
2442
|
-
if (this.isFastIdStrategy) {
|
|
2443
|
-
this.fastIdStart++;
|
|
2444
|
-
return String(this.fastIdStart);
|
|
2445
|
-
//@ts-ignore
|
|
2446
|
-
}
|
|
2447
|
-
else if (window.crypto && window.crypto.getRandomValues) {
|
|
2448
|
-
//@ts-ignore
|
|
2449
|
-
return ([1e7] + -1e3 + -4e3 + -8e3 + -1e11).replace(/[018]/g, (c) => (c ^ (crypto.getRandomValues(new Uint8Array(1))[0] & (15 >> (c / 4)))).toString(16));
|
|
2450
|
-
}
|
|
2451
|
-
else {
|
|
2452
|
-
// mainly for jest and other browsers that do not have the crypto functionality
|
|
2453
|
-
return "xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx".replace(/[xy]/g, function (c) {
|
|
2454
|
-
var r = (Math.random() * 16) | 0, v = c == "x" ? r : (r & 0x3) | 0x8;
|
|
2455
|
-
return v.toString(16);
|
|
2456
|
-
});
|
|
2457
|
-
}
|
|
2458
|
-
}
|
|
2459
|
-
}
|
|
2460
|
-
|
|
2461
2218
|
/**
|
|
2462
2219
|
* Convert from a cartesian reference to a Zone
|
|
2463
2220
|
* The range boundaries will be kept in the same order as the
|
|
@@ -3064,6 +2821,287 @@
|
|
|
3064
2821
|
return set;
|
|
3065
2822
|
}
|
|
3066
2823
|
|
|
2824
|
+
class RangeImpl {
|
|
2825
|
+
getSheetSize;
|
|
2826
|
+
_zone;
|
|
2827
|
+
parts;
|
|
2828
|
+
invalidXc;
|
|
2829
|
+
prefixSheet = false;
|
|
2830
|
+
sheetId; // the sheet on which the range is defined
|
|
2831
|
+
invalidSheetName; // the name of any sheet that is invalid
|
|
2832
|
+
constructor(args, getSheetSize) {
|
|
2833
|
+
this.getSheetSize = getSheetSize;
|
|
2834
|
+
this._zone = args.zone;
|
|
2835
|
+
this.parts = args.parts;
|
|
2836
|
+
this.prefixSheet = args.prefixSheet;
|
|
2837
|
+
this.invalidXc = args.invalidXc;
|
|
2838
|
+
this.sheetId = args.sheetId;
|
|
2839
|
+
this.invalidSheetName = args.invalidSheetName;
|
|
2840
|
+
}
|
|
2841
|
+
static fromRange(range, getters) {
|
|
2842
|
+
if (range instanceof RangeImpl) {
|
|
2843
|
+
return range;
|
|
2844
|
+
}
|
|
2845
|
+
return new RangeImpl(range, getters.getSheetSize);
|
|
2846
|
+
}
|
|
2847
|
+
get unboundedZone() {
|
|
2848
|
+
return this._zone;
|
|
2849
|
+
}
|
|
2850
|
+
get zone() {
|
|
2851
|
+
const { left, top, bottom, right } = this._zone;
|
|
2852
|
+
if (right !== undefined && bottom !== undefined)
|
|
2853
|
+
return { left, top, right, bottom };
|
|
2854
|
+
else if (bottom === undefined && right !== undefined) {
|
|
2855
|
+
return { right, top, left, bottom: this.getSheetSize(this.sheetId).numberOfRows - 1 };
|
|
2856
|
+
}
|
|
2857
|
+
else if (right === undefined && bottom !== undefined) {
|
|
2858
|
+
return { bottom, left, top, right: this.getSheetSize(this.sheetId).numberOfCols - 1 };
|
|
2859
|
+
}
|
|
2860
|
+
throw new Error(_lt("Bad zone format"));
|
|
2861
|
+
}
|
|
2862
|
+
static getRangeParts(xc, zone) {
|
|
2863
|
+
const parts = xc.split(":").map((p) => {
|
|
2864
|
+
const isFullRow = isRowReference(p);
|
|
2865
|
+
return {
|
|
2866
|
+
colFixed: isFullRow ? false : p.startsWith("$"),
|
|
2867
|
+
rowFixed: isFullRow ? p.startsWith("$") : p.includes("$", 1),
|
|
2868
|
+
};
|
|
2869
|
+
});
|
|
2870
|
+
const isFullCol = zone.bottom === undefined;
|
|
2871
|
+
const isFullRow = zone.right === undefined;
|
|
2872
|
+
if (isFullCol) {
|
|
2873
|
+
parts[0].rowFixed = parts[0].rowFixed || parts[1].rowFixed;
|
|
2874
|
+
parts[1].rowFixed = parts[0].rowFixed || parts[1].rowFixed;
|
|
2875
|
+
if (zone.left === zone.right) {
|
|
2876
|
+
parts[0].colFixed = parts[0].colFixed || parts[1].colFixed;
|
|
2877
|
+
parts[1].colFixed = parts[0].colFixed || parts[1].colFixed;
|
|
2878
|
+
}
|
|
2879
|
+
}
|
|
2880
|
+
if (isFullRow) {
|
|
2881
|
+
parts[0].colFixed = parts[0].colFixed || parts[1].colFixed;
|
|
2882
|
+
parts[1].colFixed = parts[0].colFixed || parts[1].colFixed;
|
|
2883
|
+
if (zone.top === zone.bottom) {
|
|
2884
|
+
parts[0].rowFixed = parts[0].rowFixed || parts[1].rowFixed;
|
|
2885
|
+
parts[1].rowFixed = parts[0].rowFixed || parts[1].rowFixed;
|
|
2886
|
+
}
|
|
2887
|
+
}
|
|
2888
|
+
return parts;
|
|
2889
|
+
}
|
|
2890
|
+
get isFullCol() {
|
|
2891
|
+
return this._zone.bottom === undefined;
|
|
2892
|
+
}
|
|
2893
|
+
get isFullRow() {
|
|
2894
|
+
return this._zone.right === undefined;
|
|
2895
|
+
}
|
|
2896
|
+
get rangeData() {
|
|
2897
|
+
return {
|
|
2898
|
+
_zone: this._zone,
|
|
2899
|
+
_sheetId: this.sheetId,
|
|
2900
|
+
};
|
|
2901
|
+
}
|
|
2902
|
+
/**
|
|
2903
|
+
* Check that a zone is valid regarding the order of top-bottom and left-right.
|
|
2904
|
+
* Left should be smaller than right, top should be smaller than bottom.
|
|
2905
|
+
* If it's not the case, simply invert them, and invert the linked parts
|
|
2906
|
+
*/
|
|
2907
|
+
orderZone() {
|
|
2908
|
+
const zone = { ...this._zone };
|
|
2909
|
+
let parts = this.parts;
|
|
2910
|
+
if (zone.right !== undefined && zone.right < zone.left) {
|
|
2911
|
+
let right = zone.right;
|
|
2912
|
+
zone.right = zone.left;
|
|
2913
|
+
zone.left = right;
|
|
2914
|
+
parts = [
|
|
2915
|
+
{
|
|
2916
|
+
colFixed: parts[1]?.colFixed || false,
|
|
2917
|
+
rowFixed: parts[0]?.rowFixed || false,
|
|
2918
|
+
},
|
|
2919
|
+
{
|
|
2920
|
+
colFixed: parts[0]?.colFixed || false,
|
|
2921
|
+
rowFixed: parts[1]?.rowFixed || false,
|
|
2922
|
+
},
|
|
2923
|
+
];
|
|
2924
|
+
}
|
|
2925
|
+
if (zone.bottom !== undefined && zone.bottom < zone.top) {
|
|
2926
|
+
let bottom = zone.bottom;
|
|
2927
|
+
zone.bottom = zone.top;
|
|
2928
|
+
zone.top = bottom;
|
|
2929
|
+
parts = [
|
|
2930
|
+
{
|
|
2931
|
+
colFixed: parts[0]?.colFixed || false,
|
|
2932
|
+
rowFixed: parts[1]?.rowFixed || false,
|
|
2933
|
+
},
|
|
2934
|
+
{
|
|
2935
|
+
colFixed: parts[1]?.colFixed || false,
|
|
2936
|
+
rowFixed: parts[0]?.rowFixed || false,
|
|
2937
|
+
},
|
|
2938
|
+
];
|
|
2939
|
+
}
|
|
2940
|
+
return this.clone({ zone, parts });
|
|
2941
|
+
}
|
|
2942
|
+
/**
|
|
2943
|
+
*
|
|
2944
|
+
* @param rangeParams optional, values to put in the cloned range instead of the current values of the range
|
|
2945
|
+
*/
|
|
2946
|
+
clone(rangeParams) {
|
|
2947
|
+
return new RangeImpl({
|
|
2948
|
+
zone: rangeParams?.zone ? rangeParams.zone : { ...this._zone },
|
|
2949
|
+
sheetId: rangeParams?.sheetId ? rangeParams.sheetId : this.sheetId,
|
|
2950
|
+
invalidSheetName: rangeParams && "invalidSheetName" in rangeParams // 'attr in obj' instead of just 'obj.attr' because we accept undefined values
|
|
2951
|
+
? rangeParams.invalidSheetName
|
|
2952
|
+
: this.invalidSheetName,
|
|
2953
|
+
invalidXc: rangeParams && "invalidXc" in rangeParams ? rangeParams.invalidXc : this.invalidXc,
|
|
2954
|
+
parts: rangeParams?.parts
|
|
2955
|
+
? rangeParams.parts
|
|
2956
|
+
: this.parts.map((part) => {
|
|
2957
|
+
return { rowFixed: part.rowFixed, colFixed: part.colFixed };
|
|
2958
|
+
}),
|
|
2959
|
+
prefixSheet: rangeParams?.prefixSheet ? rangeParams.prefixSheet : this.prefixSheet,
|
|
2960
|
+
}, this.getSheetSize);
|
|
2961
|
+
}
|
|
2962
|
+
}
|
|
2963
|
+
/**
|
|
2964
|
+
* Copy a range. If the range is on the sheetIdFrom, the range will target
|
|
2965
|
+
* sheetIdTo.
|
|
2966
|
+
*/
|
|
2967
|
+
function copyRangeWithNewSheetId(sheetIdFrom, sheetIdTo, range) {
|
|
2968
|
+
const sheetId = range.sheetId === sheetIdFrom ? sheetIdTo : range.sheetId;
|
|
2969
|
+
return range.clone({ sheetId });
|
|
2970
|
+
}
|
|
2971
|
+
/**
|
|
2972
|
+
* Create a range from a xc. If the xc is empty, this function returns undefined.
|
|
2973
|
+
*/
|
|
2974
|
+
function createRange(getters, sheetId, range) {
|
|
2975
|
+
return range ? getters.getRangeFromSheetXC(sheetId, range) : undefined;
|
|
2976
|
+
}
|
|
2977
|
+
/**
|
|
2978
|
+
* Spread multiple colrows zone to one row/col zone and add a many new input range as needed.
|
|
2979
|
+
* For example, A1:B4 will become [A1:A4, B1:B4]
|
|
2980
|
+
*/
|
|
2981
|
+
function spreadRange(ranges) {
|
|
2982
|
+
const postProcessedRanges = [];
|
|
2983
|
+
for (const range of ranges) {
|
|
2984
|
+
const { sheetName } = splitReference(range);
|
|
2985
|
+
const sheetPrefix = sheetName ? `${sheetName}!` : "";
|
|
2986
|
+
const zone = toUnboundedZone(range);
|
|
2987
|
+
if (zone.bottom !== zone.top && zone.left != zone.right) {
|
|
2988
|
+
if (zone.right) {
|
|
2989
|
+
for (let j = zone.left; j <= zone.right; ++j) {
|
|
2990
|
+
postProcessedRanges.push(`${sheetPrefix}${zoneToXc({
|
|
2991
|
+
left: j,
|
|
2992
|
+
right: j,
|
|
2993
|
+
top: zone.top,
|
|
2994
|
+
bottom: zone.bottom,
|
|
2995
|
+
})}`);
|
|
2996
|
+
}
|
|
2997
|
+
}
|
|
2998
|
+
else {
|
|
2999
|
+
for (let j = zone.top; j <= zone.bottom; ++j) {
|
|
3000
|
+
postProcessedRanges.push(`${sheetPrefix}${zoneToXc({
|
|
3001
|
+
left: zone.left,
|
|
3002
|
+
right: zone.right,
|
|
3003
|
+
top: j,
|
|
3004
|
+
bottom: j,
|
|
3005
|
+
})}`);
|
|
3006
|
+
}
|
|
3007
|
+
}
|
|
3008
|
+
}
|
|
3009
|
+
else {
|
|
3010
|
+
postProcessedRanges.push(range);
|
|
3011
|
+
}
|
|
3012
|
+
}
|
|
3013
|
+
return postProcessedRanges;
|
|
3014
|
+
}
|
|
3015
|
+
|
|
3016
|
+
/** Methods from Odoo Web Utils */
|
|
3017
|
+
/**
|
|
3018
|
+
* This function computes a score that represent the fact that the
|
|
3019
|
+
* string contains the pattern, or not
|
|
3020
|
+
*
|
|
3021
|
+
* - If the score is 0, the string does not contain the letters of the pattern in
|
|
3022
|
+
* the correct order.
|
|
3023
|
+
* - if the score is > 0, it actually contains the letters.
|
|
3024
|
+
*
|
|
3025
|
+
* Better matches will get a higher score: consecutive letters are better,
|
|
3026
|
+
* and a match closer to the beginning of the string is also scored higher.
|
|
3027
|
+
*/
|
|
3028
|
+
function fuzzyMatch(pattern, str) {
|
|
3029
|
+
pattern = pattern.toLocaleLowerCase();
|
|
3030
|
+
str = str.toLocaleLowerCase();
|
|
3031
|
+
let totalScore = 0;
|
|
3032
|
+
let currentScore = 0;
|
|
3033
|
+
let len = str.length;
|
|
3034
|
+
let patternIndex = 0;
|
|
3035
|
+
for (let i = 0; i < len; i++) {
|
|
3036
|
+
if (str[i] === pattern[patternIndex]) {
|
|
3037
|
+
patternIndex++;
|
|
3038
|
+
currentScore += 100 + currentScore - i / 200;
|
|
3039
|
+
}
|
|
3040
|
+
else {
|
|
3041
|
+
currentScore = 0;
|
|
3042
|
+
}
|
|
3043
|
+
totalScore = totalScore + currentScore;
|
|
3044
|
+
}
|
|
3045
|
+
return patternIndex === pattern.length ? totalScore : 0;
|
|
3046
|
+
}
|
|
3047
|
+
/**
|
|
3048
|
+
* Return a list of things that matches a pattern, ordered by their 'score' (
|
|
3049
|
+
* higher score first). An higher score means that the match is better. For
|
|
3050
|
+
* example, consecutive letters are considered a better match.
|
|
3051
|
+
*/
|
|
3052
|
+
function fuzzyLookup(pattern, list, fn) {
|
|
3053
|
+
const results = [];
|
|
3054
|
+
list.forEach((data) => {
|
|
3055
|
+
const score = fuzzyMatch(pattern, fn(data));
|
|
3056
|
+
if (score > 0) {
|
|
3057
|
+
results.push({ score, elem: data });
|
|
3058
|
+
}
|
|
3059
|
+
});
|
|
3060
|
+
// we want better matches first
|
|
3061
|
+
results.sort((a, b) => b.score - a.score);
|
|
3062
|
+
return results.map((r) => r.elem);
|
|
3063
|
+
}
|
|
3064
|
+
|
|
3065
|
+
function createDefaultRows(rowNumber) {
|
|
3066
|
+
const rows = [];
|
|
3067
|
+
for (let i = 0; i < rowNumber; i++) {
|
|
3068
|
+
const row = {
|
|
3069
|
+
cells: {},
|
|
3070
|
+
};
|
|
3071
|
+
rows.push(row);
|
|
3072
|
+
}
|
|
3073
|
+
return rows;
|
|
3074
|
+
}
|
|
3075
|
+
|
|
3076
|
+
/*
|
|
3077
|
+
* https://stackoverflow.com/questions/105034/create-guid-uuid-in-javascript
|
|
3078
|
+
* */
|
|
3079
|
+
class UuidGenerator {
|
|
3080
|
+
isFastIdStrategy = false;
|
|
3081
|
+
fastIdStart = 0;
|
|
3082
|
+
setIsFastStrategy(isFast) {
|
|
3083
|
+
this.isFastIdStrategy = isFast;
|
|
3084
|
+
}
|
|
3085
|
+
uuidv4() {
|
|
3086
|
+
if (this.isFastIdStrategy) {
|
|
3087
|
+
this.fastIdStart++;
|
|
3088
|
+
return String(this.fastIdStart);
|
|
3089
|
+
//@ts-ignore
|
|
3090
|
+
}
|
|
3091
|
+
else if (window.crypto && window.crypto.getRandomValues) {
|
|
3092
|
+
//@ts-ignore
|
|
3093
|
+
return ([1e7] + -1e3 + -4e3 + -8e3 + -1e11).replace(/[018]/g, (c) => (c ^ (crypto.getRandomValues(new Uint8Array(1))[0] & (15 >> (c / 4)))).toString(16));
|
|
3094
|
+
}
|
|
3095
|
+
else {
|
|
3096
|
+
// mainly for jest and other browsers that do not have the crypto functionality
|
|
3097
|
+
return "xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx".replace(/[xy]/g, function (c) {
|
|
3098
|
+
var r = (Math.random() * 16) | 0, v = c == "x" ? r : (r & 0x3) | 0x8;
|
|
3099
|
+
return v.toString(16);
|
|
3100
|
+
});
|
|
3101
|
+
}
|
|
3102
|
+
}
|
|
3103
|
+
}
|
|
3104
|
+
|
|
3067
3105
|
class ChartJsComponent extends owl.Component {
|
|
3068
3106
|
static template = "o-spreadsheet-ChartJsComponent";
|
|
3069
3107
|
canvas = owl.useRef("graphContainer");
|
|
@@ -17643,50 +17681,51 @@
|
|
|
17643
17681
|
css /* scss */ `
|
|
17644
17682
|
.o-selection {
|
|
17645
17683
|
.o-selection-input {
|
|
17646
|
-
|
|
17647
|
-
flex-direction: row;
|
|
17684
|
+
padding: 2px 0px;
|
|
17648
17685
|
|
|
17649
17686
|
input {
|
|
17650
17687
|
padding: 4px 6px;
|
|
17651
17688
|
border-radius: 4px;
|
|
17652
17689
|
box-sizing: border-box;
|
|
17653
|
-
flex-grow: 2;
|
|
17654
17690
|
}
|
|
17655
17691
|
input:focus {
|
|
17656
17692
|
outline: none;
|
|
17657
17693
|
}
|
|
17658
17694
|
input.o-required,
|
|
17659
17695
|
input.o-focused {
|
|
17660
|
-
border
|
|
17661
|
-
padding: 3px 5px;
|
|
17696
|
+
border: 1px solid;
|
|
17662
17697
|
}
|
|
17663
17698
|
input.o-focused {
|
|
17664
|
-
border-
|
|
17699
|
+
border-width: 2px;
|
|
17700
|
+
padding: 3px 5px;
|
|
17665
17701
|
}
|
|
17666
17702
|
input.o-invalid {
|
|
17667
|
-
|
|
17703
|
+
/* The background-color is similar to the bootstrap alert-danger class but, because of the commit 0358a76d,
|
|
17704
|
+
* which avoids being parasitized by the dark-mode in spreadsheet, we cannot use this class.
|
|
17705
|
+
* TODO: Replace with the bootstrap alert-danger class when we support dark mode
|
|
17706
|
+
*/
|
|
17707
|
+
background-color: #ffdddd;
|
|
17708
|
+
border-width: 2px;
|
|
17668
17709
|
}
|
|
17669
17710
|
button.o-btn {
|
|
17670
|
-
background: transparent;
|
|
17671
|
-
border: none;
|
|
17672
17711
|
color: #333;
|
|
17673
|
-
cursor: pointer;
|
|
17674
17712
|
}
|
|
17675
17713
|
button.o-btn-action {
|
|
17676
17714
|
margin: 8px 1px;
|
|
17677
17715
|
border-radius: 4px;
|
|
17678
|
-
background: transparent;
|
|
17679
17716
|
border: 1px solid #dadce0;
|
|
17680
17717
|
color: #188038;
|
|
17681
|
-
font-weight: bold;
|
|
17682
17718
|
font-size: 14px;
|
|
17683
17719
|
height: 25px;
|
|
17684
17720
|
}
|
|
17721
|
+
.error-icon {
|
|
17722
|
+
right: 7px;
|
|
17723
|
+
top: 7px;
|
|
17724
|
+
}
|
|
17685
17725
|
}
|
|
17686
17726
|
/** Make the character a bit bigger
|
|
17687
17727
|
compared to its neighbor INPUT box */
|
|
17688
17728
|
.o-remove-selection {
|
|
17689
|
-
font-weight: bold;
|
|
17690
17729
|
font-size: calc(100% + 4px);
|
|
17691
17730
|
}
|
|
17692
17731
|
}
|
|
@@ -17702,7 +17741,7 @@
|
|
|
17702
17741
|
class SelectionInput extends owl.Component {
|
|
17703
17742
|
static template = "o-spreadsheet-SelectionInput";
|
|
17704
17743
|
id = uuidGenerator$1.uuidv4();
|
|
17705
|
-
previousRanges = this.props.ranges || [];
|
|
17744
|
+
previousRanges = this.props.ranges() || [];
|
|
17706
17745
|
originSheet = this.env.model.getters.getActiveSheetId();
|
|
17707
17746
|
state = owl.useState({
|
|
17708
17747
|
isMissing: false,
|
|
@@ -17710,11 +17749,11 @@
|
|
|
17710
17749
|
});
|
|
17711
17750
|
focusedInput = owl.useRef("focusedInput");
|
|
17712
17751
|
get ranges() {
|
|
17713
|
-
const
|
|
17714
|
-
const ranges =
|
|
17715
|
-
?
|
|
17716
|
-
: this.props.ranges
|
|
17717
|
-
? this.props.ranges.map((xc, id) => ({
|
|
17752
|
+
const existingSelectionRanges = this.env.model.getters.getSelectionInput(this.id);
|
|
17753
|
+
const ranges = existingSelectionRanges.length
|
|
17754
|
+
? existingSelectionRanges
|
|
17755
|
+
: this.props.ranges()
|
|
17756
|
+
? this.props.ranges().map((xc, id) => ({
|
|
17718
17757
|
xc,
|
|
17719
17758
|
id,
|
|
17720
17759
|
isFocused: false,
|
|
@@ -17734,6 +17773,12 @@
|
|
|
17734
17773
|
get isInvalid() {
|
|
17735
17774
|
return this.props.isInvalid || this.state.isMissing;
|
|
17736
17775
|
}
|
|
17776
|
+
get isConfirmable() {
|
|
17777
|
+
return this.hasFocus && this.ranges.every((range) => range.isValidRange);
|
|
17778
|
+
}
|
|
17779
|
+
get isResettable() {
|
|
17780
|
+
return this.previousRanges.join() !== this.ranges.map((r) => r.xc).join();
|
|
17781
|
+
}
|
|
17737
17782
|
setup() {
|
|
17738
17783
|
owl.useEffect(() => this.focusedInput.el?.focus(), () => [this.focusedInput.el]);
|
|
17739
17784
|
owl.onMounted(() => this.enableNewSelectionInput());
|
|
@@ -17743,7 +17788,7 @@
|
|
|
17743
17788
|
enableNewSelectionInput() {
|
|
17744
17789
|
this.env.model.dispatch("ENABLE_NEW_SELECTION_INPUT", {
|
|
17745
17790
|
id: this.id,
|
|
17746
|
-
initialRanges: this.props.ranges,
|
|
17791
|
+
initialRanges: this.props.ranges(),
|
|
17747
17792
|
hasSingleRange: this.props.hasSingleRange,
|
|
17748
17793
|
});
|
|
17749
17794
|
}
|
|
@@ -17752,7 +17797,8 @@
|
|
|
17752
17797
|
}
|
|
17753
17798
|
checkChange() {
|
|
17754
17799
|
const value = this.env.model.getters.getSelectionInputValue(this.id);
|
|
17755
|
-
|
|
17800
|
+
const valid = !this.isInvalid && this.ranges.every((range) => range.isValidRange);
|
|
17801
|
+
if (valid && this.previousRanges.join() !== value.join()) {
|
|
17756
17802
|
this.triggerChange();
|
|
17757
17803
|
}
|
|
17758
17804
|
}
|
|
@@ -17763,7 +17809,6 @@
|
|
|
17763
17809
|
triggerChange() {
|
|
17764
17810
|
const ranges = this.env.model.getters.getSelectionInputValue(this.id);
|
|
17765
17811
|
this.props.onSelectionChanged?.(ranges);
|
|
17766
|
-
this.previousRanges = ranges;
|
|
17767
17812
|
}
|
|
17768
17813
|
onKeydown(ev) {
|
|
17769
17814
|
if (ev.key === "F2") {
|
|
@@ -17813,10 +17858,37 @@
|
|
|
17813
17858
|
});
|
|
17814
17859
|
this.triggerChange();
|
|
17815
17860
|
}
|
|
17861
|
+
reset() {
|
|
17862
|
+
const existingSelectionRanges = this.env.model.getters.getSelectionInput(this.id);
|
|
17863
|
+
for (const range of existingSelectionRanges) {
|
|
17864
|
+
this.env.model.dispatch("REMOVE_RANGE", {
|
|
17865
|
+
id: this.id,
|
|
17866
|
+
rangeId: range.id,
|
|
17867
|
+
});
|
|
17868
|
+
}
|
|
17869
|
+
for (const range of this.previousRanges) {
|
|
17870
|
+
this.env.model.dispatch("ADD_RANGE", { id: this.id, value: range });
|
|
17871
|
+
}
|
|
17872
|
+
this.confirm();
|
|
17873
|
+
}
|
|
17816
17874
|
confirm() {
|
|
17817
|
-
|
|
17818
|
-
const
|
|
17819
|
-
|
|
17875
|
+
let anyValidInput = false;
|
|
17876
|
+
const existingSelectionRanges = this.env.model.getters.getSelectionInput(this.id);
|
|
17877
|
+
const existingSelectionXcs = [];
|
|
17878
|
+
for (const range of existingSelectionRanges) {
|
|
17879
|
+
if (range.xc === "") {
|
|
17880
|
+
this.env.model.dispatch("REMOVE_RANGE", {
|
|
17881
|
+
id: this.id,
|
|
17882
|
+
rangeId: range.id,
|
|
17883
|
+
});
|
|
17884
|
+
continue;
|
|
17885
|
+
}
|
|
17886
|
+
existingSelectionXcs.push(range.xc);
|
|
17887
|
+
if (this.env.model.getters.isRangeValid(range.xc)) {
|
|
17888
|
+
anyValidInput = true;
|
|
17889
|
+
}
|
|
17890
|
+
}
|
|
17891
|
+
if (this.props.required && !anyValidInput) {
|
|
17820
17892
|
this.state.isMissing = true;
|
|
17821
17893
|
}
|
|
17822
17894
|
const activeSheetId = this.env.model.getters.getActiveSheetId();
|
|
@@ -17826,11 +17898,17 @@
|
|
|
17826
17898
|
sheetIdTo: this.originSheet,
|
|
17827
17899
|
});
|
|
17828
17900
|
}
|
|
17901
|
+
this.props.onSelectionChanged?.(existingSelectionXcs);
|
|
17829
17902
|
this.props.onSelectionConfirmed?.();
|
|
17903
|
+
this.previousRanges = this.props.ranges();
|
|
17904
|
+
if (existingSelectionXcs.join() !== this.previousRanges.join()) {
|
|
17905
|
+
this.enableNewSelectionInput();
|
|
17906
|
+
}
|
|
17907
|
+
this.env.model.dispatch("UNFOCUS_SELECTION_INPUT");
|
|
17830
17908
|
}
|
|
17831
17909
|
}
|
|
17832
17910
|
SelectionInput.props = {
|
|
17833
|
-
ranges:
|
|
17911
|
+
ranges: Function,
|
|
17834
17912
|
hasSingleRange: { type: Boolean, optional: true },
|
|
17835
17913
|
required: { type: Boolean, optional: true },
|
|
17836
17914
|
isInvalid: { type: Boolean, optional: true },
|
|
@@ -17878,10 +17956,14 @@
|
|
|
17878
17956
|
this.dataSeriesRanges = ranges;
|
|
17879
17957
|
}
|
|
17880
17958
|
onDataSeriesConfirmed() {
|
|
17959
|
+
this.dataSeriesRanges = spreadRange(this.dataSeriesRanges);
|
|
17881
17960
|
this.state.datasetDispatchResult = this.props.updateChart({
|
|
17882
17961
|
dataSets: this.dataSeriesRanges,
|
|
17883
17962
|
});
|
|
17884
17963
|
}
|
|
17964
|
+
getDataSeriesRanges() {
|
|
17965
|
+
return this.dataSeriesRanges;
|
|
17966
|
+
}
|
|
17885
17967
|
/**
|
|
17886
17968
|
* Change the local labelRange. The model should be updated when the
|
|
17887
17969
|
* button "confirm" is clicked
|
|
@@ -17894,6 +17976,9 @@
|
|
|
17894
17976
|
labelRange: this.labelRange,
|
|
17895
17977
|
});
|
|
17896
17978
|
}
|
|
17979
|
+
getLabelRange() {
|
|
17980
|
+
return this.labelRange || "";
|
|
17981
|
+
}
|
|
17897
17982
|
onUpdateAggregated(ev) {
|
|
17898
17983
|
this.props.updateChart({
|
|
17899
17984
|
aggregated: ev.target.checked,
|
|
@@ -18314,6 +18399,9 @@
|
|
|
18314
18399
|
dataRange: this.dataRange,
|
|
18315
18400
|
});
|
|
18316
18401
|
}
|
|
18402
|
+
getDataRange() {
|
|
18403
|
+
return this.dataRange || "";
|
|
18404
|
+
}
|
|
18317
18405
|
}
|
|
18318
18406
|
GaugeChartConfigPanel.props = {
|
|
18319
18407
|
figureId: String,
|
|
@@ -18512,6 +18600,9 @@
|
|
|
18512
18600
|
keyValue: this.keyValue,
|
|
18513
18601
|
});
|
|
18514
18602
|
}
|
|
18603
|
+
getKeyValueRange() {
|
|
18604
|
+
return this.keyValue || "";
|
|
18605
|
+
}
|
|
18515
18606
|
onBaselineRangeChanged(ranges) {
|
|
18516
18607
|
this.baseline = ranges[0];
|
|
18517
18608
|
}
|
|
@@ -18520,6 +18611,9 @@
|
|
|
18520
18611
|
baseline: this.baseline,
|
|
18521
18612
|
});
|
|
18522
18613
|
}
|
|
18614
|
+
getBaselineRange() {
|
|
18615
|
+
return this.baseline || "";
|
|
18616
|
+
}
|
|
18523
18617
|
updateBaselineMode(ev) {
|
|
18524
18618
|
this.props.updateChart({ baselineMode: ev.target.value });
|
|
18525
18619
|
}
|
|
@@ -28852,11 +28946,9 @@
|
|
|
28852
28946
|
*/
|
|
28853
28947
|
class CorePlugin extends BasePlugin {
|
|
28854
28948
|
getters;
|
|
28855
|
-
range;
|
|
28856
28949
|
uuidGenerator;
|
|
28857
28950
|
constructor({ getters, stateObserver, range, dispatch, uuidGenerator }) {
|
|
28858
28951
|
super(stateObserver, dispatch);
|
|
28859
|
-
this.range = range;
|
|
28860
28952
|
range.addRangeProvider(this.adaptRanges.bind(this));
|
|
28861
28953
|
this.getters = getters;
|
|
28862
28954
|
this.uuidGenerator = uuidGenerator;
|
|
@@ -38619,6 +38711,7 @@
|
|
|
38619
38711
|
// ---------------------------------------------------------------------------
|
|
38620
38712
|
allowDispatch(cmd) {
|
|
38621
38713
|
switch (cmd.type) {
|
|
38714
|
+
case "ADD_RANGE":
|
|
38622
38715
|
case "ADD_EMPTY_RANGE":
|
|
38623
38716
|
if (this.inputHasSingleRange && this.ranges.length === 1) {
|
|
38624
38717
|
return 30 /* CommandResult.MaximumRangesReached */;
|
|
@@ -38663,6 +38756,10 @@
|
|
|
38663
38756
|
this.insertNewRange(this.ranges.length, [""]);
|
|
38664
38757
|
this.focusLast();
|
|
38665
38758
|
break;
|
|
38759
|
+
case "ADD_RANGE":
|
|
38760
|
+
this.insertNewRange(this.ranges.length, [cmd.value]);
|
|
38761
|
+
this.focusLast();
|
|
38762
|
+
break;
|
|
38666
38763
|
case "REMOVE_RANGE":
|
|
38667
38764
|
const index = this.getIndex(cmd.rangeId);
|
|
38668
38765
|
if (index !== null) {
|
|
@@ -38874,6 +38971,7 @@
|
|
|
38874
38971
|
case "UNFOCUS_SELECTION_INPUT":
|
|
38875
38972
|
this.unfocus();
|
|
38876
38973
|
break;
|
|
38974
|
+
case "ADD_RANGE":
|
|
38877
38975
|
case "ADD_EMPTY_RANGE":
|
|
38878
38976
|
case "REMOVE_RANGE":
|
|
38879
38977
|
if (cmd.id !== this.focusedInputId) {
|
|
@@ -38887,9 +38985,11 @@
|
|
|
38887
38985
|
if (cmd.id !== this.focusedInputId) {
|
|
38888
38986
|
const input = this.inputs[cmd.id];
|
|
38889
38987
|
const range = input.ranges.find((range) => range.id === cmd.rangeId);
|
|
38890
|
-
|
|
38891
|
-
|
|
38892
|
-
|
|
38988
|
+
if (this.isRangeValid(range?.xc || "A1")) {
|
|
38989
|
+
const sheetId = this.getters.getActiveSheetId();
|
|
38990
|
+
const zone = this.getters.getRangeFromSheetXC(sheetId, range?.xc || "A1").zone;
|
|
38991
|
+
this.selection.capture(input, { cell: { col: zone.left, row: zone.top }, zone }, { handleEvent: input.handleEvent.bind(input) });
|
|
38992
|
+
}
|
|
38893
38993
|
this.focusedInputId = cmd.id;
|
|
38894
38994
|
}
|
|
38895
38995
|
break;
|
|
@@ -48188,6 +48288,8 @@
|
|
|
48188
48288
|
exports.findCellInNewZone = findCellInNewZone;
|
|
48189
48289
|
exports.functionCache = functionCache;
|
|
48190
48290
|
exports.helpers = helpers;
|
|
48291
|
+
exports.invalidateCFEvaluationCommands = invalidateCFEvaluationCommands;
|
|
48292
|
+
exports.invalidateDependenciesCommands = invalidateDependenciesCommands;
|
|
48191
48293
|
exports.invalidateEvaluationCommands = invalidateEvaluationCommands;
|
|
48192
48294
|
exports.links = links;
|
|
48193
48295
|
exports.load = load;
|
|
@@ -48201,9 +48303,9 @@
|
|
|
48201
48303
|
Object.defineProperty(exports, '__esModule', { value: true });
|
|
48202
48304
|
|
|
48203
48305
|
|
|
48204
|
-
__info__.version = '16.4.0-alpha.
|
|
48205
|
-
__info__.date = '2023-05-
|
|
48206
|
-
__info__.hash = '
|
|
48306
|
+
__info__.version = '16.4.0-alpha.2';
|
|
48307
|
+
__info__.date = '2023-05-26T13:40:49.561Z';
|
|
48308
|
+
__info__.hash = '82328ad';
|
|
48207
48309
|
|
|
48208
48310
|
|
|
48209
48311
|
})(this.o_spreadsheet = this.o_spreadsheet || {}, owl);
|