tsgrid-ui 2.5.0 → 2.7.0
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/CHANGELOG.md +114 -0
- package/dist/tsgrid-ui.css +2 -2
- package/dist/tsgrid-ui.d.ts +35 -6
- package/dist/tsgrid-ui.es6.js +80 -63
- package/dist/tsgrid-ui.es6.js.map +1 -1
- package/dist/tsgrid-ui.es6.min.js +31 -31
- package/dist/tsgrid-ui.js +81 -64
- package/dist/tsgrid-ui.min.css +2 -2
- package/dist/tsgrid-ui.min.js +30 -30
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,120 @@
|
|
|
2
2
|
|
|
3
3
|
All notable changes to **TsGrid UI** will be documented in this file.
|
|
4
4
|
|
|
5
|
+
## v2.7.0 — 2026-05-14
|
|
6
|
+
|
|
7
|
+
### Refactor
|
|
8
|
+
|
|
9
|
+
- **`locale()` extraction — `LocaleDeps` deps-injection pattern**: Extracted the `TsUtils.locale()` method body (62 LOC, the largest remaining cluster in `tsutils.ts`) into `_locale()` in `src/tsutils-locale.ts` via a new minimal `LocaleDeps` interface (`{ extend: (target, ...sources) => object; fetch: (url, init?) => Promise<Response> }`). Class method `locale()` becomes a 4-line delegator: `return _locale(locale, keepPhrases, noMerge, this.settings, { extend: this.extend.bind(this), fetch: globalThis.fetch.bind(globalThis) }).then(result => { if (result.settings) this.settings = result.settings; return result.kind === 'load' ? { file: result.file, data: result.data } : undefined })`. Public signature `TsUtils.locale(locale, keepPhrases?, noMerge?): Promise<{ file; data } | void>` is **byte-identical** to v2.6.0. `vi.spyOn(TsUtils.prototype, 'locale')` continues to work (prototype delegator — INV-SPY PASS). Follows the v2.3 `MessageDeps` / v2.6 `DateDeps` canonical patterns. Array-form branch uses **direct `_locale()` recursion** (not delegator round-trip — design OQ-2/D2) to keep the internal call graph leaf-friendly.
|
|
10
|
+
- `LocaleDeps` / `LocaleResult` are **internal** types backing the deps-injection delegation pattern. They are NOT exported through `src/index.ts` barrel (INV-L7-DEPS-INTERNAL PASS; INV-7 byte-identical barrel maintained). Consumers of `TsUtils.locale()` are unaffected.
|
|
11
|
+
|
|
12
|
+
### Fixed
|
|
13
|
+
|
|
14
|
+
- **Object-form `Utils.locale({...})` Promise hang (R-LOC-4 / INV-L7-OBJFIX)**: The v2.6.0 `locale()` body had `if (typeof locale === 'object') { this.settings = this.extend({}, this.settings, TsLocale, locale); return }` — the bare `return` inside the `new Promise((resolve, reject) => {...})` executor neither called `resolve()` nor `reject()`, so the Promise hung **forever** for any object-form input. v2.7.0 replaces the bare `return` with a value-returning path: the extracted `async function _locale()` returns `{ kind: 'merge', settings: mergedSettings }` for the object branch; the delegator unwraps to `undefined` (matching the original public contract for object form). `await Utils.locale({ dateFormat: 'dd.mm.yyyy' })` now resolves within ~1 ms (was: pending forever). Covered by test `T-LOC-1` with `vitest timeout: 1000`.
|
|
15
|
+
|
|
16
|
+
### Tests
|
|
17
|
+
|
|
18
|
+
- Added 10-case safety-net suite for `_locale()` in `test/unit/tsutils-locale.test.ts` committed as Phase 4 RED before extraction (INV-TDD PASS — `c7e64b43` < `dda197ad` < `32538179`):
|
|
19
|
+
- **T-LOC-1** — object-form `_locale({ dateFormat: 'dd.mm.yyyy' })` resolves within 1 s with `{ kind: 'merge', settings.dateFormat: 'dd.mm.yyyy' }` (INV-L7-OBJFIX gate)
|
|
20
|
+
- **T-LOC-2** — 5-char string `'ru-ru'` auto-expanded to `'locale/ru-ru.json'` before `deps.fetch`
|
|
21
|
+
- **T-LOC-3** — Full-path string `'locale/ru-ru.json'` passed verbatim (no double-expansion)
|
|
22
|
+
- **T-LOC-4** — `keepPhrases=true` preserves pre-existing phrase keys in returned settings
|
|
23
|
+
- **T-LOC-5** — `keepPhrases=false` (default) executes the TsLocale-merge branch; documents `R-LOC-V26-PRESERVED` (see Known Issues)
|
|
24
|
+
- **T-LOC-6** — `noMerge=true` returns `{ kind: 'load', file, data }` without including merged settings
|
|
25
|
+
- **T-LOC-7** — Array form `['en-us', 'ru-ru']` triggers N fetch calls and resolves `{ kind: 'void' }` (delegator maps to `undefined`)
|
|
26
|
+
- **T-LOC-8** — `deps.fetch` rejection re-throws and rejects the returned Promise
|
|
27
|
+
- **T-LOC-9** — Design constraint: T-LOC-2..T-LOC-8 pass in jsdom **without** a global `fetch` polyfill (proven by T-LOC-2..T-LOC-8 collectively)
|
|
28
|
+
- **T-LOC-10** — `vi.spyOn(TsUtils, 'locale')` observes exactly one call when `TsUtils.locale('en-us')` is invoked (INV-SPY / INV-L7-DELEGATOR gate)
|
|
29
|
+
- Total Vitest tests: **297/297 GREEN** (v2.6.0 baseline: 288/288).
|
|
30
|
+
|
|
31
|
+
### Bundle
|
|
32
|
+
|
|
33
|
+
Delta vs v2.6.0 baseline:
|
|
34
|
+
|
|
35
|
+
| Artifact | v2.6.0 | v2.7.0 | Δ bytes | Δ % |
|
|
36
|
+
|----------|--------|--------|---------|-----|
|
|
37
|
+
| `dist/tsgrid-ui.js` (CJS) | 946,686 B | 947,274 B | +588 B | +0.0621% |
|
|
38
|
+
| `dist/tsgrid-ui.es6.js` (ESM) | 944,878 B | 945,466 B | +588 B | +0.0622% |
|
|
39
|
+
| `dist/tsgrid-ui.min.js` (CJS min) | 508,954 B | 509,260 B | +306 B | +0.0601% |
|
|
40
|
+
| `dist/tsgrid-ui.es6.min.js` (ESM min) | 507,819 B | 508,125 B | +306 B | +0.0603% |
|
|
41
|
+
| `dist/tsgrid-ui.d.ts` | 94,446 B | 94,446 B | 0 B | 0.0000% |
|
|
42
|
+
| `dist/tsgrid-ui.css` | 246,980 B | 246,980 B | 0 B | 0.0000% |
|
|
43
|
+
| `dist/tsgrid-ui.min.css` | 229,707 B | 229,707 B | 0 B | 0.0000% |
|
|
44
|
+
|
|
45
|
+
All within ±2% gate. PASSED. The JS bundles grew by ~0.06% (the new `_locale` function body + deps wiring); the `d.ts` is **byte-identical** to v2.6.0 (INV-L7-API PASS — `locale(` signature unchanged, `LocaleDeps`/`LocaleResult` declared `@internal` and stripped by tsup `stripInternal: true`). CSS/min.css unchanged (no css edits in v2.7).
|
|
46
|
+
|
|
47
|
+
### BC
|
|
48
|
+
|
|
49
|
+
- `TsUtils.locale(locale, keepPhrases?, noMerge?): Promise<{ file: string; data: unknown } | void>` — signature and (now-resolving) runtime behavior **BYTE-IDENTICAL** to v2.6.0 for all string/array inputs. **Object-form input changes**: previously hung forever (effectively unusable); now resolves with `undefined` after merging. Existing call sites using string/array inputs work unchanged; existing call sites using object-form were necessarily broken (Promise never settled) and now begin working.
|
|
50
|
+
- `src/index.ts` barrel — **byte-identical** to v2.6.0 (INV-7 PASS).
|
|
51
|
+
- `LocaleDeps` / `LocaleResult` — NEW **internal** types. NOT exported through `src/index.ts` barrel (INV-L7-DEPS-INTERNAL PASS). `LocaleResult` declared `/** @internal */` and stripped from the rolled d.ts. `LocaleDeps` is type-only at the delegator call site and does not affect the d.ts surface.
|
|
52
|
+
- SEMVER MINOR. BC verdict: NONE for string/array inputs; BUGFIX for object-form input (R-LOC-4).
|
|
53
|
+
|
|
54
|
+
### Known issues
|
|
55
|
+
|
|
56
|
+
- **R-LOC-2** (deferred from v2.6 — confirmed still present): Array-form input `await Utils.locale(['en-us', 'ru-ru'])` MUTATES the caller's input array (each 5-char code is rewritten in-place to `'locale/xx-xx.json'`). Preserved verbatim in v2.7. Candidate fix in v2.8.
|
|
57
|
+
- **R-LOC-V26-PRESERVED** (newly documented): `keepPhrases=false` branch in v2.6.0 had comment "clear phrases from language before merging" but the implementation `extend({}, settings, TsLocale, { phrases: {} }, data)` does **not** actually clear pre-existing phrase keys — `extend` is deep, and `extend(existingPhrases, {})` is a no-op. Pre-existing phrase keys survive the merge in both v2.6.0 and v2.7.0. v2.7 scope was constrained to **one** behavior change (R-LOC-4 — object-form hang), so this latent behavior is preserved verbatim with `T-LOC-5` documenting the actual semantics. Candidate fix in v2.8.
|
|
58
|
+
- **R-LOC-3** (carry-forward from v2.6): Locale extraction does NOT unblock formatter extraction. The `format()` family still has its own deps-injection requirement; will be tackled separately in v2.8 via a `FormatterDeps` cluster.
|
|
59
|
+
|
|
60
|
+
### Internal
|
|
61
|
+
|
|
62
|
+
- `src/tsutils-locale.ts` is a **leaf module** (INV-L7-LEAF PASS): zero runtime imports from `./tsbase.js` or `./tsutils.js`. Only `import type { TsUISettings } from './tsutils.js'` (erased at emit) and `import { TsLocale } from './tslocale.js'` (zero-dep data leaf). DAG depth unchanged.
|
|
63
|
+
- INV-9 (zero `this.` in extracted function body) PASS.
|
|
64
|
+
- INV-LINT-INV8 canary: `src/tsutils-*.ts` ESLint glob continues to cover the new file (verified by inserting `void arguments.length;` → `pnpm lint` EXIT 1 → revert → EXIT 0).
|
|
65
|
+
|
|
66
|
+
---
|
|
67
|
+
|
|
68
|
+
## v2.6.0 — 2026-05-13
|
|
69
|
+
|
|
70
|
+
### Refactor
|
|
71
|
+
|
|
72
|
+
- **`date()` extraction — `DateDeps` deps-injection pattern**: Extracted the `TsUtils.date()` method body (22 LOC, the sole `this.lang('Yesterday')` coupling in `tsutils.ts`) into `_date()` in `src/tsutils-datetime.ts` via a new minimal `DateDeps` interface (`{ lang: (phrase: string) => string }`). Class method `date()` becomes a one-line delegator: `return _date(dateStr, this.settings, { lang: this.lang.bind(this) })`. Public signature `TsUtils.date(dateStr: unknown): string` is **byte-identical** to v2.5.0. `vi.spyOn(TsUtils, 'date')` continues to work (prototype delegator). Follows the v2.3 `MessageDeps` / `NotifyDeps` canonical pattern.
|
|
73
|
+
- `DateDeps` is an **internal** interface backing the deps-injection delegation pattern. It is NOT exported through `src/index.ts` barrel (INV-7 byte-identical). Consumers of `TsUtils.date()` are unaffected — the public method signature and runtime behavior are identical to v2.5.0 (same string outputs for all date inputs).
|
|
74
|
+
- **`TsTimeResult` dedup**: Promoted `interface TsTimeResult` in `src/tsutils-datetime.ts` from non-exported to `export interface TsTimeResult`. Deleted the local duplicate declaration in `src/tsutils.ts` (lines 102-107); replaced with `import type { TsTimeResult } from './tsutils-datetime.js'` (for internal use by `isTime()`) and `export type { TsTimeResult } from './tsutils-datetime.js'` (barrel re-export). Single source-of-truth now in `tsutils-datetime.ts`. Mirrors the `TsColorRgb` precedent (`tsutils.ts:104`). `dist/tsgrid-ui.d.ts` continues to declare `TsTimeResult` exactly once (INV-LOC-1 `grep -c` = 1, line 425). Existing `import type { TsTimeResult } from 'tsgrid-ui'` consumers continue to work unchanged — same d.ts reachability, different source file.
|
|
75
|
+
|
|
76
|
+
### Tests
|
|
77
|
+
|
|
78
|
+
- Added 7-case safety-net suite for `_date()` in `test/unit/tsutils-datetime.test.ts` committed as Phase 4 RED before extraction (INV-TDD PASS):
|
|
79
|
+
- T-DATE-1 — empty string → `''`
|
|
80
|
+
- T-DATE-2 — `null` → `''`
|
|
81
|
+
- T-DATE-3 — invalid date string → `''`
|
|
82
|
+
- T-DATE-4 — today (frozen clock, `vi.useFakeTimers()`) → regex match on `<span title="...">H:MM am|pm</span>`
|
|
83
|
+
- T-DATE-5 — yesterday (frozen clock) → regex match on `<span title="May 12, 2026 ...">Yesterday</span>` (proves `deps.lang` is called)
|
|
84
|
+
- T-DATE-6 — Unix timestamp (older) → `<span title="...">` with month-day-year
|
|
85
|
+
- T-DATE-7 — older date string → `<span title="...">` with month-day-year
|
|
86
|
+
- `vi.useFakeTimers()` / `vi.useRealTimers()` wrap the entire `_date()` describe block (INV-DATE-TIMEFREEZE PASS). Total Vitest tests: **288/288 GREEN** (v2.5.0 baseline: 280/280).
|
|
87
|
+
|
|
88
|
+
### Bundle
|
|
89
|
+
|
|
90
|
+
Delta vs v2.5.0 baseline:
|
|
91
|
+
|
|
92
|
+
| Artifact | v2.5.0 | v2.6.0 | Δ bytes | Δ % |
|
|
93
|
+
|----------|--------|--------|---------|-----|
|
|
94
|
+
| `dist/tsgrid-ui.js` (CJS) | 946,611 B | 946,686 B | +75 B | +0.0079% |
|
|
95
|
+
| `dist/tsgrid-ui.es6.js` (ESM) | 944,804 B | 944,878 B | +74 B | +0.0078% |
|
|
96
|
+
| `dist/tsgrid-ui.min.js` (CJS min) | 508,902 B | 508,954 B | +52 B | +0.0102% |
|
|
97
|
+
| `dist/tsgrid-ui.es6.min.js` (ESM min) | 507,768 B | 507,819 B | +51 B | +0.0100% |
|
|
98
|
+
| `dist/tsgrid-ui.d.ts` | 93,022 B | 94,446 B | +1,424 B | +1.5308% |
|
|
99
|
+
|
|
100
|
+
All within ±2% gate. PASSED. The JS bundles are essentially byte-stable (≤0.011%) — the mechanical extraction added nothing meaningful at runtime. The `d.ts` grew by +1,424 B because `TsTimeResult` is now emitted as a named export from `tsutils-datetime.ts` (its source-of-truth file) rather than transitively via `isTime()` from `tsutils.ts`; additionally `DateDeps` is declared in `tsutils-datetime.ts` and included in the rolled d.ts.
|
|
101
|
+
|
|
102
|
+
**Note on P6 dist commit**: `pnpm build` was run at P6-DEDUP to enable the INV-LOC-1 audit-trail gate (`grep -c` = 1 in committed dist). P8 re-emits dist with the `v2.6.0` version banner baked into the JS headers. This is intentional and mirrors the v2.5.0 release commit (`51c00836`) pattern.
|
|
103
|
+
|
|
104
|
+
### BC
|
|
105
|
+
|
|
106
|
+
- `TsUtils.date(dateStr: unknown): string` — signature and runtime behavior **BYTE-IDENTICAL** to v2.5.0. All existing call sites work unchanged.
|
|
107
|
+
- `TsUtils.isTime(val: unknown, retTime?: boolean): boolean | TsTimeResult` — signature **BYTE-IDENTICAL** to v2.5.0.
|
|
108
|
+
- `TsTimeResult` — shape `{ hours: number; minutes: number; seconds: number }` unchanged. Continues to be reachable in `dist/tsgrid-ui.d.ts` at the same surface. Existing `import type { TsTimeResult } from 'tsgrid-ui'` consumers unaffected.
|
|
109
|
+
- `DateDeps` — NEW **internal** interface. Not exported through `src/index.ts` barrel (INV-7 byte-identical). Consumers do not depend on it; `_date()` is not a public export. Spec requirement A-4 (emit `DateDeps` in d.ts as a named export) is NOT honored because honoring it would require modifying `src/index.ts`, violating INV-7. The type IS present in the rolled d.ts (tsutils-datetime.ts is included by tsup), but is not consumer-reachable via a top-level import path. This is a documented spec divergence: INV-7 (backwards compat, blocking) takes precedence over A-4 (additive surface, non-blocking for existing consumers). Future revision: export `DateDeps` from `src/index.ts` if consumers need it.
|
|
110
|
+
- `src/index.ts` barrel — **byte-identical** to v2.5.0 (INV-7 PASS).
|
|
111
|
+
- SEMVER MINOR. BC verdict: NONE.
|
|
112
|
+
|
|
113
|
+
### Internal
|
|
114
|
+
|
|
115
|
+
- v2.5 SUGG-5 (INV-8 over `src/tsutils-color.ts`) **CLOSED by verification**: ESLint `no-restricted-syntax` glob `src/tsutils-*.ts` already covers `tsutils-color.ts`. The two `arguments` occurrences at lines 110 and 145 of that file are inside explanatory comments (`// ... example: arguments[0]...`), not AST nodes — the rule does not fire on comment text. Confirmed by INV-LINT-INV8 canary: injecting `void arguments.length;` into `src/tsutils-datetime.ts` triggers `pnpm lint` EXIT 1 with the expected diagnostic; reverting restores EXIT 0. No code change required. No carry-forward to v2.7+ SUGG list.
|
|
116
|
+
|
|
117
|
+
---
|
|
118
|
+
|
|
5
119
|
## v2.5.0 — 2026-05-13
|
|
6
120
|
|
|
7
121
|
### Refactor
|
package/dist/tsgrid-ui.css
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
/* tsgrid-ui 1.0.x (nightly) (5/
|
|
1
|
+
/* tsgrid-ui 1.0.x (nightly) (5/14/2026, 12:09:58 AM) (c) 2014 vitmalina@gmail.com, (c) 2026 DaverSoGT — MIT */
|
|
2
2
|
/**
|
|
3
3
|
* TODO:
|
|
4
4
|
* - remove default styling, only keep tsg-* styles
|
|
@@ -9,7 +9,7 @@
|
|
|
9
9
|
*/
|
|
10
10
|
@font-face {
|
|
11
11
|
font-family: "tsgrid-font";
|
|
12
|
-
src: url("data:application/x-font-woff;charset=utf-8;base64,d09GRgABAAAAAApYAAsAAAAAD0wAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAABHU1VCAAABCAAAADsAAABUIIslek9TLzIAAAFEAAAAQQAAAFZdKW6ZY21hcAAAAYgAAACiAAACNBnCLmJnbHlmAAACLAAABd8AAAfo+
|
|
12
|
+
src: url("data:application/x-font-woff;charset=utf-8;base64,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") format("woff");
|
|
13
13
|
font-weight: normal;
|
|
14
14
|
font-style: normal;
|
|
15
15
|
}
|
package/dist/tsgrid-ui.d.ts
CHANGED
|
@@ -393,6 +393,41 @@ interface TsLockOptions {
|
|
|
393
393
|
onClick?: () => void;
|
|
394
394
|
}
|
|
395
395
|
|
|
396
|
+
/**
|
|
397
|
+
* TsUtils date-time sub-module — Phase 5b of v2.5 SDD.
|
|
398
|
+
* DAG position: leaf module (no tsbase/tsutils imports).
|
|
399
|
+
*
|
|
400
|
+
* Imports:
|
|
401
|
+
* ./tsutils-type-guards.js — isInt as _isInt (needed by isDate, isTime, formatDate, formatTime)
|
|
402
|
+
* ./tsutils.js — type-only import type { TsUISettings } (TS erases at emit)
|
|
403
|
+
* Precedent: tsutils-type-guards.ts:9, tsutils-message.ts:26
|
|
404
|
+
*
|
|
405
|
+
* INV-4: MUST NOT import from tsbase.ts or tsutils.ts at runtime.
|
|
406
|
+
* INV-8: No arguments.length usage.
|
|
407
|
+
* INV-9: No this.X in exported function bodies.
|
|
408
|
+
*
|
|
409
|
+
* 4-space indent convention.
|
|
410
|
+
*
|
|
411
|
+
* OQ-2 (TsTimeResult): local non-exported interface `TsTimeResult` defined inline
|
|
412
|
+
* here (structurally identical to tsutils.ts copy). Avoids back-import of a
|
|
413
|
+
* non-exported type; the class delegator in tsutils.ts casts via `as boolean | TsTimeResult`.
|
|
414
|
+
*
|
|
415
|
+
* R-DT-3 (settings reference): `settings` is passed as a reference to `this.settings`
|
|
416
|
+
* from delegators — never cloned. TsLocale mutations to fullmonths/shortmonths/dateFormat
|
|
417
|
+
* etc. flow through without restart.
|
|
418
|
+
*
|
|
419
|
+
* R-DT-2 / R-DT-8 (intra-cluster calls): _isDateTime calls _isDate + _isTime directly
|
|
420
|
+
* as module-level function refs. _formatDateTime calls _formatDate + _formatTime directly.
|
|
421
|
+
* _formatTime calls _isTime directly. Zero this.X inside any extracted body.
|
|
422
|
+
*/
|
|
423
|
+
|
|
424
|
+
/** Return value from _isTime() / TsUtils.isTime() when retTime === true — single canonical declaration (v2.6 dedup) */
|
|
425
|
+
interface TsTimeResult {
|
|
426
|
+
hours: number;
|
|
427
|
+
minutes: number;
|
|
428
|
+
seconds: number;
|
|
429
|
+
}
|
|
430
|
+
|
|
396
431
|
/**
|
|
397
432
|
* Part of TsUi 2.0 library
|
|
398
433
|
* - Dependencies: mQuery, TsUtils, TsBase, TsLocale
|
|
@@ -461,12 +496,6 @@ interface TsFormatterExtra {
|
|
|
461
496
|
}
|
|
462
497
|
/** Signature of a grid-cell formatter function */
|
|
463
498
|
type TsFormatter = (record: TsFormatterExtra, extra?: TsFormatterExtra) => string;
|
|
464
|
-
/** Return value from TsUtils.isTime() when retTime === true */
|
|
465
|
-
interface TsTimeResult {
|
|
466
|
-
hours: number;
|
|
467
|
-
minutes: number;
|
|
468
|
-
seconds: number;
|
|
469
|
-
}
|
|
470
499
|
|
|
471
500
|
/** A normalized menu item */
|
|
472
501
|
interface TsMenuItem {
|
package/dist/tsgrid-ui.es6.js
CHANGED
|
@@ -2987,6 +2987,78 @@ function _formatDateTime(dateStr, format, settings) {
|
|
|
2987
2987
|
if (fmt[1] === "h24") fmt[1] = "h24:m";
|
|
2988
2988
|
return _formatDate(dateStr, fmt[0], settings) + " " + _formatTime(dateStr, fmt[1], settings);
|
|
2989
2989
|
}
|
|
2990
|
+
function _date(dateStr, settings, deps) {
|
|
2991
|
+
if (dateStr === "" || dateStr == null || typeof dateStr === "object" && !dateStr.getMonth) return "";
|
|
2992
|
+
let d1 = new Date(dateStr);
|
|
2993
|
+
if (isInt(dateStr)) d1 = new Date(Number(dateStr));
|
|
2994
|
+
if (String(d1) === "Invalid Date") return "";
|
|
2995
|
+
const months = settings.shortmonths;
|
|
2996
|
+
const d2 = /* @__PURE__ */ new Date();
|
|
2997
|
+
const d3 = /* @__PURE__ */ new Date();
|
|
2998
|
+
d3.setTime(d3.getTime() - 864e5);
|
|
2999
|
+
const dd1 = months[d1.getMonth()] + " " + d1.getDate() + ", " + d1.getFullYear();
|
|
3000
|
+
const dd2 = months[d2.getMonth()] + " " + d2.getDate() + ", " + d2.getFullYear();
|
|
3001
|
+
const dd3 = months[d3.getMonth()] + " " + d3.getDate() + ", " + d3.getFullYear();
|
|
3002
|
+
const time = d1.getHours() - (d1.getHours() > 12 ? 12 : 0) + ":" + (d1.getMinutes() < 10 ? "0" : "") + d1.getMinutes() + " " + (d1.getHours() >= 12 ? "pm" : "am");
|
|
3003
|
+
const time2 = d1.getHours() - (d1.getHours() > 12 ? 12 : 0) + ":" + (d1.getMinutes() < 10 ? "0" : "") + d1.getMinutes() + ":" + (d1.getSeconds() < 10 ? "0" : "") + d1.getSeconds() + " " + (d1.getHours() >= 12 ? "pm" : "am");
|
|
3004
|
+
let dsp = dd1;
|
|
3005
|
+
if (dd1 === dd2) dsp = time;
|
|
3006
|
+
if (dd1 === dd3) dsp = deps.lang("Yesterday");
|
|
3007
|
+
return '<span title="' + dd1 + " " + time2 + '">' + dsp + "</span>";
|
|
3008
|
+
}
|
|
3009
|
+
|
|
3010
|
+
// src/tsutils-locale.ts
|
|
3011
|
+
async function _locale(locale, keepPhrases, noMerge, settings, deps) {
|
|
3012
|
+
if (Array.isArray(locale)) {
|
|
3013
|
+
let mergedSettings = deps.extend({}, settings, { phrases: {} });
|
|
3014
|
+
const localeArr = locale;
|
|
3015
|
+
const proms = [];
|
|
3016
|
+
const files = {};
|
|
3017
|
+
localeArr.forEach((file, ind) => {
|
|
3018
|
+
if (file.length === 5) {
|
|
3019
|
+
file = "locale/" + file.toLowerCase() + ".json";
|
|
3020
|
+
localeArr[ind] = file;
|
|
3021
|
+
}
|
|
3022
|
+
proms.push(_locale(file, true, false, mergedSettings, deps));
|
|
3023
|
+
});
|
|
3024
|
+
const res = await Promise.allSettled(proms);
|
|
3025
|
+
res.forEach((r) => {
|
|
3026
|
+
if (r.status === "fulfilled" && r.value.kind === "load") {
|
|
3027
|
+
files[r.value.file] = r.value.data;
|
|
3028
|
+
}
|
|
3029
|
+
});
|
|
3030
|
+
localeArr.forEach((file) => {
|
|
3031
|
+
mergedSettings = deps.extend({}, mergedSettings, files[file] ?? {});
|
|
3032
|
+
});
|
|
3033
|
+
return { kind: "void", settings: mergedSettings };
|
|
3034
|
+
}
|
|
3035
|
+
if (!locale) locale = "en-us";
|
|
3036
|
+
if (typeof locale === "object") {
|
|
3037
|
+
const mergedSettings = deps.extend({}, settings, TsLocale, locale);
|
|
3038
|
+
return { kind: "merge", settings: mergedSettings };
|
|
3039
|
+
}
|
|
3040
|
+
let localeStr = locale;
|
|
3041
|
+
if (localeStr.length === 5) {
|
|
3042
|
+
localeStr = "locale/" + localeStr.toLowerCase() + ".json";
|
|
3043
|
+
}
|
|
3044
|
+
try {
|
|
3045
|
+
const res = await deps.fetch(localeStr, { method: "GET" });
|
|
3046
|
+
const data = await res.json();
|
|
3047
|
+
if (noMerge !== true) {
|
|
3048
|
+
if (keepPhrases) {
|
|
3049
|
+
const newSettings = deps.extend({}, settings, data);
|
|
3050
|
+
return { kind: "load", file: localeStr, data, settings: newSettings };
|
|
3051
|
+
} else {
|
|
3052
|
+
const newSettings = deps.extend({}, settings, TsLocale, { phrases: {} }, data);
|
|
3053
|
+
return { kind: "load", file: localeStr, data, settings: newSettings };
|
|
3054
|
+
}
|
|
3055
|
+
}
|
|
3056
|
+
return { kind: "load", file: localeStr, data };
|
|
3057
|
+
} catch (err) {
|
|
3058
|
+
console.log("ERROR: Cannot load locale " + localeStr);
|
|
3059
|
+
throw err;
|
|
3060
|
+
}
|
|
3061
|
+
}
|
|
2990
3062
|
|
|
2991
3063
|
// src/tsutils.ts
|
|
2992
3064
|
var query7 = query;
|
|
@@ -3203,23 +3275,7 @@ var Utils = class {
|
|
|
3203
3275
|
return _interval(value);
|
|
3204
3276
|
}
|
|
3205
3277
|
date(dateStr) {
|
|
3206
|
-
|
|
3207
|
-
let d1 = new Date(dateStr);
|
|
3208
|
-
if (this.isInt(dateStr)) d1 = new Date(Number(dateStr));
|
|
3209
|
-
if (String(d1) === "Invalid Date") return "";
|
|
3210
|
-
const months = this.settings.shortmonths;
|
|
3211
|
-
const d2 = /* @__PURE__ */ new Date();
|
|
3212
|
-
const d3 = /* @__PURE__ */ new Date();
|
|
3213
|
-
d3.setTime(d3.getTime() - 864e5);
|
|
3214
|
-
const dd1 = months[d1.getMonth()] + " " + d1.getDate() + ", " + d1.getFullYear();
|
|
3215
|
-
const dd2 = months[d2.getMonth()] + " " + d2.getDate() + ", " + d2.getFullYear();
|
|
3216
|
-
const dd3 = months[d3.getMonth()] + " " + d3.getDate() + ", " + d3.getFullYear();
|
|
3217
|
-
const time = d1.getHours() - (d1.getHours() > 12 ? 12 : 0) + ":" + (d1.getMinutes() < 10 ? "0" : "") + d1.getMinutes() + " " + (d1.getHours() >= 12 ? "pm" : "am");
|
|
3218
|
-
const time2 = d1.getHours() - (d1.getHours() > 12 ? 12 : 0) + ":" + (d1.getMinutes() < 10 ? "0" : "") + d1.getMinutes() + ":" + (d1.getSeconds() < 10 ? "0" : "") + d1.getSeconds() + " " + (d1.getHours() >= 12 ? "pm" : "am");
|
|
3219
|
-
let dsp = dd1;
|
|
3220
|
-
if (dd1 === dd2) dsp = time;
|
|
3221
|
-
if (dd1 === dd3) dsp = this.lang("Yesterday");
|
|
3222
|
-
return '<span title="' + dd1 + " " + time2 + '">' + dsp + "</span>";
|
|
3278
|
+
return _date(dateStr, this.settings, { lang: this.lang.bind(this) });
|
|
3223
3279
|
}
|
|
3224
3280
|
formatSize(sizeStr) {
|
|
3225
3281
|
if (!this.isFloat(sizeStr) || sizeStr === "") return "";
|
|
@@ -3487,52 +3543,13 @@ var Utils = class {
|
|
|
3487
3543
|
return this.execTemplate(translation, params);
|
|
3488
3544
|
}
|
|
3489
3545
|
locale(locale, keepPhrases, noMerge) {
|
|
3490
|
-
|
|
3491
|
-
|
|
3492
|
-
|
|
3493
|
-
|
|
3494
|
-
|
|
3495
|
-
|
|
3496
|
-
|
|
3497
|
-
if (file.length === 5) {
|
|
3498
|
-
file = "locale/" + file.toLowerCase() + ".json";
|
|
3499
|
-
localeArr[ind] = file;
|
|
3500
|
-
}
|
|
3501
|
-
proms.push(this.locale(file, true, false));
|
|
3502
|
-
});
|
|
3503
|
-
Promise.allSettled(proms).then((res) => {
|
|
3504
|
-
res.forEach((r) => {
|
|
3505
|
-
if (r.value) files[r.value.file] = r.value.data;
|
|
3506
|
-
});
|
|
3507
|
-
localeArr.forEach((file) => {
|
|
3508
|
-
this.settings = this.extend({}, this.settings, files[file]);
|
|
3509
|
-
});
|
|
3510
|
-
resolve();
|
|
3511
|
-
});
|
|
3512
|
-
return;
|
|
3513
|
-
}
|
|
3514
|
-
if (!locale) locale = "en-us";
|
|
3515
|
-
if (typeof locale === "object") {
|
|
3516
|
-
this.settings = this.extend({}, this.settings, TsLocale, locale);
|
|
3517
|
-
return;
|
|
3518
|
-
}
|
|
3519
|
-
let localeStr = locale;
|
|
3520
|
-
if (localeStr.length === 5) {
|
|
3521
|
-
localeStr = "locale/" + localeStr.toLowerCase() + ".json";
|
|
3522
|
-
}
|
|
3523
|
-
fetch(localeStr, { method: "GET" }).then((res) => res.json()).then((data) => {
|
|
3524
|
-
if (noMerge !== true) {
|
|
3525
|
-
if (keepPhrases) {
|
|
3526
|
-
this.settings = this.extend({}, this.settings, data);
|
|
3527
|
-
} else {
|
|
3528
|
-
this.settings = this.extend({}, this.settings, TsLocale, { phrases: {} }, data);
|
|
3529
|
-
}
|
|
3530
|
-
}
|
|
3531
|
-
resolve({ file: localeStr, data });
|
|
3532
|
-
}).catch((err) => {
|
|
3533
|
-
console.log("ERROR: Cannot load locale " + localeStr);
|
|
3534
|
-
reject(err);
|
|
3535
|
-
});
|
|
3546
|
+
const deps = {
|
|
3547
|
+
extend: this.extend.bind(this),
|
|
3548
|
+
fetch: globalThis.fetch.bind(globalThis)
|
|
3549
|
+
};
|
|
3550
|
+
return _locale(locale, keepPhrases, noMerge, this.settings, deps).then((result) => {
|
|
3551
|
+
if (result.settings) this.settings = result.settings;
|
|
3552
|
+
return result.kind === "load" ? { file: result.file, data: result.data } : void 0;
|
|
3536
3553
|
});
|
|
3537
3554
|
}
|
|
3538
3555
|
scrollBarSize() {
|