raiutils 9.1.0 → 9.1.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +55 -3
- package/dist/build.d.ts +147 -0
- package/dist/build.d.ts.map +1 -0
- package/dist/build.js +1 -0
- package/dist/build.js.map +1 -0
- package/dist/router.js +1 -1
- package/dist/schema.js +1 -1
- package/dist/schema.js.map +1 -1
- package/dist/utils-dom.d.ts +4 -1
- package/dist/utils-dom.d.ts.map +1 -1
- package/dist/utils-dom.js +1 -1
- package/dist/utils-dom.js.map +1 -1
- package/dist/utils-node.d.ts +2 -0
- package/dist/utils-node.d.ts.map +1 -1
- package/dist/utils-node.js +1 -1
- package/dist/utils-node.js.map +1 -1
- package/dist/utils.d.ts +57 -33
- package/dist/utils.d.ts.map +1 -1
- package/dist/utils.js +1 -1
- package/dist/utils.js.map +1 -1
- package/dist/uuid.js +1 -1
- package/package.json +7 -2
- package/src/build.ts +280 -0
- package/src/utils-dom.ts +6 -0
- package/src/utils-node.ts +3 -0
- package/src/utils.ts +60 -62
package/src/build.ts
ADDED
|
@@ -0,0 +1,280 @@
|
|
|
1
|
+
//Auto Build v2.3, Pecacheu 2026. GNU GPL v3
|
|
2
|
+
|
|
3
|
+
import { spawn } from 'child_process';
|
|
4
|
+
import fs from 'fs/promises';
|
|
5
|
+
import path from 'path';
|
|
6
|
+
import mHTML from '@minify-html/node';
|
|
7
|
+
import type { HtmlFileConfiguration } from '@pecacheu/esbuild-plugin-html';
|
|
8
|
+
import C from 'chalk';
|
|
9
|
+
import type { BuildContext, BuildOptions, Plugin } from 'esbuild';
|
|
10
|
+
import { type MinifyOptions, minify as mJS } from 'terser';
|
|
11
|
+
|
|
12
|
+
let esbuild: typeof import('esbuild') | undefined,
|
|
13
|
+
pHTML: typeof import('@pecacheu/esbuild-plugin-html') | undefined;
|
|
14
|
+
|
|
15
|
+
try {esbuild = await import('esbuild')} catch(e) {}
|
|
16
|
+
if(esbuild) pHTML = await import('@pecacheu/esbuild-plugin-html');
|
|
17
|
+
|
|
18
|
+
const Mode = process.argv[2],
|
|
19
|
+
Watch = Mode === 'watch',
|
|
20
|
+
Dev = Watch || Mode === 'dev',
|
|
21
|
+
Meta = Mode === 'meta',
|
|
22
|
+
log = console.log,
|
|
23
|
+
UTF = {encoding: 'utf8' as BufferEncoding},
|
|
24
|
+
R_SC = /;\n?(\/\/#.+)?$/,
|
|
25
|
+
_hFD: HtmlFileConfiguration[] = [];
|
|
26
|
+
|
|
27
|
+
/** Default build options */
|
|
28
|
+
const defaults = {
|
|
29
|
+
//Paths
|
|
30
|
+
/** Entrypoint of app for esbuild, relative to `srcCli`.
|
|
31
|
+
You can also set `opts.esOpts.entryPoints` manually
|
|
32
|
+
*/
|
|
33
|
+
app: 'app.ts',
|
|
34
|
+
/** Source directory */
|
|
35
|
+
src: 'src',
|
|
36
|
+
/** Build output directory */
|
|
37
|
+
dist: 'dist',
|
|
38
|
+
/** Client source dir, relative to `src` */
|
|
39
|
+
srcCli: 'web',
|
|
40
|
+
/** Client build output, relative to `dist` */
|
|
41
|
+
distCli: 'web',
|
|
42
|
+
/** Server source dir, relative to `src`.
|
|
43
|
+
Set to `''` to disable server build */
|
|
44
|
+
srcSrv: 'srv',
|
|
45
|
+
|
|
46
|
+
//Hooks
|
|
47
|
+
//TODO Make them work in watch mode
|
|
48
|
+
/** Runs after build but before minify */
|
|
49
|
+
onPreMin: null as (() => Promise<void>) | null,
|
|
50
|
+
/** Runs after build completes
|
|
51
|
+
@param ctx Only defined if esbuild is installed */
|
|
52
|
+
onPostBuild: null as ((ctx?: BuildContext) => Promise<void>) | null,
|
|
53
|
+
|
|
54
|
+
//Build
|
|
55
|
+
/** JS extensions to minify */
|
|
56
|
+
jsMinExt: ['.js'],
|
|
57
|
+
/** HTML extensions to minify */
|
|
58
|
+
htmlMinExt: ['.html'],
|
|
59
|
+
/** Extensions to trim whitespace */
|
|
60
|
+
trimExt: ['.css', '.svg'],
|
|
61
|
+
/** Set to false to disable esbuild even if it is installed */
|
|
62
|
+
esbuild: true,
|
|
63
|
+
/** Esbuild HTML plugin options */
|
|
64
|
+
htmlLoadOpts: {
|
|
65
|
+
scriptLoading: 'module'
|
|
66
|
+
} as Omit<HtmlFileConfiguration, 'filename' | 'htmlFile' | 'htmlTemplate'>,
|
|
67
|
+
/** Esbuild options */
|
|
68
|
+
esOpts: {
|
|
69
|
+
bundle: true,
|
|
70
|
+
minify: !Dev,
|
|
71
|
+
sourcemap: Dev,
|
|
72
|
+
format: 'esm',
|
|
73
|
+
splitting: true,
|
|
74
|
+
metafile: Meta ? true : undefined,
|
|
75
|
+
loader: {
|
|
76
|
+
'.svg': 'file',
|
|
77
|
+
'.jpg': 'file',
|
|
78
|
+
'.woff': 'copy',
|
|
79
|
+
'.woff2': 'copy'
|
|
80
|
+
},
|
|
81
|
+
plugins: []
|
|
82
|
+
} as BuildOptions,
|
|
83
|
+
|
|
84
|
+
//Minify
|
|
85
|
+
/** Terser minify options */
|
|
86
|
+
jsMin: {
|
|
87
|
+
ecma: 2020,
|
|
88
|
+
module: true,
|
|
89
|
+
format: {inline_script: false, comments: false},
|
|
90
|
+
mangle: {properties: {regex: /^[_#]/}},
|
|
91
|
+
compress: {passes: 2, arguments: true, keep_fargs: false, keep_infinity: true, unsafe: true}
|
|
92
|
+
} as MinifyOptions,
|
|
93
|
+
/** HTML minify options */
|
|
94
|
+
htmlMin: {
|
|
95
|
+
allow_optimal_entities: true,
|
|
96
|
+
allow_removing_spaces_between_attributes: true,
|
|
97
|
+
minify_css: true,
|
|
98
|
+
minify_js: true
|
|
99
|
+
} as Parameters<typeof mHTML.minify>[1]
|
|
100
|
+
};
|
|
101
|
+
|
|
102
|
+
export type Options = typeof defaults;
|
|
103
|
+
let opts!: Options, _hPl: Plugin;
|
|
104
|
+
|
|
105
|
+
//==== Minify ====
|
|
106
|
+
|
|
107
|
+
async function minify(pin: string, _: any, fn: string) {
|
|
108
|
+
let fin = `${pin}/${fn}`, ext = path.extname(fn), f;
|
|
109
|
+
if(fin.indexOf('.min') !== -1) return;
|
|
110
|
+
const fls = fin.slice(opts.dist.length + 1);
|
|
111
|
+
try {
|
|
112
|
+
if(opts.jsMinExt.includes(ext)) { //Minify JS
|
|
113
|
+
const map = `${fin}.map`;
|
|
114
|
+
f = await fs.readFile(fin, UTF);
|
|
115
|
+
try {
|
|
116
|
+
await getSrc(map);
|
|
117
|
+
const out = await mJS(f, opts.jsMin);
|
|
118
|
+
f = out.code!.replace(R_SC, '$1');
|
|
119
|
+
if(out.map) {
|
|
120
|
+
await fs.writeFile(map, out.map as string);
|
|
121
|
+
log(C.cyan(`- ${fls}.map`));
|
|
122
|
+
}
|
|
123
|
+
} finally {delete opts.jsMin.sourceMap}
|
|
124
|
+
await fs.writeFile(fin, f);
|
|
125
|
+
log(C.cyan(`- ${fls}`));
|
|
126
|
+
} else if(opts.htmlMinExt.includes(ext)) { //Minify HTML
|
|
127
|
+
f = mHTML.minify(await fs.readFile(fin), opts.htmlMin);
|
|
128
|
+
await fs.writeFile(fin, f);
|
|
129
|
+
log(C.cyan(`- ${fls}`));
|
|
130
|
+
} else if(opts.trimExt.includes(ext)) { //Trim
|
|
131
|
+
f = (await fs.readFile(fin, UTF)).trim();
|
|
132
|
+
await fs.writeFile(fin, f);
|
|
133
|
+
log(C.magenta(`- ${fls}`));
|
|
134
|
+
} else { //Log
|
|
135
|
+
log(C.dim(`- ${fls}`));
|
|
136
|
+
}
|
|
137
|
+
} catch(e) {
|
|
138
|
+
log(C.red(`- ${fls}`));
|
|
139
|
+
throw e;
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
async function getSrc(map: string) {
|
|
144
|
+
try {
|
|
145
|
+
opts.jsMin.sourceMap = {
|
|
146
|
+
content: await fs.readFile(map, UTF),
|
|
147
|
+
url: path.basename(map)
|
|
148
|
+
};
|
|
149
|
+
} catch(e) {}
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
function addHTML(pin: string, _: any, fn: string) {
|
|
153
|
+
if(fn.endsWith('.html') && fn[0] !== '+') _hFD.push({
|
|
154
|
+
filename: fn, htmlFile: `${pin}/${fn}`, ...opts.htmlLoadOpts
|
|
155
|
+
});
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
/** Set overrides for build options */
|
|
159
|
+
function setOpts(o?: Partial<Options>) {
|
|
160
|
+
const oo = opts ? [opts.srcCli, opts.distCli, opts.srcSrv, opts.app] : [];
|
|
161
|
+
opts = o ? {...defaults, ...o} : defaults;
|
|
162
|
+
|
|
163
|
+
if(opts.srcCli !== oo[0]) opts.srcCli = path.join(opts.src, opts.srcCli);
|
|
164
|
+
if(opts.distCli !== oo[1]) opts.distCli = path.join(opts.dist, opts.distCli);
|
|
165
|
+
if(opts.srcSrv !== oo[2]) opts.srcSrv = opts.srcSrv ? path.join(opts.src, opts.srcSrv) : '';
|
|
166
|
+
if(opts.app !== oo[3]) opts.app = opts.app ? path.join(opts.srcCli, opts.app) : '';
|
|
167
|
+
|
|
168
|
+
if(opts.app) opts.esOpts.entryPoints = [opts.app];
|
|
169
|
+
opts.esOpts.target = `es${opts.jsMin.ecma}`;
|
|
170
|
+
opts.esOpts.outdir = opts.distCli;
|
|
171
|
+
};
|
|
172
|
+
|
|
173
|
+
/** Default build pipeline */
|
|
174
|
+
async function run() {
|
|
175
|
+
if(!opts) setOpts();
|
|
176
|
+
|
|
177
|
+
if(!Dev) {
|
|
178
|
+
log(C.bgYellow('Clean'));
|
|
179
|
+
await rm(opts.dist);
|
|
180
|
+
|
|
181
|
+
if(opts.srcSrv) {
|
|
182
|
+
log(C.bgYellow('Build Server'));
|
|
183
|
+
await exec(`npx tsc -p ${opts.srcSrv}`);
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
await mkdir(opts.srcCli);
|
|
188
|
+
|
|
189
|
+
log(C.bgYellow('Build'));
|
|
190
|
+
if(esbuild && opts.esbuild) {
|
|
191
|
+
await recurse(addHTML, opts.srcCli);
|
|
192
|
+
if(opts.esOpts.plugins) opts.esOpts.plugins = [];
|
|
193
|
+
else if(_hPl) opts.esOpts.plugins!.remove(_hPl);
|
|
194
|
+
_hPl = pHTML!.htmlPlugin({files: _hFD});
|
|
195
|
+
opts.esOpts.plugins!.push(_hPl);
|
|
196
|
+
const ctx = await esbuild.context(opts.esOpts);
|
|
197
|
+
|
|
198
|
+
if(Watch) {
|
|
199
|
+
await ctx.watch();
|
|
200
|
+
log('Watching for changes...');
|
|
201
|
+
} else {
|
|
202
|
+
const build = await ctx.rebuild();
|
|
203
|
+
if(Meta) await fs.writeFile('meta.json', JSON.stringify(build.metafile));
|
|
204
|
+
_minify();
|
|
205
|
+
await ctx.dispose();
|
|
206
|
+
|
|
207
|
+
await opts.onPostBuild?.(ctx);
|
|
208
|
+
log(C.green('Done!'));
|
|
209
|
+
}
|
|
210
|
+
} else {
|
|
211
|
+
await exec('npx tsc');
|
|
212
|
+
_minify();
|
|
213
|
+
|
|
214
|
+
await opts.onPostBuild?.();
|
|
215
|
+
log(C.green('Done!'));
|
|
216
|
+
}
|
|
217
|
+
};
|
|
218
|
+
|
|
219
|
+
//==== Support ====
|
|
220
|
+
|
|
221
|
+
const _minify = async () => {
|
|
222
|
+
await opts.onPreMin?.();
|
|
223
|
+
|
|
224
|
+
if(!Dev) {
|
|
225
|
+
log(C.bgYellow('Minify'));
|
|
226
|
+
await recurse(minify, opts.dist);
|
|
227
|
+
}
|
|
228
|
+
};
|
|
229
|
+
|
|
230
|
+
/** Recursively create the directory, if it doesn't exist */
|
|
231
|
+
async function mkdir(path: string) {
|
|
232
|
+
try {await fs.mkdir(path, {recursive: true})} catch(e) {
|
|
233
|
+
if((e as any).code !== 'EEXIST') throw e;
|
|
234
|
+
}
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
/** Recursively remove the file or directory, if it exists */
|
|
238
|
+
async function rm(path: string) {
|
|
239
|
+
try {await fs.rm(path, {recursive: true})} catch(e) {
|
|
240
|
+
if((e as any).code !== 'ENOENT') throw e;
|
|
241
|
+
}
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
/** Run the shell command, mirroring output to console */
|
|
245
|
+
function exec(cmd: string) {
|
|
246
|
+
return new Promise((res, rej) => {
|
|
247
|
+
const p = spawn(cmd, {shell: true, stdio: 'inherit'});
|
|
248
|
+
p.on('exit', c => c ? rej(`Exit Code ${c}`) : res());
|
|
249
|
+
p.on('error', rej);
|
|
250
|
+
}) as Promise<void>;
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
/** Recurse over all files in a directory */
|
|
254
|
+
async function recurse(func: (pin: string, pout: string | null, filename: string)
|
|
255
|
+
=> void, pin: string, pout?: string | null) {
|
|
256
|
+
const pl = [], d = await fs.readdir(pin, {withFileTypes: true});
|
|
257
|
+
if(pout) await mkdir(pout);
|
|
258
|
+
for(const f of d) {
|
|
259
|
+
if(f.isFile()) pl.push(func(pin, pout!, f.name));
|
|
260
|
+
else if(f.isDirectory()) pl.push(recurse(func,
|
|
261
|
+
path.join(pin, f.name), pout && path.join(pout, f.name)));
|
|
262
|
+
}
|
|
263
|
+
await Promise.all(pl);
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
export default {
|
|
267
|
+
/** Default build options */
|
|
268
|
+
defaults,
|
|
269
|
+
/** Current options (read-only) */
|
|
270
|
+
get opts() {return opts},
|
|
271
|
+
setOpts,
|
|
272
|
+
run,
|
|
273
|
+
mkdir,
|
|
274
|
+
rm,
|
|
275
|
+
exec,
|
|
276
|
+
recurse,
|
|
277
|
+
Watch,
|
|
278
|
+
Dev,
|
|
279
|
+
Meta
|
|
280
|
+
};
|
package/src/utils-dom.ts
CHANGED
|
@@ -390,7 +390,13 @@ export async function download(data: string | URL | Blob | ArrayBuffer, name?: s
|
|
|
390
390
|
URL.revokeObjectURL(u);
|
|
391
391
|
}
|
|
392
392
|
}
|
|
393
|
+
|
|
394
|
+
/** Import modules only in Node.js, otherwise return empty list */
|
|
395
|
+
export const importNode = async (..._: string[]) => [] as any[];
|
|
393
396
|
}
|
|
394
397
|
|
|
398
|
+
//Export types
|
|
399
|
+
export type UtilRect = InstanceType<typeof ext.UtilRect>;
|
|
400
|
+
|
|
395
401
|
export const utils = <typeof U & typeof ext>U;
|
|
396
402
|
export default utils;
|
package/src/utils-node.ts
CHANGED
|
@@ -5,6 +5,9 @@ import { utils as U } from './utils.js';
|
|
|
5
5
|
export type * from './utils.js';
|
|
6
6
|
|
|
7
7
|
namespace ext {
|
|
8
|
+
/** Import modules only in Node.js, otherwise return empty list */
|
|
9
|
+
export const importNode = async (...mods: string[]) => Promise.all(mods.map(i => import(i)));
|
|
10
|
+
|
|
8
11
|
/** Get list of system IPs */
|
|
9
12
|
export function getIPs() {
|
|
10
13
|
const ip: string[]=[], fl=os.networkInterfaces();
|
package/src/utils.ts
CHANGED
|
@@ -68,6 +68,36 @@ export interface Uint8Array {
|
|
|
68
68
|
export interface AnyMap {[k: string]: any};
|
|
69
69
|
export interface StringMap {[k: string]: string};
|
|
70
70
|
export interface QueryMap {[k: string]: true | string | string[]};
|
|
71
|
+
export type Ease = (t: number) => number;
|
|
72
|
+
|
|
73
|
+
export interface UserAgentInfo {
|
|
74
|
+
os?: string;
|
|
75
|
+
rawOS?: string;
|
|
76
|
+
type?: string;
|
|
77
|
+
version?: string;
|
|
78
|
+
browser?: string;
|
|
79
|
+
engine?: string;
|
|
80
|
+
mobile?: boolean;
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
export interface DateFormatOpts {
|
|
84
|
+
/** Include seconds */
|
|
85
|
+
sec?: boolean;
|
|
86
|
+
/** True or `3` to include milliseconds (requires sec), `2` or `1` to limit precision */
|
|
87
|
+
ms?: boolean | number;
|
|
88
|
+
/** Use 24-hour time */
|
|
89
|
+
h24?: boolean;
|
|
90
|
+
/** Show time only, false to show date only, null to show both */
|
|
91
|
+
time?: boolean;
|
|
92
|
+
/** Add date suffix (1st, 2nd, etc.), default true */
|
|
93
|
+
suf?: boolean;
|
|
94
|
+
/** Show year (default true), or a number to show year only if it differs from given year */
|
|
95
|
+
year?: boolean | number;
|
|
96
|
+
/** Put date first instead of time */
|
|
97
|
+
df?: boolean;
|
|
98
|
+
/** Date & time separator; defaults to `' '` */
|
|
99
|
+
sep?: string;
|
|
100
|
+
}
|
|
71
101
|
|
|
72
102
|
//-------------------------------------------- Extensions --------------------------------------------
|
|
73
103
|
|
|
@@ -75,14 +105,11 @@ export namespace utils {
|
|
|
75
105
|
const [document, HTMLCollection] = P;
|
|
76
106
|
|
|
77
107
|
/** Current library version */
|
|
78
|
-
export const VER = "v9.1.
|
|
108
|
+
export const VER = "v9.1.1";
|
|
79
109
|
|
|
80
110
|
/** Whether the environment is Node.js or Browser */
|
|
81
111
|
export const isNode = IsNode;
|
|
82
112
|
|
|
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
|
-
|
|
86
113
|
//==== Objects ====
|
|
87
114
|
|
|
88
115
|
/** Add getter and/or setter for `name` to `obj` */
|
|
@@ -300,35 +327,35 @@ export function formatCost(num: number, sym='$') {
|
|
|
300
327
|
|
|
301
328
|
//JavaScript Easing Library by: https://github.com/gre & https://gizma.com/easing
|
|
302
329
|
//t should be between 0 and 1
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
}
|
|
330
|
+
const _ease = {
|
|
331
|
+
/** No easing, no acceleration */
|
|
332
|
+
linear:(t: number) => t,
|
|
333
|
+
/** Accelerating from zero velocity */
|
|
334
|
+
easeInQuad:(t: number) => t*t,
|
|
335
|
+
/** Decelerating to zero velocity */
|
|
336
|
+
easeOutQuad:(t: number) => t*(2-t),
|
|
337
|
+
/** Acceleration until halfway, then deceleration */
|
|
338
|
+
easeInOutQuad:(t: number) => t<.5 ? 2*t*t : -1+(4-2*t)*t,
|
|
339
|
+
/** Accelerating from zero velocity */
|
|
340
|
+
easeInCubic:(t: number) => t*t*t,
|
|
341
|
+
/** Decelerating to zero velocity */
|
|
342
|
+
easeOutCubic:(t: number) => (--t)*t*t+1,
|
|
343
|
+
/** Acceleration until halfway, then deceleration */
|
|
344
|
+
easeInOutCubic:(t: number) => t<.5 ? 4*t*t*t : (t-1)*(2*t-2)*(2*t-2)+1,
|
|
345
|
+
/** Accelerating from zero velocity */
|
|
346
|
+
easeInQuart:(t: number) => t*t*t*t,
|
|
347
|
+
/** Decelerating to zero velocity */
|
|
348
|
+
easeOutQuart:(t: number) => 1-(--t)*t*t*t,
|
|
349
|
+
/** Acceleration until halfway, then deceleration */
|
|
350
|
+
easeInOutQuart:(t: number) => t<.5 ? 8*t*t*t*t : 1-8*(--t)*t*t*t,
|
|
351
|
+
/** Accelerating from zero velocity */
|
|
352
|
+
easeInQuint:(t: number) => t*t*t*t*t,
|
|
353
|
+
/** Decelerating to zero velocity */
|
|
354
|
+
easeOutQuint:(t: number) => 1+(--t)*t*t*t*t,
|
|
355
|
+
/** Acceleration until halfway, then deceleration */
|
|
356
|
+
easeInOutQuint:(t: number) => t<.5 ? 16*t*t*t*t*t : 1+16*(--t)*t*t*t*t
|
|
357
|
+
};
|
|
358
|
+
export const Easing = _ease as {[k in keyof typeof _ease]: Ease};
|
|
332
359
|
|
|
333
360
|
//==== Polyfills ====
|
|
334
361
|
|
|
@@ -419,16 +446,6 @@ export const delay = (ms: number): Promise<void> => new Promise(r => setTimeout(
|
|
|
419
446
|
|
|
420
447
|
//-------------------------------------------- DOM Model --------------------------------------------
|
|
421
448
|
|
|
422
|
-
export interface UserAgentInfo {
|
|
423
|
-
os?: string;
|
|
424
|
-
rawOS?: string;
|
|
425
|
-
type?: string;
|
|
426
|
-
version?: string;
|
|
427
|
-
browser?: string;
|
|
428
|
-
engine?: string;
|
|
429
|
-
mobile?: boolean;
|
|
430
|
-
}
|
|
431
|
-
|
|
432
449
|
/** UserAgent-based Mobile device detection
|
|
433
450
|
@param ua User Agent string; defaults to navigator.userAgent */
|
|
434
451
|
export function deviceInfo(ua?: string) {
|
|
@@ -559,25 +576,6 @@ export function toQuery(data: QueryMap, sep='&') {
|
|
|
559
576
|
export const months = ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"];
|
|
560
577
|
function fixed2(n: number) {return n<=9?'0'+n:n}
|
|
561
578
|
|
|
562
|
-
export interface DateFormatOpts {
|
|
563
|
-
/** Include seconds */
|
|
564
|
-
sec?: boolean;
|
|
565
|
-
/** True or `3` to include milliseconds (requires sec), `2` or `1` to limit precision */
|
|
566
|
-
ms?: boolean | number;
|
|
567
|
-
/** Use 24-hour time */
|
|
568
|
-
h24?: boolean;
|
|
569
|
-
/** Show time only, false to show date only, null to show both */
|
|
570
|
-
time?: boolean;
|
|
571
|
-
/** Add date suffix (1st, 2nd, etc.), default true */
|
|
572
|
-
suf?: boolean;
|
|
573
|
-
/** Show year (default true), or a number to show year only if it differs from given year */
|
|
574
|
-
year?: boolean | number;
|
|
575
|
-
/** Put date first instead of time */
|
|
576
|
-
df?: boolean;
|
|
577
|
-
/** Date & time separator; defaults to `' '` */
|
|
578
|
-
sep?: string;
|
|
579
|
-
}
|
|
580
|
-
|
|
581
579
|
/** Format Date object into human-readable string */
|
|
582
580
|
export function formatDate(d?: Date, opt: DateFormatOpts={}) {
|
|
583
581
|
let t='', yy: number, dd: any;
|