raiutils 9.0.6 → 9.1.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/dist/router.d.ts +1 -0
- package/dist/router.d.ts.map +1 -1
- package/dist/router.js.map +1 -1
- package/dist/utils-dom.d.ts +122 -0
- package/dist/utils-dom.d.ts.map +1 -0
- package/dist/utils-dom.js +1 -0
- package/dist/utils-dom.js.map +1 -0
- package/dist/utils-node.d.ts +12 -0
- package/dist/utils-node.d.ts.map +1 -0
- package/dist/utils-node.js +1 -0
- package/dist/utils-node.js.map +1 -0
- package/dist/utils.d.ts +8 -125
- package/dist/utils.d.ts.map +1 -1
- package/dist/utils.js +1 -1
- package/dist/utils.js.map +1 -1
- package/dist/uuid.d.ts +2 -2
- package/dist/uuid.d.ts.map +1 -1
- package/dist/uuid.js +1 -1
- package/dist/uuid.js.map +1 -1
- package/package.json +8 -3
- package/src/router.ts +3 -2
- package/src/utils-dom.ts +396 -0
- package/src/utils-node.ts +38 -0
- package/src/utils.ts +35 -442
- package/src/uuid.ts +10 -11
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
//https://github.com/Pecacheu/Utils.js; GNU GPL v3
|
|
2
|
+
|
|
3
|
+
import os from 'os';
|
|
4
|
+
import { utils as U } from './utils.js';
|
|
5
|
+
export type * from './utils.js';
|
|
6
|
+
|
|
7
|
+
namespace ext {
|
|
8
|
+
/** Get list of system IPs */
|
|
9
|
+
export function getIPs() {
|
|
10
|
+
const ip: string[]=[], fl=os.networkInterfaces();
|
|
11
|
+
for(let k in fl) fl[k]!.forEach(f => {
|
|
12
|
+
if(!f.internal && f.family == 'IPv4' && f.mac != '00:00:00:00:00:00' && f.address) ip.push(f.address);
|
|
13
|
+
});
|
|
14
|
+
return ip;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
/** Get system info
|
|
18
|
+
@returns [sysOS, arch, cpuInfo] */
|
|
19
|
+
export function getOS() {
|
|
20
|
+
let sysOS, arch;
|
|
21
|
+
switch(os.platform()) {
|
|
22
|
+
case 'win32': sysOS="Windows"; break;
|
|
23
|
+
case 'darwin': sysOS="MacOS"; break;
|
|
24
|
+
case 'linux': sysOS="Linux"; break;
|
|
25
|
+
default: sysOS=os.platform();
|
|
26
|
+
}
|
|
27
|
+
switch(os.arch()) {
|
|
28
|
+
case 'ia32': arch="32-bit"; break;
|
|
29
|
+
case 'x64': arch="64-bit"; break;
|
|
30
|
+
case 'arm': arch="ARM"; break;
|
|
31
|
+
default: arch=os.arch();
|
|
32
|
+
}
|
|
33
|
+
return [sysOS, arch, os.cpus()[0]?.model||''];
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
export const utils = <typeof U & typeof ext>U;
|
|
38
|
+
export default utils;
|
package/src/utils.ts
CHANGED
|
@@ -1,12 +1,10 @@
|
|
|
1
1
|
//https://github.com/Pecacheu/Utils.js; GNU GPL v3
|
|
2
2
|
|
|
3
3
|
//Node.js compat
|
|
4
|
-
|
|
5
|
-
|
|
4
|
+
//@ts-ignore
|
|
5
|
+
type P = [typeof document, typeof HTMLCollection];
|
|
6
6
|
//@ts-expect-error
|
|
7
|
-
const IsNode=typeof window==='undefined', P:P=IsNode?
|
|
8
|
-
[{}, {back:()=>{},forward:()=>{}}, class{}, class{}, class{}, class{}, ()=>{}]:
|
|
9
|
-
[window, history, DOMRect, HTMLCollection, Element, NodeList, addEventListener];
|
|
7
|
+
const IsNode=typeof window==='undefined', P:P=IsNode?[{}, class{}]:[document, HTMLCollection];
|
|
10
8
|
|
|
11
9
|
//-------------------------------------------- Types --------------------------------------------
|
|
12
10
|
|
|
@@ -39,19 +37,6 @@ export interface Array<T> {
|
|
|
39
37
|
at(idx: number): T | undefined;
|
|
40
38
|
}
|
|
41
39
|
|
|
42
|
-
export interface HTMLCollection {
|
|
43
|
-
each: <R>(fn: (itm: Element, idx: number, len: number) => R | "!",
|
|
44
|
-
st?: number, en?: number) => (R | undefined);
|
|
45
|
-
eachAsync: <R>(fn: (itm: Element, idx: number, len: number) => R | "!",
|
|
46
|
-
st?: number, en?: number, pe?: boolean) => Promise<R | undefined>;
|
|
47
|
-
}
|
|
48
|
-
export interface NodeList {
|
|
49
|
-
each: <R>(fn: (itm: Node, idx: number, len: number) => R | "!",
|
|
50
|
-
st?: number, en?: number) => (R | undefined);
|
|
51
|
-
eachAsync: <R>(fn: (itm: Node, idx: number, len: number) => R | "!",
|
|
52
|
-
st?: number, en?: number, pe?: boolean) => Promise<R | undefined>;
|
|
53
|
-
}
|
|
54
|
-
|
|
55
40
|
export interface Function {
|
|
56
41
|
/** Wrap a function so that it always has a preset argument list when called.
|
|
57
42
|
In the called function, `this` is set to the caller's arguments, granting access to both */
|
|
@@ -68,11 +53,6 @@ export interface RegExpConstructor {
|
|
|
68
53
|
escape(s: string): string;
|
|
69
54
|
}
|
|
70
55
|
|
|
71
|
-
export interface TouchList {
|
|
72
|
-
/** Get touch by id, if it exists */
|
|
73
|
-
get(id: number): Touch | undefined;
|
|
74
|
-
}
|
|
75
|
-
|
|
76
56
|
interface Base64Opts {
|
|
77
57
|
alphabet?: 'base64'|'base64url';
|
|
78
58
|
omitPadding?: boolean;
|
|
@@ -83,17 +63,6 @@ export interface Uint8ArrayConstructor {
|
|
|
83
63
|
export interface Uint8Array {
|
|
84
64
|
toBase64(opts: Base64Opts): string;
|
|
85
65
|
}
|
|
86
|
-
|
|
87
|
-
export interface Element {
|
|
88
|
-
/** Get an element's index in its parent. Returns -1 if the element has no parent */
|
|
89
|
-
index: number;
|
|
90
|
-
/** Insert child at index */
|
|
91
|
-
insertChildAt(el: Element, i: number): void;
|
|
92
|
-
/** Get element bounding rect as UtilRect object */
|
|
93
|
-
boundingRect: utils.UtilRect;
|
|
94
|
-
/** Get element inner rect (excluding border and padding) as UtilRect object */
|
|
95
|
-
innerRect: utils.UtilRect;
|
|
96
|
-
}
|
|
97
66
|
}
|
|
98
67
|
|
|
99
68
|
export interface AnyMap {[k: string]: any};
|
|
@@ -103,16 +72,17 @@ export interface QueryMap {[k: string]: true | string | string[]};
|
|
|
103
72
|
//-------------------------------------------- Extensions --------------------------------------------
|
|
104
73
|
|
|
105
74
|
export namespace utils {
|
|
106
|
-
|
|
107
|
-
const [window, history, DOMRect, HTMLCollection,
|
|
108
|
-
Element, NodeList, addEventListener] = P;
|
|
75
|
+
const [document, HTMLCollection] = P;
|
|
109
76
|
|
|
110
77
|
/** Current library version */
|
|
111
|
-
export const VER = "v9.0
|
|
78
|
+
export const VER = "v9.1.0";
|
|
112
79
|
|
|
113
80
|
/** Whether the environment is Node.js or Browser */
|
|
114
81
|
export const isNode = IsNode;
|
|
115
82
|
|
|
83
|
+
/** Import modules only in Node.js, otherwise return empty list */
|
|
84
|
+
export const importNode = async (...mods: string[]) => (IsNode ? Promise.all(mods.map(i => import(i))) : []) as any[];
|
|
85
|
+
|
|
116
86
|
//==== Objects ====
|
|
117
87
|
|
|
118
88
|
/** Add getter and/or setter for `name` to `obj` */
|
|
@@ -243,7 +213,9 @@ async function eachAsync(this: any[], fn: (itm: any, idx: number, len: number) =
|
|
|
243
213
|
this instanceof HTMLCollection?this[i].remove():this.splice(i,1); --i,--l;
|
|
244
214
|
} else if(r[n]!=null) return r[n];
|
|
245
215
|
}
|
|
246
|
-
|
|
216
|
+
|
|
217
|
+
proto(Array, 'each', each);
|
|
218
|
+
proto(Array, 'eachAsync', eachAsync);
|
|
247
219
|
|
|
248
220
|
//==== Numbers ====
|
|
249
221
|
|
|
@@ -422,108 +394,30 @@ if(!('at' in Array.prototype)) proto(Array, 'at', function(this: any[], idx: num
|
|
|
422
394
|
if(i>=0 && i<l) return this[i];
|
|
423
395
|
});
|
|
424
396
|
|
|
425
|
-
//====
|
|
397
|
+
//==== Utility ====
|
|
426
398
|
|
|
427
399
|
proto(Function, 'wrap', function(this: any, ...args: any[]) {
|
|
428
400
|
const f=this; return function() {return f.apply(arguments, args)}
|
|
429
401
|
}, false, true);
|
|
430
402
|
|
|
431
|
-
|
|
432
|
-
for(const t of this) if(t.identifier === id) return t;
|
|
433
|
-
});
|
|
434
|
-
|
|
435
|
-
define(Element.prototype, 'index', function(this: any) {
|
|
436
|
-
const p=this.parentElement; if(!p) return -1;
|
|
437
|
-
return Array.prototype.indexOf.call(p.children, this);
|
|
438
|
-
});
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
proto(Element, 'insertChildAt', function(this: any, el: Element, i: number) {
|
|
442
|
-
if(i<0) i=0; if(i >= this.children.length) this.appendChild(el);
|
|
443
|
-
else this.insertBefore(el, this.children[i]);
|
|
444
|
-
});
|
|
445
|
-
|
|
446
|
-
/** Get element bounding rect as UtilRect object */
|
|
447
|
-
export const boundingRect = (e: Element) => new UtilRect(e.getBoundingClientRect());
|
|
448
|
-
|
|
449
|
-
/** Get element inner rect (excluding border and padding) as UtilRect object */
|
|
450
|
-
export function innerRect(e: Element) {
|
|
451
|
-
let r=e.getBoundingClientRect(), s=getComputedStyle(e);
|
|
452
|
-
return new UtilRect(r.top+parseFloat(s.paddingTop)+parseFloat(s.borderTopWidth),
|
|
453
|
-
r.bottom-parseFloat(s.paddingBottom)-parseFloat(s.borderBottomWidth),
|
|
454
|
-
r.left+parseFloat(s.paddingLeft)+parseFloat(s.borderLeftWidth),
|
|
455
|
-
r.right-parseFloat(s.paddingRight)-parseFloat(s.borderRightWidth));
|
|
456
|
-
};
|
|
457
|
-
|
|
458
|
-
define(Element.prototype, 'boundingRect', function(this: any) {return boundingRect(this)});
|
|
459
|
-
define(Element.prototype, 'innerRect', function(this: any) {return innerRect(this)});
|
|
460
|
-
|
|
461
|
-
//-------------------------------------------- DOM Model --------------------------------------------
|
|
462
|
-
|
|
463
|
-
//==== General ====
|
|
464
|
-
|
|
465
|
-
/** Better class for bounding boxes */
|
|
466
|
-
export class UtilRect {
|
|
467
|
-
x!: number; left!: number;
|
|
468
|
-
y!: number; top!: number;
|
|
469
|
-
x2!: number; right!: number;
|
|
470
|
-
y2!: number; bottom!: number;
|
|
471
|
-
w!: number; width!: number;
|
|
472
|
-
h!: number; height!: number;
|
|
473
|
-
centerX!: number; centerY!: number;
|
|
474
|
-
|
|
475
|
-
constructor(t: number | DOMRect | UtilRect, b?: number, l?: number, r?: number) {
|
|
476
|
-
const f=Number.isFinite; let tt=0,bb=0,ll=0,rr=0;
|
|
477
|
-
define(this,'x', ()=>ll, v=>{f(v)?(rr+=v-ll,ll=v):0});
|
|
478
|
-
define(this,'y', ()=>tt, v=>{f(v)?(bb+=v-tt,tt=v):0});
|
|
479
|
-
define(this,'top', ()=>tt, v=>{tt=f(v)?v:0});
|
|
480
|
-
define(this,['bottom','y2'],()=>bb, v=>{bb=f(v)?v:0});
|
|
481
|
-
define(this,'left', ()=>ll, v=>{ll=f(v)?v:0});
|
|
482
|
-
define(this,['right','x2'], ()=>rr, v=>{rr=f(v)?v:0});
|
|
483
|
-
define(this,['width','w'], ()=>rr-ll, v=>{rr=v>=0?ll+v:0});
|
|
484
|
-
define(this,['height','h'], ()=>bb-tt, v=>{bb=v>=0?tt+v:0});
|
|
485
|
-
define(this,'centerX', ()=>ll/2+rr/2);
|
|
486
|
-
define(this,'centerY', ()=>tt/2+bb/2);
|
|
487
|
-
if(t instanceof DOMRect || t instanceof UtilRect)
|
|
488
|
-
tt=t.top, bb=t.bottom, ll=t.left, rr=t.right;
|
|
489
|
-
else tt=t, bb=b!, ll=l!, rr=r!;
|
|
490
|
-
}
|
|
491
|
-
|
|
492
|
-
/** Check if rect contains point, other rect, or Element */
|
|
493
|
-
contains(x: number | UtilRect | Element, y?: number): boolean {
|
|
494
|
-
if(x instanceof Element) return this.contains(x.boundingRect);
|
|
495
|
-
if(x instanceof UtilRect) return x.x >= this.x && x.x2 <= this.x2 && x.y >= this.y && x.y2 <= this.y2;
|
|
496
|
-
return x >= this.x && x <= this.x2 && y! >= this.y && y! <= this.y2;
|
|
497
|
-
}
|
|
403
|
+
const R_ES = /\S/;
|
|
498
404
|
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
else y = r.y >= this.y && r.y <= this.y2 || r.y2 >= this.y && r.y2 <= this.y2;
|
|
508
|
-
return x&&y;
|
|
405
|
+
/** Check if string, array, or object is empty.
|
|
406
|
+
Returns false for all other types */
|
|
407
|
+
export function isBlank(s: any) {
|
|
408
|
+
if(s == null) return true;
|
|
409
|
+
if(typeof s === 'string') return !R_ES.test(s);
|
|
410
|
+
if(typeof s === 'object') {
|
|
411
|
+
if(typeof s.length === 'number') return s.length === 0;
|
|
412
|
+
return Object.keys(s).length === 0;
|
|
509
413
|
}
|
|
414
|
+
return false;
|
|
415
|
+
}
|
|
510
416
|
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
if(x instanceof Element) return this.dist(x.boundingRect);
|
|
514
|
-
const n = x instanceof UtilRect;
|
|
515
|
-
y = Math.abs((n?(x as UtilRect).centerY:y as number)-this.centerY),
|
|
516
|
-
x = Math.abs((n?(x as UtilRect).centerX:x as number)-this.centerX);
|
|
517
|
-
return Math.sqrt(x*x+y*y);
|
|
518
|
-
}
|
|
417
|
+
/** setTimeout but async */
|
|
418
|
+
export const delay = (ms: number): Promise<void> => new Promise(r => setTimeout(r,ms));
|
|
519
419
|
|
|
520
|
-
|
|
521
|
-
@returns self for chaining */
|
|
522
|
-
expand(by: number) {
|
|
523
|
-
this.top -= by, this.left -= by, this.bottom += by, this.right += by;
|
|
524
|
-
return this;
|
|
525
|
-
}
|
|
526
|
-
}
|
|
420
|
+
//-------------------------------------------- DOM Model --------------------------------------------
|
|
527
421
|
|
|
528
422
|
export interface UserAgentInfo {
|
|
529
423
|
os?: string;
|
|
@@ -568,107 +462,6 @@ export function deviceInfo(ua?: string) {
|
|
|
568
462
|
return d;
|
|
569
463
|
}
|
|
570
464
|
|
|
571
|
-
export const device = IsNode ? null : deviceInfo();
|
|
572
|
-
export const mobile = device?.mobile;
|
|
573
|
-
|
|
574
|
-
const R_CTR = /translate(?:x|y)?\(.+?\)/gi;
|
|
575
|
-
|
|
576
|
-
/** Center element with JS
|
|
577
|
-
|
|
578
|
-
Modes:
|
|
579
|
-
- `trans`: Uses transform: translate. Responsive, no container
|
|
580
|
-
- Default: New flexbox method
|
|
581
|
-
@param only `x` for only x axis centering, `y` for only y axis, null for both */
|
|
582
|
-
export function center(el: HTMLElement, only?: "x" | "y", mode?: "trans") {
|
|
583
|
-
const os = el.style;
|
|
584
|
-
if(mode == 'trans') {
|
|
585
|
-
if(!os.position) os.position='absolute';
|
|
586
|
-
let tr = os.transform.replace(R_CTR,'').trim();
|
|
587
|
-
if(!only || only === 'x') os.left='50%', tr+=' translateX(-50%)';
|
|
588
|
-
if(!only || only === 'y') os.top='50%', tr+=' translateY(-50%)';
|
|
589
|
-
os.transform=tr;
|
|
590
|
-
} else {
|
|
591
|
-
const cont = mkDiv(el.parentNode, null, {display:'flex', top:0, left:0}), cs = cont.style;
|
|
592
|
-
cont.appendChild(el);
|
|
593
|
-
if(!only || only === 'x') cs.justifyContent='center', cs.width='100%';
|
|
594
|
-
if(!only || only === 'y') cs.alignItems='center',
|
|
595
|
-
cs.height='100%', cs.position='absolute';
|
|
596
|
-
}
|
|
597
|
-
}
|
|
598
|
-
|
|
599
|
-
//==== Navigation ====
|
|
600
|
-
|
|
601
|
-
/** Called when a virtual navigation event occurs, including on page load */
|
|
602
|
-
export let onNav: (state: any) => void;
|
|
603
|
-
|
|
604
|
-
/** Generate a virtual navigation event, updating the URL bar
|
|
605
|
-
@param state Optional data given to `onNav` whenever the user returns to this history entry */
|
|
606
|
-
export function go(url: string | URL, state?: any) {
|
|
607
|
-
history.pushState(state, '', url), doNav(state);
|
|
608
|
-
}
|
|
609
|
-
|
|
610
|
-
addEventListener('popstate', e => doNav(e.state));
|
|
611
|
-
addEventListener('load', () => setTimeout(() => doNav(history.state),1));
|
|
612
|
-
function doNav(s: any) {if(onNav) onNav.call(null,s)}
|
|
613
|
-
|
|
614
|
-
//==== DOM Creation ====
|
|
615
|
-
|
|
616
|
-
/** Create elements with ease! Just remember **PCSI** - Parent, class, style, innerHTML */
|
|
617
|
-
export function mkEl<K extends keyof HTMLElementTagNameMap>(tag: K, parent?: Node | null, cls?: string | null,
|
|
618
|
-
style?: CSSStyleDeclaration | AnyMap | null, inner?: string | null): HTMLElementTagNameMap[K] {
|
|
619
|
-
const e = document.createElement(tag);
|
|
620
|
-
if(cls != null) e.className = cls;
|
|
621
|
-
if(inner != null) e.innerHTML = inner;
|
|
622
|
-
if(style && typeof style === 'object') for(const k in style) {
|
|
623
|
-
if(k in e.style) (e.style as any)[k] = (style as any)[k];
|
|
624
|
-
else e.style.setProperty(k, (style as any)[k]);
|
|
625
|
-
}
|
|
626
|
-
if(parent != null) parent.appendChild(e);
|
|
627
|
-
return e;
|
|
628
|
-
}
|
|
629
|
-
|
|
630
|
-
/** Shorthand for `mkEl` with div tag */
|
|
631
|
-
export const mkDiv = (parent?: Node | null, cls?: string | null, style?: CSSStyleDeclaration | AnyMap | null,
|
|
632
|
-
inner?: string | null) => mkEl('div', parent, cls, style, inner);
|
|
633
|
-
|
|
634
|
-
/** Add text node to the DOM */
|
|
635
|
-
export const addText = (parent: Node, text: string) => parent.appendChild(document.createTextNode(text));
|
|
636
|
-
|
|
637
|
-
//==== CSS ====
|
|
638
|
-
|
|
639
|
-
let TWCanvas: HTMLCanvasElement;
|
|
640
|
-
|
|
641
|
-
/** Get predicted width of text w/ given CSS font style */
|
|
642
|
-
export function textWidth(txt: string, font: string) {
|
|
643
|
-
const c = TWCanvas||(TWCanvas=mkEl('canvas')), ctx = c.getContext('2d')!;
|
|
644
|
-
ctx.font = font; return ctx.measureText(txt).width;
|
|
645
|
-
}
|
|
646
|
-
|
|
647
|
-
const R_SK = /[A-Z]/g, R_SR=(s: string) => '-'+s.toLowerCase();
|
|
648
|
-
function defSty() {
|
|
649
|
-
for(const s of document.styleSheets as any) try {s.cssRules; return s} catch(e) {}
|
|
650
|
-
//let ns=mkEl('style',document.head); addText(ns,''); return ns.sheet!;
|
|
651
|
-
return mkEl('style', document.head).sheet;
|
|
652
|
-
}
|
|
653
|
-
|
|
654
|
-
/** Create a CSS rule and append it to the current document
|
|
655
|
-
@param sel CSS selector, eg. `.class` or `#id` */
|
|
656
|
-
export function addCSS(sel: string, style: CSSStyleDeclaration | AnyMap, sheet?: CSSStyleSheet) {
|
|
657
|
-
if(!sheet) sheet=defSty(); let k,s=[];
|
|
658
|
-
for(k in style) s.push(`${k.replace(R_SK, R_SR)}:${(style as AnyMap)[k]}`);
|
|
659
|
-
sheet!.insertRule(`${sel}{${s.join(';')}}`);
|
|
660
|
-
}
|
|
661
|
-
|
|
662
|
-
/** Remove a CSS selector from **all** stylesheets */
|
|
663
|
-
export function remCSS(sel: string) {
|
|
664
|
-
let s,rl;
|
|
665
|
-
for(s of document.styleSheets as any) {
|
|
666
|
-
try {rl=s.cssRules} catch(e) {continue}
|
|
667
|
-
for(let i=0,l=rl.length; i<l; ++i) if(rl[i] instanceof CSSStyleRule
|
|
668
|
-
&& rl[i].selectorText===sel) s.deleteRule(i);
|
|
669
|
-
}
|
|
670
|
-
}
|
|
671
|
-
|
|
672
465
|
//==== Cookies! (Yum) ====
|
|
673
466
|
|
|
674
467
|
/** Set a cookie
|
|
@@ -677,12 +470,14 @@ export function remCSS(sel: string) {
|
|
|
677
470
|
@param secure Only allow in HTTPS context */
|
|
678
471
|
export function setCookie(key: string, val?: string, exp?: Date | number, secure?: boolean) {
|
|
679
472
|
let c=`${encodeURIComponent(key)}=${val==null?'':encodeURIComponent(val)};path=/`;
|
|
680
|
-
if(
|
|
681
|
-
if(exp
|
|
682
|
-
|
|
683
|
-
|
|
473
|
+
if(val) {
|
|
474
|
+
if(exp != null) {
|
|
475
|
+
if(exp === -1) exp=new Date(9e14);
|
|
476
|
+
if(exp instanceof Date) c+=';expires='+exp.toUTCString();
|
|
477
|
+
else c+=';max-age='+exp;
|
|
478
|
+
}
|
|
479
|
+
if(secure) c+=';secure';
|
|
684
480
|
}
|
|
685
|
-
if(secure) c+=';secure';
|
|
686
481
|
if(!IsNode) document.cookie = c;
|
|
687
482
|
return c;
|
|
688
483
|
}
|
|
@@ -692,7 +487,7 @@ export function setCookie(key: string, val?: string, exp?: Date | number, secure
|
|
|
692
487
|
export function getCookie(key: string, ckStr?: string) {
|
|
693
488
|
if(ckStr == null) ckStr=document.cookie;
|
|
694
489
|
key=encodeURIComponent(key)+'=';
|
|
695
|
-
let l=ckStr
|
|
490
|
+
let l=ckStr!.split('; '), c: string;
|
|
696
491
|
for(c of l) if(c.startsWith(key))
|
|
697
492
|
return decodeURIComponent(c.slice(key.length));
|
|
698
493
|
}
|
|
@@ -759,123 +554,6 @@ export function toQuery(data: QueryMap, sep='&') {
|
|
|
759
554
|
return q.join(sep);
|
|
760
555
|
}
|
|
761
556
|
|
|
762
|
-
//==== Inputs ====
|
|
763
|
-
|
|
764
|
-
const R_NFZ=/\.0*$/;
|
|
765
|
-
|
|
766
|
-
export interface NumField extends HTMLInputElement {
|
|
767
|
-
num: number;
|
|
768
|
-
ns: string | null;
|
|
769
|
-
set: (num: number | string) => void;
|
|
770
|
-
setRange: (min?: number, max?: number, decMax?: number) => void;
|
|
771
|
-
onnuminput?: (this: GlobalEventHandlers, ev?: Event) => any;
|
|
772
|
-
}
|
|
773
|
-
|
|
774
|
-
/** Turns your boring <input> into a mobile-friendly number entry field with max/min & negative support!
|
|
775
|
-
|
|
776
|
-
Tips:
|
|
777
|
-
- Use `field.onnuminput` in place of oninput, get number value with field.num
|
|
778
|
-
- On mobile, use star key for decimal point and pound key for negative
|
|
779
|
-
- You can set `field.nStep` in order to change the up/down arrow step size
|
|
780
|
-
- Use `field.setRange` to change min, max, and decMax
|
|
781
|
-
|
|
782
|
-
@param min Min value, default min safe int
|
|
783
|
-
@param max Max value, default max safe int
|
|
784
|
-
@param decMax Max decimal precision (eg. 3 is 0.001), default 0
|
|
785
|
-
@param sym If a symbol (eg. '$') is given, uses currency mode */
|
|
786
|
-
export function numField(field: HTMLInputElement, min?: number, max?: number, decMax?: number, sym?: string) {
|
|
787
|
-
const f = field as NumField, RM = RegExp(`[,${sym?RegExp.escape(sym):''}]`, 'g');
|
|
788
|
-
f.type = (mobile||decMax||sym)?'tel':'number';
|
|
789
|
-
f.setAttribute('pattern', "\\d*");
|
|
790
|
-
//@ts-expect-error
|
|
791
|
-
if(!f.step) f.step = 1;
|
|
792
|
-
f.addEventListener('keydown', e => {
|
|
793
|
-
if(e.ctrlKey) return;
|
|
794
|
-
let k=e.key, kn=k.length===1&&Number.isFinite(Number(k)),
|
|
795
|
-
ns=f.ns, len=ns!.length, dec=ns!.indexOf('.');
|
|
796
|
-
|
|
797
|
-
if(k==='Tab' || k==='Enter') return;
|
|
798
|
-
else if(kn) {if(dec===-1 || len-dec < decMax!+1) ns+=k} //Number
|
|
799
|
-
else if(k==='.' || k==='*') {if(decMax && dec==-1
|
|
800
|
-
&& f.num!=max && (min!>=0 || f.num!=min)) { //Decimal
|
|
801
|
-
if(!len && min!>0) ns=Math.floor(min!)+'.';
|
|
802
|
-
else ns+='.';
|
|
803
|
-
}} else if(k==='Backspace' || k==='Delete') { //Backspace
|
|
804
|
-
if(min!>0 && f.num===min && ns!.endsWith('.')) ns='';
|
|
805
|
-
else ns=ns!.slice(0,-1);
|
|
806
|
-
} else if(k==='-' || k==='#') {if(min!<0 && !len) ns='-'} //Negative
|
|
807
|
-
else if(k==='ArrowUp') ns=null, f.set(f.num+Number(f.step)); //Up
|
|
808
|
-
else if(k==='ArrowDown') ns=null, f.set(f.num-Number(f.step)); //Down
|
|
809
|
-
|
|
810
|
-
if(ns !== null && ns !== f.ns) {
|
|
811
|
-
len=ns.length, dec=ns.indexOf('.');
|
|
812
|
-
let neg=ns==='-'||ns==='-.', s=neg?'0':ns+(ns.endsWith('.')?'0':''),
|
|
813
|
-
nr=Number(s), n=bounds(nr, min, max);
|
|
814
|
-
if(!kn || !ns || f.num !== n || (dec!==-1 && len-dec < decMax!+1)) {
|
|
815
|
-
f.ns=ns, f.num=n;
|
|
816
|
-
f.value = sym ? neg?sym+'-0.00':formatCost(n,sym):
|
|
817
|
-
(ns[0]==='-'?'-':'')+Math.floor(Math.abs(n))
|
|
818
|
-
+(dec!==-1?ns.slice(dec)+(R_NFZ.test(ns)?'0':''):'');
|
|
819
|
-
if(f.onnuminput) f.onnuminput.call(f,e);
|
|
820
|
-
}
|
|
821
|
-
}
|
|
822
|
-
e.preventDefault();
|
|
823
|
-
});
|
|
824
|
-
function numRng(n: any) {
|
|
825
|
-
if(typeof n==='string') n=n.replace(RM,'');
|
|
826
|
-
n=bounds(Number(n)||0, min, max);
|
|
827
|
-
return decMax?Number(n.toFixed(decMax)):Math.round(n);
|
|
828
|
-
}
|
|
829
|
-
f.set=n => {
|
|
830
|
-
f.num = numRng(n);
|
|
831
|
-
f.ns = f.num.toString();
|
|
832
|
-
f.value = sym?formatCost(f.num,sym):f.ns;
|
|
833
|
-
f.ns=f.ns.replace(/^(-?)0+/,'$1');
|
|
834
|
-
if(f.onnuminput) f.onnuminput.call(f);
|
|
835
|
-
}
|
|
836
|
-
f.setRange=(nMin, nMax, nDecMax) => {
|
|
837
|
-
min=nMin==null ? Number.MIN_SAFE_INTEGER : nMin;
|
|
838
|
-
max=nMax==null ? Number.MAX_SAFE_INTEGER : nMax;
|
|
839
|
-
decMax=nDecMax==null ? sym?2:0 : nDecMax;
|
|
840
|
-
if(numRng(f.num) !== f.num) f.set(f.num);
|
|
841
|
-
}
|
|
842
|
-
f.addEventListener('input', () => f.set(f.value));
|
|
843
|
-
f.addEventListener('paste', e => {f.set(e.clipboardData!.getData('text')); e.preventDefault()});
|
|
844
|
-
f.setRange(min, max, decMax);
|
|
845
|
-
return f;
|
|
846
|
-
}
|
|
847
|
-
|
|
848
|
-
export interface TextArea extends HTMLTextAreaElement {
|
|
849
|
-
set: (val: string) => void;
|
|
850
|
-
}
|
|
851
|
-
|
|
852
|
-
//By Rick Kukiela @ StackOverflow
|
|
853
|
-
/** Auto-resizing textarea, dynamically scales lineHeight based on input.
|
|
854
|
-
Use `el.set(value)` to set value & update size */
|
|
855
|
-
export function autosize(el: HTMLTextAreaElement, maxRows=5, minRows=1) {
|
|
856
|
-
const e = el as TextArea;
|
|
857
|
-
e.set = v => {e.value=v,cb()};
|
|
858
|
-
let s=e.style;
|
|
859
|
-
s.maxHeight=s.resize='none', s.minHeight='0', s.height='auto';
|
|
860
|
-
e.setAttribute('rows', minRows as any);
|
|
861
|
-
function cb() {
|
|
862
|
-
if(e.scrollHeight===0) return setTimeout(cb,1); //Still loading
|
|
863
|
-
e.setAttribute('rows', 1 as any);
|
|
864
|
-
//Override style
|
|
865
|
-
let cs=getComputedStyle(e);
|
|
866
|
-
s.setProperty('overflow', 'hidden', 'important');
|
|
867
|
-
s.width=e.innerRect.w+'px', s.boxSizing='content-box', s.borderWidth=s.paddingInline='0';
|
|
868
|
-
//Calc scroll height
|
|
869
|
-
let pad=parseFloat(cs.paddingTop) + parseFloat(cs.paddingBottom),
|
|
870
|
-
lh=cs.lineHeight==='normal' ? parseFloat(cs.height) : parseFloat(cs.lineHeight),
|
|
871
|
-
rows=Math.round((Math.round(e.scrollHeight) - pad)/lh);
|
|
872
|
-
//Undo overrides & apply
|
|
873
|
-
s.overflow=s.width=s.boxSizing=s.borderWidth=s.paddingInline='';
|
|
874
|
-
e.setAttribute('rows', bounds(rows, minRows, maxRows) as any);
|
|
875
|
-
}
|
|
876
|
-
e.addEventListener('input', cb);
|
|
877
|
-
}
|
|
878
|
-
|
|
879
557
|
//==== Dates ====
|
|
880
558
|
|
|
881
559
|
export const months = ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"];
|
|
@@ -924,89 +602,4 @@ export function suffix(n: number) {
|
|
|
924
602
|
if(j==3 && k!=13) return n+"rd";
|
|
925
603
|
return n+"th";
|
|
926
604
|
}
|
|
927
|
-
|
|
928
|
-
/** Set `datetime-local` or `date` input from JS Date object or string, adjusting for local timezone */
|
|
929
|
-
export function setDateTime(el: HTMLInputElement, date: Date | string | number) {
|
|
930
|
-
if(!(date instanceof Date)) date=new Date(date);
|
|
931
|
-
el.value = new Date(date.getTime() - date.getTimezoneOffset()*60000).
|
|
932
|
-
toISOString().slice(0, el.type==='date'?10:19);
|
|
933
|
-
}
|
|
934
|
-
|
|
935
|
-
/** Get value of `datetime-local` or `date` input as JS Date */
|
|
936
|
-
export const getDateTime = (el: HTMLInputElement) => new Date(el.value+(el.type==='date'?'T00:00':''));
|
|
937
|
-
|
|
938
|
-
//==== Utility ====
|
|
939
|
-
|
|
940
|
-
const R_ES = /\S/;
|
|
941
|
-
|
|
942
|
-
/** Check if string, array, or object is empty.
|
|
943
|
-
Returns false for all other types */
|
|
944
|
-
export function isBlank(s: any) {
|
|
945
|
-
if(s == null) return true;
|
|
946
|
-
if(typeof s === 'string') return !R_ES.test(s);
|
|
947
|
-
if(typeof s === 'object') {
|
|
948
|
-
if(typeof s.length === 'number') return s.length === 0;
|
|
949
|
-
return Object.keys(s).length === 0;
|
|
950
|
-
}
|
|
951
|
-
return false;
|
|
952
|
-
}
|
|
953
|
-
|
|
954
|
-
/** Trigger browser download of file. If `data` is a string or URL,
|
|
955
|
-
it is treated as a URL. Otherwise, it is downloaded as Blob data */
|
|
956
|
-
export async function download(data: string | URL | Blob | ArrayBuffer, name?: string) {
|
|
957
|
-
const a = mkEl('a');
|
|
958
|
-
if(typeof data === 'string' || data instanceof URL) {
|
|
959
|
-
a.href = data.toString();
|
|
960
|
-
a.download = name || a.href.split('/').at(-1)!;
|
|
961
|
-
a.click();
|
|
962
|
-
} else {
|
|
963
|
-
if(!(data instanceof Blob)) data = new Blob([data]);
|
|
964
|
-
const u = URL.createObjectURL(data);
|
|
965
|
-
a.href=u, a.download=name||'file', a.click();
|
|
966
|
-
URL.revokeObjectURL(u);
|
|
967
|
-
}
|
|
968
|
-
}
|
|
969
|
-
|
|
970
|
-
/** setTimeout but async */
|
|
971
|
-
export const delay = (ms: number): Promise<void> => new Promise(r => setTimeout(r,ms));
|
|
972
|
-
|
|
973
|
-
//-------------------------------------------- NodeJS --------------------------------------------
|
|
974
|
-
|
|
975
|
-
let os: typeof import('os');
|
|
976
|
-
async function importNode() {
|
|
977
|
-
if(os) return;
|
|
978
|
-
os = await import('os');
|
|
979
|
-
}
|
|
980
|
-
|
|
981
|
-
/** Get list of system IPs */
|
|
982
|
-
export async function getIPs() {
|
|
983
|
-
await importNode();
|
|
984
|
-
const ip: string[]=[], fl=os.networkInterfaces();
|
|
985
|
-
for(let k in fl) fl[k]!.forEach(f => {
|
|
986
|
-
if(!f.internal && f.family == 'IPv4' && f.mac != '00:00:00:00:00:00' && f.address) ip.push(f.address);
|
|
987
|
-
});
|
|
988
|
-
return ip;
|
|
989
|
-
}
|
|
990
|
-
|
|
991
|
-
/** Get system info
|
|
992
|
-
@returns [sysOS, arch, cpuInfo] */
|
|
993
|
-
export async function getOS() {
|
|
994
|
-
await importNode();
|
|
995
|
-
let sysOS, arch;
|
|
996
|
-
switch(os.platform()) {
|
|
997
|
-
case 'win32': sysOS="Windows"; break;
|
|
998
|
-
case 'darwin': sysOS="MacOS"; break;
|
|
999
|
-
case 'linux': sysOS="Linux"; break;
|
|
1000
|
-
default: sysOS=os.platform();
|
|
1001
|
-
}
|
|
1002
|
-
switch(os.arch()) {
|
|
1003
|
-
case 'ia32': arch="32-bit"; break;
|
|
1004
|
-
case 'x64': arch="64-bit"; break;
|
|
1005
|
-
case 'arm': arch="ARM"; break;
|
|
1006
|
-
default: arch=os.arch();
|
|
1007
|
-
}
|
|
1008
|
-
return [sysOS, arch, os.cpus()[0]?.model||''];
|
|
1009
|
-
}
|
|
1010
|
-
}
|
|
1011
|
-
|
|
1012
|
-
export default utils;
|
|
605
|
+
}
|
package/src/uuid.ts
CHANGED
|
@@ -3,16 +3,15 @@
|
|
|
3
3
|
import { Buffer } from 'buffer';
|
|
4
4
|
import utils from 'raiutils';
|
|
5
5
|
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
]
|
|
6
|
+
declare type os = typeof import('os');
|
|
7
|
+
declare type fs = typeof import('fs/promises');
|
|
8
|
+
interface Long {unsigned: boolean; toString(r: number): string}
|
|
9
|
+
|
|
10
|
+
const [os, fs, U, C] = await utils.importNode('os', 'fs/promises', 'util', 'crypto');
|
|
11
|
+
const cRand = utils.isNode ? U.promisify(C.randomBytes) : null;
|
|
11
12
|
|
|
12
13
|
let Long: any;
|
|
13
|
-
|
|
14
|
-
try {Long = (await import('mongodb')).Long} catch(e) {}
|
|
15
|
-
interface Long {unsigned: boolean; toString(r: number): string}
|
|
14
|
+
try {Long = (await utils.importNode('mongodb'))[0].Long} catch(e) {}
|
|
16
15
|
|
|
17
16
|
const ID_FN = import.meta.dirname+'/uuid';
|
|
18
17
|
let Cnt: number, CLT: number, LT: number,
|
|
@@ -21,7 +20,7 @@ LD: number, UT: NodeJS.Timeout | boolean;
|
|
|
21
20
|
//64-bit UUID Format
|
|
22
21
|
//<U8 Uptime><U8 Magic><U8 CryptoRand><U8 Counter><U32 Date>
|
|
23
22
|
|
|
24
|
-
|
|
23
|
+
const swapHex = (h: string) => h.match(/.{2}/g)!.reverse().join('');
|
|
25
24
|
|
|
26
25
|
async function loadId() {
|
|
27
26
|
if(!fs) return Cnt = utils.rand(0,255);
|
|
@@ -74,8 +73,8 @@ export default class UUID {
|
|
|
74
73
|
}
|
|
75
74
|
|
|
76
75
|
/** Async `crypto.randomBytes` with browser fallback */
|
|
77
|
-
static randBytes = async (size: number) => cRand ? cRand(size)
|
|
78
|
-
: crypto.getRandomValues(Buffer.allocUnsafe(size))
|
|
76
|
+
static randBytes = async (size: number) => (cRand ? cRand(size)
|
|
77
|
+
: crypto.getRandomValues(Buffer.allocUnsafe(size))) as Promise<Buffer>;
|
|
79
78
|
|
|
80
79
|
/** Generate new random UUID
|
|
81
80
|
@param date Optional Date or Unix ms timestamp; default is current time
|