@codady/utils 0.0.39 → 0.0.40
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 +21 -2
- package/dist/utils.cjs.js +110 -7
- package/dist/utils.cjs.min.js +3 -3
- package/dist/utils.esm.js +110 -7
- package/dist/utils.esm.min.js +3 -3
- package/dist/utils.umd.js +110 -7
- package/dist/utils.umd.min.js +3 -3
- package/dist.zip +0 -0
- package/examples/ajax-download.html +94 -0
- package/examples/ajax-hook.html +2 -2
- package/examples/ajax-signal.html +91 -0
- package/examples/ajax-timeout.html +85 -0
- package/examples/stringToEncodings-collision-test-registry.html +117 -0
- package/examples/stringToEncodings-collision-test.html +71 -0
- package/examples/stringToEncodings.html +138 -0
- package/examples/unicodeToEncodings.html +195 -0
- package/modules.js +5 -1
- package/modules.ts +5 -1
- package/package.json +1 -1
- package/src/ajax.js +23 -6
- package/src/ajax.ts +30 -10
- package/src/stringToEncodings.js +56 -0
- package/src/stringToEncodings.ts +110 -0
- package/src/unicodeToEncodings.js +51 -0
- package/src/unicodeToEncodings.ts +55 -0
- package/src/arrayMutableMethods - /345/211/257/346/234/254.js" +0 -5
- package/src/capitalize - /345/211/257/346/234/254.js" +0 -19
- package/src/comma - /345/211/257/346/234/254.js" +0 -2
- package/src/deepCloneToJSON - /345/211/257/346/234/254.js" +0 -47
- package/src/deepMergeMaps - /345/211/257/346/234/254.js" +0 -78
- package/src/escapeHTML - /345/211/257/346/234/254.js" +0 -29
- package/src/getDataType - /345/211/257/346/234/254.js" +0 -38
- package/src/isEmpty - /345/211/257/346/234/254.js" +0 -45
- package/src/mapMutableMethods - /345/211/257/346/234/254.js" +0 -5
- package/src/setMutableMethods - /345/211/257/346/234/254.js" +0 -5
- package/src/wrapMap - /345/211/257/346/234/254.js" +0 -119
package/modules.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Last modified: 2026/
|
|
2
|
+
* Last modified: 2026/02/05 17:00:27
|
|
3
3
|
*/
|
|
4
4
|
'use strict';
|
|
5
5
|
import deepClone from './src/deepClone';
|
|
@@ -47,6 +47,8 @@ import buildUrl from './src/buildUrl';
|
|
|
47
47
|
import ajax from './src/ajax';
|
|
48
48
|
import capitalize from './src/capitalize';
|
|
49
49
|
import cleanQueryString from './src/cleanQueryString';
|
|
50
|
+
import stringToEncodings from './src/stringToEncodings';
|
|
51
|
+
import unicodeToEncodings from './src/unicodeToEncodings';
|
|
50
52
|
const utils = {
|
|
51
53
|
//executeStr,
|
|
52
54
|
getDataType,
|
|
@@ -96,5 +98,7 @@ const utils = {
|
|
|
96
98
|
ajax,
|
|
97
99
|
capitalize,
|
|
98
100
|
cleanQueryString,
|
|
101
|
+
stringToEncodings,
|
|
102
|
+
unicodeToEncodings,
|
|
99
103
|
};
|
|
100
104
|
export default utils;
|
package/modules.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Last modified: 2026/
|
|
2
|
+
* Last modified: 2026/02/05 17:00:27
|
|
3
3
|
*/
|
|
4
4
|
'use strict'
|
|
5
5
|
import deepClone from './src/deepClone';
|
|
@@ -54,6 +54,8 @@ import buildUrl from './src/buildUrl';
|
|
|
54
54
|
import ajax from './src/ajax';
|
|
55
55
|
import capitalize from './src/capitalize';
|
|
56
56
|
import cleanQueryString from './src/cleanQueryString';
|
|
57
|
+
import stringToEncodings from './src/stringToEncodings';
|
|
58
|
+
import unicodeToEncodings from './src/unicodeToEncodings';
|
|
57
59
|
|
|
58
60
|
const utils = {
|
|
59
61
|
//executeStr,
|
|
@@ -104,6 +106,8 @@ const utils = {
|
|
|
104
106
|
ajax,
|
|
105
107
|
capitalize,
|
|
106
108
|
cleanQueryString,
|
|
109
|
+
stringToEncodings,
|
|
110
|
+
unicodeToEncodings,
|
|
107
111
|
|
|
108
112
|
};
|
|
109
113
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@codady/utils",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.40",
|
|
4
4
|
"author": "AXUI Development Team",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"description": "This is a set of general-purpose JavaScript utility functions developed by the AXUI team. All functions are pure and do not involve CSS or other third-party libraries. They are suitable for any web front-end environment.",
|
package/src/ajax.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* @since Last modified: 2026/01/20
|
|
2
|
+
* @since Last modified: 2026/01/20 18:14:18
|
|
3
3
|
* Sends an asynchronous HTTP request (AJAX).
|
|
4
4
|
* @function ajax
|
|
5
5
|
* @param {AjaxOptions} options - Configuration for the request.
|
|
@@ -37,6 +37,7 @@ const ajax = (options) => {
|
|
|
37
37
|
signal: null,
|
|
38
38
|
xhrFields: {},
|
|
39
39
|
cacheBustKey: '_t',
|
|
40
|
+
precision: 2,
|
|
40
41
|
//
|
|
41
42
|
onAbort: null,
|
|
42
43
|
onTimeout: null,
|
|
@@ -143,6 +144,10 @@ const ajax = (options) => {
|
|
|
143
144
|
config?.onTimeout?.(resp);
|
|
144
145
|
//reject只能接受一个参数
|
|
145
146
|
config.catchError ? reject(resp) : resolve(resp);
|
|
147
|
+
//超时也是不能获得数据的行为,定义为failure
|
|
148
|
+
config?.onFailure?.(resp);
|
|
149
|
+
//timeout会经过onreadystatechange,但是被及时的return了,所以这里多加一行
|
|
150
|
+
config?.onFinish?.(resp);
|
|
146
151
|
},
|
|
147
152
|
//报错监听
|
|
148
153
|
errorHandler = (resp) => {
|
|
@@ -168,6 +173,8 @@ const ajax = (options) => {
|
|
|
168
173
|
config.catchError ? reject(resp) : resolve(resp);
|
|
169
174
|
//回调,status和content在此确认
|
|
170
175
|
config?.onAbort?.(resp);
|
|
176
|
+
//abort行为不会经过onreadystatechange,这里需要多这一行以表示xhr的完成(结束)
|
|
177
|
+
config?.onFinish?.(resp);
|
|
171
178
|
}, abortHandlerWithSignal = () => {
|
|
172
179
|
//先中止请求,防止触发其他 readystate 事件
|
|
173
180
|
xhr.abort();
|
|
@@ -211,23 +218,26 @@ const ajax = (options) => {
|
|
|
211
218
|
type: 'unset',
|
|
212
219
|
//上传和下载进度
|
|
213
220
|
progress: {}
|
|
221
|
+
}, getProgressValues = (ratio) => {
|
|
222
|
+
let text = (ratio * 100).toFixed(config.precision);
|
|
223
|
+
return { percent: parseFloat(text), text };
|
|
214
224
|
},
|
|
215
225
|
//定义进度函数
|
|
216
226
|
progressHandler = (name, data, callback) => {
|
|
217
227
|
if (data.lengthComputable) {
|
|
218
|
-
const resp = { ...context, status: xhr.status }, ratio = data.loaded / data.total;
|
|
228
|
+
const resp = { ...context, status: xhr.status }, ratio = data.loaded / data.total, { percent, text } = getProgressValues(ratio);
|
|
219
229
|
resp.progress = {
|
|
220
230
|
name,
|
|
221
231
|
loaded: data.loaded,
|
|
222
232
|
total: data.total,
|
|
223
233
|
timestamp: (new Date(data.timeStamp)).getTime(),
|
|
224
234
|
ratio,
|
|
225
|
-
percent
|
|
235
|
+
percent,
|
|
236
|
+
text,
|
|
226
237
|
};
|
|
227
238
|
callback?.(resp);
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
resp.progress.percent = 100;
|
|
239
|
+
if (ratio >= 1) {
|
|
240
|
+
Object.assign(resp.progress, getProgressValues(1));
|
|
231
241
|
config?.onComplete?.(resp);
|
|
232
242
|
}
|
|
233
243
|
}
|
|
@@ -265,8 +275,15 @@ const ajax = (options) => {
|
|
|
265
275
|
config[`on${capitalize(context.type)}`]?.({ ...context });
|
|
266
276
|
return;
|
|
267
277
|
}
|
|
278
|
+
//tiemeout事件也会执行这里,此时需要让它触发onTimeout事件
|
|
279
|
+
//abort和timeout行为的status是0
|
|
280
|
+
//不过abort行为不会执行到这里
|
|
281
|
+
if (xhr.status === 0 && context.type !== 'abort') {
|
|
282
|
+
return;
|
|
283
|
+
}
|
|
268
284
|
//已经请求成功,不会有timeout事件,也不需要abort了,所以移除abort事件
|
|
269
285
|
cleanup();
|
|
286
|
+
//根据状态码判断响应结果
|
|
270
287
|
const isInformation = xhr.status >= 100 && xhr.status < 200, isSuccess = (xhr.status >= 200 && xhr.status < 300) || xhr.status === 304, isRedirection = xhr.status >= 300 && xhr.status < 400, isClientError = xhr.status >= 400 && xhr.status < 500, isServerError = xhr.status >= 500 && xhr.status < 600;
|
|
271
288
|
//已经获得返回数据
|
|
272
289
|
if (isSuccess) {
|
package/src/ajax.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* @since Last modified: 2026/01/20
|
|
2
|
+
* @since Last modified: 2026/01/20 18:14:18
|
|
3
3
|
* Sends an asynchronous HTTP request (AJAX).
|
|
4
4
|
* @function ajax
|
|
5
5
|
* @param {AjaxOptions} options - Configuration for the request.
|
|
@@ -34,6 +34,7 @@ interface AjaxOptions {
|
|
|
34
34
|
signal?: AbortSignal; // AbortSignal for canceling the request
|
|
35
35
|
xhrFields?: Record<string, any>; // Additional fields to set on the XHR object
|
|
36
36
|
cacheBustKey?: string,
|
|
37
|
+
precision?: number,
|
|
37
38
|
// Callbacks
|
|
38
39
|
onAbort?: ((resp: AjaxResponse) => void) | null;
|
|
39
40
|
onTimeout?: ((resp: AjaxResponse) => void) | null;
|
|
@@ -74,6 +75,7 @@ interface AjaxResponse {
|
|
|
74
75
|
timestamp?: number;
|
|
75
76
|
ratio?: number;
|
|
76
77
|
percent?: number;
|
|
78
|
+
text?: string;
|
|
77
79
|
};
|
|
78
80
|
}
|
|
79
81
|
|
|
@@ -101,6 +103,7 @@ const ajax = (options: AjaxOptions) => {
|
|
|
101
103
|
signal: null as any,
|
|
102
104
|
xhrFields: {},
|
|
103
105
|
cacheBustKey: '_t',
|
|
106
|
+
precision: 2,
|
|
104
107
|
//
|
|
105
108
|
onAbort: null,
|
|
106
109
|
onTimeout: null,
|
|
@@ -212,6 +215,10 @@ const ajax = (options: AjaxOptions) => {
|
|
|
212
215
|
config?.onTimeout?.(resp);
|
|
213
216
|
//reject只能接受一个参数
|
|
214
217
|
config.catchError ? reject(resp) : resolve(resp);
|
|
218
|
+
//超时也是不能获得数据的行为,定义为failure
|
|
219
|
+
config?.onFailure?.(resp);
|
|
220
|
+
//timeout会经过onreadystatechange,但是被及时的return了,所以这里多加一行
|
|
221
|
+
config?.onFinish?.(resp);
|
|
215
222
|
},
|
|
216
223
|
//报错监听
|
|
217
224
|
errorHandler = (resp: any) => {
|
|
@@ -236,6 +243,8 @@ const ajax = (options: AjaxOptions) => {
|
|
|
236
243
|
config.catchError ? reject(resp) : resolve(resp);
|
|
237
244
|
//回调,status和content在此确认
|
|
238
245
|
config?.onAbort?.(resp);
|
|
246
|
+
//abort行为不会经过onreadystatechange,这里需要多这一行以表示xhr的完成(结束)
|
|
247
|
+
config?.onFinish?.(resp);
|
|
239
248
|
},
|
|
240
249
|
abortHandlerWithSignal = () => {
|
|
241
250
|
//先中止请求,防止触发其他 readystate 事件
|
|
@@ -281,25 +290,31 @@ const ajax = (options: AjaxOptions) => {
|
|
|
281
290
|
//上传和下载进度
|
|
282
291
|
progress: {}
|
|
283
292
|
},
|
|
293
|
+
getProgressValues = (ratio: number) => {
|
|
294
|
+
let text = (ratio * 100).toFixed(config.precision);
|
|
295
|
+
return { percent: parseFloat(text), text }
|
|
296
|
+
},
|
|
284
297
|
//定义进度函数
|
|
285
298
|
progressHandler = (name: 'upload' | 'download', data: any, callback: Function) => {
|
|
286
299
|
if (data.lengthComputable) {
|
|
287
300
|
const resp = { ...context, status: xhr.status },
|
|
288
|
-
ratio = data.loaded / data.total
|
|
301
|
+
ratio = data.loaded / data.total,
|
|
302
|
+
{ percent, text } = getProgressValues(ratio);
|
|
289
303
|
resp.progress = {
|
|
290
304
|
name,
|
|
291
305
|
loaded: data.loaded,
|
|
292
306
|
total: data.total,
|
|
293
307
|
timestamp: (new Date(data.timeStamp)).getTime(),
|
|
294
308
|
ratio,
|
|
295
|
-
percent
|
|
309
|
+
percent,
|
|
310
|
+
text,
|
|
296
311
|
}
|
|
297
312
|
callback?.(resp);
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
resp.progress.percent = 100;
|
|
313
|
+
if (ratio >= 1) {
|
|
314
|
+
Object.assign(resp.progress, getProgressValues(1));
|
|
301
315
|
config?.onComplete?.(resp);
|
|
302
316
|
}
|
|
317
|
+
|
|
303
318
|
}
|
|
304
319
|
}, uploadProgressHandler = (data: any) => {
|
|
305
320
|
progressHandler('upload', data, (resp: any) => (config.onUpload as Function)(resp));
|
|
@@ -329,8 +344,6 @@ const ajax = (options: AjaxOptions) => {
|
|
|
329
344
|
// 手动触发 Created 状态
|
|
330
345
|
config.onCreated?.({ ...context, type: 'created' });
|
|
331
346
|
|
|
332
|
-
|
|
333
|
-
|
|
334
347
|
//状态判断
|
|
335
348
|
xhr.onreadystatechange = function () {
|
|
336
349
|
context.stage = xhr.readyState;
|
|
@@ -343,16 +356,24 @@ const ajax = (options: AjaxOptions) => {
|
|
|
343
356
|
(config as any)[`on${capitalize(context.type)}`]?.({ ...context });
|
|
344
357
|
return;
|
|
345
358
|
}
|
|
359
|
+
|
|
360
|
+
//tiemeout事件也会执行这里,此时需要让它触发onTimeout事件
|
|
361
|
+
//abort和timeout行为的status是0
|
|
362
|
+
//不过abort行为不会执行到这里
|
|
363
|
+
if (xhr.status === 0 && context.type !== 'abort') {
|
|
364
|
+
return;
|
|
365
|
+
}
|
|
366
|
+
|
|
346
367
|
//已经请求成功,不会有timeout事件,也不需要abort了,所以移除abort事件
|
|
347
368
|
cleanup();
|
|
348
369
|
|
|
370
|
+
//根据状态码判断响应结果
|
|
349
371
|
const isInformation = xhr.status >= 100 && xhr.status < 200,
|
|
350
372
|
isSuccess = (xhr.status >= 200 && xhr.status < 300) || xhr.status === 304,
|
|
351
373
|
isRedirection = xhr.status >= 300 && xhr.status < 400,
|
|
352
374
|
isClientError = xhr.status >= 400 && xhr.status < 500,
|
|
353
375
|
isServerError = xhr.status >= 500 && xhr.status < 600;
|
|
354
376
|
|
|
355
|
-
|
|
356
377
|
//已经获得返回数据
|
|
357
378
|
if (isSuccess) {
|
|
358
379
|
if (!config.responseType || xhr.responseType === 'text') {
|
|
@@ -446,5 +467,4 @@ const ajax = (options: AjaxOptions) => {
|
|
|
446
467
|
|
|
447
468
|
ajax.all = (requests: AjaxOptions[]): Promise<AjaxResponse[]> => Promise.all(requests.map(ajax) as any);
|
|
448
469
|
|
|
449
|
-
|
|
450
470
|
export default ajax;
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
const stringToEncodings = (name, options = {}) => {
|
|
2
|
+
// Default: Supplementary Private Use Area (Plane 15 and Plane 16)
|
|
3
|
+
//1,114,110 places,5000 strings => 0 collision
|
|
4
|
+
const start = options.start ?? 0xF0000, end = options.end ?? 0x10FFFD, range = BigInt(end - start + 1), registry = options.registryMap,
|
|
5
|
+
/** Format result */
|
|
6
|
+
formatResult = (name, codePoint, hash, collision) => {
|
|
7
|
+
const hex = codePoint.toString(16).toUpperCase();
|
|
8
|
+
return {
|
|
9
|
+
name,
|
|
10
|
+
unicode: `U+${hex}`,
|
|
11
|
+
htmlDec: `&#${codePoint};`,
|
|
12
|
+
htmlHex: `&#x${hex};`,
|
|
13
|
+
hex,
|
|
14
|
+
codePoint,
|
|
15
|
+
hash,
|
|
16
|
+
collision,
|
|
17
|
+
};
|
|
18
|
+
};
|
|
19
|
+
// -----------------------------
|
|
20
|
+
// 1. Compute FNV-1a 64-bit hash
|
|
21
|
+
// -----------------------------
|
|
22
|
+
let hash = BigInt("0xcbf29ce484222325");
|
|
23
|
+
const prime = BigInt("0x100000001b3");
|
|
24
|
+
for (const ch of name) {
|
|
25
|
+
hash ^= BigInt(ch.codePointAt(0));
|
|
26
|
+
hash *= prime;
|
|
27
|
+
}
|
|
28
|
+
const hashHex = hash.toString(16).toUpperCase();
|
|
29
|
+
// -----------------------------
|
|
30
|
+
// 2. Stateless mode (no registry)
|
|
31
|
+
// -----------------------------
|
|
32
|
+
if (!registry) {
|
|
33
|
+
const offset = Number(hash % range), codePoint = start + offset;
|
|
34
|
+
return formatResult(name, codePoint, hashHex, false);
|
|
35
|
+
}
|
|
36
|
+
// -----------------------------
|
|
37
|
+
// 3. Registry mode (0 collision)
|
|
38
|
+
// -----------------------------
|
|
39
|
+
// Already assigned → return stable mapping
|
|
40
|
+
if (registry.has(name)) {
|
|
41
|
+
return formatResult(name, registry.get(name), hashHex, false);
|
|
42
|
+
}
|
|
43
|
+
// Initial candidate from hash
|
|
44
|
+
let offset = Number(hash % range), codePoint = start + offset, collision = false;
|
|
45
|
+
const used = new Set(registry.values());
|
|
46
|
+
// Linear probing to resolve collisions
|
|
47
|
+
while (used.has(codePoint)) {
|
|
48
|
+
collision = true;
|
|
49
|
+
offset = (offset + 1) % Number(range);
|
|
50
|
+
codePoint = start + offset;
|
|
51
|
+
}
|
|
52
|
+
// Commit allocation
|
|
53
|
+
registry.set(name, codePoint);
|
|
54
|
+
return formatResult(name, codePoint, hashHex, collision);
|
|
55
|
+
};
|
|
56
|
+
export default stringToEncodings;
|
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @since Last modified: 2026/02/05 15:12:33
|
|
3
|
+
* Generate a stable Unicode code point from a string.
|
|
4
|
+
* - Without registry: FNV-1a 64-bit hash mapping (fast, stateless)
|
|
5
|
+
* - With registry: Guaranteed zero-collision allocation
|
|
6
|
+
*/
|
|
7
|
+
export type StringEncodingResult = {
|
|
8
|
+
unicode: string;
|
|
9
|
+
htmlDec: string;
|
|
10
|
+
htmlHex: string;
|
|
11
|
+
hex: string;
|
|
12
|
+
codePoint: number;
|
|
13
|
+
hash: string;
|
|
14
|
+
name: string;
|
|
15
|
+
collision?: boolean;
|
|
16
|
+
};
|
|
17
|
+
|
|
18
|
+
export type StringEncodingOptions = {
|
|
19
|
+
start?: number;
|
|
20
|
+
end?: number;
|
|
21
|
+
registryMap?: Map<string, number>; // optional
|
|
22
|
+
};
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
const stringToEncodings = (
|
|
26
|
+
name: string,
|
|
27
|
+
options: StringEncodingOptions = {}
|
|
28
|
+
): StringEncodingResult => {
|
|
29
|
+
|
|
30
|
+
// Default: Supplementary Private Use Area (Plane 15 and Plane 16)
|
|
31
|
+
//1,114,110 places,5000 strings => 0 collision
|
|
32
|
+
const start = options.start ?? 0xF0000,
|
|
33
|
+
end = options.end ?? 0x10FFFD,
|
|
34
|
+
range = BigInt(end - start + 1),
|
|
35
|
+
registry = options.registryMap,
|
|
36
|
+
/** Format result */
|
|
37
|
+
formatResult = (
|
|
38
|
+
name: string,
|
|
39
|
+
codePoint: number,
|
|
40
|
+
hash: string,
|
|
41
|
+
collision: boolean
|
|
42
|
+
): StringEncodingResult => {
|
|
43
|
+
const hex = codePoint.toString(16).toUpperCase();
|
|
44
|
+
|
|
45
|
+
return {
|
|
46
|
+
name,
|
|
47
|
+
unicode: `U+${hex}`,
|
|
48
|
+
htmlDec: `&#${codePoint};`,
|
|
49
|
+
htmlHex: `&#x${hex};`,
|
|
50
|
+
hex,
|
|
51
|
+
codePoint,
|
|
52
|
+
hash,
|
|
53
|
+
collision,
|
|
54
|
+
};
|
|
55
|
+
};
|
|
56
|
+
|
|
57
|
+
|
|
58
|
+
// -----------------------------
|
|
59
|
+
// 1. Compute FNV-1a 64-bit hash
|
|
60
|
+
// -----------------------------
|
|
61
|
+
let hash = BigInt("0xcbf29ce484222325");
|
|
62
|
+
const prime = BigInt("0x100000001b3");
|
|
63
|
+
|
|
64
|
+
for (const ch of name) {
|
|
65
|
+
hash ^= BigInt(ch.codePointAt(0)!);
|
|
66
|
+
hash *= prime;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
const hashHex = hash.toString(16).toUpperCase();
|
|
70
|
+
|
|
71
|
+
// -----------------------------
|
|
72
|
+
// 2. Stateless mode (no registry)
|
|
73
|
+
// -----------------------------
|
|
74
|
+
if (!registry) {
|
|
75
|
+
const offset = Number(hash % range),
|
|
76
|
+
codePoint = start + offset;
|
|
77
|
+
return formatResult(name, codePoint, hashHex, false);
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
// -----------------------------
|
|
81
|
+
// 3. Registry mode (0 collision)
|
|
82
|
+
// -----------------------------
|
|
83
|
+
|
|
84
|
+
// Already assigned → return stable mapping
|
|
85
|
+
if (registry.has(name)) {
|
|
86
|
+
return formatResult(name, registry.get(name)!, hashHex, false);
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
// Initial candidate from hash
|
|
90
|
+
let offset = Number(hash % range),
|
|
91
|
+
codePoint = start + offset,
|
|
92
|
+
collision = false;
|
|
93
|
+
|
|
94
|
+
const used = new Set(registry.values());
|
|
95
|
+
|
|
96
|
+
// Linear probing to resolve collisions
|
|
97
|
+
while (used.has(codePoint)) {
|
|
98
|
+
collision = true;
|
|
99
|
+
offset = (offset + 1) % Number(range);
|
|
100
|
+
codePoint = start + offset;
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
// Commit allocation
|
|
104
|
+
registry.set(name, codePoint);
|
|
105
|
+
|
|
106
|
+
return formatResult(name, codePoint, hashHex, collision);
|
|
107
|
+
};
|
|
108
|
+
|
|
109
|
+
|
|
110
|
+
export default stringToEncodings;
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @since Last modified: 2026/02/05 17:06:59
|
|
3
|
+
* Parses a Unicode input (string or number) and converts it into
|
|
4
|
+
* multiple encoding formats including Unicode notation, HTML entities,
|
|
5
|
+
* hexadecimal code, and decimal code point.
|
|
6
|
+
*
|
|
7
|
+
* Supported input formats:
|
|
8
|
+
* - Unicode string: "U+F123"
|
|
9
|
+
* - Hex string: "F123", "0xF123"
|
|
10
|
+
* - Decimal string: "61731"
|
|
11
|
+
* - Numeric code point: 61731
|
|
12
|
+
*
|
|
13
|
+
* This utility is useful for:
|
|
14
|
+
* - Icon font Unicode decoding
|
|
15
|
+
* - HTML entity generation
|
|
16
|
+
* - Unicode debugging and inspection
|
|
17
|
+
* - Glyph mapping and font tooling
|
|
18
|
+
* - PUA (Private Use Area) management workflows
|
|
19
|
+
*
|
|
20
|
+
* @param input - Unicode input as a string or numeric code point
|
|
21
|
+
* @returns An object containing Unicode, hex, decimal, and HTML encodings
|
|
22
|
+
* @throws Error if the input cannot be parsed into a valid Unicode code point
|
|
23
|
+
*/
|
|
24
|
+
const unicodeToEncodings = (input) => {
|
|
25
|
+
let codePoint;
|
|
26
|
+
if (typeof input === "number") {
|
|
27
|
+
codePoint = input;
|
|
28
|
+
}
|
|
29
|
+
else {
|
|
30
|
+
const cleaned = input.trim()
|
|
31
|
+
.replace(/^U\+/i, "")
|
|
32
|
+
.replace(/^0x/i, "");
|
|
33
|
+
codePoint = /^[0-9A-F]+$/i.test(cleaned)
|
|
34
|
+
? parseInt(cleaned, 16)
|
|
35
|
+
: parseInt(cleaned, 10);
|
|
36
|
+
}
|
|
37
|
+
// Validate parsed code point
|
|
38
|
+
if (!Number.isFinite(codePoint)) {
|
|
39
|
+
throw new Error("Invalid Unicode input");
|
|
40
|
+
}
|
|
41
|
+
// Convert code point to uppercase hexadecimal representation
|
|
42
|
+
const hex = codePoint.toString(16).toUpperCase();
|
|
43
|
+
return {
|
|
44
|
+
unicode: `U+${hex}`,
|
|
45
|
+
hex,
|
|
46
|
+
codePoint,
|
|
47
|
+
htmlDec: `&#${codePoint};`,
|
|
48
|
+
htmlHex: `&#x${hex};`,
|
|
49
|
+
};
|
|
50
|
+
};
|
|
51
|
+
export default unicodeToEncodings;
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @since Last modified: 2026/02/05 17:06:59
|
|
3
|
+
* Parses a Unicode input (string or number) and converts it into
|
|
4
|
+
* multiple encoding formats including Unicode notation, HTML entities,
|
|
5
|
+
* hexadecimal code, and decimal code point.
|
|
6
|
+
*
|
|
7
|
+
* Supported input formats:
|
|
8
|
+
* - Unicode string: "U+F123"
|
|
9
|
+
* - Hex string: "F123", "0xF123"
|
|
10
|
+
* - Decimal string: "61731"
|
|
11
|
+
* - Numeric code point: 61731
|
|
12
|
+
*
|
|
13
|
+
* This utility is useful for:
|
|
14
|
+
* - Icon font Unicode decoding
|
|
15
|
+
* - HTML entity generation
|
|
16
|
+
* - Unicode debugging and inspection
|
|
17
|
+
* - Glyph mapping and font tooling
|
|
18
|
+
* - PUA (Private Use Area) management workflows
|
|
19
|
+
*
|
|
20
|
+
* @param input - Unicode input as a string or numeric code point
|
|
21
|
+
* @returns An object containing Unicode, hex, decimal, and HTML encodings
|
|
22
|
+
* @throws Error if the input cannot be parsed into a valid Unicode code point
|
|
23
|
+
*/
|
|
24
|
+
const unicodeToEncodings = (input: string | number) => {
|
|
25
|
+
let codePoint: number;
|
|
26
|
+
|
|
27
|
+
if (typeof input === "number") {
|
|
28
|
+
codePoint = input;
|
|
29
|
+
} else {
|
|
30
|
+
const cleaned = input.trim()
|
|
31
|
+
.replace(/^U\+/i, "")
|
|
32
|
+
.replace(/^0x/i, "");
|
|
33
|
+
|
|
34
|
+
codePoint = /^[0-9A-F]+$/i.test(cleaned)
|
|
35
|
+
? parseInt(cleaned, 16)
|
|
36
|
+
: parseInt(cleaned, 10);
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
// Validate parsed code point
|
|
40
|
+
if (!Number.isFinite(codePoint)) {
|
|
41
|
+
throw new Error("Invalid Unicode input");
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
// Convert code point to uppercase hexadecimal representation
|
|
45
|
+
const hex = codePoint.toString(16).toUpperCase();
|
|
46
|
+
|
|
47
|
+
return {
|
|
48
|
+
unicode: `U+${hex}`,
|
|
49
|
+
hex,
|
|
50
|
+
codePoint,
|
|
51
|
+
htmlDec: `&#${codePoint};`,
|
|
52
|
+
htmlHex: `&#x${hex};`,
|
|
53
|
+
};
|
|
54
|
+
};
|
|
55
|
+
export default unicodeToEncodings;
|
|
@@ -1,19 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* @since Last modified: 2026/01/20 11:52:35
|
|
3
|
-
* Capitalizes the first letter of the given string.
|
|
4
|
-
*
|
|
5
|
-
* This function takes a string as input and returns a new string with the first letter
|
|
6
|
-
* capitalized, while leaving the rest of the string unchanged. If the input string is
|
|
7
|
-
* empty or undefined, it returns the input string as is.
|
|
8
|
-
*
|
|
9
|
-
* @param str - The string whose first letter will be capitalized.
|
|
10
|
-
* @returns A new string with the first letter capitalized, or the input string if it's empty.
|
|
11
|
-
*/
|
|
12
|
-
const capitalize = (str) => {
|
|
13
|
-
// Check if the input string is empty or undefined
|
|
14
|
-
if (!str)
|
|
15
|
-
return str;
|
|
16
|
-
// Capitalize the first letter and return the new string
|
|
17
|
-
return str.charAt(0).toUpperCase() + str.slice(1);
|
|
18
|
-
};
|
|
19
|
-
export default capitalize;
|
|
@@ -1,47 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* @since Last modified: 2025/12/16 14:43:51
|
|
3
|
-
* Deep clone an array or object to a JSON-compatible structure.
|
|
4
|
-
* Converts non-serializable types like functions, symbols, Date, RegExp to plain values.
|
|
5
|
-
*
|
|
6
|
-
* @template T
|
|
7
|
-
* @param {T} data - Array or object to clone
|
|
8
|
-
* @returns {T} - New object with different memory address, in a JSON-compatible structure
|
|
9
|
-
*
|
|
10
|
-
* @example
|
|
11
|
-
* const obj = { a: '', b: 0, c: [] };
|
|
12
|
-
* const cloned = deepCloneToJSON(obj);
|
|
13
|
-
*
|
|
14
|
-
* @example
|
|
15
|
-
* const arr = [{}, {}, {}];
|
|
16
|
-
* const cloned = deepCloneToJSON(arr);
|
|
17
|
-
*/
|
|
18
|
-
'use strict';
|
|
19
|
-
import getDataType from './getDataType';
|
|
20
|
-
const deepCloneToJSON = (data) => {
|
|
21
|
-
const dataType = getDataType(data);
|
|
22
|
-
// Handle objects
|
|
23
|
-
if (dataType === 'Object') {
|
|
24
|
-
const newObj = {};
|
|
25
|
-
// Clone regular properties
|
|
26
|
-
for (const key in data) {
|
|
27
|
-
newObj[key] = deepCloneToJSON(data[key]);
|
|
28
|
-
}
|
|
29
|
-
for (const key in newObj) {
|
|
30
|
-
newObj[key] === undefined && Reflect.deleteProperty(newObj, key);
|
|
31
|
-
}
|
|
32
|
-
return newObj;
|
|
33
|
-
// Handle arrays
|
|
34
|
-
}
|
|
35
|
-
else if (dataType === 'Array') {
|
|
36
|
-
let tmp = data.map((item, index) => deepCloneToJSON(item)).filter(item => item !== undefined);
|
|
37
|
-
return tmp;
|
|
38
|
-
// Handle Date objects
|
|
39
|
-
}
|
|
40
|
-
else if (!['Number', 'String', 'Boolean', 'Null'].includes(dataType)) {
|
|
41
|
-
return undefined;
|
|
42
|
-
}
|
|
43
|
-
else {
|
|
44
|
-
return data;
|
|
45
|
-
}
|
|
46
|
-
};
|
|
47
|
-
export default deepCloneToJSON;
|