@jackens/nnn 2023.10.14 → 2023.11.3
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/nnn.js +64 -821
- package/package.json +1 -1
package/nnn.js
CHANGED
|
@@ -1,91 +1,5 @@
|
|
|
1
|
-
/* eslint-disable no-console */
|
|
2
|
-
|
|
3
|
-
export const _test = {}
|
|
4
|
-
export const _version = '2023.10.14'
|
|
5
|
-
|
|
6
|
-
/**
|
|
7
|
-
* ```ts
|
|
8
|
-
* export type EscapeMap = Map<any, (value?: any) => string>;
|
|
9
|
-
* ```
|
|
10
|
-
*/
|
|
11
|
-
|
|
12
|
-
/**
|
|
13
|
-
* @typedef {Map<any, (value?: any) => string>} EscapeMap
|
|
14
|
-
*
|
|
15
|
-
* The type of arguments of the `escapeValues` and `escape` helpers.
|
|
16
|
-
*/
|
|
17
|
-
|
|
18
|
-
/**
|
|
19
|
-
* ```ts
|
|
20
|
-
* export type HArgs = [
|
|
21
|
-
* string | Node,
|
|
22
|
-
* ...(Record<string, any> | null | undefined | Node | string | number | HArgs)[]
|
|
23
|
-
* ];
|
|
24
|
-
* ```
|
|
25
|
-
*/
|
|
26
|
-
|
|
27
|
-
/**
|
|
28
|
-
* @typedef {[
|
|
29
|
-
* string | Node,
|
|
30
|
-
* ...(Record<string, any> | null | undefined | Node | string | number | HArgs)[]
|
|
31
|
-
* ]} HArgs
|
|
32
|
-
*
|
|
33
|
-
* The type of arguments of the `h` and `s` helpers.
|
|
34
|
-
*/
|
|
35
|
-
|
|
36
|
-
/**
|
|
37
|
-
* ```ts
|
|
38
|
-
* export type JcssNode = {
|
|
39
|
-
* [attributeOrSelector: string]: string | number | JcssNode;
|
|
40
|
-
* };
|
|
41
|
-
* ```
|
|
42
|
-
*/
|
|
43
|
-
|
|
44
|
-
/**
|
|
45
|
-
* @typedef {{
|
|
46
|
-
* [attributeOrSelector: string]: string | number | JcssNode;
|
|
47
|
-
* }} JcssNode
|
|
48
|
-
*
|
|
49
|
-
* The type of arguments of the `jcss` helper.
|
|
50
|
-
*/
|
|
51
|
-
|
|
52
|
-
/**
|
|
53
|
-
* ```ts
|
|
54
|
-
* export type JcssRoot = Record<string, JcssNode>;
|
|
55
|
-
* ```
|
|
56
|
-
*/
|
|
57
|
-
|
|
58
|
-
/**
|
|
59
|
-
* @typedef {Record<string, JcssNode>} JcssRoot
|
|
60
|
-
*
|
|
61
|
-
* The type of arguments of the `jcss` helper.
|
|
62
|
-
*/
|
|
63
|
-
|
|
64
1
|
const _COLORS = ['#e22', '#e73', '#fc3', '#ad4', '#4d9', '#3be', '#45d', '#c3e']
|
|
65
2
|
|
|
66
|
-
/**
|
|
67
|
-
* ```ts
|
|
68
|
-
* export function chartable(options?: {
|
|
69
|
-
* bottom?: number;
|
|
70
|
-
* gapX?: number;
|
|
71
|
-
* gapY?: number;
|
|
72
|
-
* headerColumn?: boolean;
|
|
73
|
-
* headerRow?: boolean;
|
|
74
|
-
* id?: string;
|
|
75
|
-
* left?: number;
|
|
76
|
-
* maxY?: number;
|
|
77
|
-
* right?: number;
|
|
78
|
-
* singleScale?: boolean;
|
|
79
|
-
* table?: HTMLTableElement;
|
|
80
|
-
* title?: string;
|
|
81
|
-
* top?: number;
|
|
82
|
-
* xLabels?: string[];
|
|
83
|
-
* zLabels?: string[];
|
|
84
|
-
* zxY?: number[][];
|
|
85
|
-
* }): SVGSVGElement;
|
|
86
|
-
* ```
|
|
87
|
-
*/
|
|
88
|
-
|
|
89
3
|
/**
|
|
90
4
|
* A helper for creating a chart based on a table (conf. <https://jackens.github.io/nnn/chartable/>).
|
|
91
5
|
*
|
|
@@ -174,8 +88,7 @@ export const chartable = ({
|
|
|
174
88
|
})
|
|
175
89
|
|
|
176
90
|
if (singleScale) {
|
|
177
|
-
bestScales = ranges.reduce(([totalMin, totalMax], [min, max]) =>
|
|
178
|
-
[Math.min(totalMin, min), Math.max(totalMax, max)], bestScales)
|
|
91
|
+
bestScales = ranges.reduce(([totalMin, totalMax], [min, max]) => [Math.min(totalMin, min), Math.max(totalMax, max)], bestScales)
|
|
179
92
|
} else {
|
|
180
93
|
ranges.forEach(([min1, max0]) => ranges.forEach(([min0, max1]) => {
|
|
181
94
|
if (min0 >= min1 && max1 >= max0) {
|
|
@@ -216,15 +129,14 @@ export const chartable = ({
|
|
|
216
129
|
const h = gapY * (maxY - 1)
|
|
217
130
|
const svgW = left + w + right
|
|
218
131
|
const svgH = top + h + bottom
|
|
219
|
-
const /** @type {HArgs} */ graph = ['g', { transform: `translate(${left} ${top})` },
|
|
132
|
+
const /** @type {import('./h.js').HArgs} */ graph = ['g', { transform: `translate(${left} ${top})` },
|
|
220
133
|
...yi.map(y => ['line', { x1: 0, x2: w, y1: y, y2: y, stroke: '#8888' }]),
|
|
221
134
|
...xi.map(x => ['line', { x1: x, x2: x, y1: 0, y2: h, stroke: '#8888' }])]
|
|
222
|
-
const /** @type {HArgs} */ lColors = ['g', { class: 'chartable-z-colors', transform: 'translate(-80 0)' }]
|
|
223
|
-
const /** @type {HArgs} */ lLabels = ['g', { class: 'chartable-z-labels', transform: 'translate(-95 0)' }]
|
|
224
|
-
const /** @type {HArgs} */ rColors = ['g', { class: 'chartable-z-colors', transform: 'translate(80 0)' }]
|
|
225
|
-
const /** @type {HArgs} */ rLabels = ['g', { class: 'chartable-z-labels', transform: 'translate(95 0)' }]
|
|
226
|
-
const yLabel = (/** @type {number} */ min, /** @type {number} */ max, /** @type {number} */ y) =>
|
|
227
|
-
(min + (max - min) * (maxY - 1 - y) / (maxY - 1)).toFixed(2).replace(/\.?0+$/, '')
|
|
135
|
+
const /** @type {import('./h.js').HArgs} */ lColors = ['g', { class: 'chartable-z-colors', transform: 'translate(-80 0)' }]
|
|
136
|
+
const /** @type {import('./h.js').HArgs} */ lLabels = ['g', { class: 'chartable-z-labels', transform: 'translate(-95 0)' }]
|
|
137
|
+
const /** @type {import('./h.js').HArgs} */ rColors = ['g', { class: 'chartable-z-colors', transform: 'translate(80 0)' }]
|
|
138
|
+
const /** @type {import('./h.js').HArgs} */ rLabels = ['g', { class: 'chartable-z-labels', transform: 'translate(95 0)' }]
|
|
139
|
+
const yLabel = (/** @type {number} */ min, /** @type {number} */ max, /** @type {number} */ y) => (min + (max - min) * (maxY - 1 - y) / (maxY - 1)).toFixed(2).replace(/\.?0+$/, '')
|
|
228
140
|
|
|
229
141
|
zxY.forEach((xa, z) => {
|
|
230
142
|
const cls = `chartable-z-${z + 1}`
|
|
@@ -297,16 +209,10 @@ export const chartable = ({
|
|
|
297
209
|
)
|
|
298
210
|
}
|
|
299
211
|
|
|
300
|
-
/**
|
|
301
|
-
* ```ts
|
|
302
|
-
* export function eq(x: any, y: any): boolean;
|
|
303
|
-
* ```
|
|
304
|
-
*/
|
|
305
|
-
|
|
306
212
|
/**
|
|
307
213
|
* A helper that checks equality of the given arguments.
|
|
308
214
|
*/
|
|
309
|
-
export const eq = /** @
|
|
215
|
+
export const eq = /** @return {boolean} */ (/** @type {any} */ x, /** @type {any} */ y) => {
|
|
310
216
|
if (x === y) {
|
|
311
217
|
return true
|
|
312
218
|
}
|
|
@@ -348,47 +254,19 @@ export const eq = /** @returns {boolean} */ (/** @type {any} */ x, /** @type {an
|
|
|
348
254
|
return false
|
|
349
255
|
}
|
|
350
256
|
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
// eslint-disable-next-line no-new-wrappers
|
|
357
|
-
console.assert(eq(42, new Number(42)))
|
|
358
|
-
console.assert(eq(42, Number(42)))
|
|
359
|
-
// eslint-disable-next-line no-new-wrappers
|
|
360
|
-
console.assert(eq(new Number(42), Number(42)))
|
|
361
|
-
console.assert(!eq(42, '42'))
|
|
362
|
-
console.assert(eq('42', '42'))
|
|
363
|
-
// eslint-disable-next-line no-new-wrappers
|
|
364
|
-
console.assert(eq('42', new String('42')))
|
|
365
|
-
console.assert(eq('42', String('42')))
|
|
366
|
-
// eslint-disable-next-line no-new-wrappers
|
|
367
|
-
console.assert(eq(String('42'), new String('42')))
|
|
368
|
-
console.assert(eq(/42/, /42/))
|
|
369
|
-
console.assert(!eq(/42/, /42/g))
|
|
370
|
-
console.assert(eq(new Date(42), new Date(42)))
|
|
371
|
-
console.assert(!eq(new Date(), new Date(42)))
|
|
372
|
-
console.assert(eq({ j: '42', c: 42 }, { c: 42, j: '42' }))
|
|
373
|
-
console.assert(eq([42, '42'], [42, '42']))
|
|
374
|
-
console.assert(eq(new Set(['42', 42]), new Set([42, '42'])))
|
|
375
|
-
console.assert(!eq(new Set(['42', 42]), new Set([42])))
|
|
376
|
-
console.assert(!eq(new Set([42, undefined]), new Set([42])))
|
|
377
|
-
console.assert(eq(
|
|
378
|
-
new Map([[{ j: 42 }, { J: '42' }], [{ c: 42 }, { C: '42' }]]),
|
|
379
|
-
new Map([[{ c: 42 }, { C: '42' }], [{ j: 42 }, { J: '42' }]])
|
|
380
|
-
))
|
|
381
|
-
console.assert(!eq(
|
|
382
|
-
new Map([[{ j: 42 }, { J: '42' }], [{ c: 42 }, { C: '42' }]]),
|
|
383
|
-
new Map([[{ j: '42' }, { J: 42 }], [{ c: '42' }, { C: 42 }]])
|
|
384
|
-
))
|
|
385
|
-
}
|
|
257
|
+
/**
|
|
258
|
+
* @typedef {Map<any, (value?: any) => string>} EscapeMap
|
|
259
|
+
*
|
|
260
|
+
* The type of arguments of the `escapeValues` and `escape` helpers.
|
|
261
|
+
*/
|
|
386
262
|
|
|
387
263
|
/**
|
|
388
|
-
*
|
|
389
|
-
* export function escape(escapeMap: EscapeMap, template: TemplateStringsArray, ...values: any[]): string;
|
|
390
|
-
* ```
|
|
264
|
+
* A generic helper for escaping `values` by given `escapeMap`.
|
|
391
265
|
*/
|
|
266
|
+
export const escapeValues = (
|
|
267
|
+
/** @type {EscapeMap} */ escapeMap,
|
|
268
|
+
/** @type {any[]} */ values
|
|
269
|
+
) => values.map(value => (escapeMap.get(value?.constructor) ?? escapeMap.get(undefined))?.(value) ?? '')
|
|
392
270
|
|
|
393
271
|
/**
|
|
394
272
|
* A generic helper for escaping `values` by given `escapeMap` (in *TemplateStrings* flavor).
|
|
@@ -399,55 +277,8 @@ export const escape = (
|
|
|
399
277
|
/** @type {any[]} */ ...values
|
|
400
278
|
) => String.raw(template, ...escapeValues(escapeMap, values))
|
|
401
279
|
|
|
402
|
-
_test.escape = () => {
|
|
403
|
-
// @ts-expect-error
|
|
404
|
-
const /** @type {EscapeMap} */ escapeMap = new Map([
|
|
405
|
-
[undefined, () => 'NULL'],
|
|
406
|
-
[Array, (/** @type {any[]} */ values) => escapeValues(escapeMap, values).join(', ')],
|
|
407
|
-
[Boolean, (/** @type {boolean} */ value) => `b'${+value}'`],
|
|
408
|
-
[Date, (/** @type {Date} */ value) => `'${value.toISOString().replace(/^(.+)T(.+)\..*$/, '$1 $2')}'`],
|
|
409
|
-
[Number, (/** @type {number} */ value) => `${value}`],
|
|
410
|
-
[String, (/** @type {string} */ value) => `'${value.replace(/'/g, "''")}'`]
|
|
411
|
-
])
|
|
412
|
-
|
|
413
|
-
// @ts-expect-error
|
|
414
|
-
const sql = escape.bind(null, escapeMap)
|
|
415
|
-
|
|
416
|
-
const actual = sql`
|
|
417
|
-
SELECT *
|
|
418
|
-
FROM table_name
|
|
419
|
-
WHERE column_name IN (${[true, null, undefined, 42, '42', "4'2", /42/, new Date(323325e6)]})`
|
|
420
|
-
|
|
421
|
-
const expected = `
|
|
422
|
-
SELECT *
|
|
423
|
-
FROM table_name
|
|
424
|
-
WHERE column_name IN (b'1', NULL, NULL, 42, '42', '4''2', NULL, '1980-03-31 04:30:00')`
|
|
425
|
-
|
|
426
|
-
console.assert(actual === expected)
|
|
427
|
-
}
|
|
428
|
-
|
|
429
|
-
/**
|
|
430
|
-
* ```ts
|
|
431
|
-
* export function escapeValues(escapeMap: EscapeMap, values: any[]): string[];
|
|
432
|
-
* ```
|
|
433
|
-
*/
|
|
434
|
-
|
|
435
|
-
/**
|
|
436
|
-
* A generic helper for escaping `values` by given `escapeMap`.
|
|
437
|
-
*/
|
|
438
|
-
export const escapeValues = (
|
|
439
|
-
/** @type {EscapeMap} */ escapeMap,
|
|
440
|
-
/** @type {any[]} */ values
|
|
441
|
-
) => values.map(value => (escapeMap.get(value?.constructor) ?? escapeMap.get(undefined))?.(value) ?? '')
|
|
442
|
-
|
|
443
280
|
const _TAGS_TO_SKIP = { IFRAME: 1, NOSCRIPT: 1, PRE: 1, SCRIPT: 1, STYLE: 1, TEXTAREA: 1 }
|
|
444
281
|
|
|
445
|
-
/**
|
|
446
|
-
* ```ts
|
|
447
|
-
* export function fixTypography(node: Node): void;
|
|
448
|
-
* ```
|
|
449
|
-
*/
|
|
450
|
-
|
|
451
282
|
/**
|
|
452
283
|
* A helper that implements typographic corrections specific to Polish typography.
|
|
453
284
|
*/
|
|
@@ -491,15 +322,14 @@ export const fixTypography = (/** @type {Node} */ node) => {
|
|
|
491
322
|
}
|
|
492
323
|
}
|
|
493
324
|
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
}
|
|
325
|
+
/**
|
|
326
|
+
* @typedef {[
|
|
327
|
+
* string | Node,
|
|
328
|
+
* ...(Record<string, any> | null | undefined | Node | string | number | HArgs)[]
|
|
329
|
+
* ]} HArgs
|
|
330
|
+
*
|
|
331
|
+
* The type of arguments of the `h` and `s` helpers.
|
|
332
|
+
*/
|
|
503
333
|
|
|
504
334
|
const /** @type {Record<string, string>} */ _NS = {
|
|
505
335
|
xlink: 'http://www.w3.org/1999/xlink'
|
|
@@ -595,14 +425,6 @@ const _h = (/** @type {string?=} */ namespaceURI) => {
|
|
|
595
425
|
return h
|
|
596
426
|
}
|
|
597
427
|
|
|
598
|
-
/**
|
|
599
|
-
* ```ts
|
|
600
|
-
* export function h<T extends keyof HTMLElementTagNameMap>(tag: T, ...args: HArgs[1][]): HTMLElementTagNameMap[T];
|
|
601
|
-
* export function h<N extends Node>(node: N, ...args: HArgs[1][]): N;
|
|
602
|
-
* export function h(...args: HArgs): Node;
|
|
603
|
-
* ```
|
|
604
|
-
*/
|
|
605
|
-
|
|
606
428
|
/**
|
|
607
429
|
* A lightweight [HyperScript](https://github.com/hyperhype/hyperscript)-style helper for creating and modifying `HTMLElement`s (see also `s`).
|
|
608
430
|
*
|
|
@@ -620,129 +442,27 @@ const _h = (/** @type {string?=} */ namespaceURI) => {
|
|
|
620
442
|
*/
|
|
621
443
|
export const h = _h()
|
|
622
444
|
|
|
623
|
-
_test.h = () => {
|
|
624
|
-
const b = h('b')
|
|
625
|
-
|
|
626
|
-
console.assert(b.outerHTML === '<b></b>')
|
|
627
|
-
|
|
628
|
-
const i = h('i', 'text')
|
|
629
|
-
|
|
630
|
-
h(b, i)
|
|
631
|
-
|
|
632
|
-
console.assert(i.outerHTML === '<i>text</i>')
|
|
633
|
-
console.assert(b.outerHTML === '<b><i>text</i></b>')
|
|
634
|
-
|
|
635
|
-
h(i, { $className: 'some class' })
|
|
636
|
-
|
|
637
|
-
console.assert(i.outerHTML === '<i class="some class">text</i>')
|
|
638
|
-
console.assert(b.outerHTML === '<b><i class="some class">text</i></b>')
|
|
639
|
-
}
|
|
640
|
-
|
|
641
|
-
_test['h: innerText vs items'] = () => {
|
|
642
|
-
console.assert(h('span', 'text').outerHTML === '<span>text</span>')
|
|
643
|
-
console.assert(h('span', { $innerText: 'text' }).outerHTML === '<span>text</span>')
|
|
644
|
-
}
|
|
645
|
-
|
|
646
|
-
_test['h: style'] = () => {
|
|
647
|
-
console.assert(h('div', { style: 'margin:0;padding:0' }).outerHTML ===
|
|
648
|
-
'<div style="margin:0;padding:0"></div>')
|
|
649
|
-
console.assert(h('div', { $style: 'margin:0;padding:0' }).outerHTML ===
|
|
650
|
-
'<div style="margin: 0px; padding: 0px;"></div>')
|
|
651
|
-
console.assert(h('div', { $style: { margin: 0, padding: 0 } }).outerHTML ===
|
|
652
|
-
'<div style="margin: 0px; padding: 0px;"></div>')
|
|
653
|
-
}
|
|
654
|
-
|
|
655
|
-
_test['h: attributes vs properties'] = () => {
|
|
656
|
-
const input1 = h('input', { value: 42 })
|
|
657
|
-
const input2 = h('input', { $value: '42' })
|
|
658
|
-
|
|
659
|
-
console.assert(input1.value === '42')
|
|
660
|
-
console.assert(input2.value === '42')
|
|
661
|
-
|
|
662
|
-
console.assert(input1.outerHTML === '<input value="42">')
|
|
663
|
-
console.assert(input2.outerHTML === '<input>')
|
|
664
|
-
|
|
665
|
-
const checkbox1 = h('input', { type: 'checkbox', checked: true })
|
|
666
|
-
const checkbox2 = h('input', { type: 'checkbox', $checked: true })
|
|
667
|
-
|
|
668
|
-
console.assert(checkbox1.checked === true)
|
|
669
|
-
console.assert(checkbox2.checked === true)
|
|
670
|
-
|
|
671
|
-
console.assert(checkbox1.outerHTML === '<input type="checkbox" checked="">')
|
|
672
|
-
console.assert(checkbox2.outerHTML === '<input type="checkbox">')
|
|
673
|
-
}
|
|
674
|
-
|
|
675
|
-
_test['h: nested properties'] = () => {
|
|
676
|
-
const div = h('div')
|
|
677
|
-
|
|
678
|
-
// @ts-expect-error
|
|
679
|
-
console.assert(div.key === undefined)
|
|
680
|
-
|
|
681
|
-
h(div, { $key: { one: 1 } })
|
|
682
|
-
|
|
683
|
-
// @ts-expect-error
|
|
684
|
-
console.assert(eq(div.key, { one: 1 }))
|
|
685
|
-
|
|
686
|
-
h(div, { $key: { two: 2 } })
|
|
687
|
-
|
|
688
|
-
// @ts-expect-error
|
|
689
|
-
console.assert(eq(div.key, { one: 1, two: 2 }))
|
|
690
|
-
}
|
|
691
|
-
|
|
692
445
|
/**
|
|
693
|
-
*
|
|
694
|
-
*
|
|
695
|
-
*
|
|
446
|
+
* A lightweight [HyperScript](https://github.com/hyperhype/hyperscript)-style helper for creating and modifying `SVGElement`s (see also `h`).
|
|
447
|
+
*
|
|
448
|
+
* - The first argument of type `string` specifies the tag of the element to be created.
|
|
449
|
+
* - The first argument of type `Node` specifies the element to be modified.
|
|
450
|
+
* - All other arguments of type `Record<string, any>` are mappings of attributes and properties.
|
|
451
|
+
* Keys starting with `$` specify *properties* (without the leading `$`) to be set on the element being created or modified.
|
|
452
|
+
* (Note that `$` is not a valid attribute name character.)
|
|
453
|
+
* All other keys specify *attributes* to be set by `setAttributeNS`.
|
|
454
|
+
* An attribute equal to `false` causes the attribute to be removed by `removeAttributeNS`.
|
|
455
|
+
* - All other arguments of type `null` or `undefined` are simply ignored.
|
|
456
|
+
* - All other arguments of type `Node` are appended to the element being created or modified.
|
|
457
|
+
* - All other arguments of type `string`/`number` are converted to `Text` nodes and appended to the element being created or modified.
|
|
458
|
+
* - All other arguments of type `HArgs` are passed to `s` and the results are appended to the element being created or modified.
|
|
696
459
|
*/
|
|
460
|
+
export const s = _h('http://www.w3.org/2000/svg')
|
|
697
461
|
|
|
698
462
|
/**
|
|
699
463
|
* A replacement for the `in` operator (not to be confused with the `for-in` loop) that works properly.
|
|
700
464
|
*/
|
|
701
|
-
export const has = (/** @type {any} */ key, /** @type {any} */ ref) =>
|
|
702
|
-
(is(String, key) || is(Number, key) || is(Symbol, key)) && Object.hasOwnProperty.call(ref ?? Object, key)
|
|
703
|
-
|
|
704
|
-
_test.has = () => {
|
|
705
|
-
const obj = { key: 'K', null: 'N' }
|
|
706
|
-
|
|
707
|
-
console.assert('key' in obj)
|
|
708
|
-
console.assert(has('key', obj))
|
|
709
|
-
|
|
710
|
-
console.assert('null' in obj)
|
|
711
|
-
console.assert(has('null', obj))
|
|
712
|
-
|
|
713
|
-
// @ts-expect-error
|
|
714
|
-
console.assert(null in obj)
|
|
715
|
-
console.assert(!has(null, obj))
|
|
716
|
-
|
|
717
|
-
console.assert('toString' in obj)
|
|
718
|
-
console.assert(!has('toString', obj))
|
|
719
|
-
}
|
|
720
|
-
|
|
721
|
-
_test['has: null'] = () => {
|
|
722
|
-
let typeError
|
|
723
|
-
|
|
724
|
-
try {
|
|
725
|
-
// @ts-expect-error
|
|
726
|
-
console.assert('key' in null)
|
|
727
|
-
} catch (error) {
|
|
728
|
-
typeError = error
|
|
729
|
-
}
|
|
730
|
-
|
|
731
|
-
console.assert(typeError instanceof TypeError) // Cannot use 'in' operator to search for 'key' in null
|
|
732
|
-
console.assert(!has('key', null))
|
|
733
|
-
}
|
|
734
|
-
|
|
735
|
-
/**
|
|
736
|
-
* ```ts
|
|
737
|
-
* export function is(type: BigIntConstructor, arg: any): arg is bigint;
|
|
738
|
-
* export function is(type: BooleanConstructor, arg: any): arg is boolean;
|
|
739
|
-
* export function is(type: NumberConstructor, arg: any): arg is number;
|
|
740
|
-
* export function is(type: StringConstructor, arg: any): arg is string;
|
|
741
|
-
* export function is(type: SymbolConstructor, arg: any): arg is symbol;
|
|
742
|
-
* export function is(type: undefined, arg: any): arg is null | undefined;
|
|
743
|
-
* export function is<T extends abstract new (...args: any[]) => any>(type: T, arg: any): arg is InstanceType<T>;
|
|
744
|
-
* ```
|
|
745
|
-
*/
|
|
465
|
+
export const has = (/** @type {any} */ key, /** @type {any} */ ref) => (is(String, key) || is(Number, key) || is(Symbol, key)) && Object.hasOwnProperty.call(ref ?? Object, key)
|
|
746
466
|
|
|
747
467
|
/**
|
|
748
468
|
* A helper that checks if the given argument is of a certain type.
|
|
@@ -759,42 +479,23 @@ _test['has: null'] = () => {
|
|
|
759
479
|
* <T extends abstract new (...args: any[]) => any>(type: T, arg: any): arg is InstanceType<T>;
|
|
760
480
|
* }}
|
|
761
481
|
*
|
|
762
|
-
* @
|
|
482
|
+
* @return {arg is bigint | boolean | number | string | symbol | undefined | null | InstanceType<T>}
|
|
763
483
|
*/
|
|
764
484
|
export const is = (/** @type {T} */ type, /** @type {any} */ arg) => arg?.constructor === type
|
|
765
485
|
|
|
766
|
-
|
|
767
|
-
|
|
768
|
-
|
|
769
|
-
|
|
770
|
-
|
|
771
|
-
|
|
772
|
-
|
|
773
|
-
console.assert(is(String, String('42')))
|
|
774
|
-
// eslint-disable-next-line no-new-wrappers
|
|
775
|
-
console.assert(is(String, new String('42')))
|
|
776
|
-
console.assert(is(Symbol, Symbol('42')))
|
|
777
|
-
console.assert(is(Symbol, Object(Symbol('42'))))
|
|
778
|
-
console.assert(is(undefined, undefined))
|
|
779
|
-
console.assert(is(undefined, null))
|
|
780
|
-
console.assert(is(Object, {}))
|
|
781
|
-
console.assert(is(Array, []))
|
|
782
|
-
console.assert(is(RegExp, /42/))
|
|
783
|
-
console.assert(is(Date, new Date(42)))
|
|
784
|
-
console.assert(is(Set, new Set(['42', 42])))
|
|
785
|
-
console.assert(is(Map, new Map([[{ j: 42 }, { J: '42' }], [{ c: 42 }, { C: '42' }]])))
|
|
786
|
-
}
|
|
787
|
-
|
|
788
|
-
_test['is vs ‘toString.call’'] = () => {
|
|
789
|
-
class FooBar { }
|
|
790
|
-
|
|
791
|
-
console.assert(is(FooBar, new FooBar()))
|
|
792
|
-
|
|
793
|
-
const fakeFooBar = { [Symbol.toStringTag]: 'FooBar' }
|
|
486
|
+
/**
|
|
487
|
+
* @typedef {{
|
|
488
|
+
* [attributeOrSelector: string]: string | number | JcssNode;
|
|
489
|
+
* }} JcssNode
|
|
490
|
+
*
|
|
491
|
+
* The type of arguments of the `jcss` helper.
|
|
492
|
+
*/
|
|
794
493
|
|
|
795
|
-
|
|
796
|
-
|
|
797
|
-
|
|
494
|
+
/**
|
|
495
|
+
* @typedef {Record<string, JcssNode>} JcssRoot
|
|
496
|
+
*
|
|
497
|
+
* The type of arguments of the `jcss` helper.
|
|
498
|
+
*/
|
|
798
499
|
|
|
799
500
|
const _jcss = (
|
|
800
501
|
/** @type {JcssNode} */ node,
|
|
@@ -850,12 +551,6 @@ const _jcss = (
|
|
|
850
551
|
}
|
|
851
552
|
}
|
|
852
553
|
|
|
853
|
-
/**
|
|
854
|
-
* ```ts
|
|
855
|
-
* export function jcss(root: JcssRoot, splitter?: string): string;
|
|
856
|
-
* ```
|
|
857
|
-
*/
|
|
858
|
-
|
|
859
554
|
/**
|
|
860
555
|
* A simple CSS-in-JS helper.
|
|
861
556
|
*
|
|
@@ -886,206 +581,6 @@ export const jcss = (/** @type {JcssRoot} */ root, splitter = '$$') => {
|
|
|
886
581
|
return result.join('')
|
|
887
582
|
}
|
|
888
583
|
|
|
889
|
-
_test['jcss: #1'] = () => {
|
|
890
|
-
const actual = jcss({
|
|
891
|
-
a: {
|
|
892
|
-
color: 'red',
|
|
893
|
-
margin: 1,
|
|
894
|
-
'.c': { margin: 2, padding: 2 },
|
|
895
|
-
padding: 1
|
|
896
|
-
}
|
|
897
|
-
})
|
|
898
|
-
|
|
899
|
-
const expected = `
|
|
900
|
-
a{
|
|
901
|
-
color:red;
|
|
902
|
-
margin:1
|
|
903
|
-
}
|
|
904
|
-
a.c{
|
|
905
|
-
margin:2;
|
|
906
|
-
padding:2
|
|
907
|
-
}
|
|
908
|
-
a{
|
|
909
|
-
padding:1
|
|
910
|
-
}`.replace(/\n\s*/g, '')
|
|
911
|
-
|
|
912
|
-
console.assert(actual === expected)
|
|
913
|
-
}
|
|
914
|
-
|
|
915
|
-
_test['jcss: #2'] = () => {
|
|
916
|
-
const actual = jcss({
|
|
917
|
-
a: {
|
|
918
|
-
'.b': {
|
|
919
|
-
color: 'red',
|
|
920
|
-
margin: 1,
|
|
921
|
-
'.c': { margin: 2, padding: 2 },
|
|
922
|
-
padding: 1
|
|
923
|
-
}
|
|
924
|
-
}
|
|
925
|
-
})
|
|
926
|
-
|
|
927
|
-
const expected = `
|
|
928
|
-
a.b{
|
|
929
|
-
color:red;
|
|
930
|
-
margin:1
|
|
931
|
-
}
|
|
932
|
-
a.b.c{
|
|
933
|
-
margin:2;
|
|
934
|
-
padding:2
|
|
935
|
-
}
|
|
936
|
-
a.b{
|
|
937
|
-
padding:1
|
|
938
|
-
}`.replace(/\n\s*/g, '')
|
|
939
|
-
|
|
940
|
-
console.assert(actual === expected)
|
|
941
|
-
}
|
|
942
|
-
|
|
943
|
-
_test['jcss: #3'] = () => {
|
|
944
|
-
const actual = jcss({
|
|
945
|
-
'@font-face$$1': {
|
|
946
|
-
fontFamily: 'Jackens',
|
|
947
|
-
src$$1: 'url(otf/jackens.otf)',
|
|
948
|
-
src$$2: "url(otf/jackens.otf) format('opentype')," +
|
|
949
|
-
"url(svg/jackens.svg) format('svg')",
|
|
950
|
-
fontWeight: 'normal',
|
|
951
|
-
fontStyle: 'normal'
|
|
952
|
-
},
|
|
953
|
-
'@font-face$$2': {
|
|
954
|
-
fontFamily: 'C64',
|
|
955
|
-
src: 'url(fonts/C64_Pro_Mono-STYLE.woff)'
|
|
956
|
-
},
|
|
957
|
-
'@keyframes spin': {
|
|
958
|
-
'0%': { transform: 'rotate(0deg)' },
|
|
959
|
-
'100%': { transform: 'rotate(360deg)' }
|
|
960
|
-
},
|
|
961
|
-
div: {
|
|
962
|
-
border: 'solid red 1px',
|
|
963
|
-
'.c1': { 'background-color': '#000' },
|
|
964
|
-
' .c1': { backgroundColor: 'black' },
|
|
965
|
-
'.c2': { backgroundColor: 'rgb(0,0,0)' }
|
|
966
|
-
},
|
|
967
|
-
'@media(min-width:200px)': {
|
|
968
|
-
div: { margin: 0, padding: 0 },
|
|
969
|
-
span: { color: '#000' }
|
|
970
|
-
}
|
|
971
|
-
})
|
|
972
|
-
|
|
973
|
-
const expected = `
|
|
974
|
-
@font-face{
|
|
975
|
-
font-family:Jackens;
|
|
976
|
-
src:url(otf/jackens.otf);
|
|
977
|
-
src:url(otf/jackens.otf) format('opentype'),url(svg/jackens.svg) format('svg');
|
|
978
|
-
font-weight:normal;
|
|
979
|
-
font-style:normal
|
|
980
|
-
}
|
|
981
|
-
@font-face{
|
|
982
|
-
font-family:C64;
|
|
983
|
-
src:url(fonts/C64_Pro_Mono-STYLE.woff)
|
|
984
|
-
}
|
|
985
|
-
@keyframes spin{
|
|
986
|
-
0%{
|
|
987
|
-
transform:rotate(0deg)
|
|
988
|
-
}
|
|
989
|
-
100%{
|
|
990
|
-
transform:rotate(360deg)
|
|
991
|
-
}
|
|
992
|
-
}
|
|
993
|
-
div{
|
|
994
|
-
border:solid red 1px
|
|
995
|
-
}
|
|
996
|
-
div.c1{
|
|
997
|
-
background-color:#000
|
|
998
|
-
}
|
|
999
|
-
div .c1{
|
|
1000
|
-
background-color:black
|
|
1001
|
-
}
|
|
1002
|
-
div.c2{
|
|
1003
|
-
background-color:rgb(0,0,0)
|
|
1004
|
-
}
|
|
1005
|
-
@media(min-width:200px){
|
|
1006
|
-
div{
|
|
1007
|
-
margin:0;
|
|
1008
|
-
padding:0
|
|
1009
|
-
}
|
|
1010
|
-
span{
|
|
1011
|
-
color:#000
|
|
1012
|
-
}
|
|
1013
|
-
}`.replace(/\n\s*/g, '')
|
|
1014
|
-
|
|
1015
|
-
console.assert(actual === expected)
|
|
1016
|
-
}
|
|
1017
|
-
|
|
1018
|
-
_test['jcss: #4'] = () => {
|
|
1019
|
-
const actual = jcss({
|
|
1020
|
-
a: {
|
|
1021
|
-
'.b,.c': {
|
|
1022
|
-
margin: 1,
|
|
1023
|
-
'.d': {
|
|
1024
|
-
margin: 2
|
|
1025
|
-
}
|
|
1026
|
-
}
|
|
1027
|
-
}
|
|
1028
|
-
})
|
|
1029
|
-
|
|
1030
|
-
const expected = `
|
|
1031
|
-
a.b,a.c{
|
|
1032
|
-
margin:1
|
|
1033
|
-
}
|
|
1034
|
-
a.b.d,a.c.d{
|
|
1035
|
-
margin:2
|
|
1036
|
-
}`.replace(/\n\s*/g, '')
|
|
1037
|
-
|
|
1038
|
-
console.assert(actual === expected)
|
|
1039
|
-
}
|
|
1040
|
-
|
|
1041
|
-
_test['jcss: #5'] = () => {
|
|
1042
|
-
const actual = jcss({
|
|
1043
|
-
'.b,.c': {
|
|
1044
|
-
margin: 1,
|
|
1045
|
-
'.d': {
|
|
1046
|
-
margin: 2
|
|
1047
|
-
}
|
|
1048
|
-
}
|
|
1049
|
-
})
|
|
1050
|
-
|
|
1051
|
-
const expected = `
|
|
1052
|
-
.b,.c{
|
|
1053
|
-
margin:1
|
|
1054
|
-
}
|
|
1055
|
-
.b.d,.c.d{
|
|
1056
|
-
margin:2
|
|
1057
|
-
}`.replace(/\n\s*/g, '')
|
|
1058
|
-
|
|
1059
|
-
console.assert(actual === expected)
|
|
1060
|
-
}
|
|
1061
|
-
|
|
1062
|
-
_test['jcss: #6'] = () => {
|
|
1063
|
-
const actual = jcss({
|
|
1064
|
-
'.a,.b': {
|
|
1065
|
-
margin: 1,
|
|
1066
|
-
'.c,.d': {
|
|
1067
|
-
margin: 2
|
|
1068
|
-
}
|
|
1069
|
-
}
|
|
1070
|
-
})
|
|
1071
|
-
|
|
1072
|
-
const expected = `
|
|
1073
|
-
.a,.b{
|
|
1074
|
-
margin:1
|
|
1075
|
-
}
|
|
1076
|
-
.a.c,.a.d,.b.c,.b.d{
|
|
1077
|
-
margin:2
|
|
1078
|
-
}`.replace(/\n\s*/g, '')
|
|
1079
|
-
|
|
1080
|
-
console.assert(actual === expected)
|
|
1081
|
-
}
|
|
1082
|
-
|
|
1083
|
-
/**
|
|
1084
|
-
* ```ts
|
|
1085
|
-
* export function jsOnParse(handlers: Record<string, Function>, text: string): any;
|
|
1086
|
-
* ```
|
|
1087
|
-
*/
|
|
1088
|
-
|
|
1089
584
|
/**
|
|
1090
585
|
* `JSON.parse` with “JavaScript turned on”.
|
|
1091
586
|
*
|
|
@@ -1123,49 +618,6 @@ export const jsOnParse = (
|
|
|
1123
618
|
return value
|
|
1124
619
|
})
|
|
1125
620
|
|
|
1126
|
-
_test.jsOnParse = () => {
|
|
1127
|
-
const handlers = {
|
|
1128
|
-
$hello: (/** @type {string} */ name) => `Hello ${name}!`,
|
|
1129
|
-
$foo: () => 'bar'
|
|
1130
|
-
}
|
|
1131
|
-
const actual = jsOnParse(handlers, `
|
|
1132
|
-
[
|
|
1133
|
-
{
|
|
1134
|
-
"$hello": ["World"]
|
|
1135
|
-
},
|
|
1136
|
-
{
|
|
1137
|
-
"nested": {
|
|
1138
|
-
"$hello": ["nested World"]
|
|
1139
|
-
},
|
|
1140
|
-
"one": 1,
|
|
1141
|
-
"two": 2
|
|
1142
|
-
},
|
|
1143
|
-
{
|
|
1144
|
-
"$foo": []
|
|
1145
|
-
},
|
|
1146
|
-
{
|
|
1147
|
-
"$foo": ["The parent object does not have exactly one property!"],
|
|
1148
|
-
"one": 1,
|
|
1149
|
-
"two": 2
|
|
1150
|
-
}
|
|
1151
|
-
]`)
|
|
1152
|
-
const expected = [
|
|
1153
|
-
'Hello World!',
|
|
1154
|
-
{
|
|
1155
|
-
nested: 'Hello nested World!',
|
|
1156
|
-
one: 1,
|
|
1157
|
-
two: 2
|
|
1158
|
-
},
|
|
1159
|
-
'bar',
|
|
1160
|
-
{
|
|
1161
|
-
$foo: ['The parent object does not have exactly one property!'],
|
|
1162
|
-
one: 1,
|
|
1163
|
-
two: 2
|
|
1164
|
-
}]
|
|
1165
|
-
|
|
1166
|
-
console.assert(eq(actual, expected))
|
|
1167
|
-
}
|
|
1168
|
-
|
|
1169
621
|
const _locale = (
|
|
1170
622
|
/** @type {Record<string, Record<string, string | Record<string, string>>>} */ locales,
|
|
1171
623
|
/** @type {string} */ language,
|
|
@@ -1184,16 +636,6 @@ const _locale = (
|
|
|
1184
636
|
return is(String, t) ? t : text
|
|
1185
637
|
}
|
|
1186
638
|
|
|
1187
|
-
/**
|
|
1188
|
-
* ```ts
|
|
1189
|
-
* export function locale(
|
|
1190
|
-
* locales: Record<string, Record<string, string | Record<string, string>>>,
|
|
1191
|
-
* defaultLanguage: string,
|
|
1192
|
-
* languages?: readonly string[]
|
|
1193
|
-
* ): (text?: string, version?: string) => string;
|
|
1194
|
-
* ```
|
|
1195
|
-
*/
|
|
1196
|
-
|
|
1197
639
|
/**
|
|
1198
640
|
* Language translations helper.
|
|
1199
641
|
*/
|
|
@@ -1213,48 +655,15 @@ export const locale = (
|
|
|
1213
655
|
return _locale.bind(0, locales, defaultLanguage)
|
|
1214
656
|
}
|
|
1215
657
|
|
|
1216
|
-
_test.locale = () => {
|
|
1217
|
-
const locales = {
|
|
1218
|
-
pl: {
|
|
1219
|
-
Password: 'Hasło',
|
|
1220
|
-
button: { Login: 'Zaloguj' }
|
|
1221
|
-
}
|
|
1222
|
-
}
|
|
1223
|
-
const _ = locale(locales, 'pl', [])
|
|
1224
|
-
|
|
1225
|
-
console.assert(_('Login') === 'Login')
|
|
1226
|
-
console.assert(_('Password') === 'Hasło')
|
|
1227
|
-
|
|
1228
|
-
console.assert(_('Undefined text') === 'Undefined text')
|
|
1229
|
-
|
|
1230
|
-
console.assert(_('Login', 'button') === 'Zaloguj')
|
|
1231
|
-
|
|
1232
|
-
console.assert(_('Password', 'undefined_version') === 'Hasło')
|
|
1233
|
-
console.assert(_('Undefined text', 'undefined_version') === 'Undefined text')
|
|
1234
|
-
|
|
1235
|
-
console.assert(_('toString') === 'toString')
|
|
1236
|
-
console.assert(_('toString', 'undefined_version') === 'toString')
|
|
1237
|
-
}
|
|
1238
|
-
|
|
1239
|
-
/**
|
|
1240
|
-
* ```ts
|
|
1241
|
-
* export function nanolight(
|
|
1242
|
-
* pattern: RegExp,
|
|
1243
|
-
* highlighters: ((chunk: string, index: number) => HArgs[1])[],
|
|
1244
|
-
* code: string
|
|
1245
|
-
* ): HArgs[1][];
|
|
1246
|
-
* ```
|
|
1247
|
-
*/
|
|
1248
|
-
|
|
1249
658
|
/**
|
|
1250
659
|
* A generic helper for syntax highlighting (see also `nanolightJs`).
|
|
1251
660
|
*/
|
|
1252
661
|
export const nanolight = (
|
|
1253
662
|
/** @type {RegExp} */ pattern,
|
|
1254
|
-
/** @type {((chunk: string, index: number) => HArgs[1])[]} */ highlighters,
|
|
663
|
+
/** @type {((chunk: string, index: number) => import('./h.js').HArgs[1])[]} */ highlighters,
|
|
1255
664
|
/** @type {string} */ code
|
|
1256
665
|
) => {
|
|
1257
|
-
const /** @type {HArgs[1][]} */ result = []
|
|
666
|
+
const /** @type {import('./h.js').HArgs[1][]} */ result = []
|
|
1258
667
|
|
|
1259
668
|
code.split(pattern).forEach((chunk, index) => {
|
|
1260
669
|
index %= highlighters.length
|
|
@@ -1266,12 +675,6 @@ export const nanolight = (
|
|
|
1266
675
|
return result
|
|
1267
676
|
}
|
|
1268
677
|
|
|
1269
|
-
/**
|
|
1270
|
-
* ```ts
|
|
1271
|
-
* export function nanolightJs(codeJs: string): HArgs[1][];
|
|
1272
|
-
* ```
|
|
1273
|
-
*/
|
|
1274
|
-
|
|
1275
678
|
/**
|
|
1276
679
|
* A helper for highlighting JavaScript.
|
|
1277
680
|
*/
|
|
@@ -1290,19 +693,6 @@ export const nanolightJs = nanolight.bind(0,
|
|
|
1290
693
|
]
|
|
1291
694
|
)
|
|
1292
695
|
|
|
1293
|
-
_test.nanolightJs = () => {
|
|
1294
|
-
const codeJs = 'const answerToLifeTheUniverseAndEverything = 42'
|
|
1295
|
-
|
|
1296
|
-
console.assert(h('pre', ['code', ...nanolightJs(codeJs)]).outerHTML ===
|
|
1297
|
-
'<pre><code><b>const</b> <i>answerToLifeTheUniverseAndEverything</i> <b>=</b> <u>42</u></code></pre>')
|
|
1298
|
-
}
|
|
1299
|
-
|
|
1300
|
-
/**
|
|
1301
|
-
* ```ts
|
|
1302
|
-
* export function plUral(singular: string, plural2: string, plural5: string, value: number): string;
|
|
1303
|
-
* ```
|
|
1304
|
-
*/
|
|
1305
|
-
|
|
1306
696
|
/**
|
|
1307
697
|
* A helper for choosing the correct singular and plural.
|
|
1308
698
|
*/
|
|
@@ -1324,30 +714,6 @@ export const plUral = (
|
|
|
1324
714
|
: plural5
|
|
1325
715
|
}
|
|
1326
716
|
|
|
1327
|
-
_test.plUral = () => {
|
|
1328
|
-
// @ts-expect-error
|
|
1329
|
-
const auto = plUral.bind(null, 'auto', 'auta', 'aut')
|
|
1330
|
-
|
|
1331
|
-
console.assert(auto(0) === 'aut')
|
|
1332
|
-
console.assert(auto(1) === 'auto')
|
|
1333
|
-
console.assert(auto(17) === 'aut')
|
|
1334
|
-
console.assert(auto(42) === 'auta')
|
|
1335
|
-
|
|
1336
|
-
// @ts-expect-error
|
|
1337
|
-
const car = plUral.bind(null, 'car', 'cars', 'cars')
|
|
1338
|
-
|
|
1339
|
-
console.assert(car(0) === 'cars')
|
|
1340
|
-
console.assert(car(1) === 'car')
|
|
1341
|
-
console.assert(car(17) === 'cars')
|
|
1342
|
-
console.assert(car(42) === 'cars')
|
|
1343
|
-
}
|
|
1344
|
-
|
|
1345
|
-
/**
|
|
1346
|
-
* ```ts
|
|
1347
|
-
* export function pro(ref: any): any;
|
|
1348
|
-
* ```
|
|
1349
|
-
*/
|
|
1350
|
-
|
|
1351
717
|
/**
|
|
1352
718
|
* A helper that protect calls to nested properties by `Proxy` which initializes non-existent values with an empty object.
|
|
1353
719
|
*/
|
|
@@ -1358,40 +724,6 @@ export const pro = (/** @type {any} */ ref) => new Proxy(ref, {
|
|
|
1358
724
|
}
|
|
1359
725
|
})
|
|
1360
726
|
|
|
1361
|
-
_test.pro = () => {
|
|
1362
|
-
const ref = {}
|
|
1363
|
-
|
|
1364
|
-
pro(ref).one.two[3][4] = 1234
|
|
1365
|
-
|
|
1366
|
-
console.assert(eq(ref, { one: { two: { 3: { 4: 1234 } } } }))
|
|
1367
|
-
|
|
1368
|
-
pro(ref).one.two.tree = 123
|
|
1369
|
-
|
|
1370
|
-
console.assert(eq(ref, { one: { two: { 3: { 4: 1234 }, tree: 123 } } }))
|
|
1371
|
-
|
|
1372
|
-
pro(ref).one.two = undefined
|
|
1373
|
-
|
|
1374
|
-
console.assert(eq(ref, { one: { two: undefined } }))
|
|
1375
|
-
|
|
1376
|
-
delete pro(ref).one.two
|
|
1377
|
-
|
|
1378
|
-
console.assert(eq(ref, { one: {} }))
|
|
1379
|
-
|
|
1380
|
-
pro(ref).one.two.three.four
|
|
1381
|
-
|
|
1382
|
-
console.assert(eq(ref, { one: { two: { three: { four: {} } } } }))
|
|
1383
|
-
|
|
1384
|
-
pro(ref).one.two.three.four = 1234
|
|
1385
|
-
|
|
1386
|
-
console.assert(eq(ref, { one: { two: { three: { four: 1234 } } } }))
|
|
1387
|
-
}
|
|
1388
|
-
|
|
1389
|
-
/**
|
|
1390
|
-
* ```ts
|
|
1391
|
-
* export function refsInfo(...refs: any[]): [string, string, string[]][];
|
|
1392
|
-
* ```
|
|
1393
|
-
*/
|
|
1394
|
-
|
|
1395
727
|
/**
|
|
1396
728
|
* A helper that provides information about the given `refs`.
|
|
1397
729
|
*
|
|
@@ -1407,81 +739,15 @@ export const refsInfo = (/** @type {any[]} */ ...refs) => {
|
|
|
1407
739
|
}
|
|
1408
740
|
})
|
|
1409
741
|
|
|
1410
|
-
return Array.from(fns.values()).map(/** @
|
|
742
|
+
return Array.from(fns.values()).map(/** @return {[string, string, string[]]} */ fn => [
|
|
1411
743
|
fn.name,
|
|
1412
744
|
Object.getPrototypeOf(fn)?.name ?? '',
|
|
1413
745
|
Object.getOwnPropertyNames(fn.prototype ?? Object.create(null)).sort()
|
|
1414
746
|
]).sort((a, b) => -(a[0] < b[0]))
|
|
1415
747
|
}
|
|
1416
748
|
|
|
1417
|
-
|
|
1418
|
-
|
|
1419
|
-
|
|
1420
|
-
console.assert(info.find(([name]) => name === 'Array')?.[2]?.includes?.('length'))
|
|
1421
|
-
console.assert(info.find(([name]) => name === 'Function')?.[2]?.includes?.('length'))
|
|
1422
|
-
}
|
|
1423
|
-
|
|
1424
|
-
_test['refsInfo: browserFingerprint'] = () => {
|
|
1425
|
-
const browserFingerprint = () => {
|
|
1426
|
-
// @ts-expect-error
|
|
1427
|
-
const refs = Object.getOwnPropertyNames(window).map(name => window[name])
|
|
1428
|
-
const info = refsInfo(...refs)
|
|
1429
|
-
const json = JSON.stringify(info)
|
|
1430
|
-
const hash = Array(32).fill(0)
|
|
1431
|
-
let j = 0
|
|
1432
|
-
|
|
1433
|
-
for (let i = 0; i < json.length; i++) {
|
|
1434
|
-
let charCode = json.charCodeAt(i)
|
|
1435
|
-
|
|
1436
|
-
while (charCode > 0) {
|
|
1437
|
-
hash[j] = hash[j] ^ (charCode & 15)
|
|
1438
|
-
charCode >>= 4
|
|
1439
|
-
j = (j + 1) & 31
|
|
1440
|
-
}
|
|
1441
|
-
}
|
|
1442
|
-
|
|
1443
|
-
return hash.map(x => x.toString(16)).join('')
|
|
1444
|
-
}
|
|
1445
|
-
|
|
1446
|
-
console.log(browserFingerprint())
|
|
1447
|
-
}
|
|
1448
|
-
|
|
1449
|
-
/**
|
|
1450
|
-
* ```ts
|
|
1451
|
-
* export function s<T extends keyof SVGElementTagNameMap>(tag: T, ...args: HArgs[1][]): SVGElementTagNameMap[T];
|
|
1452
|
-
* export function s<N extends Node>(node: N, ...args: HArgs[1][]): N;
|
|
1453
|
-
* export function s(...args: HArgs): Node;
|
|
1454
|
-
* ```
|
|
1455
|
-
*/
|
|
1456
|
-
|
|
1457
|
-
/**
|
|
1458
|
-
* A lightweight [HyperScript](https://github.com/hyperhype/hyperscript)-style helper for creating and modifying `SVGElement`s (see also `h`).
|
|
1459
|
-
*
|
|
1460
|
-
* - The first argument of type `string` specifies the tag of the element to be created.
|
|
1461
|
-
* - The first argument of type `Node` specifies the element to be modified.
|
|
1462
|
-
* - All other arguments of type `Record<string, any>` are mappings of attributes and properties.
|
|
1463
|
-
* Keys starting with `$` specify *properties* (without the leading `$`) to be set on the element being created or modified.
|
|
1464
|
-
* (Note that `$` is not a valid attribute name character.)
|
|
1465
|
-
* All other keys specify *attributes* to be set by `setAttributeNS`.
|
|
1466
|
-
* An attribute equal to `false` causes the attribute to be removed by `removeAttributeNS`.
|
|
1467
|
-
* - All other arguments of type `null` or `undefined` are simply ignored.
|
|
1468
|
-
* - All other arguments of type `Node` are appended to the element being created or modified.
|
|
1469
|
-
* - All other arguments of type `string`/`number` are converted to `Text` nodes and appended to the element being created or modified.
|
|
1470
|
-
* - All other arguments of type `HArgs` are passed to `s` and the results are appended to the element being created or modified.
|
|
1471
|
-
*/
|
|
1472
|
-
export const s = _h('http://www.w3.org/2000/svg')
|
|
1473
|
-
|
|
1474
|
-
const _ZEROS = '0'.repeat(16)
|
|
1475
|
-
let _counter = 0
|
|
1476
|
-
|
|
1477
|
-
/**
|
|
1478
|
-
* ```ts
|
|
1479
|
-
* export function uuid1(options?: {
|
|
1480
|
-
* date?: Date;
|
|
1481
|
-
* node?: string;
|
|
1482
|
-
* }): string;
|
|
1483
|
-
* ```
|
|
1484
|
-
*/
|
|
749
|
+
const ZEROS = '0'.repeat(16)
|
|
750
|
+
let counter = 0
|
|
1485
751
|
|
|
1486
752
|
/**
|
|
1487
753
|
* A helper that generates a UUID v1 identifier (with a creation timestamp).
|
|
@@ -1493,9 +759,9 @@ export const uuid1 = ({
|
|
|
1493
759
|
date = new Date(),
|
|
1494
760
|
node = Math.random().toString(16).slice(2)
|
|
1495
761
|
} = {}) => {
|
|
1496
|
-
const time =
|
|
762
|
+
const time = ZEROS + (10000 * (+date + 12219292800000)).toString(16)
|
|
1497
763
|
|
|
1498
|
-
|
|
764
|
+
counter = (counter + 1) & 16383
|
|
1499
765
|
|
|
1500
766
|
return time.slice(-8).concat(
|
|
1501
767
|
'-',
|
|
@@ -1504,31 +770,8 @@ export const uuid1 = ({
|
|
|
1504
770
|
-1,
|
|
1505
771
|
time.slice(-15, -12),
|
|
1506
772
|
'-',
|
|
1507
|
-
(8 | (
|
|
1508
|
-
(
|
|
773
|
+
(8 | (counter >> 12)).toString(16),
|
|
774
|
+
(ZEROS + (counter & 4095).toString(16)).slice(-3),
|
|
1509
775
|
'-',
|
|
1510
|
-
(
|
|
1511
|
-
}
|
|
1512
|
-
|
|
1513
|
-
_test.uuid1 = () => {
|
|
1514
|
-
for (let counter = 1; counter <= 0x5678; ++counter) {
|
|
1515
|
-
const uuid = uuid1()
|
|
1516
|
-
|
|
1517
|
-
counter === 0x0001 && console.assert(uuid.split('-')[3] === '8001')
|
|
1518
|
-
counter === 0x0fff && console.assert(uuid.split('-')[3] === '8fff')
|
|
1519
|
-
counter === 0x1000 && console.assert(uuid.split('-')[3] === '9000')
|
|
1520
|
-
counter === 0x2345 && console.assert(uuid.split('-')[3] === 'a345')
|
|
1521
|
-
counter === 0x3456 && console.assert(uuid.split('-')[3] === 'b456')
|
|
1522
|
-
counter === 0x4000 && console.assert(uuid.split('-')[3] === '8000')
|
|
1523
|
-
counter === 0x4567 && console.assert(uuid.split('-')[3] === '8567')
|
|
1524
|
-
}
|
|
1525
|
-
}
|
|
1526
|
-
|
|
1527
|
-
_test['uuid1: node'] = () => {
|
|
1528
|
-
console.assert(uuid1({ node: '000123456789abc' }).split('-')[4] === '123456789abc')
|
|
1529
|
-
console.assert(uuid1({ node: '123456789' }).split('-')[4] === '000123456789')
|
|
1530
|
-
}
|
|
1531
|
-
|
|
1532
|
-
_test['uuid1: date'] = () => {
|
|
1533
|
-
console.assert(uuid1({ date: new Date(323325e6) }).startsWith('c1399400-9a71-11bd'))
|
|
776
|
+
(ZEROS + node).slice(-12))
|
|
1534
777
|
}
|
package/package.json
CHANGED