@whitesev/utils 2.0.0 → 2.0.1
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/package.json +4 -3
- package/src/AjaxHookerType.ts +155 -0
- package/src/ColorConversion.ts +130 -0
- package/src/Dictionary.ts +161 -0
- package/src/Event.ts +189 -0
- package/src/GBKEncoder.ts +121 -0
- package/src/Hooks.ts +87 -0
- package/src/Httpx.ts +2524 -0
- package/src/LockFunction.ts +67 -0
- package/src/Log.ts +285 -0
- package/src/Progress.ts +149 -0
- package/src/TryCatch.ts +107 -0
- package/src/Utils.ts +4937 -0
- package/src/UtilsCommon.ts +20 -0
- package/src/UtilsCore.ts +47 -0
- package/src/UtilsGMCookie.ts +262 -0
- package/src/UtilsGMMenu.ts +532 -0
- package/src/VueObject.ts +128 -0
- package/src/ajaxHooker/ajaxHooker.js +566 -0
- package/src/indexedDB.ts +427 -0
package/src/Utils.ts
ADDED
|
@@ -0,0 +1,4937 @@
|
|
|
1
|
+
import { ColorConversion } from "./ColorConversion";
|
|
2
|
+
import { GBKEncoder } from "./GBKEncoder";
|
|
3
|
+
import { UtilsCore } from "./UtilsCore";
|
|
4
|
+
import { UtilsGMCookie } from "./UtilsGMCookie";
|
|
5
|
+
import { AjaxHooker } from "./ajaxHooker/ajaxHooker.js";
|
|
6
|
+
import { GMMenu } from "./UtilsGMMenu";
|
|
7
|
+
import { Hooks } from "./Hooks";
|
|
8
|
+
import { Httpx } from "./Httpx";
|
|
9
|
+
import { indexedDB } from "./indexedDB";
|
|
10
|
+
import { LockFunction } from "./LockFunction";
|
|
11
|
+
import { Log } from "./Log";
|
|
12
|
+
import { Progress } from "./Progress";
|
|
13
|
+
import { TryCatch } from "./TryCatch";
|
|
14
|
+
import { UtilsDictionary } from "./Dictionary";
|
|
15
|
+
import type { DOMUtils_EventType } from "./Event";
|
|
16
|
+
import type { UtilsCoreOption } from "./UtilsCore";
|
|
17
|
+
import type { Vue2Object } from "./VueObject";
|
|
18
|
+
import type { UtilsAjaxHookResult } from "./AjaxHookerType";
|
|
19
|
+
import { GenerateUUID } from "./UtilsCommon";
|
|
20
|
+
|
|
21
|
+
export declare var unsafeWindow: Window & typeof globalThis;
|
|
22
|
+
|
|
23
|
+
export type JSTypeMap = {
|
|
24
|
+
string: string;
|
|
25
|
+
number: number;
|
|
26
|
+
boolean: boolean;
|
|
27
|
+
object: object;
|
|
28
|
+
symbol: symbol;
|
|
29
|
+
bigint: bigint;
|
|
30
|
+
undefined: undefined;
|
|
31
|
+
null: null;
|
|
32
|
+
};
|
|
33
|
+
|
|
34
|
+
export type JSTypeNames = keyof JSTypeMap;
|
|
35
|
+
|
|
36
|
+
export type ArgsType<T extends JSTypeNames[]> = {
|
|
37
|
+
[I in keyof T]: JSTypeMap[T[I]];
|
|
38
|
+
};
|
|
39
|
+
|
|
40
|
+
export declare interface UtilsOwnObject<V extends any> {
|
|
41
|
+
[key: string]: V | UtilsOwnObject<V>;
|
|
42
|
+
}
|
|
43
|
+
export declare interface AnyObject {
|
|
44
|
+
[key: string]: any | AnyObject;
|
|
45
|
+
toString(): string;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
export declare interface Vue2Context extends Vue2Object {}
|
|
49
|
+
|
|
50
|
+
class Utils {
|
|
51
|
+
constructor(option?: UtilsCoreOption) {
|
|
52
|
+
UtilsCore.init(option);
|
|
53
|
+
}
|
|
54
|
+
/** 版本号 */
|
|
55
|
+
version = "2024.7.20";
|
|
56
|
+
|
|
57
|
+
/**
|
|
58
|
+
* 在页面中增加style元素,如果html节点存在子节点,添加子节点第一个,反之,添加到html节点的子节点最后一个
|
|
59
|
+
* @param cssText css字符串
|
|
60
|
+
* @returns 返回添加的CSS标签
|
|
61
|
+
* @example
|
|
62
|
+
* Utils.GM_addStyle("html{}");
|
|
63
|
+
* > <style type="text/css">html{}</style>
|
|
64
|
+
*/
|
|
65
|
+
addStyle(cssText: string): HTMLStyleElement;
|
|
66
|
+
addStyle(cssText: string) {
|
|
67
|
+
if (typeof cssText !== "string") {
|
|
68
|
+
throw new Error("Utils.addStyle 参数cssText 必须为String类型");
|
|
69
|
+
}
|
|
70
|
+
let cssNode = UtilsCore.document.createElement("style");
|
|
71
|
+
cssNode.setAttribute("type", "text/css");
|
|
72
|
+
cssNode.innerHTML = cssText;
|
|
73
|
+
if (UtilsCore.document.head) {
|
|
74
|
+
/* 插入head最后 */
|
|
75
|
+
UtilsCore.document.head.appendChild(cssNode);
|
|
76
|
+
} else if (UtilsCore.document.body) {
|
|
77
|
+
/* 插入body后 */
|
|
78
|
+
UtilsCore.document.body.appendChild(cssNode);
|
|
79
|
+
} else if (UtilsCore.document.documentElement.childNodes.length === 0) {
|
|
80
|
+
/* 插入#html第一个元素后 */
|
|
81
|
+
UtilsCore.document.documentElement.appendChild(cssNode);
|
|
82
|
+
} else {
|
|
83
|
+
/* 插入head前面 */
|
|
84
|
+
UtilsCore.document.documentElement.insertBefore(
|
|
85
|
+
cssNode,
|
|
86
|
+
UtilsCore.document.documentElement.childNodes[0]
|
|
87
|
+
);
|
|
88
|
+
}
|
|
89
|
+
return cssNode;
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
/**
|
|
93
|
+
* JSON数据从源端替换到目标端中,如果目标端存在该数据则替换,不添加,返回结果为目标端替换完毕的结果
|
|
94
|
+
* @param target 目标数据
|
|
95
|
+
* @param source 源数据
|
|
96
|
+
* @param isAdd 是否可以追加键,默认false
|
|
97
|
+
* @example
|
|
98
|
+
* Utils.assign({"1":1,"2":{"3":3}}, {"2":{"3":4}});
|
|
99
|
+
* >
|
|
100
|
+
* {
|
|
101
|
+
"1": 1,
|
|
102
|
+
"2": {
|
|
103
|
+
"3": 4
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
*/
|
|
107
|
+
assign<T1, T2 extends object, T3 extends boolean>(
|
|
108
|
+
target: T1,
|
|
109
|
+
source: T2,
|
|
110
|
+
isAdd?: T3
|
|
111
|
+
): T3 extends true ? T1 & T2 : T1;
|
|
112
|
+
assign(target = {}, source = {}, isAdd = false) {
|
|
113
|
+
let UtilsContext = this;
|
|
114
|
+
if (Array.isArray(source)) {
|
|
115
|
+
let canTraverse = source.filter((item) => {
|
|
116
|
+
return typeof item === "object";
|
|
117
|
+
});
|
|
118
|
+
if (!canTraverse.length) {
|
|
119
|
+
return source;
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
if (source == null) {
|
|
123
|
+
return target;
|
|
124
|
+
}
|
|
125
|
+
if (target == null) {
|
|
126
|
+
target = {};
|
|
127
|
+
}
|
|
128
|
+
if (isAdd) {
|
|
129
|
+
for (const sourceKeyName in source) {
|
|
130
|
+
const targetKeyName = sourceKeyName;
|
|
131
|
+
let targetValue = (target as any)[targetKeyName];
|
|
132
|
+
let sourceValue = (source as any)[sourceKeyName];
|
|
133
|
+
if (
|
|
134
|
+
typeof sourceValue === "object" &&
|
|
135
|
+
sourceValue != null &&
|
|
136
|
+
sourceKeyName in target &&
|
|
137
|
+
!UtilsContext.isDOM(sourceValue)
|
|
138
|
+
) {
|
|
139
|
+
/* 源端的值是object类型,且不是元素节点 */
|
|
140
|
+
(target as any)[sourceKeyName] = UtilsContext.assign(
|
|
141
|
+
targetValue,
|
|
142
|
+
sourceValue,
|
|
143
|
+
isAdd
|
|
144
|
+
);
|
|
145
|
+
continue;
|
|
146
|
+
}
|
|
147
|
+
(target as any)[sourceKeyName] = sourceValue;
|
|
148
|
+
}
|
|
149
|
+
} else {
|
|
150
|
+
for (const targetKeyName in target) {
|
|
151
|
+
if (targetKeyName in source) {
|
|
152
|
+
let targetValue = (target as any)[targetKeyName];
|
|
153
|
+
let sourceValue = (source as any)[targetKeyName];
|
|
154
|
+
if (
|
|
155
|
+
typeof sourceValue === "object" &&
|
|
156
|
+
sourceValue != null &&
|
|
157
|
+
!UtilsContext.isDOM(sourceValue) &&
|
|
158
|
+
Object.keys(sourceValue).length
|
|
159
|
+
) {
|
|
160
|
+
/* 源端的值是object类型,且不是元素节点 */
|
|
161
|
+
(target as any)[targetKeyName] = UtilsContext.assign(
|
|
162
|
+
targetValue,
|
|
163
|
+
sourceValue,
|
|
164
|
+
isAdd
|
|
165
|
+
);
|
|
166
|
+
continue;
|
|
167
|
+
}
|
|
168
|
+
/* 直接赋值 */
|
|
169
|
+
(target as any)[targetKeyName] = sourceValue;
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
return target;
|
|
175
|
+
}
|
|
176
|
+
/**
|
|
177
|
+
* 异步替换字符串
|
|
178
|
+
* @param string 需要被替换的目标字符串
|
|
179
|
+
* @param pattern 正则匹配模型
|
|
180
|
+
* @param asyncFn 异步获取的函数
|
|
181
|
+
*/
|
|
182
|
+
asyncReplaceAll(
|
|
183
|
+
string: string,
|
|
184
|
+
pattern: RegExp | string,
|
|
185
|
+
asyncFn: (item: string) => Promise<string>
|
|
186
|
+
): Promise<string>;
|
|
187
|
+
async asyncReplaceAll(
|
|
188
|
+
string: string,
|
|
189
|
+
pattern: RegExp | string,
|
|
190
|
+
asyncFn: (item: string) => Promise<string>
|
|
191
|
+
) {
|
|
192
|
+
let UtilsContext = this;
|
|
193
|
+
if (typeof string !== "string") {
|
|
194
|
+
throw new TypeError("string必须是字符串");
|
|
195
|
+
}
|
|
196
|
+
if (typeof asyncFn !== "function") {
|
|
197
|
+
throw new TypeError("asyncFn必须是函数");
|
|
198
|
+
}
|
|
199
|
+
let reg;
|
|
200
|
+
if (typeof pattern === "string") {
|
|
201
|
+
reg = new RegExp(UtilsContext.parseStringToRegExpString(pattern), "g");
|
|
202
|
+
} else if (pattern instanceof RegExp) {
|
|
203
|
+
if (!pattern.global) {
|
|
204
|
+
throw new TypeError("pattern必须是全局匹配");
|
|
205
|
+
}
|
|
206
|
+
reg = new RegExp(pattern);
|
|
207
|
+
} else {
|
|
208
|
+
throw new TypeError("pattern必须是正则对象");
|
|
209
|
+
}
|
|
210
|
+
let result = [];
|
|
211
|
+
let match;
|
|
212
|
+
let lastIndex = 0;
|
|
213
|
+
while ((match = reg.exec(string)) !== null) {
|
|
214
|
+
/* 异步获取匹配对应的字符串 */
|
|
215
|
+
const item = asyncFn(match[0]);
|
|
216
|
+
/* 获取该匹配项和上一个匹配项的中间的字符串 */
|
|
217
|
+
const prefix = string.slice(lastIndex, match.index);
|
|
218
|
+
lastIndex = match.index + match[0].length;
|
|
219
|
+
result.push(item);
|
|
220
|
+
result.push(prefix);
|
|
221
|
+
}
|
|
222
|
+
result.push(string.slice(lastIndex));
|
|
223
|
+
/* 等待所有异步完成 */
|
|
224
|
+
result = await Promise.all(result);
|
|
225
|
+
return result.join("");
|
|
226
|
+
}
|
|
227
|
+
/**
|
|
228
|
+
* ajax劫持库,支持xhr和fetch劫持。
|
|
229
|
+
* + 来源:https://bbs.tampermonkey.net.cn/thread-3284-1-1.html
|
|
230
|
+
* + 作者:cxxjackie
|
|
231
|
+
* + 版本:1.4.1
|
|
232
|
+
* + 文档:https://scriptcat.org/zh-CN/script-show-page/637/
|
|
233
|
+
*/
|
|
234
|
+
ajaxHooker: () => UtilsAjaxHookResult = AjaxHooker;
|
|
235
|
+
/**
|
|
236
|
+
* 根据坐标点击canvas元素的内部位置
|
|
237
|
+
* @param canvasElement 画布元素
|
|
238
|
+
* @param clientX X坐标,默认值0
|
|
239
|
+
* @param clientY Y坐标,默认值0
|
|
240
|
+
* @param view 触发的事件目标
|
|
241
|
+
*/
|
|
242
|
+
canvasClickByPosition(
|
|
243
|
+
canvasElement: HTMLCanvasElement,
|
|
244
|
+
clientX?: number | string,
|
|
245
|
+
clientY?: number | string,
|
|
246
|
+
view?: Window & typeof globalThis
|
|
247
|
+
): void;
|
|
248
|
+
canvasClickByPosition(
|
|
249
|
+
canvasElement: HTMLCanvasElement,
|
|
250
|
+
clientX = 0,
|
|
251
|
+
clientY = 0,
|
|
252
|
+
view = globalThis
|
|
253
|
+
) {
|
|
254
|
+
if (!(canvasElement instanceof HTMLCanvasElement)) {
|
|
255
|
+
throw new Error(
|
|
256
|
+
"Utils.canvasClickByPosition 参数canvasElement必须是canvas元素"
|
|
257
|
+
);
|
|
258
|
+
}
|
|
259
|
+
clientX = parseInt(clientX.toString());
|
|
260
|
+
clientY = parseInt(clientY.toString());
|
|
261
|
+
const eventInit: MouseEventInit = {
|
|
262
|
+
cancelBubble: true,
|
|
263
|
+
cancelable: true,
|
|
264
|
+
clientX: clientX,
|
|
265
|
+
clientY: clientY,
|
|
266
|
+
// @ts-ignore
|
|
267
|
+
view: view,
|
|
268
|
+
detail: 1,
|
|
269
|
+
};
|
|
270
|
+
canvasElement.dispatchEvent(new MouseEvent("mousedown", eventInit));
|
|
271
|
+
canvasElement.dispatchEvent(new MouseEvent("mouseup", eventInit));
|
|
272
|
+
}
|
|
273
|
+
/**
|
|
274
|
+
* 【手机】检测点击的地方是否在该元素区域内
|
|
275
|
+
* @param element 需要检测的元素
|
|
276
|
+
* @returns
|
|
277
|
+
* + true 点击在元素上
|
|
278
|
+
* + false 未点击在元素上
|
|
279
|
+
* @example
|
|
280
|
+
* Utils.checkUserClickInNode(document.querySelector(".xxx"));
|
|
281
|
+
* > false
|
|
282
|
+
**/
|
|
283
|
+
checkUserClickInNode(element: Element | Node | HTMLElement): boolean;
|
|
284
|
+
checkUserClickInNode(element: Element | Node | HTMLElement) {
|
|
285
|
+
let UtilsContext = this;
|
|
286
|
+
if (!UtilsContext.isDOM(element)) {
|
|
287
|
+
throw new Error(
|
|
288
|
+
"Utils.checkUserClickInNode 参数 targetNode 必须为 Element|Node 类型"
|
|
289
|
+
);
|
|
290
|
+
}
|
|
291
|
+
let mouseClickPosX = Number(
|
|
292
|
+
(window!.event as any).clientX.toString()
|
|
293
|
+
); /* 鼠标相对屏幕横坐标 */
|
|
294
|
+
let mouseClickPosY = Number(
|
|
295
|
+
(window!.event as any).clientY.toString()
|
|
296
|
+
); /* 鼠标相对屏幕纵坐标 */
|
|
297
|
+
let elementPosXLeft = Number(
|
|
298
|
+
(element as HTMLElement).getBoundingClientRect().left
|
|
299
|
+
); /* 要检测的元素的相对屏幕的横坐标最左边 */
|
|
300
|
+
let elementPosXRight = Number(
|
|
301
|
+
(element as HTMLElement).getBoundingClientRect().right
|
|
302
|
+
); /* 要检测的元素的相对屏幕的横坐标最右边 */
|
|
303
|
+
let elementPosYTop = Number(
|
|
304
|
+
(element as HTMLElement).getBoundingClientRect().top
|
|
305
|
+
); /* 要检测的元素的相对屏幕的纵坐标最上边 */
|
|
306
|
+
let elementPosYBottom = Number(
|
|
307
|
+
(element as HTMLElement).getBoundingClientRect().bottom
|
|
308
|
+
); /* 要检测的元素的相对屏幕的纵坐标最下边 */
|
|
309
|
+
let clickNodeHTML = (UtilsCore.window.event as any).target
|
|
310
|
+
.innerHTML as string;
|
|
311
|
+
if (
|
|
312
|
+
mouseClickPosX >= elementPosXLeft &&
|
|
313
|
+
mouseClickPosX <= elementPosXRight &&
|
|
314
|
+
mouseClickPosY >= elementPosYTop &&
|
|
315
|
+
mouseClickPosY <= elementPosYBottom
|
|
316
|
+
) {
|
|
317
|
+
return true;
|
|
318
|
+
} else if (
|
|
319
|
+
clickNodeHTML &&
|
|
320
|
+
(element as HTMLElement).innerHTML.includes(clickNodeHTML)
|
|
321
|
+
) {
|
|
322
|
+
/* 这种情况是应对在界面中隐藏的元素,getBoundingClientRect获取的都是0 */
|
|
323
|
+
return true;
|
|
324
|
+
} else {
|
|
325
|
+
return false;
|
|
326
|
+
}
|
|
327
|
+
}
|
|
328
|
+
/**
|
|
329
|
+
* 复制formData数据
|
|
330
|
+
* @param formData 需要clone的数据
|
|
331
|
+
*/
|
|
332
|
+
cloneFormData<T extends FormData>(formData: T): T;
|
|
333
|
+
cloneFormData<T extends FormData>(formData: T) {
|
|
334
|
+
let clonedFormData = new FormData();
|
|
335
|
+
for (let [key, value] of (formData as any).entries()) {
|
|
336
|
+
clonedFormData.append(key, value);
|
|
337
|
+
}
|
|
338
|
+
return clonedFormData;
|
|
339
|
+
}
|
|
340
|
+
/**
|
|
341
|
+
* 函数重载实现
|
|
342
|
+
* @example
|
|
343
|
+
* let getUsers = Utils.createOverload();
|
|
344
|
+
* getUsers.addImpl("",()=>{
|
|
345
|
+
* console.log("无参数");
|
|
346
|
+
* });
|
|
347
|
+
*
|
|
348
|
+
* getUsers.addImpl("boolean",()=>{
|
|
349
|
+
* console.log("boolean");
|
|
350
|
+
* });
|
|
351
|
+
*
|
|
352
|
+
* getUsers.addImpl("string",()=>{
|
|
353
|
+
* console.log("string");
|
|
354
|
+
* });
|
|
355
|
+
*
|
|
356
|
+
* getUsers.addImpl("number","string",()=>{
|
|
357
|
+
* console.log("number string");
|
|
358
|
+
* });
|
|
359
|
+
*/
|
|
360
|
+
createOverload(): {
|
|
361
|
+
/**
|
|
362
|
+
* 前面的参数都是字符串,最后一个参数是函数
|
|
363
|
+
*/
|
|
364
|
+
addImpl<T extends JSTypeNames[]>(
|
|
365
|
+
...args: [...T, (...args: ArgsType<T>) => any]
|
|
366
|
+
): void;
|
|
367
|
+
};
|
|
368
|
+
createOverload(): {
|
|
369
|
+
/**
|
|
370
|
+
* 前面的参数都是字符串,最后一个参数是函数
|
|
371
|
+
*/
|
|
372
|
+
addImpl<T extends JSTypeNames[]>(
|
|
373
|
+
...args: [...T, (...args: ArgsType<T>) => any]
|
|
374
|
+
): void;
|
|
375
|
+
} {
|
|
376
|
+
let fnMap = new Map();
|
|
377
|
+
function overload(this: any, ...args: any[]) {
|
|
378
|
+
let key = args.map((it) => typeof it).join(",");
|
|
379
|
+
let fn = fnMap.get(key);
|
|
380
|
+
if (!fn) {
|
|
381
|
+
throw new TypeError("没有找到对应的实现");
|
|
382
|
+
}
|
|
383
|
+
return fn.apply(this, args);
|
|
384
|
+
}
|
|
385
|
+
overload.addImpl = function (...args: any[]) {
|
|
386
|
+
let fn = args.pop();
|
|
387
|
+
if (typeof fn !== "function") {
|
|
388
|
+
throw new TypeError("最后一个参数必须是函数");
|
|
389
|
+
}
|
|
390
|
+
let key = args.join(",");
|
|
391
|
+
fnMap.set(key, fn);
|
|
392
|
+
};
|
|
393
|
+
return overload;
|
|
394
|
+
}
|
|
395
|
+
/**
|
|
396
|
+
* 颜色转换
|
|
397
|
+
* @returns
|
|
398
|
+
*/
|
|
399
|
+
ColorConversion = ColorConversion;
|
|
400
|
+
/**
|
|
401
|
+
* 深拷贝
|
|
402
|
+
* @param obj 对象
|
|
403
|
+
*/
|
|
404
|
+
deepClone<T extends object | undefined | null>(obj?: T): T;
|
|
405
|
+
deepClone<T extends object | undefined | null>(obj?: T) {
|
|
406
|
+
let UtilsContext = this;
|
|
407
|
+
if (obj === void 0) return void 0;
|
|
408
|
+
if (obj === null) return null;
|
|
409
|
+
let clone = obj instanceof Array ? [] : {};
|
|
410
|
+
for (const [key, value] of Object.entries(obj)) {
|
|
411
|
+
(clone as any)[key] =
|
|
412
|
+
typeof value === "object" ? UtilsContext.deepClone(value) : value;
|
|
413
|
+
}
|
|
414
|
+
return clone;
|
|
415
|
+
}
|
|
416
|
+
/**
|
|
417
|
+
* 防抖函数
|
|
418
|
+
* @param fn 需要触发的回调
|
|
419
|
+
* @param delay 防抖判定时间(毫秒),默认是0ms
|
|
420
|
+
*/
|
|
421
|
+
debounce<A extends any[], R>(
|
|
422
|
+
fn: (...args: A) => R,
|
|
423
|
+
delay?: number
|
|
424
|
+
): (...args: A) => void;
|
|
425
|
+
debounce<A extends any[], R>(fn: (...args: A) => R, delay = 0) {
|
|
426
|
+
let timer: any = null as any;
|
|
427
|
+
const context = this;
|
|
428
|
+
return function (...args: A) {
|
|
429
|
+
clearTimeout(timer);
|
|
430
|
+
timer = setTimeout(function () {
|
|
431
|
+
fn.apply(context, args);
|
|
432
|
+
}, delay);
|
|
433
|
+
};
|
|
434
|
+
}
|
|
435
|
+
/**
|
|
436
|
+
* 删除某个父元素,父元素可能在上层或上上层或上上上层...
|
|
437
|
+
* @param element 当前元素
|
|
438
|
+
* @param targetSelector 判断是否满足父元素,参数为当前处理的父元素,满足返回true,否则false
|
|
439
|
+
* @returns
|
|
440
|
+
* + true 已删除
|
|
441
|
+
* + false 未删除
|
|
442
|
+
* @example
|
|
443
|
+
* Utils.deleteParentNode(document.querySelector("a"),".xxx");
|
|
444
|
+
* > true
|
|
445
|
+
**/
|
|
446
|
+
deleteParentNode(
|
|
447
|
+
element: Node | HTMLElement | Element | null,
|
|
448
|
+
targetSelector: string
|
|
449
|
+
): boolean;
|
|
450
|
+
deleteParentNode(
|
|
451
|
+
element: Node | HTMLElement | Element | null,
|
|
452
|
+
targetSelector: string
|
|
453
|
+
) {
|
|
454
|
+
let UtilsContext = this;
|
|
455
|
+
if (element == null) {
|
|
456
|
+
return;
|
|
457
|
+
}
|
|
458
|
+
if (!UtilsContext.isDOM(element)) {
|
|
459
|
+
throw new Error(
|
|
460
|
+
"Utils.deleteParentNode 参数 target 必须为 Node|HTMLElement 类型"
|
|
461
|
+
);
|
|
462
|
+
}
|
|
463
|
+
if (typeof targetSelector !== "string") {
|
|
464
|
+
throw new Error(
|
|
465
|
+
"Utils.deleteParentNode 参数 targetSelector 必须为 string 类型"
|
|
466
|
+
);
|
|
467
|
+
}
|
|
468
|
+
let result = false;
|
|
469
|
+
let needRemoveDOM = (element as HTMLElement).closest(targetSelector);
|
|
470
|
+
if (needRemoveDOM) {
|
|
471
|
+
needRemoveDOM.remove();
|
|
472
|
+
result = true;
|
|
473
|
+
}
|
|
474
|
+
return result;
|
|
475
|
+
}
|
|
476
|
+
|
|
477
|
+
/**
|
|
478
|
+
* 字典
|
|
479
|
+
* @example
|
|
480
|
+
* let dictionary = new Utils.Dictionary();
|
|
481
|
+
* let dictionary2 = new Utils.Dictionary();
|
|
482
|
+
* dictionary.set("test","111");
|
|
483
|
+
* dictionary.get("test");
|
|
484
|
+
* > '111'
|
|
485
|
+
* dictionary.has("test");
|
|
486
|
+
* > true
|
|
487
|
+
* dictionary.concat(dictionary2);
|
|
488
|
+
**/
|
|
489
|
+
Dictionary = UtilsDictionary;
|
|
490
|
+
/**
|
|
491
|
+
* 主动触发事件
|
|
492
|
+
* @param element 元素
|
|
493
|
+
* @param eventName 事件名称,可以是字符串,也可是字符串格式的列表
|
|
494
|
+
* @param details (可选)赋予触发的Event的额外属性
|
|
495
|
+
* + true 使用Proxy代理Event并设置获取isTrusted永远为True
|
|
496
|
+
* + false (默认) 不对Event进行Proxy代理
|
|
497
|
+
* @example
|
|
498
|
+
* Utils.dispatchEvent(document.querySelector("input","input"))
|
|
499
|
+
*/
|
|
500
|
+
dispatchEvent(
|
|
501
|
+
element: HTMLElement | Document,
|
|
502
|
+
eventName: DOMUtils_EventType | DOMUtils_EventType[],
|
|
503
|
+
details?: UtilsOwnObject<any>
|
|
504
|
+
): void;
|
|
505
|
+
/**
|
|
506
|
+
* 主动触发事件
|
|
507
|
+
* @param element 元素
|
|
508
|
+
* @param eventName 事件名称,可以是字符串,也可是字符串格式的列表
|
|
509
|
+
* @param details (可选)赋予触发的Event的额外属性
|
|
510
|
+
* + true 使用Proxy代理Event并设置获取isTrusted永远为True
|
|
511
|
+
* + false (默认) 不对Event进行Proxy代理
|
|
512
|
+
* @example
|
|
513
|
+
* Utils.dispatchEvent(document.querySelector("input","input"))
|
|
514
|
+
*/
|
|
515
|
+
dispatchEvent(
|
|
516
|
+
element: HTMLElement | Document,
|
|
517
|
+
eventName: string,
|
|
518
|
+
details?: UtilsOwnObject<any>
|
|
519
|
+
): void;
|
|
520
|
+
dispatchEvent(
|
|
521
|
+
element: HTMLElement | Document,
|
|
522
|
+
eventName: DOMUtils_EventType | DOMUtils_EventType[] | string,
|
|
523
|
+
details?: UtilsOwnObject<any>
|
|
524
|
+
) {
|
|
525
|
+
let eventNameList: string[] = [];
|
|
526
|
+
if (typeof eventName === "string") {
|
|
527
|
+
eventNameList = [eventName];
|
|
528
|
+
}
|
|
529
|
+
if (Array.isArray(eventName)) {
|
|
530
|
+
eventNameList = [...eventName];
|
|
531
|
+
}
|
|
532
|
+
eventNameList.forEach((_eventName_) => {
|
|
533
|
+
let event = new Event(_eventName_);
|
|
534
|
+
if (details) {
|
|
535
|
+
Object.assign(event, details);
|
|
536
|
+
}
|
|
537
|
+
element.dispatchEvent(event);
|
|
538
|
+
});
|
|
539
|
+
}
|
|
540
|
+
/**
|
|
541
|
+
* 下载base64格式的数据
|
|
542
|
+
* @param base64Data 需要转换的base64数据
|
|
543
|
+
* @param fileName 需要保存的文件名
|
|
544
|
+
* @param isIFrame (可选)是否使用iframe进行下载
|
|
545
|
+
* @example
|
|
546
|
+
* Utils.downloadBase64("data:image/jpeg:base64/,xxxxxx");
|
|
547
|
+
**/
|
|
548
|
+
downloadBase64(
|
|
549
|
+
base64Data: string,
|
|
550
|
+
fileName: string,
|
|
551
|
+
isIFrame?: boolean
|
|
552
|
+
): void;
|
|
553
|
+
downloadBase64(base64Data: string, fileName: string, isIFrame = false) {
|
|
554
|
+
if (typeof base64Data !== "string") {
|
|
555
|
+
throw new Error(
|
|
556
|
+
"Utils.downloadBase64 参数 base64Data 必须为 string 类型"
|
|
557
|
+
);
|
|
558
|
+
}
|
|
559
|
+
if (typeof fileName !== "string") {
|
|
560
|
+
throw new Error("Utils.downloadBase64 参数 fileName 必须为 string 类型");
|
|
561
|
+
}
|
|
562
|
+
if (isIFrame) {
|
|
563
|
+
/* 使用iframe */
|
|
564
|
+
const iframeElement = UtilsCore.document.createElement("iframe");
|
|
565
|
+
iframeElement.style.display = "none";
|
|
566
|
+
iframeElement.src = base64Data;
|
|
567
|
+
UtilsCore.document.body.appendChild(iframeElement);
|
|
568
|
+
setTimeout(() => {
|
|
569
|
+
iframeElement!.contentWindow!.document.execCommand(
|
|
570
|
+
"SaveAs",
|
|
571
|
+
true,
|
|
572
|
+
fileName
|
|
573
|
+
);
|
|
574
|
+
UtilsCore.document.body.removeChild(iframeElement);
|
|
575
|
+
}, 100);
|
|
576
|
+
} else {
|
|
577
|
+
/* 使用A标签 */
|
|
578
|
+
const linkElement = UtilsCore.document.createElement("a");
|
|
579
|
+
linkElement.setAttribute("target", "_blank");
|
|
580
|
+
linkElement.download = fileName;
|
|
581
|
+
linkElement.href = base64Data;
|
|
582
|
+
linkElement.click();
|
|
583
|
+
}
|
|
584
|
+
}
|
|
585
|
+
|
|
586
|
+
/**
|
|
587
|
+
* 选中页面中的文字,类似Ctrl+F的选中
|
|
588
|
+
* @param str (可选)需要寻找的字符串,默认为空
|
|
589
|
+
* @param caseSensitive(可选)默认false
|
|
590
|
+
* + true 区分大小写
|
|
591
|
+
* + false (默认) 不区分大小写
|
|
592
|
+
* @returns
|
|
593
|
+
* + true 找到
|
|
594
|
+
* + false 未找到
|
|
595
|
+
* + undefined 不可使用该Api
|
|
596
|
+
* @example
|
|
597
|
+
* Utils.findWebPageVisibleText("xxxxx");
|
|
598
|
+
* > true
|
|
599
|
+
**/
|
|
600
|
+
findWebPageVisibleText(str?: string, caseSensitive?: boolean): boolean | void;
|
|
601
|
+
findWebPageVisibleText(str = "", caseSensitive = false) {
|
|
602
|
+
let TRange = null;
|
|
603
|
+
let strFound;
|
|
604
|
+
if ((UtilsCore.globalThis as any).find) {
|
|
605
|
+
/* CODE FOR BROWSERS THAT SUPPORT window.find */
|
|
606
|
+
let windowFind = (UtilsCore.self as any).find;
|
|
607
|
+
strFound = windowFind(str, caseSensitive, true, true, false);
|
|
608
|
+
if (
|
|
609
|
+
strFound &&
|
|
610
|
+
UtilsCore.self.getSelection &&
|
|
611
|
+
!UtilsCore.self.getSelection()!.anchorNode
|
|
612
|
+
) {
|
|
613
|
+
strFound = windowFind(str, caseSensitive, true, true, false);
|
|
614
|
+
}
|
|
615
|
+
if (!strFound) {
|
|
616
|
+
strFound = windowFind(str, 0, 1);
|
|
617
|
+
while (windowFind(str, 0, 1)) continue;
|
|
618
|
+
}
|
|
619
|
+
} else if (navigator.appName.indexOf("Microsoft") != -1) {
|
|
620
|
+
/* EXPLORER-SPECIFIC CODE */
|
|
621
|
+
if (TRange != null) {
|
|
622
|
+
TRange = TRange as any;
|
|
623
|
+
TRange.collapse(false);
|
|
624
|
+
strFound = TRange.findText(str);
|
|
625
|
+
if (strFound) TRange.select();
|
|
626
|
+
}
|
|
627
|
+
if (TRange == null || strFound == 0) {
|
|
628
|
+
TRange = (UtilsCore.self.document.body as any).createTextRange();
|
|
629
|
+
strFound = TRange.findText(str);
|
|
630
|
+
if (strFound) TRange.select();
|
|
631
|
+
}
|
|
632
|
+
} else if (navigator.appName == "Opera") {
|
|
633
|
+
alert("Opera browsers not supported, sorry...");
|
|
634
|
+
return;
|
|
635
|
+
}
|
|
636
|
+
return strFound ? true : false;
|
|
637
|
+
}
|
|
638
|
+
/**
|
|
639
|
+
* 定位元素上的字符串,返回一个迭代器
|
|
640
|
+
* @param element 目标元素
|
|
641
|
+
* @param text 需要定位的字符串
|
|
642
|
+
* @param filter (可选)过滤器函数,返回值为true是排除该元素
|
|
643
|
+
* @example
|
|
644
|
+
* let textIterator = Utils.findElementsWithText(document.documentElement,"xxxx");
|
|
645
|
+
* textIterator.next();
|
|
646
|
+
* > {value: ?HTMLElement, done: boolean, next: Function}
|
|
647
|
+
*/
|
|
648
|
+
findElementsWithText<T extends HTMLElement | Element | Node>(
|
|
649
|
+
element: T,
|
|
650
|
+
text: string,
|
|
651
|
+
filter?: (element: T) => boolean
|
|
652
|
+
): Generator<HTMLElement | ChildNode, void, any>;
|
|
653
|
+
*findElementsWithText<T extends HTMLElement | Element | Node>(
|
|
654
|
+
element: T,
|
|
655
|
+
text: string,
|
|
656
|
+
filter?: (element: T) => boolean
|
|
657
|
+
) {
|
|
658
|
+
let that = this;
|
|
659
|
+
if ((element as HTMLElement).outerHTML.includes(text)) {
|
|
660
|
+
if ((element as HTMLElement).children.length === 0) {
|
|
661
|
+
let filterResult =
|
|
662
|
+
typeof filter === "function" ? filter(element) : false;
|
|
663
|
+
if (!filterResult) {
|
|
664
|
+
yield element as any;
|
|
665
|
+
}
|
|
666
|
+
} else {
|
|
667
|
+
let textElement = Array.from(element.childNodes).filter(
|
|
668
|
+
(ele) => ele.nodeType === Node.TEXT_NODE
|
|
669
|
+
);
|
|
670
|
+
for (let ele of textElement) {
|
|
671
|
+
if ((ele as any).textContent.includes(text)) {
|
|
672
|
+
let filterResult =
|
|
673
|
+
typeof filter === "function" ? filter(element) : false;
|
|
674
|
+
if (!filterResult) {
|
|
675
|
+
yield ele;
|
|
676
|
+
}
|
|
677
|
+
}
|
|
678
|
+
}
|
|
679
|
+
}
|
|
680
|
+
}
|
|
681
|
+
|
|
682
|
+
for (
|
|
683
|
+
let index = 0;
|
|
684
|
+
index < (element as HTMLElement).children.length;
|
|
685
|
+
index++
|
|
686
|
+
) {
|
|
687
|
+
let childElement = (element as HTMLElement).children[index] as any;
|
|
688
|
+
yield* that.findElementsWithText(childElement, text, filter);
|
|
689
|
+
}
|
|
690
|
+
}
|
|
691
|
+
/**
|
|
692
|
+
* 判断该元素是否可见,如果不可见,向上找它的父元素直至找到可见的元素
|
|
693
|
+
* @param element
|
|
694
|
+
* @example
|
|
695
|
+
* let visibleElement = Utils.findVisibleElement(document.querySelector("a.xx"));
|
|
696
|
+
* > <HTMLElement>
|
|
697
|
+
*/
|
|
698
|
+
findVisibleElement(element: HTMLElement | Element | Node) {
|
|
699
|
+
let currentElement = element as HTMLElement;
|
|
700
|
+
while (currentElement) {
|
|
701
|
+
let elementRect = currentElement.getBoundingClientRect();
|
|
702
|
+
if (Boolean((elementRect as any).length)) {
|
|
703
|
+
return currentElement;
|
|
704
|
+
}
|
|
705
|
+
currentElement = currentElement.parentElement as any;
|
|
706
|
+
}
|
|
707
|
+
return null;
|
|
708
|
+
}
|
|
709
|
+
/**
|
|
710
|
+
* 格式化byte为KB、MB、GB、TB、PB、EB、ZB、YB、BB、NB、DB
|
|
711
|
+
* @param byteSize 字节
|
|
712
|
+
* @param addType (可选)是否添加单位
|
|
713
|
+
* + true (默认) 添加单位
|
|
714
|
+
* + false 不添加单位
|
|
715
|
+
* @returns
|
|
716
|
+
* + {string} 当addType为true时,且保留小数点末尾2位
|
|
717
|
+
* + {number} 当addType为false时,且保留小数点末尾2位
|
|
718
|
+
* @example
|
|
719
|
+
* Utils.formatByteToSize("812304");
|
|
720
|
+
* > '793.27KB'
|
|
721
|
+
* @example
|
|
722
|
+
* Utils.formatByteToSize("812304",false);
|
|
723
|
+
* > 793.27
|
|
724
|
+
**/
|
|
725
|
+
formatByteToSize<T extends boolean>(
|
|
726
|
+
byteSize: number | string,
|
|
727
|
+
addType?: T
|
|
728
|
+
): T extends true ? string : number;
|
|
729
|
+
formatByteToSize(byteSize: number | string, addType = true) {
|
|
730
|
+
byteSize = parseInt(byteSize.toString());
|
|
731
|
+
if (isNaN(byteSize)) {
|
|
732
|
+
throw new Error("Utils.formatByteToSize 参数 byteSize 格式不正确");
|
|
733
|
+
}
|
|
734
|
+
let result = 0;
|
|
735
|
+
let resultType = "KB";
|
|
736
|
+
let sizeData: UtilsOwnObject<number> = {};
|
|
737
|
+
sizeData.B = 1;
|
|
738
|
+
sizeData.KB = 1024;
|
|
739
|
+
sizeData.MB = sizeData.KB * sizeData.KB;
|
|
740
|
+
sizeData.GB = sizeData.MB * sizeData.KB;
|
|
741
|
+
sizeData.TB = sizeData.GB * sizeData.KB;
|
|
742
|
+
sizeData.PB = sizeData.TB * sizeData.KB;
|
|
743
|
+
sizeData.EB = sizeData.PB * sizeData.KB;
|
|
744
|
+
sizeData.ZB = sizeData.EB * sizeData.KB;
|
|
745
|
+
sizeData.YB = sizeData.ZB * sizeData.KB;
|
|
746
|
+
sizeData.BB = sizeData.YB * sizeData.KB;
|
|
747
|
+
sizeData.NB = sizeData.BB * sizeData.KB;
|
|
748
|
+
sizeData.DB = sizeData.NB * sizeData.KB;
|
|
749
|
+
for (let key in sizeData) {
|
|
750
|
+
result = byteSize / (sizeData as any)[key];
|
|
751
|
+
resultType = key;
|
|
752
|
+
if (sizeData.KB >= result) {
|
|
753
|
+
break;
|
|
754
|
+
}
|
|
755
|
+
}
|
|
756
|
+
result = result.toFixed(2) as any;
|
|
757
|
+
result = addType
|
|
758
|
+
? result + resultType.toString()
|
|
759
|
+
: (parseFloat(result.toString()) as any);
|
|
760
|
+
return result;
|
|
761
|
+
}
|
|
762
|
+
/**
|
|
763
|
+
* 应用场景: 当你想要获取数组形式的元素时,它可能是其它的选择器,那么需要按照先后顺序填入参数
|
|
764
|
+
* 第一个是优先级最高的,依次下降,如果都没有,返回空列表
|
|
765
|
+
* 支持document.querySelectorAll、$("")、()=>{return document.querySelectorAll("")}
|
|
766
|
+
* @param NodeList
|
|
767
|
+
* @example
|
|
768
|
+
* Utils.getNodeListValue(
|
|
769
|
+
* document.querySelectorAll("div.xxx"),
|
|
770
|
+
* document.querySelectorAll("a.xxx")
|
|
771
|
+
* );
|
|
772
|
+
* > [...div,div,div]
|
|
773
|
+
* @example
|
|
774
|
+
* Utils.getNodeListValue(divGetFunction,aGetFunction);
|
|
775
|
+
* > [...div,div,div]
|
|
776
|
+
*/
|
|
777
|
+
getNodeListValue(...args: (NodeList | (() => HTMLElement))[]): HTMLElement[];
|
|
778
|
+
getNodeListValue(...args: (NodeList | (() => HTMLElement))[]) {
|
|
779
|
+
let resultArray: HTMLElement[] = [];
|
|
780
|
+
for (let arg of args) {
|
|
781
|
+
let value = arg as any;
|
|
782
|
+
if (typeof arg === "function") {
|
|
783
|
+
/* 方法 */
|
|
784
|
+
value = arg();
|
|
785
|
+
}
|
|
786
|
+
if (value.length !== 0) {
|
|
787
|
+
resultArray = [...value];
|
|
788
|
+
break;
|
|
789
|
+
}
|
|
790
|
+
}
|
|
791
|
+
return resultArray;
|
|
792
|
+
}
|
|
793
|
+
/**
|
|
794
|
+
* 自动判断N个参数,获取非空的值,如果都是空,返回最后一个值
|
|
795
|
+
*/
|
|
796
|
+
getNonNullValue(...args: any[]): any;
|
|
797
|
+
getNonNullValue(...args: any[]) {
|
|
798
|
+
let resultValue = args[args.length - 1];
|
|
799
|
+
let UtilsContext = this;
|
|
800
|
+
for (const argValue of args) {
|
|
801
|
+
if (UtilsContext.isNotNull(argValue)) {
|
|
802
|
+
resultValue = argValue;
|
|
803
|
+
break;
|
|
804
|
+
}
|
|
805
|
+
}
|
|
806
|
+
return resultValue;
|
|
807
|
+
}
|
|
808
|
+
/**
|
|
809
|
+
* 获取格式化后的时间
|
|
810
|
+
* @param text (可选)需要格式化的字符串或者时间戳,默认:new Date()
|
|
811
|
+
* @param formatType (可选)格式化成的显示类型,默认:yyyy-MM-dd HH:mm:ss
|
|
812
|
+
* + yyyy 年
|
|
813
|
+
* + MM 月
|
|
814
|
+
* + dd 天
|
|
815
|
+
* + HH 时 (24小时制)
|
|
816
|
+
* + hh 时 (12小时制)
|
|
817
|
+
* + mm 分
|
|
818
|
+
* + ss 秒
|
|
819
|
+
* @returns {string} 返回格式化后的时间
|
|
820
|
+
* @example
|
|
821
|
+
* Utils.formatTime("2022-08-21 23:59:00","HH:mm:ss");
|
|
822
|
+
* > '23:59:00'
|
|
823
|
+
* @example
|
|
824
|
+
* Utils.formatTime(1899187424988,"HH:mm:ss");
|
|
825
|
+
* > '15:10:13'
|
|
826
|
+
* @example
|
|
827
|
+
* Utils.formatTime()
|
|
828
|
+
* > '2023-1-1 00:00:00'
|
|
829
|
+
**/
|
|
830
|
+
formatTime(text?: string | number | Date, formatType?: string): string;
|
|
831
|
+
/**
|
|
832
|
+
* 获取格式化后的时间
|
|
833
|
+
* @param text (可选)需要格式化的字符串或者时间戳,默认:new Date()
|
|
834
|
+
* @param formatType (可选)格式化成的显示类型,默认:yyyy-MM-dd HH:mm:ss
|
|
835
|
+
* + yyyy 年
|
|
836
|
+
* + MM 月
|
|
837
|
+
* + dd 天
|
|
838
|
+
* + HH 时 (24小时制)
|
|
839
|
+
* + hh 时 (12小时制)
|
|
840
|
+
* + mm 分
|
|
841
|
+
* + ss 秒
|
|
842
|
+
* @returns {string} 返回格式化后的时间
|
|
843
|
+
* @example
|
|
844
|
+
* Utils.formatTime("2022-08-21 23:59:00","HH:mm:ss");
|
|
845
|
+
* > '23:59:00'
|
|
846
|
+
* @example
|
|
847
|
+
* Utils.formatTime(1899187424988,"HH:mm:ss");
|
|
848
|
+
* > '15:10:13'
|
|
849
|
+
* @example
|
|
850
|
+
* Utils.formatTime()
|
|
851
|
+
* > '2023-1-1 00:00:00'
|
|
852
|
+
**/
|
|
853
|
+
formatTime(
|
|
854
|
+
text?: string | number | Date,
|
|
855
|
+
formatType?:
|
|
856
|
+
| "yyyy-MM-dd HH:mm:ss"
|
|
857
|
+
| "yyyy/MM/dd HH:mm:ss"
|
|
858
|
+
| "yyyy_MM_dd_HH_mm_ss"
|
|
859
|
+
| "yyyy年MM月dd日 HH时mm分ss秒"
|
|
860
|
+
| "yyyy年MM月dd日 hh:mm:ss"
|
|
861
|
+
| "yyyy年MM月dd日 HH:mm:ss"
|
|
862
|
+
| "yyyy-MM-dd"
|
|
863
|
+
| "yyyyMMdd"
|
|
864
|
+
| "HH:mm:ss"
|
|
865
|
+
): string;
|
|
866
|
+
formatTime(text = new Date(), formatType = "yyyy-MM-dd HH:mm:ss") {
|
|
867
|
+
let time = text == null ? new Date() : new Date(text);
|
|
868
|
+
/**
|
|
869
|
+
* 校验时间补0
|
|
870
|
+
* @param timeNum
|
|
871
|
+
* @returns
|
|
872
|
+
*/
|
|
873
|
+
function checkTime(timeNum: number) {
|
|
874
|
+
if (timeNum < 10) return "0" + timeNum;
|
|
875
|
+
return timeNum;
|
|
876
|
+
}
|
|
877
|
+
|
|
878
|
+
/**
|
|
879
|
+
* 时间制修改 24小时制转12小时制
|
|
880
|
+
* @param hourNum 小时
|
|
881
|
+
* @returns
|
|
882
|
+
*/
|
|
883
|
+
function timeSystemChange(hourNum: number) {
|
|
884
|
+
return hourNum > 12 ? hourNum - 12 : hourNum;
|
|
885
|
+
}
|
|
886
|
+
|
|
887
|
+
let timeRegexp = {
|
|
888
|
+
yyyy: time.getFullYear(),
|
|
889
|
+
/* 年 */
|
|
890
|
+
MM: checkTime(time.getMonth() + 1),
|
|
891
|
+
/* 月 */
|
|
892
|
+
dd: checkTime(time.getDate()),
|
|
893
|
+
/* 日 */
|
|
894
|
+
HH: checkTime(time.getHours()),
|
|
895
|
+
/* 时 (24小时制) */
|
|
896
|
+
hh: checkTime(timeSystemChange(time.getHours())),
|
|
897
|
+
/* 时 (12小时制) */
|
|
898
|
+
mm: checkTime(time.getMinutes()),
|
|
899
|
+
/* 分 */
|
|
900
|
+
ss: checkTime(time.getSeconds()),
|
|
901
|
+
/* 秒 */
|
|
902
|
+
};
|
|
903
|
+
Object.keys(timeRegexp).forEach(function (key) {
|
|
904
|
+
let replaecRegexp = new RegExp(key, "g");
|
|
905
|
+
formatType = formatType.replace(replaecRegexp, (timeRegexp as any)[key]);
|
|
906
|
+
});
|
|
907
|
+
return formatType;
|
|
908
|
+
}
|
|
909
|
+
/**
|
|
910
|
+
* 字符串格式的时间转时间戳
|
|
911
|
+
* @param text 字符串格式的时间,例如:
|
|
912
|
+
* + 2022-11-21 00:00:00
|
|
913
|
+
* + 00:00:00
|
|
914
|
+
* @returns 返回时间戳
|
|
915
|
+
* @example
|
|
916
|
+
* Utils.formatToTimeStamp("2022-11-21 00:00:00");
|
|
917
|
+
* > 1668960000000
|
|
918
|
+
**/
|
|
919
|
+
formatToTimeStamp(text: string): number;
|
|
920
|
+
formatToTimeStamp(text: string) {
|
|
921
|
+
/* 把字符串格式的时间(完整,包括日期和时间)格式化成时间 */
|
|
922
|
+
if (typeof text !== "string") {
|
|
923
|
+
throw new Error("Utils.formatToTimeStamp 参数 text 必须为 string 类型");
|
|
924
|
+
}
|
|
925
|
+
if (text.length === 8) {
|
|
926
|
+
/* 该字符串只有时分秒 */
|
|
927
|
+
let today = new Date();
|
|
928
|
+
text =
|
|
929
|
+
today.getFullYear() +
|
|
930
|
+
"-" +
|
|
931
|
+
(today.getMonth() + 1) +
|
|
932
|
+
"-" +
|
|
933
|
+
today.getDate() +
|
|
934
|
+
" " +
|
|
935
|
+
text;
|
|
936
|
+
}
|
|
937
|
+
text = text.substring(0, 19);
|
|
938
|
+
text = text.replace(/-/g, "/");
|
|
939
|
+
let timestamp = new Date(text).getTime();
|
|
940
|
+
return timestamp;
|
|
941
|
+
}
|
|
942
|
+
/**
|
|
943
|
+
* gbk格式的url编码,来自https://greasyfork.org/zh-CN/scripts/427726-gbk-url-js
|
|
944
|
+
* @example
|
|
945
|
+
* let gbkEncoder = new Utils.GBKEncoder();
|
|
946
|
+
* gbkEncoder.encode("测试");
|
|
947
|
+
* > '%B2%E2%CA%D4'
|
|
948
|
+
* gbkEncoder.decode("%B2%E2%CA%D4");
|
|
949
|
+
* > 测试
|
|
950
|
+
*/
|
|
951
|
+
GBKEncoder = GBKEncoder;
|
|
952
|
+
/**
|
|
953
|
+
* 获取 transitionend 的在各个浏览器的兼容名
|
|
954
|
+
*/
|
|
955
|
+
getTransitionEndNameList() {
|
|
956
|
+
return [
|
|
957
|
+
"webkitTransitionEnd",
|
|
958
|
+
"mozTransitionEnd",
|
|
959
|
+
"MSTransitionEnd",
|
|
960
|
+
"otransitionend",
|
|
961
|
+
"transitionend",
|
|
962
|
+
];
|
|
963
|
+
}
|
|
964
|
+
/**
|
|
965
|
+
* 获取 animationend 的在各个浏览器的兼容名
|
|
966
|
+
*/
|
|
967
|
+
getAnimationEndNameList() {
|
|
968
|
+
return [
|
|
969
|
+
"webkitAnimationEnd",
|
|
970
|
+
"mozAnimationEnd",
|
|
971
|
+
"MSAnimationEnd",
|
|
972
|
+
"oanimationend",
|
|
973
|
+
"animationend",
|
|
974
|
+
];
|
|
975
|
+
}
|
|
976
|
+
/**
|
|
977
|
+
* 获取NodeList或Array对象中的最后一个的值
|
|
978
|
+
* @param targetObj
|
|
979
|
+
* @returns
|
|
980
|
+
* @example
|
|
981
|
+
* Utils.getArrayLastValue(document.querySelectorAll("div"));
|
|
982
|
+
* > div
|
|
983
|
+
* @example
|
|
984
|
+
* Utils.getArrayLastValue([1,2,3,4,5]);
|
|
985
|
+
* > 5
|
|
986
|
+
*/
|
|
987
|
+
getArrayLastValue<R extends any>(targetObj: NodeList | any[]): R;
|
|
988
|
+
getArrayLastValue(targetObj: NodeList | any[]) {
|
|
989
|
+
return targetObj[targetObj.length - 1];
|
|
990
|
+
}
|
|
991
|
+
/**
|
|
992
|
+
* 应用场景: 当想获取的元素可能是不同的选择器的时候,按顺序优先级获取
|
|
993
|
+
* 参数类型可以是Element或者是Function
|
|
994
|
+
* @returns 如果都没有的话,返回null
|
|
995
|
+
* @example
|
|
996
|
+
* // 如果a.aaa不存在的话,取a.bbb,这里假设a.aaa不存在
|
|
997
|
+
* Utils.getArrayRealValue(document.querySelector("a.aaa"),document.querySelector("a.bbb"));
|
|
998
|
+
* > a.bbb
|
|
999
|
+
* @example
|
|
1000
|
+
* Utils.getArrayRealValue(()=>{return document.querySelector("a.aaa").href},()=>{document.querySelector("a.bbb").getAttribute("data-href")});
|
|
1001
|
+
* > javascript:;
|
|
1002
|
+
*/
|
|
1003
|
+
getArrayRealValue(...args: (NodeList | (() => HTMLElement))[]): any;
|
|
1004
|
+
getArrayRealValue(...args: (NodeList | (() => HTMLElement))[]) {
|
|
1005
|
+
let result = null;
|
|
1006
|
+
for (let arg of args) {
|
|
1007
|
+
if (typeof arg === "function") {
|
|
1008
|
+
/* 方法 */
|
|
1009
|
+
(arg as any) = arg();
|
|
1010
|
+
}
|
|
1011
|
+
if (arg != null) {
|
|
1012
|
+
result = arg;
|
|
1013
|
+
break;
|
|
1014
|
+
}
|
|
1015
|
+
}
|
|
1016
|
+
return result;
|
|
1017
|
+
}
|
|
1018
|
+
/**
|
|
1019
|
+
* 获取天数差异,如何获取某个时间与另一个时间相差的天数
|
|
1020
|
+
* @param timestamp1 (可选)时间戳(毫秒|秒),不区分哪个更大,默认为:Date.now()
|
|
1021
|
+
* @param timestamp2 (可选)时间戳(毫秒|秒),不区分哪个更大,默认为:Date.now()
|
|
1022
|
+
* @param type (可选)返回的数字的表达的类型,比如:年、月、天、时、分、秒、auto,默认天
|
|
1023
|
+
* @example
|
|
1024
|
+
* Utils.getDaysDifference(new Date().getTime());
|
|
1025
|
+
* > 0
|
|
1026
|
+
* @example
|
|
1027
|
+
* Utils.getDaysDifference(new Date().getTime(),undefined,"秒");
|
|
1028
|
+
* > 0
|
|
1029
|
+
*/
|
|
1030
|
+
getDaysDifference(
|
|
1031
|
+
timestamp1?: number,
|
|
1032
|
+
timestamp2?: number,
|
|
1033
|
+
type?: "auto"
|
|
1034
|
+
): string;
|
|
1035
|
+
/**
|
|
1036
|
+
* 获取天数差异,如何获取某个时间与另一个时间相差的天数
|
|
1037
|
+
* @param timestamp1 (可选)时间戳(毫秒|秒),不区分哪个更大,默认为:Date.now()
|
|
1038
|
+
* @param timestamp2 (可选)时间戳(毫秒|秒),不区分哪个更大,默认为:Date.now()
|
|
1039
|
+
* @param type (可选)返回的数字的表达的类型,比如:年、月、天、时、分、秒、auto,默认天
|
|
1040
|
+
* @example
|
|
1041
|
+
* Utils.getDaysDifference(new Date().getTime());
|
|
1042
|
+
* > 0
|
|
1043
|
+
* @example
|
|
1044
|
+
* Utils.getDaysDifference(new Date().getTime(),undefined,"秒");
|
|
1045
|
+
* > 0
|
|
1046
|
+
*/
|
|
1047
|
+
getDaysDifference(
|
|
1048
|
+
timestamp1?: number,
|
|
1049
|
+
timestamp2?: number,
|
|
1050
|
+
type?: "年" | "月" | "天" | "时" | "分" | "秒"
|
|
1051
|
+
): number;
|
|
1052
|
+
getDaysDifference(
|
|
1053
|
+
timestamp1 = Date.now(),
|
|
1054
|
+
timestamp2 = Date.now(),
|
|
1055
|
+
type = "天"
|
|
1056
|
+
): number | string {
|
|
1057
|
+
type = type.trim();
|
|
1058
|
+
if (timestamp1.toString().length === 10) {
|
|
1059
|
+
timestamp1 = timestamp1 * 1000;
|
|
1060
|
+
}
|
|
1061
|
+
if (timestamp2.toString().length === 10) {
|
|
1062
|
+
timestamp2 = timestamp2 * 1000;
|
|
1063
|
+
}
|
|
1064
|
+
let smallTimeStamp = timestamp1 > timestamp2 ? timestamp2 : timestamp1;
|
|
1065
|
+
let bigTimeStamp = timestamp1 > timestamp2 ? timestamp1 : timestamp2;
|
|
1066
|
+
let oneSecond = 1000; /* 一秒的毫秒数 */
|
|
1067
|
+
let oneMinute = 60 * oneSecond; /* 一分钟的毫秒数 */
|
|
1068
|
+
let oneHour = 60 * oneMinute; /* 一小时的毫秒数 */
|
|
1069
|
+
let oneDay = 24 * oneHour; /* 一天的毫秒数 */
|
|
1070
|
+
let oneMonth = 30 * oneDay; /* 一个月的毫秒数(30天) */
|
|
1071
|
+
let oneYear = 12 * oneMonth; /* 一年的毫秒数 */
|
|
1072
|
+
let bigDate = new Date(bigTimeStamp);
|
|
1073
|
+
let smallDate = new Date(smallTimeStamp);
|
|
1074
|
+
let remainderValue = 1;
|
|
1075
|
+
if (type === "年") {
|
|
1076
|
+
remainderValue = oneYear;
|
|
1077
|
+
} else if (type === "月") {
|
|
1078
|
+
remainderValue = oneMonth;
|
|
1079
|
+
} else if (type === "天") {
|
|
1080
|
+
remainderValue = oneDay;
|
|
1081
|
+
} else if (type === "时") {
|
|
1082
|
+
remainderValue = oneHour;
|
|
1083
|
+
} else if (type === "分") {
|
|
1084
|
+
remainderValue = oneMinute;
|
|
1085
|
+
} else if (type === "秒") {
|
|
1086
|
+
remainderValue = oneSecond;
|
|
1087
|
+
}
|
|
1088
|
+
let diffValue = Math.round(
|
|
1089
|
+
Math.abs(((bigDate as any) - (smallDate as any)) / remainderValue)
|
|
1090
|
+
);
|
|
1091
|
+
if (type === "auto") {
|
|
1092
|
+
let timeDifference = bigTimeStamp - smallTimeStamp;
|
|
1093
|
+
diffValue = Math.floor(timeDifference / (24 * 3600 * 1000));
|
|
1094
|
+
if (diffValue > 0) {
|
|
1095
|
+
(diffValue as any) = diffValue + "天";
|
|
1096
|
+
} else {
|
|
1097
|
+
/* 计算出小时数 */
|
|
1098
|
+
let leave1 =
|
|
1099
|
+
timeDifference % (24 * 3600 * 1000); /* 计算天数后剩余的毫秒数 */
|
|
1100
|
+
let hours = Math.floor(leave1 / (3600 * 1000));
|
|
1101
|
+
if (hours > 0) {
|
|
1102
|
+
(diffValue as any) = hours + "小时";
|
|
1103
|
+
} else {
|
|
1104
|
+
/* 计算相差分钟数 */
|
|
1105
|
+
let leave2 = leave1 % (3600 * 1000); /* 计算小时数后剩余的毫秒数 */
|
|
1106
|
+
let minutes = Math.floor(leave2 / (60 * 1000));
|
|
1107
|
+
if (minutes > 0) {
|
|
1108
|
+
(diffValue as any) = minutes + "分钟";
|
|
1109
|
+
} else {
|
|
1110
|
+
/* 计算相差秒数 */
|
|
1111
|
+
let leave3 = leave2 % (60 * 1000); /* 计算分钟数后剩余的毫秒数 */
|
|
1112
|
+
let seconds = Math.round(leave3 / 1000);
|
|
1113
|
+
(diffValue as any) = seconds + "秒";
|
|
1114
|
+
}
|
|
1115
|
+
}
|
|
1116
|
+
}
|
|
1117
|
+
}
|
|
1118
|
+
return diffValue;
|
|
1119
|
+
}
|
|
1120
|
+
/**
|
|
1121
|
+
* 获取元素的选择器字符串
|
|
1122
|
+
* @param element
|
|
1123
|
+
* @example
|
|
1124
|
+
* Utils.getElementSelector(document.querySelector("a"))
|
|
1125
|
+
* > '.....'
|
|
1126
|
+
*/
|
|
1127
|
+
getElementSelector(element: HTMLElement): string;
|
|
1128
|
+
getElementSelector(element: HTMLElement): string {
|
|
1129
|
+
let UtilsContext = this;
|
|
1130
|
+
// @ts-ignore
|
|
1131
|
+
if (!element) return;
|
|
1132
|
+
// @ts-ignore
|
|
1133
|
+
if (!element.parentElement) return;
|
|
1134
|
+
/* 如果元素有id属性,则直接返回id选择器 */
|
|
1135
|
+
if (element.id) return "#" + element.id;
|
|
1136
|
+
|
|
1137
|
+
/* 递归地获取父元素的选择器 */
|
|
1138
|
+
let selector = UtilsContext.getElementSelector(element.parentElement);
|
|
1139
|
+
if (!selector) {
|
|
1140
|
+
return element.tagName.toLowerCase();
|
|
1141
|
+
}
|
|
1142
|
+
/* 如果有多个相同类型的兄弟元素,则需要添加索引 */
|
|
1143
|
+
if (element.parentElement.querySelectorAll(element.tagName).length > 1) {
|
|
1144
|
+
let index =
|
|
1145
|
+
Array.prototype.indexOf.call(element.parentElement.children, element) +
|
|
1146
|
+
1;
|
|
1147
|
+
selector +=
|
|
1148
|
+
" > " + element.tagName.toLowerCase() + ":nth-child(" + index + ")";
|
|
1149
|
+
} else {
|
|
1150
|
+
selector += " > " + element.tagName.toLowerCase();
|
|
1151
|
+
}
|
|
1152
|
+
return selector;
|
|
1153
|
+
}
|
|
1154
|
+
/**
|
|
1155
|
+
* 获取最大值
|
|
1156
|
+
* @example
|
|
1157
|
+
* Utils.getMaxValue(1,3,5,7,9)
|
|
1158
|
+
* > 9
|
|
1159
|
+
*/
|
|
1160
|
+
getMaxValue(...args: number[]): number;
|
|
1161
|
+
/**
|
|
1162
|
+
* 获取最大值
|
|
1163
|
+
* @example
|
|
1164
|
+
* Utils.getMaxValue([1,3,5])
|
|
1165
|
+
* > 5
|
|
1166
|
+
*/
|
|
1167
|
+
getMaxValue(val: number[]): number;
|
|
1168
|
+
/**
|
|
1169
|
+
* 获取最大值
|
|
1170
|
+
* @example
|
|
1171
|
+
* Utils.getMaxValue({1:123,2:345,3:456},(key,value)=>{return parseInt(value)})
|
|
1172
|
+
* > 456
|
|
1173
|
+
*/
|
|
1174
|
+
getMaxValue(
|
|
1175
|
+
val: UtilsOwnObject<number>,
|
|
1176
|
+
handler: (key: any, value: any) => number
|
|
1177
|
+
): number;
|
|
1178
|
+
/**
|
|
1179
|
+
* 获取最大值
|
|
1180
|
+
* @example
|
|
1181
|
+
* Utils.getMaxValue([{1:123},{2:345},{3:456}],(index,value)=>{return parseInt(index)})
|
|
1182
|
+
* > 2
|
|
1183
|
+
*/
|
|
1184
|
+
getMaxValue(...args: any[]): number {
|
|
1185
|
+
let result = [...args];
|
|
1186
|
+
let newResult: number[] = [];
|
|
1187
|
+
if (result.length === 0) {
|
|
1188
|
+
// @ts-ignore
|
|
1189
|
+
return;
|
|
1190
|
+
}
|
|
1191
|
+
if (result.length > 1) {
|
|
1192
|
+
if (
|
|
1193
|
+
result.length === 2 &&
|
|
1194
|
+
typeof result[0] === "object" &&
|
|
1195
|
+
typeof result[1] === "function"
|
|
1196
|
+
) {
|
|
1197
|
+
let data = result[0];
|
|
1198
|
+
let handleDataFunc = result[1];
|
|
1199
|
+
Object.keys(data).forEach((keyName) => {
|
|
1200
|
+
newResult = [...newResult, handleDataFunc(keyName, data[keyName])];
|
|
1201
|
+
});
|
|
1202
|
+
} else {
|
|
1203
|
+
result.forEach((item) => {
|
|
1204
|
+
if (!isNaN(parseFloat(item))) {
|
|
1205
|
+
newResult = [...newResult, parseFloat(item)];
|
|
1206
|
+
}
|
|
1207
|
+
});
|
|
1208
|
+
}
|
|
1209
|
+
return Math.max(...newResult);
|
|
1210
|
+
} else {
|
|
1211
|
+
result[0].forEach((item: any) => {
|
|
1212
|
+
if (!isNaN(parseFloat(item))) {
|
|
1213
|
+
newResult = [...newResult, parseFloat(item)];
|
|
1214
|
+
}
|
|
1215
|
+
});
|
|
1216
|
+
return Math.max(...newResult);
|
|
1217
|
+
}
|
|
1218
|
+
}
|
|
1219
|
+
/**
|
|
1220
|
+
* 获取页面中最大的z-index
|
|
1221
|
+
* @param deviation 获取最大的z-index值的偏移,默认是+1
|
|
1222
|
+
* @example
|
|
1223
|
+
* Utils.getMaxZIndex();
|
|
1224
|
+
* > 1001
|
|
1225
|
+
**/
|
|
1226
|
+
getMaxZIndex(deviation?: number): number;
|
|
1227
|
+
getMaxZIndex(deviation = 1): number {
|
|
1228
|
+
deviation = Number.isNaN(deviation) ? 1 : deviation;
|
|
1229
|
+
// 最大值2147483647
|
|
1230
|
+
let maxZIndex = Math.pow(2, 31) - 1;
|
|
1231
|
+
// 比较值2000000000
|
|
1232
|
+
let maxZIndexCompare = 2 * Math.pow(10, 9);
|
|
1233
|
+
// 当前页面最大的z-index
|
|
1234
|
+
let zIndex = 0;
|
|
1235
|
+
// 当前的最大z-index的元素,调试使用
|
|
1236
|
+
// @ts-ignore
|
|
1237
|
+
let maxZIndexNode = null;
|
|
1238
|
+
UtilsCore.document.querySelectorAll("*").forEach(($ele, index) => {
|
|
1239
|
+
let nodeStyle = UtilsCore.window.getComputedStyle($ele);
|
|
1240
|
+
/* 不对position为static和display为none的元素进行获取它们的z-index */
|
|
1241
|
+
if (nodeStyle.position !== "static" && nodeStyle.display !== "none") {
|
|
1242
|
+
let nodeZIndex = parseInt(nodeStyle.zIndex);
|
|
1243
|
+
if (!isNaN(nodeZIndex)) {
|
|
1244
|
+
if (nodeZIndex > zIndex) {
|
|
1245
|
+
zIndex = nodeZIndex;
|
|
1246
|
+
maxZIndexNode = $ele;
|
|
1247
|
+
}
|
|
1248
|
+
}
|
|
1249
|
+
}
|
|
1250
|
+
});
|
|
1251
|
+
zIndex += deviation;
|
|
1252
|
+
if (zIndex >= maxZIndexCompare) {
|
|
1253
|
+
// 最好不要超过最大值
|
|
1254
|
+
zIndex = maxZIndex;
|
|
1255
|
+
}
|
|
1256
|
+
return zIndex;
|
|
1257
|
+
}
|
|
1258
|
+
/**
|
|
1259
|
+
* 获取最小值
|
|
1260
|
+
* @example
|
|
1261
|
+
* Utils.getMinValue(1,3,5,7,9)
|
|
1262
|
+
* > 1
|
|
1263
|
+
*/
|
|
1264
|
+
getMinValue(...args: number[]): number;
|
|
1265
|
+
/**
|
|
1266
|
+
* 获取最小值
|
|
1267
|
+
* @example
|
|
1268
|
+
* Utils.getMinValue([1,3,5])
|
|
1269
|
+
* > 1
|
|
1270
|
+
*/
|
|
1271
|
+
getMinValue(val: number[]): number;
|
|
1272
|
+
/**
|
|
1273
|
+
* 获取最小值
|
|
1274
|
+
* @example
|
|
1275
|
+
* Utils.getMinValue({1:123,2:345,3:456},(key,value)=>{return parseInt(value)})
|
|
1276
|
+
* > 123
|
|
1277
|
+
*/
|
|
1278
|
+
getMinValue(
|
|
1279
|
+
val: UtilsOwnObject<number>,
|
|
1280
|
+
handler: (key: any, value: any) => number
|
|
1281
|
+
): number;
|
|
1282
|
+
/**
|
|
1283
|
+
* 获取最小值
|
|
1284
|
+
* @example
|
|
1285
|
+
* Utils.getMinValue([{1:123},{2:345},{3:456}],(index,value)=>{return parseInt(index)})
|
|
1286
|
+
* > 0
|
|
1287
|
+
*/
|
|
1288
|
+
getMinValue(
|
|
1289
|
+
val: UtilsOwnObject<number>[],
|
|
1290
|
+
handler: (index: number, value: any) => number
|
|
1291
|
+
): number;
|
|
1292
|
+
getMinValue(...args: any[]): number {
|
|
1293
|
+
let result = [...args];
|
|
1294
|
+
let newResult: number[] = [];
|
|
1295
|
+
if (result.length === 0) {
|
|
1296
|
+
// @ts-ignore
|
|
1297
|
+
return;
|
|
1298
|
+
}
|
|
1299
|
+
if (result.length > 1) {
|
|
1300
|
+
if (
|
|
1301
|
+
result.length === 2 &&
|
|
1302
|
+
typeof result[0] === "object" &&
|
|
1303
|
+
typeof result[1] === "function"
|
|
1304
|
+
) {
|
|
1305
|
+
let data = result[0];
|
|
1306
|
+
let handleDataFunc = result[1];
|
|
1307
|
+
Object.keys(data).forEach((keyName) => {
|
|
1308
|
+
newResult = [...newResult, handleDataFunc(keyName, data[keyName])];
|
|
1309
|
+
});
|
|
1310
|
+
} else {
|
|
1311
|
+
result.forEach((item) => {
|
|
1312
|
+
if (!isNaN(parseFloat(item))) {
|
|
1313
|
+
newResult = [...newResult, parseFloat(item)];
|
|
1314
|
+
}
|
|
1315
|
+
});
|
|
1316
|
+
}
|
|
1317
|
+
return Math.min(...newResult);
|
|
1318
|
+
} else {
|
|
1319
|
+
result[0].forEach((item: any) => {
|
|
1320
|
+
if (!isNaN(parseFloat(item))) {
|
|
1321
|
+
newResult = [...newResult, parseFloat(item)];
|
|
1322
|
+
}
|
|
1323
|
+
});
|
|
1324
|
+
return Math.min(...newResult);
|
|
1325
|
+
}
|
|
1326
|
+
}
|
|
1327
|
+
/**
|
|
1328
|
+
* 获取随机的安卓手机User-Agent
|
|
1329
|
+
* @example
|
|
1330
|
+
* Utils.getRandomAndroidUA();
|
|
1331
|
+
* > 'Mozilla/5.0 (Linux; Android 10; MI 13 Build/OPR1.170623.027; wv) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/103.0.3490.40 Mobile Safari/537.36'
|
|
1332
|
+
**/
|
|
1333
|
+
getRandomAndroidUA(): string;
|
|
1334
|
+
getRandomAndroidUA(): string {
|
|
1335
|
+
let UtilsContext = this;
|
|
1336
|
+
let mobileNameList = [
|
|
1337
|
+
"LDN-LX3",
|
|
1338
|
+
"RNE-L03",
|
|
1339
|
+
"ASUS_X00ID Build/NMF26F",
|
|
1340
|
+
"WAS-LX3",
|
|
1341
|
+
"PRA-LX3",
|
|
1342
|
+
"MYA-L03",
|
|
1343
|
+
"Moto G Play",
|
|
1344
|
+
"Moto C Build/NRD90M.063",
|
|
1345
|
+
"Redmi Note 4 Build/NRD90M",
|
|
1346
|
+
"HUAWEI VNS-L21 Build/HUAWEIVNS-L21",
|
|
1347
|
+
"VTR-L09",
|
|
1348
|
+
"TRT-LX3",
|
|
1349
|
+
"M2003J15SC Build/RP1A.200720.011; wv",
|
|
1350
|
+
"MI 13 Build/OPR1.170623.027; wv",
|
|
1351
|
+
];
|
|
1352
|
+
let androidVersion = UtilsContext.getRandomValue(12, 14);
|
|
1353
|
+
let randomMobile = UtilsContext.getRandomValue(mobileNameList);
|
|
1354
|
+
let chromeVersion1 = UtilsContext.getRandomValue(115, 127);
|
|
1355
|
+
let chromeVersion2 = UtilsContext.getRandomValue(0, 0);
|
|
1356
|
+
let chromeVersion3 = UtilsContext.getRandomValue(2272, 6099);
|
|
1357
|
+
let chromeVersion4 = UtilsContext.getRandomValue(1, 218);
|
|
1358
|
+
return `Mozilla/5.0 (Linux; Android ${androidVersion}; ${randomMobile}) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/${chromeVersion1}.${chromeVersion2}.${chromeVersion3}.${chromeVersion4} Mobile Safari/537.36`;
|
|
1359
|
+
}
|
|
1360
|
+
/**
|
|
1361
|
+
* 获取随机值
|
|
1362
|
+
* @example
|
|
1363
|
+
* Utils.getRandomValue(1,9,6,99)
|
|
1364
|
+
* > 6
|
|
1365
|
+
*/
|
|
1366
|
+
getRandomValue<T extends any>(...args: T[]): T;
|
|
1367
|
+
/**
|
|
1368
|
+
* 获取随机值
|
|
1369
|
+
* @example
|
|
1370
|
+
* Utils.getRandomValue([1,2,3])
|
|
1371
|
+
* > 3
|
|
1372
|
+
* @example
|
|
1373
|
+
* Utils.getRandomValue({1:"结果1",2:"结果2",3:"结果3"}})
|
|
1374
|
+
* > 结果2
|
|
1375
|
+
*/
|
|
1376
|
+
getRandomValue<T extends any>(val: T[] | UtilsOwnObject<T>): T;
|
|
1377
|
+
/**
|
|
1378
|
+
* 获取两个数之间随机值
|
|
1379
|
+
* @example
|
|
1380
|
+
* Utils.getRandomValue(1,9)
|
|
1381
|
+
* > 6
|
|
1382
|
+
*/
|
|
1383
|
+
getRandomValue(val_1: number, val_2: number): number;
|
|
1384
|
+
/**
|
|
1385
|
+
* 获取随机值
|
|
1386
|
+
* @example
|
|
1387
|
+
* Utils.getRandomValue({1:1},{2:2})
|
|
1388
|
+
* > {1: 1}
|
|
1389
|
+
*/
|
|
1390
|
+
getRandomValue<T extends any>(
|
|
1391
|
+
val_1: UtilsOwnObject<T>,
|
|
1392
|
+
val_2: UtilsOwnObject<T>
|
|
1393
|
+
): T;
|
|
1394
|
+
getRandomValue(...args: any[]): any {
|
|
1395
|
+
let result = [...args];
|
|
1396
|
+
if (result.length > 1) {
|
|
1397
|
+
if (
|
|
1398
|
+
result.length === 2 &&
|
|
1399
|
+
typeof result[0] === "number" &&
|
|
1400
|
+
typeof result[1] === "number"
|
|
1401
|
+
) {
|
|
1402
|
+
let leftNumber = result[0] > result[1] ? result[1] : result[0];
|
|
1403
|
+
let rightNumber = result[0] > result[1] ? result[0] : result[1];
|
|
1404
|
+
return (
|
|
1405
|
+
Math.round(Math.random() * (rightNumber - leftNumber)) + leftNumber
|
|
1406
|
+
);
|
|
1407
|
+
} else {
|
|
1408
|
+
return result[Math.floor(Math.random() * result.length)];
|
|
1409
|
+
}
|
|
1410
|
+
} else if (result.length === 1) {
|
|
1411
|
+
let paramData = result[0];
|
|
1412
|
+
if (Array.isArray(paramData)) {
|
|
1413
|
+
return paramData[Math.floor(Math.random() * paramData.length)];
|
|
1414
|
+
} else if (
|
|
1415
|
+
typeof paramData === "object" &&
|
|
1416
|
+
Object.keys(paramData).length > 0
|
|
1417
|
+
) {
|
|
1418
|
+
let paramObjDataKey =
|
|
1419
|
+
Object.keys(paramData)[
|
|
1420
|
+
Math.floor(Math.random() * Object.keys(paramData).length)
|
|
1421
|
+
];
|
|
1422
|
+
return paramData[paramObjDataKey];
|
|
1423
|
+
} else {
|
|
1424
|
+
return paramData;
|
|
1425
|
+
}
|
|
1426
|
+
}
|
|
1427
|
+
}
|
|
1428
|
+
/**
|
|
1429
|
+
* 获取随机的电脑端User-Agent
|
|
1430
|
+
* + Mozilla/5.0:以前用于Netscape浏览器,目前大多数浏览器UA都会带有
|
|
1431
|
+
* + Windows NT 13:代表Window11系统
|
|
1432
|
+
* + Windows NT 10.0:代表Window10系统
|
|
1433
|
+
* + Windows NT 6.1:代表windows7系统
|
|
1434
|
+
* + WOW64:Windows-on-Windows 64-bit,32位的应用程序运行于此64位处理器上
|
|
1435
|
+
* + Win64:64位
|
|
1436
|
+
* + AppleWebKit/537.36:浏览器内核
|
|
1437
|
+
* + KHTML:HTML排版引擎
|
|
1438
|
+
* + like Gecko:这不是Geckeo 浏览器,但是运行起来像Geckeo浏览器
|
|
1439
|
+
* + Chrome/106.0.5068.19:Chrome版本号
|
|
1440
|
+
* + Safari/537.36:宣称自己是Safari?
|
|
1441
|
+
* @returns 返回随机字符串
|
|
1442
|
+
* @example
|
|
1443
|
+
* Utils.getRandomPCUA();
|
|
1444
|
+
* > 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/106.0.5068.19 Safari/537.36'
|
|
1445
|
+
**/
|
|
1446
|
+
getRandomPCUA(): string {
|
|
1447
|
+
let UtilsContext = this;
|
|
1448
|
+
let chromeVersion1 = UtilsContext.getRandomValue(115, 127);
|
|
1449
|
+
let chromeVersion2 = UtilsContext.getRandomValue(0, 0);
|
|
1450
|
+
let chromeVersion3 = UtilsContext.getRandomValue(2272, 6099);
|
|
1451
|
+
let chromeVersion4 = UtilsContext.getRandomValue(1, 218);
|
|
1452
|
+
return `Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/${chromeVersion1}.${chromeVersion2}.${chromeVersion3}.${chromeVersion4} Safari/537.36`;
|
|
1453
|
+
}
|
|
1454
|
+
/**
|
|
1455
|
+
* 获取元素上的使用React框架的实例属性,目前包括reactFiber、reactProps、reactEvents、reactEventHandlers、reactInternalInstance
|
|
1456
|
+
* @param element 需要获取的目标元素
|
|
1457
|
+
* @returns
|
|
1458
|
+
* @example
|
|
1459
|
+
* Utils.getReactObj(document.querySelector("input"))?.reactProps?.onChange({target:{value:"123"}});
|
|
1460
|
+
*/
|
|
1461
|
+
getReactObj(element: HTMLElement | Element): {
|
|
1462
|
+
reactFiber?: AnyObject;
|
|
1463
|
+
reactProps?: AnyObject;
|
|
1464
|
+
reactEvents?: AnyObject;
|
|
1465
|
+
reactEventHandlers?: AnyObject;
|
|
1466
|
+
reactInternalInstance?: AnyObject;
|
|
1467
|
+
reactContainer?: AnyObject;
|
|
1468
|
+
} {
|
|
1469
|
+
let result = {};
|
|
1470
|
+
Object.keys(element).forEach((domPropsName) => {
|
|
1471
|
+
if (domPropsName.startsWith("__react")) {
|
|
1472
|
+
let propsName = domPropsName.replace(/__(.+)\$.+/i, "$1");
|
|
1473
|
+
if (propsName in result) {
|
|
1474
|
+
new Error("重复属性 " + domPropsName);
|
|
1475
|
+
} else {
|
|
1476
|
+
(result as any)[propsName] = (element as any)[domPropsName];
|
|
1477
|
+
}
|
|
1478
|
+
}
|
|
1479
|
+
});
|
|
1480
|
+
return result;
|
|
1481
|
+
}
|
|
1482
|
+
/**
|
|
1483
|
+
* 获取对象上的Symbol属性,如果没设置keyName,那么返回一个对象,对象是所有遍历到的Symbol对象
|
|
1484
|
+
* @param target 目标对象
|
|
1485
|
+
* @param keyName (可选)Symbol名或者Symbol对象
|
|
1486
|
+
*/
|
|
1487
|
+
getSymbol(target: any, keyName?: string | symbol) {
|
|
1488
|
+
if (typeof target !== "object") {
|
|
1489
|
+
throw new TypeError("target不是一个对象");
|
|
1490
|
+
}
|
|
1491
|
+
let objectsSymbols = Object.getOwnPropertySymbols(target);
|
|
1492
|
+
if (typeof keyName === "string") {
|
|
1493
|
+
let findSymbol = objectsSymbols.find((key) => {
|
|
1494
|
+
return key.toString() === keyName;
|
|
1495
|
+
});
|
|
1496
|
+
if (findSymbol) {
|
|
1497
|
+
return target[findSymbol];
|
|
1498
|
+
}
|
|
1499
|
+
} else if (typeof keyName === "symbol") {
|
|
1500
|
+
let findSymbol = objectsSymbols.find((key) => {
|
|
1501
|
+
return key === keyName;
|
|
1502
|
+
});
|
|
1503
|
+
if (findSymbol) {
|
|
1504
|
+
return (target as any)[findSymbol];
|
|
1505
|
+
}
|
|
1506
|
+
} else {
|
|
1507
|
+
let result = {};
|
|
1508
|
+
objectsSymbols.forEach((item) => {
|
|
1509
|
+
(result as any)[item] = target[item];
|
|
1510
|
+
});
|
|
1511
|
+
return result;
|
|
1512
|
+
}
|
|
1513
|
+
}
|
|
1514
|
+
/**
|
|
1515
|
+
* 获取文本的字符长度
|
|
1516
|
+
* @param text
|
|
1517
|
+
* @example
|
|
1518
|
+
* Utils.getTextLength("测试文本")
|
|
1519
|
+
* > 12
|
|
1520
|
+
*/
|
|
1521
|
+
getTextLength(text: string): number {
|
|
1522
|
+
let encoder = new TextEncoder();
|
|
1523
|
+
let bytes = encoder.encode(text);
|
|
1524
|
+
return bytes.length;
|
|
1525
|
+
}
|
|
1526
|
+
/**
|
|
1527
|
+
* 获取文本占据的空间大小,返回自动的单位,如12 Kb,14 K,20 MB,1 GB
|
|
1528
|
+
* @param text 目标字符串
|
|
1529
|
+
* @param addType (可选)是否添加单位
|
|
1530
|
+
* + true (默认) 自动添加单位
|
|
1531
|
+
* + false 不添加单位
|
|
1532
|
+
* @example
|
|
1533
|
+
* Utils.getTextStorageSize("测试文本");
|
|
1534
|
+
* > '12.00B'
|
|
1535
|
+
*/
|
|
1536
|
+
getTextStorageSize<T extends boolean>(
|
|
1537
|
+
text: string,
|
|
1538
|
+
addType?: T
|
|
1539
|
+
): T extends true ? string : number;
|
|
1540
|
+
getTextStorageSize(text: string, addType = true) {
|
|
1541
|
+
let UtilsContext = this;
|
|
1542
|
+
return UtilsContext.formatByteToSize(
|
|
1543
|
+
UtilsContext.getTextLength(text),
|
|
1544
|
+
addType
|
|
1545
|
+
);
|
|
1546
|
+
}
|
|
1547
|
+
/**
|
|
1548
|
+
* 获取迅雷协议的Url
|
|
1549
|
+
* @param url Url链接或者其它信息
|
|
1550
|
+
*/
|
|
1551
|
+
getThunderUrl(url: string): string;
|
|
1552
|
+
getThunderUrl(url: string): string {
|
|
1553
|
+
if (url == null) {
|
|
1554
|
+
throw new TypeError("url不能为空");
|
|
1555
|
+
}
|
|
1556
|
+
if (typeof url !== "string") {
|
|
1557
|
+
throw new TypeError("url必须是string类型");
|
|
1558
|
+
}
|
|
1559
|
+
if (url.trim() === "") {
|
|
1560
|
+
throw new TypeError("url不能为空字符串或纯空格");
|
|
1561
|
+
}
|
|
1562
|
+
return `thunder://${UtilsCore.globalThis.btoa("AA" + url + "ZZ")}`;
|
|
1563
|
+
}
|
|
1564
|
+
/**
|
|
1565
|
+
* 对于GM_cookie的兼容写法,当无法使用GM_cookie时可以使用这个,但是并不完全兼容,有些写不出来且限制了httponly是无法访问的
|
|
1566
|
+
* @example
|
|
1567
|
+
let GM_cookie = new Utils.GM_Cookie();
|
|
1568
|
+
GM_cookie.list({name:"xxx_cookie_xxx"},function(cookies,error){
|
|
1569
|
+
if (!error) {
|
|
1570
|
+
console.log(cookies);
|
|
1571
|
+
console.log(cookies.value);
|
|
1572
|
+
} else {
|
|
1573
|
+
console.error(error);
|
|
1574
|
+
}
|
|
1575
|
+
});
|
|
1576
|
+
GM_cookie.set({name:"xxx_cookie_test_xxx",value:"这是Cookie测试值"},function(error){
|
|
1577
|
+
if (error) {
|
|
1578
|
+
console.error(error);
|
|
1579
|
+
} else {
|
|
1580
|
+
console.log('Cookie set successfully.');
|
|
1581
|
+
}
|
|
1582
|
+
})
|
|
1583
|
+
GM_cookie.delete({name:"xxx_cookie_test_xxx"},function(error){
|
|
1584
|
+
if (error) {
|
|
1585
|
+
console.error(error);
|
|
1586
|
+
} else {
|
|
1587
|
+
console.log('Cookie set successfully.');
|
|
1588
|
+
}
|
|
1589
|
+
})
|
|
1590
|
+
**/
|
|
1591
|
+
GM_Cookie = UtilsGMCookie;
|
|
1592
|
+
/**
|
|
1593
|
+
* 注册油猴菜单,要求本地存储的键名不能存在其它键名`GM_Menu_Local_Map`会冲突/覆盖
|
|
1594
|
+
* @example
|
|
1595
|
+
let GM_Menu = new Utils.GM_Menu({
|
|
1596
|
+
data: [
|
|
1597
|
+
{
|
|
1598
|
+
menu_key: "menu_key",
|
|
1599
|
+
text: "测试按钮",
|
|
1600
|
+
enable: true,
|
|
1601
|
+
accessKey: "a",
|
|
1602
|
+
autoClose: false,
|
|
1603
|
+
showText(text, enable) {
|
|
1604
|
+
return "[" + (enable ? "√" : "×") + "]" + text;
|
|
1605
|
+
},
|
|
1606
|
+
callback(data) {
|
|
1607
|
+
console.log("点击菜单,值修改为", data.enable);
|
|
1608
|
+
},
|
|
1609
|
+
},
|
|
1610
|
+
],
|
|
1611
|
+
autoReload: false,
|
|
1612
|
+
GM_getValue,
|
|
1613
|
+
GM_setValue,
|
|
1614
|
+
GM_registerMenuCommand,
|
|
1615
|
+
GM_unregisterMenuCommand,
|
|
1616
|
+
});
|
|
1617
|
+
|
|
1618
|
+
|
|
1619
|
+
// 获取某个菜单项的值
|
|
1620
|
+
GM_Menu.get("menu_key");
|
|
1621
|
+
> true
|
|
1622
|
+
|
|
1623
|
+
// 获取某个菜单项的开启/关闭后显示的文本
|
|
1624
|
+
GM_Menu.getShowTextValue("menu_key");
|
|
1625
|
+
> √测试按钮
|
|
1626
|
+
|
|
1627
|
+
// 添加键为menu_key2的菜单项
|
|
1628
|
+
GM_Menu.add({
|
|
1629
|
+
key:"menu_key2",
|
|
1630
|
+
text: "测试按钮2",
|
|
1631
|
+
enable: false,
|
|
1632
|
+
showText(text,enable){
|
|
1633
|
+
return "[" + (enable ? "√" : "×") + "]" + text;
|
|
1634
|
+
},
|
|
1635
|
+
callback(data){
|
|
1636
|
+
console.log("点击菜单,值修改为",data.enable);
|
|
1637
|
+
}
|
|
1638
|
+
});
|
|
1639
|
+
// 使用数组的方式添加多个菜单,如menu_key3、menu_key4
|
|
1640
|
+
GM_Menu.add([
|
|
1641
|
+
{
|
|
1642
|
+
key:"menu_key3",
|
|
1643
|
+
text: "测试按钮3",
|
|
1644
|
+
enable: false,
|
|
1645
|
+
showText(text,enable){
|
|
1646
|
+
return "[" + (enable ? "√" : "×") + "]" + text;
|
|
1647
|
+
},
|
|
1648
|
+
callback(data){
|
|
1649
|
+
console.log("点击菜单,值修改为",data.enable);
|
|
1650
|
+
}
|
|
1651
|
+
},
|
|
1652
|
+
{
|
|
1653
|
+
key:"menu_key4",
|
|
1654
|
+
text: "测试按钮4",
|
|
1655
|
+
enable: false,
|
|
1656
|
+
showText(text,enable){
|
|
1657
|
+
return "[" + (enable ? "√" : "×") + "]" + text;
|
|
1658
|
+
},
|
|
1659
|
+
callback(data){
|
|
1660
|
+
console.log("点击菜单,值修改为",data.enable);
|
|
1661
|
+
}
|
|
1662
|
+
}
|
|
1663
|
+
]);
|
|
1664
|
+
|
|
1665
|
+
// 更新键为menu_key的显示文字和点击回调
|
|
1666
|
+
GM_Menu.update({
|
|
1667
|
+
menu_key:{
|
|
1668
|
+
text: "更新后的测试按钮",
|
|
1669
|
+
enable: true,
|
|
1670
|
+
showText(text,enable){
|
|
1671
|
+
return "[" + (enable ? "√" : "×") + "]" + text;
|
|
1672
|
+
},
|
|
1673
|
+
callback(data){
|
|
1674
|
+
console.log("点击菜单更新后的测试按钮,新值修改为",data.enable);
|
|
1675
|
+
}
|
|
1676
|
+
}
|
|
1677
|
+
});
|
|
1678
|
+
|
|
1679
|
+
// 删除键为menu_key的菜单
|
|
1680
|
+
GM_Menu.delete("menu_key");
|
|
1681
|
+
**/
|
|
1682
|
+
GM_Menu = GMMenu;
|
|
1683
|
+
/**
|
|
1684
|
+
* 基于Function prototype,能够勾住和释放任何函数
|
|
1685
|
+
*
|
|
1686
|
+
* .hook
|
|
1687
|
+
* + realFunc {string} 用于保存原始函数的函数名称,用于unHook
|
|
1688
|
+
* + hookFunc {string} 替换的hook函数
|
|
1689
|
+
* + context {object} 目标函数所在对象,用于hook非window对象下的函数,如String.protype.slice,carInstance1
|
|
1690
|
+
* + methodName {string} 匿名函数需显式传入目标函数名eg:this.Begin = function(){....};}
|
|
1691
|
+
*
|
|
1692
|
+
* .unhook
|
|
1693
|
+
* + realFunc {string} 用于保存原始函数的函数名称,用于unHook
|
|
1694
|
+
* + funcName {string} 被Hook的函数名称
|
|
1695
|
+
* + context {object} 目标函数所在对象,用于hook非window对象下的函数,如String.protype.slice,carInstance1
|
|
1696
|
+
* @example
|
|
1697
|
+
let hook = new Utils.Hooks();
|
|
1698
|
+
hook.initEnv();
|
|
1699
|
+
function myFunction(){
|
|
1700
|
+
console.log("我自己需要执行的函数");
|
|
1701
|
+
}
|
|
1702
|
+
function testFunction(){
|
|
1703
|
+
console.log("正常执行的函数");
|
|
1704
|
+
}
|
|
1705
|
+
testFunction.hook(testFunction,myFunction,window);
|
|
1706
|
+
**/
|
|
1707
|
+
Hooks = Hooks;
|
|
1708
|
+
|
|
1709
|
+
/**
|
|
1710
|
+
* 为减少代码量和回调,把GM_xmlhttpRequest封装
|
|
1711
|
+
* 文档地址: https://www.tampermonkey.net/documentation.php?ext=iikm
|
|
1712
|
+
* 其中onloadstart、onprogress、onreadystatechange是回调形式,onabort、ontimeout、onerror可以设置全局回调函数
|
|
1713
|
+
* @param _GM_xmlHttpRequest_ 油猴中的GM_xmlhttpRequest
|
|
1714
|
+
* @example
|
|
1715
|
+
let httpx = new Utils.Httpx(GM_xmlhttpRequest);
|
|
1716
|
+
let postResp = await httpx.post({
|
|
1717
|
+
url:url,
|
|
1718
|
+
data:JSON.stringify({
|
|
1719
|
+
test:1
|
|
1720
|
+
}),
|
|
1721
|
+
timeout: 5000
|
|
1722
|
+
});
|
|
1723
|
+
console.log(postResp);
|
|
1724
|
+
> {
|
|
1725
|
+
status: true,
|
|
1726
|
+
data: {responseText: "...", response: xxx,...},
|
|
1727
|
+
msg: "请求完毕",
|
|
1728
|
+
type: "onload",
|
|
1729
|
+
}
|
|
1730
|
+
|
|
1731
|
+
if(postResp === "onload" && postResp.status){
|
|
1732
|
+
// onload
|
|
1733
|
+
}else if(postResp === "ontimeout"){
|
|
1734
|
+
// ontimeout
|
|
1735
|
+
}
|
|
1736
|
+
* @example
|
|
1737
|
+
// 也可以先配置全局参数
|
|
1738
|
+
let httpx = new Utils.Httpx(GM_xmlhttpRequest);
|
|
1739
|
+
httpx.config({
|
|
1740
|
+
timeout: 5000,
|
|
1741
|
+
async: false,
|
|
1742
|
+
responseType: "html",
|
|
1743
|
+
redirect: "follow",
|
|
1744
|
+
})
|
|
1745
|
+
// 优先级为 默认details < 全局details < 单独的details
|
|
1746
|
+
*/
|
|
1747
|
+
Httpx = Httpx;
|
|
1748
|
+
/**
|
|
1749
|
+
* 浏览器端的indexedDB操作封装
|
|
1750
|
+
* @example
|
|
1751
|
+
let db = new Utils.indexedDB('web_DB', 'nav_text')
|
|
1752
|
+
let data = {name:'管理员', roleId: 1, type: 1};
|
|
1753
|
+
db.save('list',data).then((resolve)=>{
|
|
1754
|
+
console.log(resolve,'存储成功')
|
|
1755
|
+
})
|
|
1756
|
+
|
|
1757
|
+
db.get('list').then((resolve)=>{
|
|
1758
|
+
console.log(resolve,'查询成功')
|
|
1759
|
+
})
|
|
1760
|
+
|
|
1761
|
+
db.getPaging('list',20,10).then((resolve)=>{
|
|
1762
|
+
console.log(resolve,'查询分页偏移第20,一共10行成功');
|
|
1763
|
+
})
|
|
1764
|
+
|
|
1765
|
+
db.delete('list').then(resolve=>{
|
|
1766
|
+
console.log(resolve,'删除成功---->>>>>>name')
|
|
1767
|
+
})
|
|
1768
|
+
|
|
1769
|
+
db.deleteAll().then(resolve=>{
|
|
1770
|
+
console.log(resolve,'清除数据库---->>>>>>name')
|
|
1771
|
+
})
|
|
1772
|
+
**/
|
|
1773
|
+
indexedDB = indexedDB;
|
|
1774
|
+
/**
|
|
1775
|
+
* 判断目标函数是否是Native Code
|
|
1776
|
+
* @param target
|
|
1777
|
+
* @returns
|
|
1778
|
+
* + true 是Native
|
|
1779
|
+
* + false 不是Native
|
|
1780
|
+
* @example
|
|
1781
|
+
* Utils.isNativeFunc(window.location.assign)
|
|
1782
|
+
* > true
|
|
1783
|
+
*/
|
|
1784
|
+
isNativeFunc(target: Function): boolean;
|
|
1785
|
+
isNativeFunc(target: Function): boolean {
|
|
1786
|
+
return Boolean(
|
|
1787
|
+
target.toString().match(/^function .*\(\) { \[native code\] }$/)
|
|
1788
|
+
);
|
|
1789
|
+
}
|
|
1790
|
+
/**
|
|
1791
|
+
* 判断当前的位置是否位于页面底部附近
|
|
1792
|
+
* @param nearValue (可选)判断在页面底部的误差值,默认:50
|
|
1793
|
+
* @returns
|
|
1794
|
+
* + true 在底部附近
|
|
1795
|
+
* + false 不在底部附近
|
|
1796
|
+
*/
|
|
1797
|
+
isNearBottom(nearValue?: number): boolean;
|
|
1798
|
+
isNearBottom(nearValue: number = 50): boolean {
|
|
1799
|
+
var scrollTop =
|
|
1800
|
+
UtilsCore.window.pageYOffset ||
|
|
1801
|
+
UtilsCore.document.documentElement.scrollTop;
|
|
1802
|
+
var windowHeight =
|
|
1803
|
+
UtilsCore.window.innerHeight ||
|
|
1804
|
+
UtilsCore.document.documentElement.clientHeight;
|
|
1805
|
+
var documentHeight = UtilsCore.document.documentElement.scrollHeight;
|
|
1806
|
+
return scrollTop + windowHeight >= documentHeight - nearValue;
|
|
1807
|
+
}
|
|
1808
|
+
/**
|
|
1809
|
+
* 判断对象是否是元素
|
|
1810
|
+
* @param target
|
|
1811
|
+
* @returns
|
|
1812
|
+
* + true 是元素
|
|
1813
|
+
* + false 不是元素
|
|
1814
|
+
* @example
|
|
1815
|
+
* Utils.isDOM(document.querySelector("a"))
|
|
1816
|
+
* > true
|
|
1817
|
+
*/
|
|
1818
|
+
isDOM(target: any): boolean;
|
|
1819
|
+
isDOM(target: any): boolean {
|
|
1820
|
+
return target instanceof Node;
|
|
1821
|
+
}
|
|
1822
|
+
/**
|
|
1823
|
+
* 判断浏览器是否支持全屏
|
|
1824
|
+
*/
|
|
1825
|
+
isFullscreenEnabled(): boolean;
|
|
1826
|
+
isFullscreenEnabled(): boolean {
|
|
1827
|
+
return !!(
|
|
1828
|
+
(UtilsCore.document as any).fullscreenEnabled ||
|
|
1829
|
+
(UtilsCore.document as any).webkitFullScreenEnabled ||
|
|
1830
|
+
(UtilsCore.document as any).mozFullScreenEnabled ||
|
|
1831
|
+
(UtilsCore.document as any).msFullScreenEnabled
|
|
1832
|
+
);
|
|
1833
|
+
}
|
|
1834
|
+
/**
|
|
1835
|
+
* 判断对象是否是jQuery对象
|
|
1836
|
+
* @param target
|
|
1837
|
+
* @returns
|
|
1838
|
+
* + true 是jQuery对象
|
|
1839
|
+
* + false 不是jQuery对象
|
|
1840
|
+
* @example
|
|
1841
|
+
* Utils.isJQuery($("a"))
|
|
1842
|
+
* > true
|
|
1843
|
+
*/
|
|
1844
|
+
isJQuery(target: any): boolean;
|
|
1845
|
+
isJQuery(target: any): boolean {
|
|
1846
|
+
let result = false;
|
|
1847
|
+
// @ts-ignore
|
|
1848
|
+
if (typeof jQuery === "object" && target instanceof jQuery) {
|
|
1849
|
+
result = true;
|
|
1850
|
+
}
|
|
1851
|
+
if (target == null) {
|
|
1852
|
+
return false;
|
|
1853
|
+
}
|
|
1854
|
+
if (typeof target === "object") {
|
|
1855
|
+
/* 也有种可能,这个jQuery对象是1.8.3版本的,页面中的jQuery是3.4.1版本的 */
|
|
1856
|
+
let jQueryProps = [
|
|
1857
|
+
"add",
|
|
1858
|
+
"addBack",
|
|
1859
|
+
"addClass",
|
|
1860
|
+
"after",
|
|
1861
|
+
"ajaxComplete",
|
|
1862
|
+
"ajaxError",
|
|
1863
|
+
"ajaxSend",
|
|
1864
|
+
"ajaxStart",
|
|
1865
|
+
"ajaxStop",
|
|
1866
|
+
"ajaxSuccess",
|
|
1867
|
+
"animate",
|
|
1868
|
+
"append",
|
|
1869
|
+
"appendTo",
|
|
1870
|
+
"attr",
|
|
1871
|
+
"before",
|
|
1872
|
+
"bind",
|
|
1873
|
+
"blur",
|
|
1874
|
+
"change",
|
|
1875
|
+
"children",
|
|
1876
|
+
"clearQueue",
|
|
1877
|
+
"click",
|
|
1878
|
+
"clone",
|
|
1879
|
+
"closest",
|
|
1880
|
+
"constructor",
|
|
1881
|
+
"contents",
|
|
1882
|
+
"contextmenu",
|
|
1883
|
+
"css",
|
|
1884
|
+
"data",
|
|
1885
|
+
"dblclick",
|
|
1886
|
+
"delay",
|
|
1887
|
+
"delegate",
|
|
1888
|
+
"dequeue",
|
|
1889
|
+
"each",
|
|
1890
|
+
"empty",
|
|
1891
|
+
"end",
|
|
1892
|
+
"eq",
|
|
1893
|
+
"extend",
|
|
1894
|
+
"fadeIn",
|
|
1895
|
+
"fadeOut",
|
|
1896
|
+
"fadeTo",
|
|
1897
|
+
"fadeToggle",
|
|
1898
|
+
"filter",
|
|
1899
|
+
"find",
|
|
1900
|
+
"first",
|
|
1901
|
+
"focus",
|
|
1902
|
+
"focusin",
|
|
1903
|
+
"focusout",
|
|
1904
|
+
"get",
|
|
1905
|
+
"has",
|
|
1906
|
+
"hasClass",
|
|
1907
|
+
"height",
|
|
1908
|
+
"hide",
|
|
1909
|
+
"hover",
|
|
1910
|
+
"html",
|
|
1911
|
+
"index",
|
|
1912
|
+
"init",
|
|
1913
|
+
"innerHeight",
|
|
1914
|
+
"innerWidth",
|
|
1915
|
+
"insertAfter",
|
|
1916
|
+
"insertBefore",
|
|
1917
|
+
"is",
|
|
1918
|
+
"jquery",
|
|
1919
|
+
"keydown",
|
|
1920
|
+
"keypress",
|
|
1921
|
+
"keyup",
|
|
1922
|
+
"last",
|
|
1923
|
+
"load",
|
|
1924
|
+
"map",
|
|
1925
|
+
"mousedown",
|
|
1926
|
+
"mouseenter",
|
|
1927
|
+
"mouseleave",
|
|
1928
|
+
"mousemove",
|
|
1929
|
+
"mouseout",
|
|
1930
|
+
"mouseover",
|
|
1931
|
+
"mouseup",
|
|
1932
|
+
"next",
|
|
1933
|
+
"nextAll",
|
|
1934
|
+
"not",
|
|
1935
|
+
"off",
|
|
1936
|
+
"offset",
|
|
1937
|
+
"offsetParent",
|
|
1938
|
+
"on",
|
|
1939
|
+
"one",
|
|
1940
|
+
"outerHeight",
|
|
1941
|
+
"outerWidth",
|
|
1942
|
+
"parent",
|
|
1943
|
+
"parents",
|
|
1944
|
+
"position",
|
|
1945
|
+
"prepend",
|
|
1946
|
+
"prependTo",
|
|
1947
|
+
"prev",
|
|
1948
|
+
"prevAll",
|
|
1949
|
+
"prevUntil",
|
|
1950
|
+
"promise",
|
|
1951
|
+
"prop",
|
|
1952
|
+
"pushStack",
|
|
1953
|
+
"queue",
|
|
1954
|
+
"ready",
|
|
1955
|
+
"remove",
|
|
1956
|
+
"removeAttr",
|
|
1957
|
+
"removeClass",
|
|
1958
|
+
"removeData",
|
|
1959
|
+
"removeProp",
|
|
1960
|
+
"replaceAll",
|
|
1961
|
+
"replaceWith",
|
|
1962
|
+
"resize",
|
|
1963
|
+
"scroll",
|
|
1964
|
+
"scrollLeft",
|
|
1965
|
+
"scrollTop",
|
|
1966
|
+
"select",
|
|
1967
|
+
"show",
|
|
1968
|
+
"siblings",
|
|
1969
|
+
"slice",
|
|
1970
|
+
"slideDown",
|
|
1971
|
+
"slideToggle",
|
|
1972
|
+
"slideUp",
|
|
1973
|
+
"sort",
|
|
1974
|
+
"splice",
|
|
1975
|
+
"text",
|
|
1976
|
+
"toArray",
|
|
1977
|
+
"toggle",
|
|
1978
|
+
"toggleClass",
|
|
1979
|
+
"trigger",
|
|
1980
|
+
"triggerHandler",
|
|
1981
|
+
"unbind",
|
|
1982
|
+
"width",
|
|
1983
|
+
"wrap",
|
|
1984
|
+
];
|
|
1985
|
+
for (const jQueryPropsName of jQueryProps) {
|
|
1986
|
+
if (!(jQueryPropsName in target)) {
|
|
1987
|
+
result = false;
|
|
1988
|
+
/* console.log(jQueryPropsName); */
|
|
1989
|
+
break;
|
|
1990
|
+
} else {
|
|
1991
|
+
result = true;
|
|
1992
|
+
}
|
|
1993
|
+
}
|
|
1994
|
+
}
|
|
1995
|
+
return result;
|
|
1996
|
+
}
|
|
1997
|
+
/**
|
|
1998
|
+
* 判断当前设备是否是移动端
|
|
1999
|
+
* @param userAgent (可选)UA字符串,默认使用当前的navigator.userAgent
|
|
2000
|
+
* @returns
|
|
2001
|
+
* + true 是移动端
|
|
2002
|
+
* + false 不是移动端
|
|
2003
|
+
* @example
|
|
2004
|
+
* Utils.isPhone();
|
|
2005
|
+
* > true
|
|
2006
|
+
**/
|
|
2007
|
+
isPhone(userAgent?: string): boolean;
|
|
2008
|
+
isPhone(userAgent: string = navigator.userAgent): boolean {
|
|
2009
|
+
return Boolean(/(iPhone|iPad|iPod|iOS|Android|Mobile)/i.test(userAgent));
|
|
2010
|
+
}
|
|
2011
|
+
/**
|
|
2012
|
+
* 判断传递的字符串是否是由相同的字符组成
|
|
2013
|
+
* @param targetStr 需要判断的字符串,长度(.length)需要≥2
|
|
2014
|
+
* @param coefficient 系数(默认:1),某个字符重复的系数大于它那么就是返回true,默认全部
|
|
2015
|
+
*/
|
|
2016
|
+
isSameChars(targetStr: string, coefficient?: number): boolean;
|
|
2017
|
+
isSameChars(targetStr: string, coefficient: number = 1): boolean {
|
|
2018
|
+
if (typeof targetStr !== "string") {
|
|
2019
|
+
throw new TypeError("参数 str 必须是 string 类型");
|
|
2020
|
+
}
|
|
2021
|
+
if (targetStr.length < 2) {
|
|
2022
|
+
return false;
|
|
2023
|
+
}
|
|
2024
|
+
targetStr = targetStr.toLowerCase();
|
|
2025
|
+
const targetCharMap: UtilsOwnObject<string> = {};
|
|
2026
|
+
let targetStrLength = 0;
|
|
2027
|
+
for (const char of targetStr) {
|
|
2028
|
+
if (Reflect.has(targetCharMap, char)) {
|
|
2029
|
+
(targetCharMap as any)[char]++;
|
|
2030
|
+
} else {
|
|
2031
|
+
(targetCharMap as any)[char] = 1;
|
|
2032
|
+
}
|
|
2033
|
+
targetStrLength++;
|
|
2034
|
+
}
|
|
2035
|
+
let result = false;
|
|
2036
|
+
for (const char in targetCharMap) {
|
|
2037
|
+
if ((targetCharMap as any)[char] / targetStrLength >= coefficient) {
|
|
2038
|
+
result = true;
|
|
2039
|
+
break;
|
|
2040
|
+
}
|
|
2041
|
+
}
|
|
2042
|
+
return result;
|
|
2043
|
+
}
|
|
2044
|
+
/**
|
|
2045
|
+
* 判断对象是否不为空
|
|
2046
|
+
* @returns {boolean}
|
|
2047
|
+
* + true 不为空
|
|
2048
|
+
* + false 为空
|
|
2049
|
+
* @example
|
|
2050
|
+
* Utils.isNotNull("123");
|
|
2051
|
+
* > true
|
|
2052
|
+
*/
|
|
2053
|
+
isNotNull<T>(value: T | null): value is T;
|
|
2054
|
+
isNotNull(...args: any[]): boolean;
|
|
2055
|
+
isNotNull(...args: any[]): boolean {
|
|
2056
|
+
let UtilsContext = this;
|
|
2057
|
+
return !UtilsContext.isNull.apply(this, args);
|
|
2058
|
+
}
|
|
2059
|
+
/**
|
|
2060
|
+
* 判断对象或数据是否为空
|
|
2061
|
+
* + `String`判空的值,如 ""、"null"、"undefined"、" "
|
|
2062
|
+
* + `Number`判空的值,如 0
|
|
2063
|
+
* + `Object`判空的值,如 {}、null、undefined
|
|
2064
|
+
* + `Array`(存在属性Symbol.iterator)判空的值,如 []
|
|
2065
|
+
* + `Boolean`判空的值,如false
|
|
2066
|
+
* + `Function`判空的值,如()=>{}、(xxx="")=>{}、function(){}、function(xxx=""){}
|
|
2067
|
+
* @returns
|
|
2068
|
+
* + true 为空
|
|
2069
|
+
* + false 不为空
|
|
2070
|
+
* @example
|
|
2071
|
+
Utils.isNull({});
|
|
2072
|
+
> true
|
|
2073
|
+
* @example
|
|
2074
|
+
Utils.isNull([]);
|
|
2075
|
+
> true
|
|
2076
|
+
* @example
|
|
2077
|
+
Utils.isNull(" ");
|
|
2078
|
+
> true
|
|
2079
|
+
* @example
|
|
2080
|
+
Utils.isNull(function(){});
|
|
2081
|
+
> true
|
|
2082
|
+
* @example
|
|
2083
|
+
Utils.isNull(()=>{}));
|
|
2084
|
+
> true
|
|
2085
|
+
* @example
|
|
2086
|
+
Utils.isNull("undefined");
|
|
2087
|
+
> true
|
|
2088
|
+
* @example
|
|
2089
|
+
Utils.isNull("null");
|
|
2090
|
+
> true
|
|
2091
|
+
* @example
|
|
2092
|
+
Utils.isNull(" ", false);
|
|
2093
|
+
> true
|
|
2094
|
+
* @example
|
|
2095
|
+
Utils.isNull([1],[]);
|
|
2096
|
+
> false
|
|
2097
|
+
* @example
|
|
2098
|
+
Utils.isNull([],[1]);
|
|
2099
|
+
> false
|
|
2100
|
+
* @example
|
|
2101
|
+
Utils.isNull(false,[123]);
|
|
2102
|
+
> false
|
|
2103
|
+
**/
|
|
2104
|
+
isNull<T>(value: T | null): value is null;
|
|
2105
|
+
isNull(...args: any[]): boolean;
|
|
2106
|
+
isNull(...args: any[]): boolean {
|
|
2107
|
+
let result = true;
|
|
2108
|
+
let checkList = [...args];
|
|
2109
|
+
for (const objItem of checkList) {
|
|
2110
|
+
let itemResult = false;
|
|
2111
|
+
if (objItem === null || objItem === undefined) {
|
|
2112
|
+
itemResult = true;
|
|
2113
|
+
} else {
|
|
2114
|
+
switch (typeof objItem) {
|
|
2115
|
+
case "object":
|
|
2116
|
+
if (typeof objItem[Symbol.iterator] === "function") {
|
|
2117
|
+
/* 可迭代 */
|
|
2118
|
+
itemResult = objItem.length === 0;
|
|
2119
|
+
} else {
|
|
2120
|
+
itemResult = Object.keys(objItem).length === 0;
|
|
2121
|
+
}
|
|
2122
|
+
break;
|
|
2123
|
+
case "number":
|
|
2124
|
+
itemResult = objItem === 0;
|
|
2125
|
+
break;
|
|
2126
|
+
case "string":
|
|
2127
|
+
itemResult =
|
|
2128
|
+
objItem.trim() === "" ||
|
|
2129
|
+
objItem === "null" ||
|
|
2130
|
+
objItem === "undefined";
|
|
2131
|
+
break;
|
|
2132
|
+
case "boolean":
|
|
2133
|
+
itemResult = !objItem;
|
|
2134
|
+
break;
|
|
2135
|
+
case "function":
|
|
2136
|
+
let funcStr = objItem.toString().replace(/\s/g, "");
|
|
2137
|
+
/* 排除()=>{}、(xxx="")=>{}、function(){}、function(xxx=""){} */
|
|
2138
|
+
itemResult = Boolean(
|
|
2139
|
+
funcStr.match(/^\(.*?\)=>\{\}$|^function.*?\(.*?\)\{\}$/)
|
|
2140
|
+
);
|
|
2141
|
+
break;
|
|
2142
|
+
}
|
|
2143
|
+
}
|
|
2144
|
+
result = result && itemResult;
|
|
2145
|
+
}
|
|
2146
|
+
|
|
2147
|
+
return result;
|
|
2148
|
+
}
|
|
2149
|
+
|
|
2150
|
+
/**
|
|
2151
|
+
* 判断浏览器主题是否是暗黑|深色模式
|
|
2152
|
+
*/
|
|
2153
|
+
isThemeDark(): boolean;
|
|
2154
|
+
isThemeDark(): boolean {
|
|
2155
|
+
return UtilsCore.globalThis.matchMedia("(prefers-color-scheme: dark)")
|
|
2156
|
+
.matches;
|
|
2157
|
+
}
|
|
2158
|
+
/**
|
|
2159
|
+
* 判断元素是否在页面中可见
|
|
2160
|
+
* @param element 需要检查的元素,可以是普通元素|数组形式的元素|通过querySelectorAll获取的元素数组
|
|
2161
|
+
* @param inView
|
|
2162
|
+
* + true 在窗口可视区域
|
|
2163
|
+
* + false 不在窗口可视区域
|
|
2164
|
+
* @returns
|
|
2165
|
+
* + true 可见
|
|
2166
|
+
* + false 不可见
|
|
2167
|
+
* @example
|
|
2168
|
+
* Utils.isVisible(document.documentElement)
|
|
2169
|
+
* > true
|
|
2170
|
+
*/
|
|
2171
|
+
isVisible(element: HTMLElement[] | NodeList, inView?: boolean): boolean;
|
|
2172
|
+
isVisible(
|
|
2173
|
+
element: HTMLElement[] | NodeList,
|
|
2174
|
+
inView: boolean = false
|
|
2175
|
+
): boolean {
|
|
2176
|
+
let needCheckDomList = [];
|
|
2177
|
+
if (element instanceof Array || element instanceof NodeList) {
|
|
2178
|
+
element = element as HTMLElement[];
|
|
2179
|
+
needCheckDomList = [...element];
|
|
2180
|
+
} else {
|
|
2181
|
+
needCheckDomList = [element];
|
|
2182
|
+
}
|
|
2183
|
+
let result = true;
|
|
2184
|
+
for (const domItem of needCheckDomList) {
|
|
2185
|
+
let domDisplay = UtilsCore.window.getComputedStyle(domItem);
|
|
2186
|
+
if (domDisplay.display === "none") {
|
|
2187
|
+
result = false;
|
|
2188
|
+
} else {
|
|
2189
|
+
let domClientRect = domItem.getBoundingClientRect();
|
|
2190
|
+
if (inView) {
|
|
2191
|
+
let viewportWidth =
|
|
2192
|
+
UtilsCore.window.innerWidth ||
|
|
2193
|
+
UtilsCore.document.documentElement.clientWidth;
|
|
2194
|
+
let viewportHeight =
|
|
2195
|
+
UtilsCore.window.innerHeight ||
|
|
2196
|
+
UtilsCore.document.documentElement.clientHeight;
|
|
2197
|
+
result = !(
|
|
2198
|
+
domClientRect.right < 0 ||
|
|
2199
|
+
domClientRect.left > viewportWidth ||
|
|
2200
|
+
domClientRect.bottom < 0 ||
|
|
2201
|
+
domClientRect.top > viewportHeight
|
|
2202
|
+
);
|
|
2203
|
+
} else {
|
|
2204
|
+
result = Boolean(domItem.getClientRects().length);
|
|
2205
|
+
}
|
|
2206
|
+
}
|
|
2207
|
+
if (!result) {
|
|
2208
|
+
/* 有一个不可见就退出循环 */
|
|
2209
|
+
break;
|
|
2210
|
+
}
|
|
2211
|
+
}
|
|
2212
|
+
return result;
|
|
2213
|
+
}
|
|
2214
|
+
|
|
2215
|
+
/**
|
|
2216
|
+
* 判断是否是Via浏览器环境
|
|
2217
|
+
* @returns
|
|
2218
|
+
* + true 是Via
|
|
2219
|
+
* + false 不是Via
|
|
2220
|
+
* @example
|
|
2221
|
+
* Utils.isWebView_Via()
|
|
2222
|
+
* > false
|
|
2223
|
+
*/
|
|
2224
|
+
isWebView_Via(): boolean;
|
|
2225
|
+
isWebView_Via(): boolean {
|
|
2226
|
+
let result = true;
|
|
2227
|
+
let UtilsContext = this;
|
|
2228
|
+
if (typeof (UtilsCore.top.window as any).via === "object") {
|
|
2229
|
+
for (const key in Object.values((UtilsCore.top.window as any).via)) {
|
|
2230
|
+
if (Reflect.has((UtilsCore.top.window as any).via, key)) {
|
|
2231
|
+
let objValueFunc = (UtilsCore.top.window as any).via[key];
|
|
2232
|
+
if (
|
|
2233
|
+
typeof objValueFunc === "function" &&
|
|
2234
|
+
UtilsContext.isNativeFunc(objValueFunc)
|
|
2235
|
+
) {
|
|
2236
|
+
result = true;
|
|
2237
|
+
} else {
|
|
2238
|
+
result = false;
|
|
2239
|
+
break;
|
|
2240
|
+
}
|
|
2241
|
+
}
|
|
2242
|
+
}
|
|
2243
|
+
} else {
|
|
2244
|
+
result = false;
|
|
2245
|
+
}
|
|
2246
|
+
return result;
|
|
2247
|
+
}
|
|
2248
|
+
/**
|
|
2249
|
+
* 判断是否是X浏览器环境
|
|
2250
|
+
* @returns
|
|
2251
|
+
* + true 是X浏览器
|
|
2252
|
+
* + false 不是X浏览器
|
|
2253
|
+
* @example
|
|
2254
|
+
* Utils.isWebView_X()
|
|
2255
|
+
* > false
|
|
2256
|
+
*/
|
|
2257
|
+
isWebView_X(): boolean;
|
|
2258
|
+
isWebView_X(): boolean {
|
|
2259
|
+
let result = true;
|
|
2260
|
+
let UtilsContext = this;
|
|
2261
|
+
if (typeof (UtilsCore.top.window as any).mbrowser === "object") {
|
|
2262
|
+
for (const key in Object.values((UtilsCore.top.window as any).mbrowser)) {
|
|
2263
|
+
if (Reflect.has((UtilsCore.top.window as any).mbrowser, key)) {
|
|
2264
|
+
let objValueFunc = (UtilsCore.top.window as any).mbrowser[key];
|
|
2265
|
+
if (
|
|
2266
|
+
typeof objValueFunc === "function" &&
|
|
2267
|
+
UtilsContext.isNativeFunc(objValueFunc)
|
|
2268
|
+
) {
|
|
2269
|
+
result = true;
|
|
2270
|
+
} else {
|
|
2271
|
+
result = false;
|
|
2272
|
+
break;
|
|
2273
|
+
}
|
|
2274
|
+
}
|
|
2275
|
+
}
|
|
2276
|
+
} else {
|
|
2277
|
+
result = false;
|
|
2278
|
+
}
|
|
2279
|
+
return result;
|
|
2280
|
+
}
|
|
2281
|
+
/**
|
|
2282
|
+
* 把对象内的value值全部取出成数组
|
|
2283
|
+
* @param target 目标对象
|
|
2284
|
+
* @returns 返回数组
|
|
2285
|
+
* @example
|
|
2286
|
+
* Utils.parseObjectToArray({"工具类":"jsonToArray","return","Array"});
|
|
2287
|
+
* > ['jsonToArray', 'Array']
|
|
2288
|
+
**/
|
|
2289
|
+
parseObjectToArray(target: AnyObject): any;
|
|
2290
|
+
parseObjectToArray(target: AnyObject): any {
|
|
2291
|
+
if (typeof target !== "object") {
|
|
2292
|
+
throw new Error(
|
|
2293
|
+
"Utils.parseObjectToArray 参数 target 必须为 object 类型"
|
|
2294
|
+
);
|
|
2295
|
+
}
|
|
2296
|
+
let result: any[] = [];
|
|
2297
|
+
Object.keys(target).forEach(function (keyName) {
|
|
2298
|
+
result = result.concat(target[keyName]);
|
|
2299
|
+
});
|
|
2300
|
+
return result;
|
|
2301
|
+
}
|
|
2302
|
+
/**
|
|
2303
|
+
* 监听某个元素键盘按键事件或window全局按键事件
|
|
2304
|
+
* 按下有值的键时触发,按下Ctrl\Alt\Shift\Meta是无值键。按下先触发keydown事件,再触发keypress事件。
|
|
2305
|
+
* @param target 需要监听的对象,可以是全局Window或者某个元素
|
|
2306
|
+
* @param eventName 事件名,默认keypress
|
|
2307
|
+
* @param callback 自己定义的回调事件,参数1为当前的key,参数2为组合按键,数组类型,包含ctrl、shift、alt和meta(win键或mac的cmd键)
|
|
2308
|
+
* @example
|
|
2309
|
+
Utils.listenKeyboard(window,(keyName,keyValue,otherKey,event)=>{
|
|
2310
|
+
if(keyName === "Enter"){
|
|
2311
|
+
console.log("回车按键的值是:"+keyValue)
|
|
2312
|
+
}
|
|
2313
|
+
if(otherKey.indexOf("ctrl") && keyName === "Enter" ){
|
|
2314
|
+
console.log("Ctrl和回车键");
|
|
2315
|
+
}
|
|
2316
|
+
})
|
|
2317
|
+
* @example
|
|
2318
|
+
字母和数字键的键码值(keyCode)
|
|
2319
|
+
按键 键码 按键 键码 按键 键码 按键 键码
|
|
2320
|
+
A 65 J 74 S 83 1 49
|
|
2321
|
+
B 66 K 75 T 84 2 50
|
|
2322
|
+
C 67 L 76 U 85 3 51
|
|
2323
|
+
D 68 M 77 V 86 4 52
|
|
2324
|
+
E 69 N 78 W 87 5 53
|
|
2325
|
+
F 70 O 79 X 88 6 54
|
|
2326
|
+
G 71 P 80 Y 89 7 55
|
|
2327
|
+
H 72 Q 81 Z 90 8 56
|
|
2328
|
+
I 73 R 82 0 48 9 57
|
|
2329
|
+
|
|
2330
|
+
数字键盘上的键的键码值(keyCode)
|
|
2331
|
+
功能键键码值(keyCode)
|
|
2332
|
+
按键 键码 按键 键码 按键 键码 按键 键码
|
|
2333
|
+
0 96 8 104 F1 112 F7 118
|
|
2334
|
+
1 97 9 105 F2 113 F8 119
|
|
2335
|
+
2 98 * 106 F3 114 F9 120
|
|
2336
|
+
3 99 + 107 F4 115 F10 121
|
|
2337
|
+
4 100 Enter 108 F5 116 F11 122
|
|
2338
|
+
5 101 - 109 F6 117 F12 123
|
|
2339
|
+
6 102 . 110
|
|
2340
|
+
7 103 / 111
|
|
2341
|
+
|
|
2342
|
+
控制键键码值(keyCode)
|
|
2343
|
+
按键 键码 按键 键码 按键 键码 按键 键码
|
|
2344
|
+
BackSpace 8 Esc 27 → 39 -_ 189
|
|
2345
|
+
Tab 9 Spacebar 32 ↓ 40 .> 190
|
|
2346
|
+
Clear 12 Page Up 33 Insert 45 /? 191
|
|
2347
|
+
Enter 13 Page Down 34 Delete 46 `~ 192
|
|
2348
|
+
Shift 16 End 35 Num Lock 144 [{ 219
|
|
2349
|
+
Control 17 Home 36 ;: 186 \| 220
|
|
2350
|
+
Alt 18 ← 37 =+ 187 ]} 221
|
|
2351
|
+
Cape Lock 20 ↑ 38 ,< 188 '" 222
|
|
2352
|
+
|
|
2353
|
+
多媒体键码值(keyCode)
|
|
2354
|
+
按键 键码
|
|
2355
|
+
音量加 175
|
|
2356
|
+
音量减 174
|
|
2357
|
+
停止 179
|
|
2358
|
+
静音 173
|
|
2359
|
+
浏览器 172
|
|
2360
|
+
邮件 180
|
|
2361
|
+
搜索 170
|
|
2362
|
+
收藏 171
|
|
2363
|
+
**/
|
|
2364
|
+
listenKeyboard(
|
|
2365
|
+
target: Window | Node | HTMLElement | typeof globalThis,
|
|
2366
|
+
eventName: "keyup" | "keypress" | "keydown",
|
|
2367
|
+
callback: (
|
|
2368
|
+
keyName: string,
|
|
2369
|
+
keyValue: string,
|
|
2370
|
+
otherCodeList: string[],
|
|
2371
|
+
event: KeyboardEvent
|
|
2372
|
+
) => void
|
|
2373
|
+
): {
|
|
2374
|
+
removeListen(): void;
|
|
2375
|
+
};
|
|
2376
|
+
listenKeyboard(
|
|
2377
|
+
target: Window | Node | HTMLElement | typeof globalThis,
|
|
2378
|
+
eventName: "keyup" | "keypress" | "keydown" = "keypress",
|
|
2379
|
+
callback: (
|
|
2380
|
+
keyName: string,
|
|
2381
|
+
keyValue: string,
|
|
2382
|
+
otherCodeList: string[],
|
|
2383
|
+
event: KeyboardEvent
|
|
2384
|
+
) => void
|
|
2385
|
+
): {
|
|
2386
|
+
removeListen(): void;
|
|
2387
|
+
} {
|
|
2388
|
+
if (
|
|
2389
|
+
typeof target !== "object" ||
|
|
2390
|
+
(typeof target["addEventListener"] !== "function" &&
|
|
2391
|
+
typeof target["removeEventListener"] !== "function")
|
|
2392
|
+
) {
|
|
2393
|
+
throw new Error(
|
|
2394
|
+
"Utils.listenKeyboard 参数 target 必须为 Window|HTMLElement 类型"
|
|
2395
|
+
);
|
|
2396
|
+
}
|
|
2397
|
+
let keyEvent = function (event: KeyboardEvent) {
|
|
2398
|
+
let keyName = event.key || event.code;
|
|
2399
|
+
let keyValue = event.charCode || event.keyCode || event.which;
|
|
2400
|
+
let otherCodeList = [];
|
|
2401
|
+
if (event.ctrlKey) {
|
|
2402
|
+
otherCodeList.push("ctrl");
|
|
2403
|
+
}
|
|
2404
|
+
if (event.altKey) {
|
|
2405
|
+
otherCodeList.push("alt");
|
|
2406
|
+
}
|
|
2407
|
+
if (event.metaKey) {
|
|
2408
|
+
otherCodeList.push("meta");
|
|
2409
|
+
}
|
|
2410
|
+
if (event.shiftKey) {
|
|
2411
|
+
otherCodeList.push("shift");
|
|
2412
|
+
}
|
|
2413
|
+
if (typeof callback === "function") {
|
|
2414
|
+
callback(keyName, keyValue.toString(), otherCodeList, event);
|
|
2415
|
+
}
|
|
2416
|
+
};
|
|
2417
|
+
target.addEventListener(eventName, keyEvent as any);
|
|
2418
|
+
return {
|
|
2419
|
+
removeListen() {
|
|
2420
|
+
target.removeEventListener(eventName, keyEvent as any);
|
|
2421
|
+
},
|
|
2422
|
+
};
|
|
2423
|
+
}
|
|
2424
|
+
/**
|
|
2425
|
+
* 自动锁对象,用于循环判断运行的函数,在循环外new后使用,注意,如果函数内部存在异步操作,需要使用await
|
|
2426
|
+
* @example
|
|
2427
|
+
let lock = new Utils.LockFunction(()=>{console.log(1)}))
|
|
2428
|
+
lock.run();
|
|
2429
|
+
> 1
|
|
2430
|
+
* @example
|
|
2431
|
+
let lock = new Utils.LockFunction(()=>{console.log(1)}),true) -- 异步操作
|
|
2432
|
+
await lock.run();
|
|
2433
|
+
> 1
|
|
2434
|
+
**/
|
|
2435
|
+
LockFunction = LockFunction;
|
|
2436
|
+
/**
|
|
2437
|
+
* 日志对象
|
|
2438
|
+
* @param _GM_info_ 油猴管理器的API GM_info,或者是一个对象,如{"script":{name:"Utils.Log"}}
|
|
2439
|
+
* @example
|
|
2440
|
+
let log = new Utils.Log(GM_info);
|
|
2441
|
+
log.info("普通输出");
|
|
2442
|
+
> 普通输出
|
|
2443
|
+
|
|
2444
|
+
log.success("成功输出");
|
|
2445
|
+
> 成功输出
|
|
2446
|
+
|
|
2447
|
+
log.error("错误输出");
|
|
2448
|
+
> 错误输出
|
|
2449
|
+
|
|
2450
|
+
log.warn("警告输出");
|
|
2451
|
+
> 警告输出
|
|
2452
|
+
|
|
2453
|
+
log.tag = "自定义tag信息";
|
|
2454
|
+
log.info("自定义info的颜色","#e0e0e0");
|
|
2455
|
+
> 自定义info的颜色
|
|
2456
|
+
|
|
2457
|
+
log.config({
|
|
2458
|
+
successColor: "#31dc02",
|
|
2459
|
+
errorColor: "#e02d2d",
|
|
2460
|
+
infoColor: "black",
|
|
2461
|
+
})
|
|
2462
|
+
log.success("颜色为#31dc02");
|
|
2463
|
+
> 颜色为#31dc02
|
|
2464
|
+
*/
|
|
2465
|
+
Log = Log;
|
|
2466
|
+
/**
|
|
2467
|
+
* 合并数组内的JSON的值字符串
|
|
2468
|
+
* @param data 需要合并的数组
|
|
2469
|
+
* @param handleFunc 处理的函数|JSON的key
|
|
2470
|
+
* @example
|
|
2471
|
+
* Utils.mergeArrayToString([{"name":"数组内数据部分字段合并成字符串->"},{"name":"mergeToString"}],(item)=>{return item["name"]});
|
|
2472
|
+
* > '数组内数据部分字段合并成字符串->mergeToString'
|
|
2473
|
+
**/
|
|
2474
|
+
mergeArrayToString(data: any[], handleFunc?: (val: any) => any): string;
|
|
2475
|
+
mergeArrayToString(data: any[], handleFunc?: (val: any) => any): string {
|
|
2476
|
+
if (!(data instanceof Array)) {
|
|
2477
|
+
throw new Error("Utils.mergeArrayToString 参数 data 必须为 Array 类型");
|
|
2478
|
+
}
|
|
2479
|
+
let content = "";
|
|
2480
|
+
if (typeof handleFunc === "function") {
|
|
2481
|
+
data.forEach((item) => {
|
|
2482
|
+
content += handleFunc(item);
|
|
2483
|
+
});
|
|
2484
|
+
} else if (typeof handleFunc === "string") {
|
|
2485
|
+
data.forEach((item) => {
|
|
2486
|
+
content += item[handleFunc];
|
|
2487
|
+
});
|
|
2488
|
+
} else {
|
|
2489
|
+
data.forEach((item) => {
|
|
2490
|
+
Object.values(item)
|
|
2491
|
+
.filter((item2) => typeof item2 === "string")
|
|
2492
|
+
.forEach((item3) => {
|
|
2493
|
+
content += item3;
|
|
2494
|
+
});
|
|
2495
|
+
});
|
|
2496
|
+
}
|
|
2497
|
+
return content;
|
|
2498
|
+
}
|
|
2499
|
+
/**
|
|
2500
|
+
* 监听页面元素改变并处理
|
|
2501
|
+
* @param target 需要监听的元素,如果不存在,可以等待它出现
|
|
2502
|
+
* @param observer_config MutationObserver的配置
|
|
2503
|
+
* @example
|
|
2504
|
+
Utils.mutationObserver(document.querySelector("div.xxxx"),{
|
|
2505
|
+
"callback":(mutations, observer)=>{},
|
|
2506
|
+
"config":{childList:true,attributes:true}
|
|
2507
|
+
});
|
|
2508
|
+
* @example
|
|
2509
|
+
Utils.mutationObserver(document.querySelectorAll("div.xxxx"),{
|
|
2510
|
+
"callback":(mutations, observer)=>{},
|
|
2511
|
+
"config":{childList:true,attributes:true}}
|
|
2512
|
+
);
|
|
2513
|
+
* @example
|
|
2514
|
+
Utils.mutationObserver($("div.xxxx"),{
|
|
2515
|
+
"callback":(mutations, observer)=>{},
|
|
2516
|
+
"config":{childList:true,attributes:true}}
|
|
2517
|
+
);
|
|
2518
|
+
**/
|
|
2519
|
+
mutationObserver(
|
|
2520
|
+
target: HTMLElement | Node | NodeList | Document,
|
|
2521
|
+
observer_config: {
|
|
2522
|
+
/**
|
|
2523
|
+
* observer的配置
|
|
2524
|
+
*/
|
|
2525
|
+
config?: MutationObserverInit;
|
|
2526
|
+
/**
|
|
2527
|
+
* 是否主动触发一次
|
|
2528
|
+
*/
|
|
2529
|
+
immediate?: boolean;
|
|
2530
|
+
/**
|
|
2531
|
+
* 触发的回调函数
|
|
2532
|
+
*/
|
|
2533
|
+
callback: MutationCallback;
|
|
2534
|
+
}
|
|
2535
|
+
): MutationObserver;
|
|
2536
|
+
mutationObserver(
|
|
2537
|
+
target: HTMLElement | Node | NodeList | Document,
|
|
2538
|
+
observer_config: {
|
|
2539
|
+
/**
|
|
2540
|
+
* observer的配置
|
|
2541
|
+
*/
|
|
2542
|
+
config?: MutationObserverInit;
|
|
2543
|
+
/**
|
|
2544
|
+
* 是否主动触发一次
|
|
2545
|
+
*/
|
|
2546
|
+
immediate?: boolean;
|
|
2547
|
+
/**
|
|
2548
|
+
* 触发的回调函数
|
|
2549
|
+
*/
|
|
2550
|
+
callback: MutationCallback;
|
|
2551
|
+
}
|
|
2552
|
+
): MutationObserver {
|
|
2553
|
+
let UtilsContext = this;
|
|
2554
|
+
|
|
2555
|
+
let default_obverser_config = {
|
|
2556
|
+
/* 监听到元素有反馈,需执行的函数 */
|
|
2557
|
+
callback: () => {},
|
|
2558
|
+
config: <MutationObserverInit>{
|
|
2559
|
+
/**
|
|
2560
|
+
* + true 监听以 target 为根节点的整个子树。包括子树中所有节点的属性,而不仅仅是针对 target
|
|
2561
|
+
* + false (默认) 不生效
|
|
2562
|
+
*/
|
|
2563
|
+
subtree: void 0 as any as boolean,
|
|
2564
|
+
/**
|
|
2565
|
+
* + true 监听 target 节点中发生的节点的新增与删除(同时,如果 subtree 为 true,会针对整个子树生效)
|
|
2566
|
+
* + false (默认) 不生效
|
|
2567
|
+
*/
|
|
2568
|
+
childList: void 0 as any as boolean,
|
|
2569
|
+
/**
|
|
2570
|
+
* + true 观察所有监听的节点属性值的变化。默认值为 true,当声明了 attributeFilter 或 attributeOldValue
|
|
2571
|
+
* + false (默认) 不生效
|
|
2572
|
+
*/
|
|
2573
|
+
attributes: void 0 as any as boolean,
|
|
2574
|
+
/**
|
|
2575
|
+
* 一个用于声明哪些属性名会被监听的数组。如果不声明该属性,所有属性的变化都将触发通知
|
|
2576
|
+
*/
|
|
2577
|
+
attributeFilter: void 0 as any as string[],
|
|
2578
|
+
/**
|
|
2579
|
+
* + true 记录上一次被监听的节点的属性变化;可查阅 MutationObserver 中的 Monitoring attribute values 了解关于观察属性变化和属性值记录的详情
|
|
2580
|
+
* + false (默认) 不生效
|
|
2581
|
+
*/
|
|
2582
|
+
attributeOldValue: void 0 as any as boolean,
|
|
2583
|
+
/**
|
|
2584
|
+
* + true 监听声明的 target 节点上所有字符的变化。默认值为 true,如果声明了 characterDataOldValue
|
|
2585
|
+
* + false (默认) 不生效
|
|
2586
|
+
*/
|
|
2587
|
+
characterData: void 0 as any as boolean,
|
|
2588
|
+
/**
|
|
2589
|
+
* + true 记录前一个被监听的节点中发生的文本变化
|
|
2590
|
+
* + false (默认) 不生效
|
|
2591
|
+
*/
|
|
2592
|
+
characterDataOldValue: void 0 as any as boolean,
|
|
2593
|
+
},
|
|
2594
|
+
immediate: false,
|
|
2595
|
+
};
|
|
2596
|
+
observer_config = UtilsContext.assign(
|
|
2597
|
+
default_obverser_config,
|
|
2598
|
+
observer_config
|
|
2599
|
+
);
|
|
2600
|
+
let windowMutationObserver =
|
|
2601
|
+
UtilsCore.window.MutationObserver ||
|
|
2602
|
+
(UtilsCore.window as any).webkitMutationObserver ||
|
|
2603
|
+
(UtilsCore.window as any).MozMutationObserver;
|
|
2604
|
+
// 观察者对象
|
|
2605
|
+
let mutationObserver = new windowMutationObserver(function (
|
|
2606
|
+
mutations: MutationRecord[],
|
|
2607
|
+
observer: MutationObserver
|
|
2608
|
+
) {
|
|
2609
|
+
if (typeof observer_config.callback === "function") {
|
|
2610
|
+
observer_config.callback(mutations, observer);
|
|
2611
|
+
}
|
|
2612
|
+
});
|
|
2613
|
+
|
|
2614
|
+
if (Array.isArray(target) || target instanceof NodeList) {
|
|
2615
|
+
// 传入的是数组或者元素数组
|
|
2616
|
+
target.forEach((item) => {
|
|
2617
|
+
mutationObserver.observe(item, observer_config.config);
|
|
2618
|
+
});
|
|
2619
|
+
} else if (UtilsContext.isJQuery(target)) {
|
|
2620
|
+
/* 传入的参数是jQuery对象 */
|
|
2621
|
+
(target as any).each((index: any, item: any) => {
|
|
2622
|
+
mutationObserver.observe(item, observer_config.config);
|
|
2623
|
+
});
|
|
2624
|
+
} else {
|
|
2625
|
+
mutationObserver.observe(target, observer_config.config);
|
|
2626
|
+
}
|
|
2627
|
+
if (observer_config.immediate) {
|
|
2628
|
+
/* 主动触发一次 */
|
|
2629
|
+
if (typeof observer_config.callback === "function") {
|
|
2630
|
+
observer_config.callback([], mutationObserver);
|
|
2631
|
+
}
|
|
2632
|
+
}
|
|
2633
|
+
return mutationObserver;
|
|
2634
|
+
}
|
|
2635
|
+
/**
|
|
2636
|
+
* 去除全局window下的Utils,返回控制权
|
|
2637
|
+
* @example
|
|
2638
|
+
* let utils = Utils.noConflict();
|
|
2639
|
+
* > ...
|
|
2640
|
+
*/
|
|
2641
|
+
noConflict = function () {
|
|
2642
|
+
if ((UtilsCore.window as any).Utils) {
|
|
2643
|
+
Reflect.deleteProperty(UtilsCore.window as any, "Utils");
|
|
2644
|
+
}
|
|
2645
|
+
(UtilsCore.window as any).Utils = utils;
|
|
2646
|
+
return utils;
|
|
2647
|
+
};
|
|
2648
|
+
/**
|
|
2649
|
+
* 恢复/释放该对象内的为function,让它无效/有效
|
|
2650
|
+
* @param needReleaseObject 需要操作的对象
|
|
2651
|
+
* @param needReleaseName 需要操作的对象的名字
|
|
2652
|
+
* @param functionNameList (可选)需要释放的方法,默认:全部方法
|
|
2653
|
+
* @param release (可选)
|
|
2654
|
+
* + true (默认) 释放该对象下的某些方法
|
|
2655
|
+
* + false 恢复该对象下的某些方法
|
|
2656
|
+
* @example
|
|
2657
|
+
// 释放该方法
|
|
2658
|
+
Utils.noConflictFunc(console,"console",["log"],true);
|
|
2659
|
+
console.log;
|
|
2660
|
+
> () => {}
|
|
2661
|
+
|
|
2662
|
+
* @example
|
|
2663
|
+
// 恢复该方法
|
|
2664
|
+
Utils.noConflictFunc(console,"console",["log"],false);
|
|
2665
|
+
console.log;
|
|
2666
|
+
> ƒ log() { [native code] }
|
|
2667
|
+
|
|
2668
|
+
* @example
|
|
2669
|
+
// 释放所有方法
|
|
2670
|
+
Utils.noConflictFunc(console,"console",[],true);
|
|
2671
|
+
console.debug;
|
|
2672
|
+
> () => {}
|
|
2673
|
+
|
|
2674
|
+
* @example
|
|
2675
|
+
// 恢复所有方法
|
|
2676
|
+
Utils.noConflictFunc(console,"console",[],false);
|
|
2677
|
+
console.debug;
|
|
2678
|
+
> ƒ log() { [native code] }
|
|
2679
|
+
**/
|
|
2680
|
+
noConflictFunc(
|
|
2681
|
+
needReleaseObject: object,
|
|
2682
|
+
needReleaseName: string,
|
|
2683
|
+
functionNameList?: any[],
|
|
2684
|
+
release?: boolean
|
|
2685
|
+
): void;
|
|
2686
|
+
noConflictFunc(
|
|
2687
|
+
needReleaseObject: object,
|
|
2688
|
+
needReleaseName: string,
|
|
2689
|
+
functionNameList: any[] = [],
|
|
2690
|
+
release: boolean = true
|
|
2691
|
+
): void {
|
|
2692
|
+
let UtilsContext = this;
|
|
2693
|
+
if (typeof needReleaseObject !== "object") {
|
|
2694
|
+
throw new Error(
|
|
2695
|
+
"Utils.noConflictFunc 参数 needReleaseObject 必须为 object 类型"
|
|
2696
|
+
);
|
|
2697
|
+
}
|
|
2698
|
+
if (typeof needReleaseName !== "string") {
|
|
2699
|
+
throw new Error(
|
|
2700
|
+
"Utils.noConflictFunc 参数 needReleaseName 必须为 string 类型"
|
|
2701
|
+
);
|
|
2702
|
+
}
|
|
2703
|
+
if (!Array.isArray(functionNameList)) {
|
|
2704
|
+
throw new Error(
|
|
2705
|
+
"Utils.noConflictFunc 参数 functionNameList 必须为 Array 类型"
|
|
2706
|
+
);
|
|
2707
|
+
}
|
|
2708
|
+
let needReleaseKey = "__" + needReleaseName;
|
|
2709
|
+
/**
|
|
2710
|
+
* 释放所有
|
|
2711
|
+
*/
|
|
2712
|
+
function releaseAll() {
|
|
2713
|
+
if (typeof (UtilsCore.window as any)[needReleaseKey] !== "undefined") {
|
|
2714
|
+
/* 已存在 */
|
|
2715
|
+
return;
|
|
2716
|
+
}
|
|
2717
|
+
(UtilsCore.window as any)[needReleaseKey] =
|
|
2718
|
+
UtilsContext.deepClone(needReleaseObject);
|
|
2719
|
+
Object.values(needReleaseObject).forEach((value) => {
|
|
2720
|
+
if (typeof value === "function") {
|
|
2721
|
+
(needReleaseObject as any)[value.name] = () => {};
|
|
2722
|
+
}
|
|
2723
|
+
});
|
|
2724
|
+
}
|
|
2725
|
+
/**
|
|
2726
|
+
* 释放单个
|
|
2727
|
+
*/
|
|
2728
|
+
function releaseOne() {
|
|
2729
|
+
Array.from(functionNameList).forEach((item) => {
|
|
2730
|
+
Object.values(needReleaseObject).forEach((value) => {
|
|
2731
|
+
if (typeof value === "function") {
|
|
2732
|
+
if (
|
|
2733
|
+
typeof (UtilsCore.window as any)[needReleaseKey] === "undefined"
|
|
2734
|
+
) {
|
|
2735
|
+
(UtilsCore.window as any)[needReleaseKey] = {};
|
|
2736
|
+
}
|
|
2737
|
+
if (item === value.name) {
|
|
2738
|
+
(UtilsCore.window as any)[needReleaseKey][value.name] = (
|
|
2739
|
+
needReleaseObject as any
|
|
2740
|
+
)[value.name];
|
|
2741
|
+
(needReleaseObject as any)[value.name] = () => {};
|
|
2742
|
+
}
|
|
2743
|
+
}
|
|
2744
|
+
});
|
|
2745
|
+
});
|
|
2746
|
+
}
|
|
2747
|
+
/**
|
|
2748
|
+
* 恢复所有
|
|
2749
|
+
*/
|
|
2750
|
+
function recoveryAll() {
|
|
2751
|
+
if (typeof (UtilsCore.window as any)[needReleaseKey] === "undefined") {
|
|
2752
|
+
/* 未存在 */
|
|
2753
|
+
return;
|
|
2754
|
+
}
|
|
2755
|
+
Object.assign(
|
|
2756
|
+
needReleaseObject,
|
|
2757
|
+
(UtilsCore.window as any)[needReleaseKey]
|
|
2758
|
+
);
|
|
2759
|
+
Reflect.deleteProperty(UtilsCore.window as any, "needReleaseKey");
|
|
2760
|
+
}
|
|
2761
|
+
|
|
2762
|
+
/**
|
|
2763
|
+
* 恢复单个
|
|
2764
|
+
*/
|
|
2765
|
+
function recoveryOne() {
|
|
2766
|
+
if (typeof (UtilsCore.window as any)[needReleaseKey] === "undefined") {
|
|
2767
|
+
/* 未存在 */
|
|
2768
|
+
return;
|
|
2769
|
+
}
|
|
2770
|
+
Array.from(functionNameList).forEach((item) => {
|
|
2771
|
+
if ((UtilsCore.window as any)[needReleaseKey][item]) {
|
|
2772
|
+
(needReleaseObject as any)[item] = (UtilsCore.window as any)[
|
|
2773
|
+
needReleaseKey
|
|
2774
|
+
][item];
|
|
2775
|
+
Reflect.deleteProperty(
|
|
2776
|
+
(UtilsCore.window as any)[needReleaseKey],
|
|
2777
|
+
item
|
|
2778
|
+
);
|
|
2779
|
+
if (
|
|
2780
|
+
Object.keys((UtilsCore.window as any)[needReleaseKey]).length === 0
|
|
2781
|
+
) {
|
|
2782
|
+
Reflect.deleteProperty(window, needReleaseKey);
|
|
2783
|
+
}
|
|
2784
|
+
}
|
|
2785
|
+
});
|
|
2786
|
+
}
|
|
2787
|
+
if (release) {
|
|
2788
|
+
/* 释放 */
|
|
2789
|
+
if (functionNameList.length === 0) {
|
|
2790
|
+
releaseAll();
|
|
2791
|
+
} else {
|
|
2792
|
+
/* 对单个进行操作 */
|
|
2793
|
+
releaseOne();
|
|
2794
|
+
}
|
|
2795
|
+
} else {
|
|
2796
|
+
/* 恢复 */
|
|
2797
|
+
if (functionNameList.length === 0) {
|
|
2798
|
+
recoveryAll();
|
|
2799
|
+
} else {
|
|
2800
|
+
/* 对单个进行操作 */
|
|
2801
|
+
recoveryOne();
|
|
2802
|
+
}
|
|
2803
|
+
}
|
|
2804
|
+
}
|
|
2805
|
+
/**
|
|
2806
|
+
* base64转blob
|
|
2807
|
+
* @param dataUri base64的数据
|
|
2808
|
+
* @returns blob的链接
|
|
2809
|
+
* @example
|
|
2810
|
+
* Utils.parseBase64ToBlob("data:image/jpeg;base64,.....");
|
|
2811
|
+
* > blob://xxxxxxx
|
|
2812
|
+
**/
|
|
2813
|
+
parseBase64ToBlob(dataUri: string): Blob;
|
|
2814
|
+
parseBase64ToBlob(dataUri: string): Blob {
|
|
2815
|
+
if (typeof dataUri !== "string") {
|
|
2816
|
+
throw new Error(
|
|
2817
|
+
"Utils.parseBase64ToBlob 参数 dataUri 必须为 string 类型"
|
|
2818
|
+
);
|
|
2819
|
+
}
|
|
2820
|
+
let dataUriSplit = dataUri.split(","),
|
|
2821
|
+
dataUriMime = (dataUriSplit[0] as any).match(/:(.*?);/)[1],
|
|
2822
|
+
dataUriBase64Str = atob(dataUriSplit[1]),
|
|
2823
|
+
dataUriLength = dataUriBase64Str.length,
|
|
2824
|
+
u8arr = new Uint8Array(dataUriLength);
|
|
2825
|
+
while (dataUriLength--) {
|
|
2826
|
+
u8arr[dataUriLength] = dataUriBase64Str.charCodeAt(dataUriLength);
|
|
2827
|
+
}
|
|
2828
|
+
return new Blob([u8arr], {
|
|
2829
|
+
type: dataUriMime,
|
|
2830
|
+
});
|
|
2831
|
+
}
|
|
2832
|
+
/**
|
|
2833
|
+
* base64转File对象
|
|
2834
|
+
* @param dataUri base64的数据
|
|
2835
|
+
* @param fileName (可选)文件名,默认:example
|
|
2836
|
+
* @returns blob的链接
|
|
2837
|
+
* @example
|
|
2838
|
+
* Utils.parseBase64ToFile("data:image/jpeg;base64,.....","测试文件");
|
|
2839
|
+
* > object
|
|
2840
|
+
**/
|
|
2841
|
+
parseBase64ToFile(dataUri: string, fileName?: string): File;
|
|
2842
|
+
parseBase64ToFile(dataUri: string, fileName = "example") {
|
|
2843
|
+
if (typeof dataUri !== "string") {
|
|
2844
|
+
throw new Error(
|
|
2845
|
+
"Utils.parseBase64ToFile 参数 dataUri 必须为 string 类型"
|
|
2846
|
+
);
|
|
2847
|
+
}
|
|
2848
|
+
if (typeof fileName !== "string") {
|
|
2849
|
+
throw new Error(
|
|
2850
|
+
"Utils.parseBase64ToFile 参数 fileName 必须为 string 类型"
|
|
2851
|
+
);
|
|
2852
|
+
}
|
|
2853
|
+
let dataUriSplit = dataUri.split(","),
|
|
2854
|
+
dataUriMime = (dataUriSplit[0] as any).match(/:(.*?);/)[1],
|
|
2855
|
+
dataUriBase64Str = atob(dataUriSplit[1]),
|
|
2856
|
+
dataUriLength = dataUriBase64Str.length,
|
|
2857
|
+
u8arr = new Uint8Array(dataUriLength);
|
|
2858
|
+
while (dataUriLength--) {
|
|
2859
|
+
u8arr[dataUriLength] = dataUriBase64Str.charCodeAt(dataUriLength);
|
|
2860
|
+
}
|
|
2861
|
+
return new File([u8arr], fileName, {
|
|
2862
|
+
type: dataUriMime,
|
|
2863
|
+
});
|
|
2864
|
+
}
|
|
2865
|
+
/**
|
|
2866
|
+
* 将正则匹配到的结果取出最后一个值并转换成int格式
|
|
2867
|
+
* @param matchList 正则匹配的列表
|
|
2868
|
+
* @param defaultValue 正则匹配的列表为空时,或者正则匹配的列表最后一项不为Int,返回该默认值0
|
|
2869
|
+
* @example
|
|
2870
|
+
* Utils.parseInt(["dadaadada123124","123124"],0);
|
|
2871
|
+
* > 123124
|
|
2872
|
+
*
|
|
2873
|
+
* @example
|
|
2874
|
+
* Utils.parseInt(null,0);
|
|
2875
|
+
* > 0
|
|
2876
|
+
* @example
|
|
2877
|
+
* Utils.parseInt(["aaaaaa"]);
|
|
2878
|
+
* > 0
|
|
2879
|
+
*
|
|
2880
|
+
* @example
|
|
2881
|
+
* Utils.parseInt(["aaaaaa"],"66");
|
|
2882
|
+
* > 66
|
|
2883
|
+
*
|
|
2884
|
+
* @example
|
|
2885
|
+
* Utils.parseInt(["aaaaaaa"],"aa");
|
|
2886
|
+
* > NaN
|
|
2887
|
+
**/
|
|
2888
|
+
parseInt(matchList?: any[], defaultValue?: number): number;
|
|
2889
|
+
parseInt(matchList: any[] = [], defaultValue = 0): number {
|
|
2890
|
+
if (matchList == null) {
|
|
2891
|
+
return parseInt(defaultValue.toString());
|
|
2892
|
+
}
|
|
2893
|
+
let parseValue = parseInt(matchList[matchList.length - 1]);
|
|
2894
|
+
if (isNaN(parseValue)) {
|
|
2895
|
+
parseValue = parseInt(defaultValue.toString());
|
|
2896
|
+
}
|
|
2897
|
+
return parseValue;
|
|
2898
|
+
}
|
|
2899
|
+
/**
|
|
2900
|
+
* blob转File对象
|
|
2901
|
+
* @param blobUrl 需要转换的blob的链接
|
|
2902
|
+
* @param fileName (可选)转换成的File对象的文件名称,默认:example
|
|
2903
|
+
* @example
|
|
2904
|
+
* Utils.parseBlobToFile("blob://xxxxx");
|
|
2905
|
+
* > object
|
|
2906
|
+
**/
|
|
2907
|
+
parseBlobToFile(blobUrl: string, fileName?: string): Promise<File | Error>;
|
|
2908
|
+
async parseBlobToFile(
|
|
2909
|
+
blobUrl: string,
|
|
2910
|
+
fileName: string = "example"
|
|
2911
|
+
): Promise<File | Error> {
|
|
2912
|
+
return new Promise((resolve, reject) => {
|
|
2913
|
+
fetch(blobUrl)
|
|
2914
|
+
.then((response) => response.blob())
|
|
2915
|
+
.then((blob) => {
|
|
2916
|
+
const file = new File([blob], fileName, { type: blob.type });
|
|
2917
|
+
resolve(file);
|
|
2918
|
+
})
|
|
2919
|
+
.catch((error) => {
|
|
2920
|
+
console.error("Error:", error);
|
|
2921
|
+
reject(error);
|
|
2922
|
+
});
|
|
2923
|
+
});
|
|
2924
|
+
}
|
|
2925
|
+
/**
|
|
2926
|
+
* 解析CDATA格式的内容字符串
|
|
2927
|
+
* @param text 传入CDATA字符串
|
|
2928
|
+
* @returns 返回解析出的内容
|
|
2929
|
+
* @example
|
|
2930
|
+
* let xml = "<root><![CDATA[This is some CDATA content.]]></root>";
|
|
2931
|
+
* console.log(Utils.parseCDATA(xml));
|
|
2932
|
+
* > This is some CDATA content.
|
|
2933
|
+
*/
|
|
2934
|
+
parseCDATA(text: string): string;
|
|
2935
|
+
parseCDATA(text: string = ""): string {
|
|
2936
|
+
let result = "";
|
|
2937
|
+
let cdataRegexp = /<\!\[CDATA\[([\s\S]*)\]\]>/;
|
|
2938
|
+
let cdataMatch = cdataRegexp.exec(text.trim());
|
|
2939
|
+
if (cdataMatch && cdataMatch.length > 1) {
|
|
2940
|
+
result = cdataMatch[cdataMatch.length - 1];
|
|
2941
|
+
}
|
|
2942
|
+
return result;
|
|
2943
|
+
}
|
|
2944
|
+
/**
|
|
2945
|
+
* 【异步函数】File对象转base64
|
|
2946
|
+
* @param fileObj 需要转换的File对象
|
|
2947
|
+
* @example
|
|
2948
|
+
* await Utils.parseFileToBase64(object);
|
|
2949
|
+
* > 'data:image/jpeg:base64/,xxxxxx'
|
|
2950
|
+
**/
|
|
2951
|
+
parseFileToBase64(fileObj: File): Promise<string>;
|
|
2952
|
+
async parseFileToBase64(fileObj: File): Promise<string> {
|
|
2953
|
+
let reader = new FileReader();
|
|
2954
|
+
reader.readAsDataURL(fileObj);
|
|
2955
|
+
return new Promise((resolve) => {
|
|
2956
|
+
reader.onload = function (event: any) {
|
|
2957
|
+
resolve(event.target.result as string);
|
|
2958
|
+
};
|
|
2959
|
+
});
|
|
2960
|
+
}
|
|
2961
|
+
/**
|
|
2962
|
+
* 解析字符串
|
|
2963
|
+
* @param text 要解析的 DOMString。它必须包含 HTML、xml、xhtml+xml 或 svg 文档。
|
|
2964
|
+
* @param mimeType (可选)解析成的类型
|
|
2965
|
+
* + (默认)text/html
|
|
2966
|
+
* + text/xml
|
|
2967
|
+
* + application/xml
|
|
2968
|
+
* + application/xhtml+xml
|
|
2969
|
+
* + image/svg+xml
|
|
2970
|
+
* @example
|
|
2971
|
+
* Utils.parseFromString("<p>123<p>");
|
|
2972
|
+
* > #document
|
|
2973
|
+
*/
|
|
2974
|
+
parseFromString(
|
|
2975
|
+
text: string,
|
|
2976
|
+
mimeType?:
|
|
2977
|
+
| "text/html"
|
|
2978
|
+
| "text/xml"
|
|
2979
|
+
| "application/xml"
|
|
2980
|
+
| "application/xhtml+xml"
|
|
2981
|
+
| "image/svg+xml"
|
|
2982
|
+
): HTMLElement | XMLDocument | SVGElement;
|
|
2983
|
+
parseFromString(
|
|
2984
|
+
text: string,
|
|
2985
|
+
mimeType:
|
|
2986
|
+
| "text/html"
|
|
2987
|
+
| "text/xml"
|
|
2988
|
+
| "application/xml"
|
|
2989
|
+
| "application/xhtml+xml"
|
|
2990
|
+
| "image/svg+xml" = "text/html"
|
|
2991
|
+
): HTMLElement | XMLDocument | SVGElement {
|
|
2992
|
+
let parser = new DOMParser();
|
|
2993
|
+
return parser.parseFromString(text, mimeType);
|
|
2994
|
+
}
|
|
2995
|
+
/**
|
|
2996
|
+
* 将字符串进行正则转义
|
|
2997
|
+
* 例如:^替换$
|
|
2998
|
+
* 转换:\^替换\$
|
|
2999
|
+
*/
|
|
3000
|
+
parseStringToRegExpString(text: string): string;
|
|
3001
|
+
parseStringToRegExpString(text: string): string {
|
|
3002
|
+
if (typeof text !== "string") {
|
|
3003
|
+
throw new TypeError("string必须是字符串");
|
|
3004
|
+
}
|
|
3005
|
+
let regString = text.replace(/[.*+\-?^${}()|[\]\\]/g, "\\$&");
|
|
3006
|
+
return regString;
|
|
3007
|
+
}
|
|
3008
|
+
/**
|
|
3009
|
+
* 阻止事件传递
|
|
3010
|
+
* @param element 要进行处理的元素
|
|
3011
|
+
* @param eventNameList (可选)要阻止的事件名|列表
|
|
3012
|
+
* @param capture (可选)是否捕获,默认false
|
|
3013
|
+
* @example
|
|
3014
|
+
* Utils.preventEvent(document.querySelector("a"),"click")
|
|
3015
|
+
* @example
|
|
3016
|
+
* Utils.preventEvent(event);
|
|
3017
|
+
*/
|
|
3018
|
+
preventEvent(event: Event): boolean;
|
|
3019
|
+
/**
|
|
3020
|
+
* 阻止事件传递
|
|
3021
|
+
* @param element 要进行处理的元素
|
|
3022
|
+
* @param eventNameList (可选)要阻止的事件名|列表
|
|
3023
|
+
* @param capture (可选)是否捕获,默认false
|
|
3024
|
+
* @example
|
|
3025
|
+
* Utils.preventEvent(document.querySelector("a"),"click")
|
|
3026
|
+
* @example
|
|
3027
|
+
* Utils.preventEvent(event);
|
|
3028
|
+
*/
|
|
3029
|
+
preventEvent(
|
|
3030
|
+
element: HTMLElement,
|
|
3031
|
+
eventNameList?: string | string[],
|
|
3032
|
+
capture?: boolean
|
|
3033
|
+
): boolean;
|
|
3034
|
+
preventEvent(
|
|
3035
|
+
element: HTMLElement | Event,
|
|
3036
|
+
eventNameList: string | string[] = [],
|
|
3037
|
+
capture?: boolean
|
|
3038
|
+
): boolean | undefined {
|
|
3039
|
+
function stopEvent(event: Event) {
|
|
3040
|
+
/* 阻止事件的默认行为发生。例如,当点击一个链接时,浏览器会默认打开链接的URL */
|
|
3041
|
+
event?.preventDefault();
|
|
3042
|
+
/* 停止事件的传播,阻止它继续向更上层的元素冒泡,事件将不会再传播给其他的元素 */
|
|
3043
|
+
event?.stopPropagation();
|
|
3044
|
+
/* 阻止事件传播,并且还能阻止元素上的其他事件处理程序被触发 */
|
|
3045
|
+
event?.stopImmediatePropagation();
|
|
3046
|
+
return false;
|
|
3047
|
+
}
|
|
3048
|
+
if (arguments.length === 1) {
|
|
3049
|
+
/* 直接阻止事件 */
|
|
3050
|
+
return stopEvent(arguments[0]);
|
|
3051
|
+
} else {
|
|
3052
|
+
/* 添加对应的事件来阻止触发 */
|
|
3053
|
+
if (typeof eventNameList === "string") {
|
|
3054
|
+
eventNameList = [eventNameList];
|
|
3055
|
+
}
|
|
3056
|
+
eventNameList.forEach((eventName) => {
|
|
3057
|
+
(element as HTMLElement).addEventListener(eventName, stopEvent, {
|
|
3058
|
+
capture: Boolean(capture),
|
|
3059
|
+
});
|
|
3060
|
+
});
|
|
3061
|
+
}
|
|
3062
|
+
}
|
|
3063
|
+
/**
|
|
3064
|
+
* 在canvas元素节点上绘制进度圆圈
|
|
3065
|
+
* @example
|
|
3066
|
+
let progress = new Utils.Process({canvasNode:document.querySelector("canvas")});
|
|
3067
|
+
progress.draw();
|
|
3068
|
+
* **/
|
|
3069
|
+
Progress = Progress;
|
|
3070
|
+
/**
|
|
3071
|
+
* 劫持Event的isTrust为true,注入时刻,ducument-start
|
|
3072
|
+
* @param isTrustValue (可选)让isTrusted为true
|
|
3073
|
+
* @param filter (可选)过滤出需要的事件名,true为需要,false为不需要
|
|
3074
|
+
* @example
|
|
3075
|
+
* Utils.registerTrustClickEvent()
|
|
3076
|
+
*/
|
|
3077
|
+
registerTrustClickEvent(
|
|
3078
|
+
isTrustValue?: boolean,
|
|
3079
|
+
filter?: (typeName: string) => boolean
|
|
3080
|
+
): void;
|
|
3081
|
+
registerTrustClickEvent(
|
|
3082
|
+
isTrustValue: boolean = true,
|
|
3083
|
+
filter?: (typeName: string) => boolean
|
|
3084
|
+
): void {
|
|
3085
|
+
function trustEvent(event: Event) {
|
|
3086
|
+
return new Proxy(event, {
|
|
3087
|
+
get: function (target, property) {
|
|
3088
|
+
if (property === "isTrusted") {
|
|
3089
|
+
return isTrustValue;
|
|
3090
|
+
} else {
|
|
3091
|
+
return Reflect.get(target, property);
|
|
3092
|
+
}
|
|
3093
|
+
},
|
|
3094
|
+
});
|
|
3095
|
+
}
|
|
3096
|
+
if (filter == null) {
|
|
3097
|
+
filter = function (typeName) {
|
|
3098
|
+
return typeName === "click";
|
|
3099
|
+
};
|
|
3100
|
+
}
|
|
3101
|
+
const originalListener = EventTarget.prototype.addEventListener;
|
|
3102
|
+
EventTarget.prototype.addEventListener = function (...args) {
|
|
3103
|
+
let type = args[0];
|
|
3104
|
+
let callback = args[1];
|
|
3105
|
+
// @ts-ignore
|
|
3106
|
+
let options = args[2];
|
|
3107
|
+
if (filter(type)) {
|
|
3108
|
+
if (typeof callback === "function") {
|
|
3109
|
+
args[1] = function (event) {
|
|
3110
|
+
callback.call(this, trustEvent(event));
|
|
3111
|
+
};
|
|
3112
|
+
} else if (
|
|
3113
|
+
typeof callback === "object" &&
|
|
3114
|
+
"handleEvent" in (callback as any)
|
|
3115
|
+
) {
|
|
3116
|
+
let oldHandleEvent = (callback as any)["handleEvent"];
|
|
3117
|
+
|
|
3118
|
+
(args[1] as any)["handleEvent"] = function (event: Event) {
|
|
3119
|
+
if (event == null) {
|
|
3120
|
+
return;
|
|
3121
|
+
}
|
|
3122
|
+
try {
|
|
3123
|
+
/* Proxy对象使用instanceof会报错 */
|
|
3124
|
+
event instanceof Proxy;
|
|
3125
|
+
oldHandleEvent.call(this, trustEvent(event));
|
|
3126
|
+
} catch (error) {
|
|
3127
|
+
// @ts-ignore
|
|
3128
|
+
event["isTrusted"] = isTrustValue;
|
|
3129
|
+
}
|
|
3130
|
+
};
|
|
3131
|
+
}
|
|
3132
|
+
}
|
|
3133
|
+
return originalListener.apply(this, args);
|
|
3134
|
+
};
|
|
3135
|
+
}
|
|
3136
|
+
/**
|
|
3137
|
+
* 将数字进行正/负转换
|
|
3138
|
+
* @param num 需要进行转换的数字
|
|
3139
|
+
*/
|
|
3140
|
+
reverseNumber(num: number): number;
|
|
3141
|
+
reverseNumber(num: number): number {
|
|
3142
|
+
let reversedNum = 0;
|
|
3143
|
+
let isNegative = false;
|
|
3144
|
+
|
|
3145
|
+
if (num < 0) {
|
|
3146
|
+
isNegative = true;
|
|
3147
|
+
num = Math.abs(num);
|
|
3148
|
+
}
|
|
3149
|
+
|
|
3150
|
+
while (num > 0) {
|
|
3151
|
+
reversedNum = reversedNum * 10 + (num % 10);
|
|
3152
|
+
num = Math.floor(num / 10);
|
|
3153
|
+
}
|
|
3154
|
+
|
|
3155
|
+
return isNegative ? -reversedNum : reversedNum;
|
|
3156
|
+
}
|
|
3157
|
+
/**
|
|
3158
|
+
* 将元素上的文本或元素使用光标进行选中
|
|
3159
|
+
*
|
|
3160
|
+
* 注意,如果设置startIndex和endIndex,且元素上并无可选则的坐标,那么会报错
|
|
3161
|
+
* @param element 目标元素
|
|
3162
|
+
* @param childTextNode 目标元素下的#text元素
|
|
3163
|
+
* @param startIndex (可选)开始坐标,可为空
|
|
3164
|
+
* @param endIndex (可选)结束坐标,可为空
|
|
3165
|
+
*/
|
|
3166
|
+
selectElementText(
|
|
3167
|
+
element: HTMLElement | Element | Node,
|
|
3168
|
+
childTextNode: ChildNode,
|
|
3169
|
+
startIndex?: number,
|
|
3170
|
+
endIndex?: number
|
|
3171
|
+
): void;
|
|
3172
|
+
selectElementText(
|
|
3173
|
+
element: HTMLElement | Element | Node,
|
|
3174
|
+
childTextNode: ChildNode,
|
|
3175
|
+
startIndex?: number,
|
|
3176
|
+
endIndex?: number
|
|
3177
|
+
): void {
|
|
3178
|
+
let range = UtilsCore.document.createRange();
|
|
3179
|
+
range.selectNodeContents(element);
|
|
3180
|
+
if (childTextNode) {
|
|
3181
|
+
if (childTextNode.nodeType !== Node.TEXT_NODE) {
|
|
3182
|
+
throw new TypeError("childTextNode必须是#text元素");
|
|
3183
|
+
}
|
|
3184
|
+
if (startIndex != null && endIndex != null) {
|
|
3185
|
+
range.setStart(childTextNode, startIndex);
|
|
3186
|
+
range.setEnd(childTextNode, endIndex);
|
|
3187
|
+
}
|
|
3188
|
+
}
|
|
3189
|
+
|
|
3190
|
+
let selection = UtilsCore.globalThis.getSelection();
|
|
3191
|
+
if (selection) {
|
|
3192
|
+
selection.removeAllRanges();
|
|
3193
|
+
selection.addRange(range);
|
|
3194
|
+
}
|
|
3195
|
+
}
|
|
3196
|
+
/**
|
|
3197
|
+
* 复制到剪贴板
|
|
3198
|
+
* @param data 需要复制到剪贴板的文本
|
|
3199
|
+
* @param info (可选)默认:text/plain
|
|
3200
|
+
* @example
|
|
3201
|
+
* Utils.setClip({1:2});
|
|
3202
|
+
* > {"1":2}
|
|
3203
|
+
* @example
|
|
3204
|
+
* Utils.setClip( ()=>{
|
|
3205
|
+
* console.log(1)
|
|
3206
|
+
* });
|
|
3207
|
+
* > ()=>{console.log(1)}
|
|
3208
|
+
* @example
|
|
3209
|
+
* Utils.setClip("xxxx");
|
|
3210
|
+
* > xxxx
|
|
3211
|
+
* @example
|
|
3212
|
+
* Utils.setClip("xxxx","html");
|
|
3213
|
+
* > xxxx
|
|
3214
|
+
* @example
|
|
3215
|
+
* Utils.setClip("xxxx","text/plain");
|
|
3216
|
+
* > xxxx
|
|
3217
|
+
**/
|
|
3218
|
+
setClip(
|
|
3219
|
+
data: any,
|
|
3220
|
+
info?:
|
|
3221
|
+
| {
|
|
3222
|
+
type: string;
|
|
3223
|
+
mimetype: string;
|
|
3224
|
+
}
|
|
3225
|
+
| string
|
|
3226
|
+
): Promise<boolean>;
|
|
3227
|
+
setClip(
|
|
3228
|
+
data: any,
|
|
3229
|
+
info:
|
|
3230
|
+
| {
|
|
3231
|
+
type: string;
|
|
3232
|
+
mimetype: string;
|
|
3233
|
+
}
|
|
3234
|
+
| string = {
|
|
3235
|
+
type: "text",
|
|
3236
|
+
mimetype: "text/plain",
|
|
3237
|
+
}
|
|
3238
|
+
): Promise<boolean> {
|
|
3239
|
+
if (typeof data === "object") {
|
|
3240
|
+
if (data instanceof Element) {
|
|
3241
|
+
data = data.outerHTML;
|
|
3242
|
+
} else {
|
|
3243
|
+
data = JSON.stringify(data);
|
|
3244
|
+
}
|
|
3245
|
+
} else if (typeof data !== "string") {
|
|
3246
|
+
data = data.toString();
|
|
3247
|
+
}
|
|
3248
|
+
let textType = typeof info === "object" ? info.type : (info as string);
|
|
3249
|
+
if (textType.includes("html")) {
|
|
3250
|
+
textType = "text/html";
|
|
3251
|
+
} else {
|
|
3252
|
+
textType = "text/plain";
|
|
3253
|
+
}
|
|
3254
|
+
class UtilsClipboard {
|
|
3255
|
+
#resolve;
|
|
3256
|
+
#copyData;
|
|
3257
|
+
#copyDataType;
|
|
3258
|
+
constructor(
|
|
3259
|
+
resolve: (value: boolean | PromiseLike<boolean>) => void,
|
|
3260
|
+
copyData: any,
|
|
3261
|
+
copyDataType: string
|
|
3262
|
+
) {
|
|
3263
|
+
this.#resolve = resolve;
|
|
3264
|
+
this.#copyData = copyData;
|
|
3265
|
+
this.#copyDataType = copyDataType;
|
|
3266
|
+
}
|
|
3267
|
+
async init() {
|
|
3268
|
+
let copyStatus = false;
|
|
3269
|
+
// @ts-ignore
|
|
3270
|
+
let requestPermissionStatus = await this.requestClipboardPermission();
|
|
3271
|
+
if (
|
|
3272
|
+
this.hasClipboard() &&
|
|
3273
|
+
(this.hasClipboardWrite() || this.hasClipboardWriteText())
|
|
3274
|
+
) {
|
|
3275
|
+
try {
|
|
3276
|
+
copyStatus = await this.copyDataByClipboard();
|
|
3277
|
+
} catch (error) {
|
|
3278
|
+
console.error("复制失败,使用第二种方式,error👉", error);
|
|
3279
|
+
copyStatus = this.copyTextByTextArea();
|
|
3280
|
+
}
|
|
3281
|
+
} else {
|
|
3282
|
+
copyStatus = this.copyTextByTextArea();
|
|
3283
|
+
}
|
|
3284
|
+
this.#resolve(copyStatus);
|
|
3285
|
+
this.destroy();
|
|
3286
|
+
}
|
|
3287
|
+
destroy() {
|
|
3288
|
+
// @ts-ignore
|
|
3289
|
+
this.#resolve = null;
|
|
3290
|
+
// @ts-ignore
|
|
3291
|
+
this.#copyData = null;
|
|
3292
|
+
// @ts-ignore
|
|
3293
|
+
this.#copyDataType = null;
|
|
3294
|
+
}
|
|
3295
|
+
isText() {
|
|
3296
|
+
return this.#copyDataType.includes("text");
|
|
3297
|
+
}
|
|
3298
|
+
hasClipboard() {
|
|
3299
|
+
return navigator?.clipboard != null;
|
|
3300
|
+
}
|
|
3301
|
+
hasClipboardWrite() {
|
|
3302
|
+
return navigator?.clipboard?.write != null;
|
|
3303
|
+
}
|
|
3304
|
+
hasClipboardWriteText() {
|
|
3305
|
+
return navigator?.clipboard?.writeText != null;
|
|
3306
|
+
}
|
|
3307
|
+
/**
|
|
3308
|
+
* 使用textarea和document.execCommand("copy")来复制文字
|
|
3309
|
+
*/
|
|
3310
|
+
copyTextByTextArea() {
|
|
3311
|
+
try {
|
|
3312
|
+
let copyElement = UtilsCore.document.createElement("textarea");
|
|
3313
|
+
copyElement.value = this.#copyData;
|
|
3314
|
+
copyElement.setAttribute("type", "text");
|
|
3315
|
+
copyElement.setAttribute("style", "opacity:0;position:absolute;");
|
|
3316
|
+
copyElement.setAttribute("readonly", "readonly");
|
|
3317
|
+
UtilsCore.document.body.appendChild(copyElement);
|
|
3318
|
+
copyElement.select();
|
|
3319
|
+
UtilsCore.document.execCommand("copy");
|
|
3320
|
+
UtilsCore.document.body.removeChild(copyElement);
|
|
3321
|
+
return true;
|
|
3322
|
+
} catch (error) {
|
|
3323
|
+
console.error("复制失败,error👉", error);
|
|
3324
|
+
return false;
|
|
3325
|
+
}
|
|
3326
|
+
}
|
|
3327
|
+
/**
|
|
3328
|
+
* 申请剪贴板权限
|
|
3329
|
+
* @returns {Promise<boolean>}
|
|
3330
|
+
*/
|
|
3331
|
+
requestClipboardPermission() {
|
|
3332
|
+
return new Promise((resolve, reject) => {
|
|
3333
|
+
if (navigator.permissions && navigator.permissions.query) {
|
|
3334
|
+
navigator.permissions
|
|
3335
|
+
.query({
|
|
3336
|
+
// @ts-ignore
|
|
3337
|
+
name: "clipboard-write",
|
|
3338
|
+
})
|
|
3339
|
+
.then((permissionStatus) => {
|
|
3340
|
+
resolve(true);
|
|
3341
|
+
})
|
|
3342
|
+
.catch(
|
|
3343
|
+
/** @param {TypeError} error */
|
|
3344
|
+
(error) => {
|
|
3345
|
+
console.error([
|
|
3346
|
+
"申请剪贴板权限失败,尝试直接写入👉",
|
|
3347
|
+
error.message ?? error.name ?? error.stack,
|
|
3348
|
+
]);
|
|
3349
|
+
resolve(false);
|
|
3350
|
+
}
|
|
3351
|
+
);
|
|
3352
|
+
} else {
|
|
3353
|
+
resolve(false);
|
|
3354
|
+
}
|
|
3355
|
+
});
|
|
3356
|
+
}
|
|
3357
|
+
/**
|
|
3358
|
+
* 使用clipboard直接写入数据到剪贴板
|
|
3359
|
+
*/
|
|
3360
|
+
copyDataByClipboard(): Promise<boolean> {
|
|
3361
|
+
return new Promise((resolve, reject) => {
|
|
3362
|
+
if (this.isText()) {
|
|
3363
|
+
/* 只复制文字 */
|
|
3364
|
+
navigator.clipboard
|
|
3365
|
+
.writeText(this.#copyData)
|
|
3366
|
+
.then(() => {
|
|
3367
|
+
resolve(true);
|
|
3368
|
+
})
|
|
3369
|
+
.catch((error) => {
|
|
3370
|
+
reject(error);
|
|
3371
|
+
});
|
|
3372
|
+
} else {
|
|
3373
|
+
/* 可复制对象 */
|
|
3374
|
+
let textBlob = new Blob([this.#copyData], {
|
|
3375
|
+
type: this.#copyDataType,
|
|
3376
|
+
});
|
|
3377
|
+
navigator.clipboard
|
|
3378
|
+
.write([
|
|
3379
|
+
new ClipboardItem({
|
|
3380
|
+
[this.#copyDataType]: textBlob,
|
|
3381
|
+
}),
|
|
3382
|
+
])
|
|
3383
|
+
.then(() => {
|
|
3384
|
+
resolve(true);
|
|
3385
|
+
})
|
|
3386
|
+
.catch((error) => {
|
|
3387
|
+
reject(error);
|
|
3388
|
+
});
|
|
3389
|
+
}
|
|
3390
|
+
});
|
|
3391
|
+
}
|
|
3392
|
+
}
|
|
3393
|
+
return new Promise((resolve) => {
|
|
3394
|
+
const utilsClipboard = new UtilsClipboard(resolve, data, textType);
|
|
3395
|
+
if (UtilsCore.document.hasFocus()) {
|
|
3396
|
+
utilsClipboard.init();
|
|
3397
|
+
} else {
|
|
3398
|
+
UtilsCore.window.addEventListener(
|
|
3399
|
+
"focus",
|
|
3400
|
+
() => {
|
|
3401
|
+
utilsClipboard.init();
|
|
3402
|
+
},
|
|
3403
|
+
{ once: true }
|
|
3404
|
+
);
|
|
3405
|
+
}
|
|
3406
|
+
});
|
|
3407
|
+
}
|
|
3408
|
+
/**
|
|
3409
|
+
* 【异步函数】等待N秒执行函数
|
|
3410
|
+
* @param callback 待执行的函数(字符串)
|
|
3411
|
+
* @param delayTime (可选)延时时间(ms),默认:0
|
|
3412
|
+
* @example
|
|
3413
|
+
* await Utils.setTimeout(()=>{}, 2500);
|
|
3414
|
+
* > ƒ tryCatchObj() {}
|
|
3415
|
+
* @example
|
|
3416
|
+
* await Utils.setTimeout("()=>{console.log(12345)}", 2500);
|
|
3417
|
+
* > ƒ tryCatchObj() {}
|
|
3418
|
+
**/
|
|
3419
|
+
setTimeout(callback: (() => void) | string, delayTime?: number): Promise<any>;
|
|
3420
|
+
setTimeout(
|
|
3421
|
+
callback: (() => void) | string,
|
|
3422
|
+
delayTime: number = 0
|
|
3423
|
+
): Promise<any> {
|
|
3424
|
+
let UtilsContext = this;
|
|
3425
|
+
if (typeof callback !== "function" && typeof callback !== "string") {
|
|
3426
|
+
throw new TypeError(
|
|
3427
|
+
"Utils.setTimeout 参数 callback 必须为 function|string 类型"
|
|
3428
|
+
);
|
|
3429
|
+
}
|
|
3430
|
+
if (typeof delayTime !== "number") {
|
|
3431
|
+
throw new TypeError("Utils.setTimeout 参数 delayTime 必须为 number 类型");
|
|
3432
|
+
}
|
|
3433
|
+
return new Promise((resolve) => {
|
|
3434
|
+
setTimeout(() => {
|
|
3435
|
+
resolve(UtilsContext.tryCatch().run(callback));
|
|
3436
|
+
}, delayTime);
|
|
3437
|
+
});
|
|
3438
|
+
}
|
|
3439
|
+
/**
|
|
3440
|
+
* 【异步函数】延迟xxx毫秒
|
|
3441
|
+
* @param delayTime (可选)延时时间(ms),默认:0
|
|
3442
|
+
* @example
|
|
3443
|
+
* await Utils.sleep(2500)
|
|
3444
|
+
**/
|
|
3445
|
+
sleep(delayTime?: number): Promise<void>;
|
|
3446
|
+
sleep(delayTime: number = 0): Promise<void> {
|
|
3447
|
+
if (typeof delayTime !== "number") {
|
|
3448
|
+
throw new Error("Utils.sleep 参数 delayTime 必须为 number 类型");
|
|
3449
|
+
}
|
|
3450
|
+
return new Promise((resolve) => {
|
|
3451
|
+
setTimeout(() => {
|
|
3452
|
+
resolve(void 0);
|
|
3453
|
+
}, delayTime);
|
|
3454
|
+
});
|
|
3455
|
+
}
|
|
3456
|
+
/**
|
|
3457
|
+
* 向右拖动滑块
|
|
3458
|
+
* @param selector 选择器|元素
|
|
3459
|
+
* @param offsetX (可选)水平拖动长度,默认:window.innerWidth
|
|
3460
|
+
* @example
|
|
3461
|
+
* Utils.dragSlider("#xxxx");
|
|
3462
|
+
* @example
|
|
3463
|
+
* Utils.dragSlider("#xxxx",100);
|
|
3464
|
+
*/
|
|
3465
|
+
dragSlider(selector: string | Element | Node, offsetX?: number): void;
|
|
3466
|
+
dragSlider(
|
|
3467
|
+
selector: string | Element | Node,
|
|
3468
|
+
offsetX: number = UtilsCore.window.innerWidth
|
|
3469
|
+
): void {
|
|
3470
|
+
function initMouseEvent(
|
|
3471
|
+
eventName: string,
|
|
3472
|
+
offSetX: number,
|
|
3473
|
+
offSetY: number
|
|
3474
|
+
) {
|
|
3475
|
+
let win = unsafeWindow || window;
|
|
3476
|
+
let mouseEvent = UtilsCore.document.createEvent("MouseEvents");
|
|
3477
|
+
mouseEvent.initMouseEvent(
|
|
3478
|
+
eventName,
|
|
3479
|
+
true,
|
|
3480
|
+
true,
|
|
3481
|
+
win,
|
|
3482
|
+
0,
|
|
3483
|
+
offSetX,
|
|
3484
|
+
offSetY,
|
|
3485
|
+
offSetX,
|
|
3486
|
+
offSetY,
|
|
3487
|
+
false,
|
|
3488
|
+
false,
|
|
3489
|
+
false,
|
|
3490
|
+
false,
|
|
3491
|
+
0,
|
|
3492
|
+
null
|
|
3493
|
+
);
|
|
3494
|
+
return mouseEvent;
|
|
3495
|
+
}
|
|
3496
|
+
let sliderElement =
|
|
3497
|
+
typeof selector === "string"
|
|
3498
|
+
? UtilsCore.document.querySelector(selector)
|
|
3499
|
+
: selector;
|
|
3500
|
+
if (
|
|
3501
|
+
!(sliderElement instanceof Node) ||
|
|
3502
|
+
!(sliderElement instanceof Element)
|
|
3503
|
+
) {
|
|
3504
|
+
throw new Error("Utils.dragSlider 参数selector 必须为Node/Element类型");
|
|
3505
|
+
}
|
|
3506
|
+
let rect = sliderElement.getBoundingClientRect(),
|
|
3507
|
+
x0 = rect.x || rect.left,
|
|
3508
|
+
y0 = rect.y || rect.top,
|
|
3509
|
+
x1 = x0 + offsetX,
|
|
3510
|
+
y1 = y0;
|
|
3511
|
+
sliderElement.dispatchEvent(initMouseEvent("mousedown", x0, y0));
|
|
3512
|
+
sliderElement.dispatchEvent(initMouseEvent("mousemove", x1, y1));
|
|
3513
|
+
sliderElement.dispatchEvent(initMouseEvent("mouseleave", x1, y1));
|
|
3514
|
+
sliderElement.dispatchEvent(initMouseEvent("mouseout", x1, y1));
|
|
3515
|
+
}
|
|
3516
|
+
/**
|
|
3517
|
+
* 使目标元素进入全屏
|
|
3518
|
+
* @param element (可选)目标元素,默认:document.documentElement
|
|
3519
|
+
* @param options (可选)配置,一般不用
|
|
3520
|
+
* @example
|
|
3521
|
+
* Utils.enterFullScreen();
|
|
3522
|
+
*/
|
|
3523
|
+
enterFullScreen(element: HTMLElement, options?: FullscreenOptions): void;
|
|
3524
|
+
enterFullScreen(
|
|
3525
|
+
element: HTMLElement = UtilsCore.document.documentElement,
|
|
3526
|
+
options?: FullscreenOptions
|
|
3527
|
+
): void {
|
|
3528
|
+
try {
|
|
3529
|
+
if (element.requestFullscreen) {
|
|
3530
|
+
element.requestFullscreen(options);
|
|
3531
|
+
} else if ((element as any).webkitRequestFullScreen) {
|
|
3532
|
+
(element as any).webkitRequestFullScreen();
|
|
3533
|
+
} else if ((element as any).mozRequestFullScreen) {
|
|
3534
|
+
(element as any).mozRequestFullScreen();
|
|
3535
|
+
} else if ((element as any).msRequestFullscreen) {
|
|
3536
|
+
(element as any).msRequestFullscreen();
|
|
3537
|
+
} else {
|
|
3538
|
+
throw new TypeError("该浏览器不支持全屏API");
|
|
3539
|
+
}
|
|
3540
|
+
} catch (err) {
|
|
3541
|
+
console.error(err);
|
|
3542
|
+
}
|
|
3543
|
+
}
|
|
3544
|
+
/**
|
|
3545
|
+
* 使浏览器退出全屏
|
|
3546
|
+
* @param element (可选)目标元素,默认:document.documentElement
|
|
3547
|
+
* @example
|
|
3548
|
+
* Utils.exitFullScreen();
|
|
3549
|
+
*/
|
|
3550
|
+
exitFullScreen(element?: HTMLElement): Promise<void>;
|
|
3551
|
+
exitFullScreen(
|
|
3552
|
+
element: HTMLElement = UtilsCore.document.documentElement
|
|
3553
|
+
): Promise<void> {
|
|
3554
|
+
if (UtilsCore.document.exitFullscreen) {
|
|
3555
|
+
return UtilsCore.document.exitFullscreen();
|
|
3556
|
+
} else if ((UtilsCore.document as any).msExitFullscreen) {
|
|
3557
|
+
return (UtilsCore.document as any).msExitFullscreen();
|
|
3558
|
+
} else if ((UtilsCore.document as any).mozCancelFullScreen) {
|
|
3559
|
+
return (UtilsCore.document as any).mozCancelFullScreen();
|
|
3560
|
+
} else if ((UtilsCore.document as any).webkitCancelFullScreen) {
|
|
3561
|
+
return (UtilsCore.document as any).webkitCancelFullScreen();
|
|
3562
|
+
} else {
|
|
3563
|
+
return new Promise((resolve, reject) => {
|
|
3564
|
+
reject(new TypeError("该浏览器不支持全屏API"));
|
|
3565
|
+
});
|
|
3566
|
+
}
|
|
3567
|
+
}
|
|
3568
|
+
/**
|
|
3569
|
+
* 数组按照内部某个值的大小比对排序,如[{"time":"2022-1-1"},{"time":"2022-2-2"}]
|
|
3570
|
+
* @param data 数据|获取数据的方法
|
|
3571
|
+
* @param getPropertyValueFunc 数组内部项的某个属性的值的方法,参数为这个项
|
|
3572
|
+
* @param sortByDesc (可选)排序方式
|
|
3573
|
+
* + true (默认)倒序(值最大排第一个,如:6、5、4、3...)
|
|
3574
|
+
* + false 升序(值最小排第一个,如:1、2、3、4...)
|
|
3575
|
+
* @returns 返回比较排序完成的数组
|
|
3576
|
+
* @example
|
|
3577
|
+
* Utils.sortListByProperty([{"time":"2022-1-1"},{"time":"2022-2-2"}],(item)=>{return item["time"]})
|
|
3578
|
+
* > [{time: '2022-2-2'},{time: '2022-1-1'}]
|
|
3579
|
+
* @example
|
|
3580
|
+
* Utils.sortListByProperty([{"time":"2022-1-1"},{"time":"2022-2-2"}],(item)=>{return item["time"]},false)
|
|
3581
|
+
* > [{time: '2022-1-1'},{time: '2022-2-2'}]
|
|
3582
|
+
**/
|
|
3583
|
+
sortListByProperty<T extends any[] | NodeList>(
|
|
3584
|
+
data: T,
|
|
3585
|
+
getPropertyValueFunc: string | ((value: T) => any),
|
|
3586
|
+
sortByDesc?: boolean
|
|
3587
|
+
): T;
|
|
3588
|
+
sortListByProperty<T extends any[] | NodeList>(
|
|
3589
|
+
data: T,
|
|
3590
|
+
getPropertyValueFunc: string | ((value: T) => any),
|
|
3591
|
+
sortByDesc: boolean = true
|
|
3592
|
+
): T {
|
|
3593
|
+
let UtilsContext = this;
|
|
3594
|
+
if (
|
|
3595
|
+
typeof getPropertyValueFunc !== "function" &&
|
|
3596
|
+
typeof getPropertyValueFunc !== "string"
|
|
3597
|
+
) {
|
|
3598
|
+
throw new Error(
|
|
3599
|
+
"Utils.sortListByProperty 参数 getPropertyValueFunc 必须为 function|string 类型"
|
|
3600
|
+
);
|
|
3601
|
+
}
|
|
3602
|
+
if (typeof sortByDesc !== "boolean") {
|
|
3603
|
+
throw new Error(
|
|
3604
|
+
"Utils.sortListByProperty 参数 sortByDesc 必须为 boolean 类型"
|
|
3605
|
+
);
|
|
3606
|
+
}
|
|
3607
|
+
let getObjValue = function (obj: any) {
|
|
3608
|
+
return typeof getPropertyValueFunc === "string"
|
|
3609
|
+
? obj[getPropertyValueFunc]
|
|
3610
|
+
: getPropertyValueFunc(obj);
|
|
3611
|
+
};
|
|
3612
|
+
/**
|
|
3613
|
+
* 排序方法
|
|
3614
|
+
* @param {any} after_obj
|
|
3615
|
+
* @param {any} before_obj
|
|
3616
|
+
* @returns
|
|
3617
|
+
*/
|
|
3618
|
+
let sortFunc = function (after_obj: any, before_obj: any) {
|
|
3619
|
+
let beforeValue = getObjValue(before_obj); /* 前 */
|
|
3620
|
+
let afterValue = getObjValue(after_obj); /* 后 */
|
|
3621
|
+
if (sortByDesc) {
|
|
3622
|
+
if (afterValue > beforeValue) {
|
|
3623
|
+
return -1;
|
|
3624
|
+
} else if (afterValue < beforeValue) {
|
|
3625
|
+
return 1;
|
|
3626
|
+
} else {
|
|
3627
|
+
return 0;
|
|
3628
|
+
}
|
|
3629
|
+
} else {
|
|
3630
|
+
if (afterValue < beforeValue) {
|
|
3631
|
+
return -1;
|
|
3632
|
+
} else if (afterValue > beforeValue) {
|
|
3633
|
+
return 1;
|
|
3634
|
+
} else {
|
|
3635
|
+
return 0;
|
|
3636
|
+
}
|
|
3637
|
+
}
|
|
3638
|
+
};
|
|
3639
|
+
/**
|
|
3640
|
+
* 排序元素方法
|
|
3641
|
+
* @param nodeList 元素列表
|
|
3642
|
+
* @param getNodeListFunc 获取元素列表的函数
|
|
3643
|
+
*/
|
|
3644
|
+
let sortNodeFunc = function (
|
|
3645
|
+
nodeList: NodeListOf<HTMLElement>,
|
|
3646
|
+
getNodeListFunc: () => NodeListOf<HTMLElement>
|
|
3647
|
+
) {
|
|
3648
|
+
let nodeListLength = nodeList.length;
|
|
3649
|
+
for (let i = 0; i < nodeListLength - 1; i++) {
|
|
3650
|
+
for (let j = 0; j < nodeListLength - 1 - i; j++) {
|
|
3651
|
+
let beforeNode = nodeList[j];
|
|
3652
|
+
let afterNode = nodeList[j + 1];
|
|
3653
|
+
let beforeValue = getObjValue(beforeNode); /* 前 */
|
|
3654
|
+
let afterValue = getObjValue(afterNode); /* 后 */
|
|
3655
|
+
if (
|
|
3656
|
+
(sortByDesc == true && beforeValue < afterValue) ||
|
|
3657
|
+
(sortByDesc == false && beforeValue > afterValue)
|
|
3658
|
+
) {
|
|
3659
|
+
/* 升序/降序 */
|
|
3660
|
+
/* 相邻元素两两对比 */
|
|
3661
|
+
let temp = beforeNode.nextElementSibling;
|
|
3662
|
+
afterNode.after(beforeNode);
|
|
3663
|
+
if (temp == null) {
|
|
3664
|
+
/* 如果为空,那么是最后一个元素,使用append */
|
|
3665
|
+
(temp as any).parentNode.appendChild(afterNode);
|
|
3666
|
+
} else {
|
|
3667
|
+
/* 不为空,使用before */
|
|
3668
|
+
temp.before(afterNode);
|
|
3669
|
+
}
|
|
3670
|
+
nodeList = getNodeListFunc();
|
|
3671
|
+
}
|
|
3672
|
+
}
|
|
3673
|
+
}
|
|
3674
|
+
};
|
|
3675
|
+
let result = data;
|
|
3676
|
+
let getDataFunc = null;
|
|
3677
|
+
if (data instanceof Function) {
|
|
3678
|
+
getDataFunc = data;
|
|
3679
|
+
data = (data as any)();
|
|
3680
|
+
}
|
|
3681
|
+
if (Array.isArray(data)) {
|
|
3682
|
+
data.sort(sortFunc);
|
|
3683
|
+
} else if (data instanceof NodeList || UtilsContext.isJQuery(data)) {
|
|
3684
|
+
sortNodeFunc(data as any, getDataFunc as any);
|
|
3685
|
+
result = (getDataFunc as any)();
|
|
3686
|
+
} else {
|
|
3687
|
+
throw new Error(
|
|
3688
|
+
"Utils.sortListByProperty 参数 data 必须为 Array|NodeList|jQuery 类型"
|
|
3689
|
+
);
|
|
3690
|
+
}
|
|
3691
|
+
return result;
|
|
3692
|
+
}
|
|
3693
|
+
/**
|
|
3694
|
+
* 字符串转正则,用于把字符串中不规范的字符进行转义
|
|
3695
|
+
* @param targetString 需要进行转换的字符串
|
|
3696
|
+
* @param flags 正则标志
|
|
3697
|
+
*/
|
|
3698
|
+
stringToRegular(
|
|
3699
|
+
targetString: string | RegExp,
|
|
3700
|
+
flags?: "g" | "i" | "m" | "u" | "y" | string
|
|
3701
|
+
): RegExp;
|
|
3702
|
+
stringToRegular(
|
|
3703
|
+
targetString: string | RegExp,
|
|
3704
|
+
flags: "g" | "i" | "m" | "u" | "y" | string = "ig"
|
|
3705
|
+
): RegExp {
|
|
3706
|
+
let reg;
|
|
3707
|
+
// @ts-ignore
|
|
3708
|
+
flags = flags.toLowerCase();
|
|
3709
|
+
if (typeof targetString === "string") {
|
|
3710
|
+
reg = new RegExp(
|
|
3711
|
+
targetString.replace(/[.*+\-?^${}()|[\]\\]/g, "\\$&"),
|
|
3712
|
+
flags
|
|
3713
|
+
);
|
|
3714
|
+
} else if ((targetString as any) instanceof RegExp) {
|
|
3715
|
+
reg = targetString;
|
|
3716
|
+
} else {
|
|
3717
|
+
throw new Error(
|
|
3718
|
+
"Utils.stringToRegular 参数targetString必须是string|Regexp类型"
|
|
3719
|
+
);
|
|
3720
|
+
}
|
|
3721
|
+
return reg;
|
|
3722
|
+
}
|
|
3723
|
+
/**
|
|
3724
|
+
* 字符串首字母转大写
|
|
3725
|
+
* @param targetString 目标字符串
|
|
3726
|
+
* @param otherStrToLowerCase (可选)剩余部分字符串转小写,默认false
|
|
3727
|
+
*/
|
|
3728
|
+
stringTitleToUpperCase(
|
|
3729
|
+
targetString: string,
|
|
3730
|
+
otherStrToLowerCase?: boolean
|
|
3731
|
+
): string;
|
|
3732
|
+
stringTitleToUpperCase(
|
|
3733
|
+
targetString: string,
|
|
3734
|
+
otherStrToLowerCase: boolean = false
|
|
3735
|
+
): string {
|
|
3736
|
+
let newTargetString = targetString.slice(0, 1).toUpperCase();
|
|
3737
|
+
if (otherStrToLowerCase) {
|
|
3738
|
+
newTargetString = newTargetString + targetString.slice(1).toLowerCase();
|
|
3739
|
+
} else {
|
|
3740
|
+
newTargetString = newTargetString + targetString.slice(1);
|
|
3741
|
+
}
|
|
3742
|
+
return newTargetString;
|
|
3743
|
+
}
|
|
3744
|
+
/**
|
|
3745
|
+
* 判断目标字符串是否是以xxx开始
|
|
3746
|
+
*
|
|
3747
|
+
* 如果searchString是字符串数组,那么判断的结果则是字符串数组中的任意字符匹配到返回true
|
|
3748
|
+
* @param target 目标字符串
|
|
3749
|
+
* @param searchString 需要搜索的字符串
|
|
3750
|
+
* @param position (可选)目标字符串的判断起点,要求≥0,默认为0
|
|
3751
|
+
*/
|
|
3752
|
+
startsWith(
|
|
3753
|
+
target: string,
|
|
3754
|
+
searchString: string | RegExp | string[],
|
|
3755
|
+
position?: number
|
|
3756
|
+
): boolean;
|
|
3757
|
+
startsWith(
|
|
3758
|
+
target: string,
|
|
3759
|
+
searchString: string | RegExp | string[],
|
|
3760
|
+
position: number = 0
|
|
3761
|
+
): boolean {
|
|
3762
|
+
let UtilsContext = this;
|
|
3763
|
+
if (position > target.length) {
|
|
3764
|
+
/* 超出目标字符串的长度 */
|
|
3765
|
+
return false;
|
|
3766
|
+
}
|
|
3767
|
+
if (position !== 0) {
|
|
3768
|
+
target = target.slice(position, target.length);
|
|
3769
|
+
}
|
|
3770
|
+
let searchStringRegexp = searchString;
|
|
3771
|
+
if (typeof searchString === "string") {
|
|
3772
|
+
searchStringRegexp = new RegExp(`^${searchString}`);
|
|
3773
|
+
} else if (Array.isArray(searchString)) {
|
|
3774
|
+
let flag = false;
|
|
3775
|
+
for (const searcStr of searchString) {
|
|
3776
|
+
if (!UtilsContext.startsWith(target, searcStr, position)) {
|
|
3777
|
+
flag = true;
|
|
3778
|
+
break;
|
|
3779
|
+
}
|
|
3780
|
+
}
|
|
3781
|
+
return flag;
|
|
3782
|
+
}
|
|
3783
|
+
return Boolean(target.match(searchStringRegexp as RegExp));
|
|
3784
|
+
}
|
|
3785
|
+
/**
|
|
3786
|
+
* 字符串首字母转小写
|
|
3787
|
+
* @param targetString 目标字符串
|
|
3788
|
+
* @param otherStrToLowerCase (可选)剩余部分字符串转大写,默认false
|
|
3789
|
+
*/
|
|
3790
|
+
stringTitleToLowerCase(
|
|
3791
|
+
targetString: string,
|
|
3792
|
+
otherStrToUpperCase?: boolean
|
|
3793
|
+
): string;
|
|
3794
|
+
stringTitleToLowerCase(
|
|
3795
|
+
targetString: string,
|
|
3796
|
+
otherStrToUpperCase: boolean = false
|
|
3797
|
+
): string {
|
|
3798
|
+
let newTargetString = targetString.slice(0, 1).toLowerCase();
|
|
3799
|
+
if (otherStrToUpperCase) {
|
|
3800
|
+
newTargetString = newTargetString + targetString.slice(1).toUpperCase();
|
|
3801
|
+
} else {
|
|
3802
|
+
newTargetString = newTargetString + targetString.slice(1);
|
|
3803
|
+
}
|
|
3804
|
+
return newTargetString;
|
|
3805
|
+
}
|
|
3806
|
+
/**
|
|
3807
|
+
* 字符串转Object对象,类似'{"test":""}' => {"test":""}
|
|
3808
|
+
* @param data
|
|
3809
|
+
* @param errorCallBack (可选)错误回调
|
|
3810
|
+
* @example
|
|
3811
|
+
* Utils.toJSON("{123:123}")
|
|
3812
|
+
* > {123:123}
|
|
3813
|
+
*/
|
|
3814
|
+
toJSON<T extends AnyObject>(
|
|
3815
|
+
data: string | null,
|
|
3816
|
+
errorCallBack?: (error: Error) => void
|
|
3817
|
+
): T;
|
|
3818
|
+
toJSON<T extends AnyObject>(
|
|
3819
|
+
data: string | null,
|
|
3820
|
+
errorCallBack?: (error: Error) => void
|
|
3821
|
+
): T {
|
|
3822
|
+
let UtilsContext = this;
|
|
3823
|
+
let result: AnyObject = {};
|
|
3824
|
+
if (typeof data === "object") {
|
|
3825
|
+
return data as any;
|
|
3826
|
+
}
|
|
3827
|
+
UtilsContext.tryCatch()
|
|
3828
|
+
.config({ log: false })
|
|
3829
|
+
.error((error: Error) => {
|
|
3830
|
+
UtilsContext.tryCatch()
|
|
3831
|
+
.error(() => {
|
|
3832
|
+
try {
|
|
3833
|
+
result = (UtilsCore.window as any).eval("(" + data + ")");
|
|
3834
|
+
} catch (error2: any) {
|
|
3835
|
+
if (typeof errorCallBack === "function") {
|
|
3836
|
+
errorCallBack(error2);
|
|
3837
|
+
}
|
|
3838
|
+
}
|
|
3839
|
+
})
|
|
3840
|
+
.run(() => {
|
|
3841
|
+
if (
|
|
3842
|
+
data &&
|
|
3843
|
+
/^[\],:{}\s]*$/.test(
|
|
3844
|
+
data
|
|
3845
|
+
.replace(/\\(?:["\\\/bfnrt]|u[0-9a-fA-F]{4})/g, "@")
|
|
3846
|
+
.replace(
|
|
3847
|
+
/"[^"\\\n\r]*"|true|false|null|-?\d+(?:\.\d*)?(?:[eE][+\-]?\d+)?/g,
|
|
3848
|
+
"]"
|
|
3849
|
+
)
|
|
3850
|
+
.replace(/(?:^|:|,)(?:\s*\[)+/g, "")
|
|
3851
|
+
)
|
|
3852
|
+
) {
|
|
3853
|
+
result = new Function("return " + data)();
|
|
3854
|
+
} else {
|
|
3855
|
+
if (typeof errorCallBack === "function") {
|
|
3856
|
+
errorCallBack(new Error("target is not a JSON"));
|
|
3857
|
+
}
|
|
3858
|
+
}
|
|
3859
|
+
});
|
|
3860
|
+
})
|
|
3861
|
+
.run(() => {
|
|
3862
|
+
data = (data as string).trim();
|
|
3863
|
+
result = JSON.parse(data);
|
|
3864
|
+
});
|
|
3865
|
+
return result as any;
|
|
3866
|
+
}
|
|
3867
|
+
/**
|
|
3868
|
+
* 对象转为UrlSearchParams格式的字符串
|
|
3869
|
+
* @param obj 目标对象,可以是对象组成的数组
|
|
3870
|
+
*/
|
|
3871
|
+
toSearchParamsStr(obj: object | object[]): string;
|
|
3872
|
+
toSearchParamsStr(obj: object | object[]): string {
|
|
3873
|
+
let UtilsContext = this;
|
|
3874
|
+
let searhParamsStr = "";
|
|
3875
|
+
if (Array.isArray(obj)) {
|
|
3876
|
+
obj.forEach((item) => {
|
|
3877
|
+
if (searhParamsStr === "") {
|
|
3878
|
+
searhParamsStr += UtilsContext.toSearchParamsStr(item);
|
|
3879
|
+
} else {
|
|
3880
|
+
searhParamsStr += "&" + UtilsContext.toSearchParamsStr(item);
|
|
3881
|
+
}
|
|
3882
|
+
});
|
|
3883
|
+
} else {
|
|
3884
|
+
searhParamsStr = new URLSearchParams(Object.entries(obj)).toString();
|
|
3885
|
+
}
|
|
3886
|
+
return searhParamsStr;
|
|
3887
|
+
}
|
|
3888
|
+
/**
|
|
3889
|
+
* 提供一个封装了 try-catch 的函数,可以执行传入的函数并捕获其可能抛出的错误,并通过传入的错误处理函数进行处理。
|
|
3890
|
+
* @example
|
|
3891
|
+
* Utils.tryCatch().error().run(()=>{console.log(1)});
|
|
3892
|
+
* > 1
|
|
3893
|
+
* @example
|
|
3894
|
+
* Utils.tryCatch().config({log:true}).error((error)=>{console.log(error)}).run(()=>{throw new Error('测试错误')});
|
|
3895
|
+
* > ()=>{throw new Error('测试错误')}出现错误
|
|
3896
|
+
*/
|
|
3897
|
+
tryCatch = TryCatch;
|
|
3898
|
+
/**
|
|
3899
|
+
* 数组去重,去除重复的值
|
|
3900
|
+
* @param uniqueArrayData 需要去重的数组
|
|
3901
|
+
* @param compareArrayData 用来比较的数组
|
|
3902
|
+
* @param compareFun 数组比较方法,如果值相同,去除该数据
|
|
3903
|
+
* @returns 返回去重完毕的数组
|
|
3904
|
+
* @example
|
|
3905
|
+
* Utils.uniqueArray([1,2,3],[1,2],(item,item2)=>{return item===item2 ? true:false});
|
|
3906
|
+
* > [3]
|
|
3907
|
+
*
|
|
3908
|
+
* @example
|
|
3909
|
+
* Utils.uniqueArray([1,2,3],[1,2]);
|
|
3910
|
+
* > [3]
|
|
3911
|
+
*
|
|
3912
|
+
* @example
|
|
3913
|
+
* Utils.uniqueArray([{"key":1,"value":2},{"key":2}],[{"key":1}],(item,item2)=>{return item["key"] === item2["key"] ? true:false});
|
|
3914
|
+
* > [{"key": 2}]
|
|
3915
|
+
**/
|
|
3916
|
+
uniqueArray<T extends any, TT extends any>(
|
|
3917
|
+
uniqueArrayData?: T[],
|
|
3918
|
+
compareArrayData?: TT[],
|
|
3919
|
+
compareFun?: (item1: T, item2: TT) => boolean
|
|
3920
|
+
): any[];
|
|
3921
|
+
uniqueArray<T extends any, TT extends any>(
|
|
3922
|
+
uniqueArrayData: T[] = [],
|
|
3923
|
+
compareArrayData: TT[] = [],
|
|
3924
|
+
compareFun: (item1: T, item2: TT) => boolean = (item, item2) => {
|
|
3925
|
+
// @ts-ignore
|
|
3926
|
+
return item === item2;
|
|
3927
|
+
}
|
|
3928
|
+
): any[] {
|
|
3929
|
+
return Array.from(uniqueArrayData).filter(
|
|
3930
|
+
(item) =>
|
|
3931
|
+
!Array.from(compareArrayData).some(function (item2) {
|
|
3932
|
+
return compareFun(item, item2);
|
|
3933
|
+
})
|
|
3934
|
+
);
|
|
3935
|
+
}
|
|
3936
|
+
/**
|
|
3937
|
+
* 等待函数数组全部执行完毕,注意,每个函数的顺序不是同步
|
|
3938
|
+
* @param data 需要遍历的数组
|
|
3939
|
+
* @param handleFunc 对该数组进行操作的函数,该函数的参数为数组格式的参数,[数组下标,数组项]
|
|
3940
|
+
* @example
|
|
3941
|
+
* await Utils.waitArrayLoopToEnd([callback,callback,callback],xxxcallback);
|
|
3942
|
+
**/
|
|
3943
|
+
waitArrayLoopToEnd(
|
|
3944
|
+
data: any[] | HTMLElement[],
|
|
3945
|
+
handleFunc: Function
|
|
3946
|
+
): Promise<void[]>;
|
|
3947
|
+
waitArrayLoopToEnd(
|
|
3948
|
+
data: any[] | HTMLElement[],
|
|
3949
|
+
handleFunc: Function
|
|
3950
|
+
): Promise<void[]> {
|
|
3951
|
+
let UtilsContext = this;
|
|
3952
|
+
if (typeof handleFunc !== "function" && typeof handleFunc !== "string") {
|
|
3953
|
+
throw new Error(
|
|
3954
|
+
"Utils.waitArrayLoopToEnd 参数 handleDataFunction 必须为 function|string 类型"
|
|
3955
|
+
);
|
|
3956
|
+
}
|
|
3957
|
+
return Promise.all(
|
|
3958
|
+
Array.from(data).map(async (item, index) => {
|
|
3959
|
+
await UtilsContext.tryCatch(index, item).run(handleFunc);
|
|
3960
|
+
})
|
|
3961
|
+
);
|
|
3962
|
+
}
|
|
3963
|
+
/**
|
|
3964
|
+
* 等待元素出现
|
|
3965
|
+
* @param selector CSS选择器
|
|
3966
|
+
* @param parent (可选)父元素,默认document
|
|
3967
|
+
* @example
|
|
3968
|
+
* Utils.waitNode("div").then( $div =>{
|
|
3969
|
+
* console.log($div); // div => HTMLDivELement
|
|
3970
|
+
* })
|
|
3971
|
+
* Utils.waitNode("div",document).then( $div =>{
|
|
3972
|
+
* console.log($div); // div => HTMLDivELement
|
|
3973
|
+
* })
|
|
3974
|
+
*/
|
|
3975
|
+
waitNode<K extends keyof HTMLElementTagNameMap>(
|
|
3976
|
+
selector: K,
|
|
3977
|
+
parent?: Node | Element | Document | HTMLElement
|
|
3978
|
+
): Promise<HTMLElementTagNameMap[K]>;
|
|
3979
|
+
waitNode<T extends Element>(
|
|
3980
|
+
selector: string,
|
|
3981
|
+
parent?: Node | Element | Document | HTMLElement
|
|
3982
|
+
): Promise<T>;
|
|
3983
|
+
/**
|
|
3984
|
+
* 等待元素出现
|
|
3985
|
+
* @param selectorList CSS选择器数组
|
|
3986
|
+
* @param parent (可选)父元素,默认document
|
|
3987
|
+
* @example
|
|
3988
|
+
* Utils.waitNode(["div"]).then( ([$div]) =>{
|
|
3989
|
+
* console.log($div); // div => HTMLDivELement[]
|
|
3990
|
+
* })
|
|
3991
|
+
* Utils.waitNode(["div"],document).then( ([$div]) =>{
|
|
3992
|
+
* console.log($div); // div => HTMLDivELement[]
|
|
3993
|
+
* })
|
|
3994
|
+
*/
|
|
3995
|
+
waitNode<K extends keyof HTMLElementTagNameMap>(
|
|
3996
|
+
selectorList: K[],
|
|
3997
|
+
parent?: Node | Element | Document | HTMLElement
|
|
3998
|
+
): Promise<HTMLElementTagNameMap[K][]>;
|
|
3999
|
+
waitNode<T extends Element[]>(
|
|
4000
|
+
selectorList: string[],
|
|
4001
|
+
parent?: Node | Element | Document | HTMLElement
|
|
4002
|
+
): Promise<T>;
|
|
4003
|
+
/**
|
|
4004
|
+
* 等待元素出现
|
|
4005
|
+
* @param selector CSS选择器
|
|
4006
|
+
* @param parent 父元素,默认document
|
|
4007
|
+
* @param timeout 超时时间,默认0
|
|
4008
|
+
* @example
|
|
4009
|
+
* Utils.waitNode("div",document,1000).then( $div =>{
|
|
4010
|
+
* console.log($div); // $div => HTMLDivELement | null
|
|
4011
|
+
* })
|
|
4012
|
+
*/
|
|
4013
|
+
waitNode<K extends keyof HTMLElementTagNameMap>(
|
|
4014
|
+
selector: K,
|
|
4015
|
+
parent: Node | Element | Document | HTMLElement,
|
|
4016
|
+
timeout: number
|
|
4017
|
+
): Promise<HTMLElementTagNameMap[K] | null>;
|
|
4018
|
+
waitNode<T extends Element>(
|
|
4019
|
+
selector: string,
|
|
4020
|
+
parent: Node | Element | Document | HTMLElement,
|
|
4021
|
+
timeout: number
|
|
4022
|
+
): Promise<T | null>;
|
|
4023
|
+
/**
|
|
4024
|
+
* 等待元素出现
|
|
4025
|
+
* @param selectorList CSS选择器数组
|
|
4026
|
+
* @param parent 父元素,默认document
|
|
4027
|
+
* @param timeout 超时时间,默认0
|
|
4028
|
+
* @example
|
|
4029
|
+
* Utils.waitNode(["div"],document,1000).then( ([$div]) =>{
|
|
4030
|
+
* console.log($div); // $div => HTMLDivELement[] | null
|
|
4031
|
+
* })
|
|
4032
|
+
*/
|
|
4033
|
+
waitNode<K extends keyof HTMLElementTagNameMap>(
|
|
4034
|
+
selectorList: K[],
|
|
4035
|
+
parent: Node | Element | Document | HTMLElement,
|
|
4036
|
+
timeout: number
|
|
4037
|
+
): Promise<HTMLElementTagNameMap[K] | null>;
|
|
4038
|
+
waitNode<T extends Element[]>(
|
|
4039
|
+
selectorList: string[],
|
|
4040
|
+
parent: Node | Element | Document | HTMLElement,
|
|
4041
|
+
timeout: number
|
|
4042
|
+
): Promise<T | null>;
|
|
4043
|
+
/**
|
|
4044
|
+
* 等待元素出现
|
|
4045
|
+
* @param selector CSS选择器
|
|
4046
|
+
* @param timeout 超时时间,默认0
|
|
4047
|
+
* @example
|
|
4048
|
+
* Utils.waitNode("div",1000).then( $div =>{
|
|
4049
|
+
* console.log($div); // $div => HTMLDivELement | null
|
|
4050
|
+
* })
|
|
4051
|
+
*/
|
|
4052
|
+
waitNode<K extends keyof HTMLElementTagNameMap>(
|
|
4053
|
+
selector: K,
|
|
4054
|
+
timeout: number
|
|
4055
|
+
): Promise<HTMLElementTagNameMap[K] | null>;
|
|
4056
|
+
waitNode<T extends Element>(
|
|
4057
|
+
selector: string,
|
|
4058
|
+
timeout: number
|
|
4059
|
+
): Promise<T | null>;
|
|
4060
|
+
/**
|
|
4061
|
+
* 等待元素出现
|
|
4062
|
+
* @param selectorList CSS选择器数组
|
|
4063
|
+
* @param timeout 超时时间,默认0
|
|
4064
|
+
* @example
|
|
4065
|
+
* Utils.waitNode(["div"],1000).then( [$div] =>{
|
|
4066
|
+
* console.log($div); // $div => HTMLDivELement[] | null
|
|
4067
|
+
* })
|
|
4068
|
+
*/
|
|
4069
|
+
waitNode<K extends keyof HTMLElementTagNameMap>(
|
|
4070
|
+
selectorList: K[],
|
|
4071
|
+
timeout: number
|
|
4072
|
+
): Promise<HTMLElementTagNameMap[K] | null>;
|
|
4073
|
+
waitNode<T extends Element[]>(
|
|
4074
|
+
selectorList: string[],
|
|
4075
|
+
timeout: number
|
|
4076
|
+
): Promise<T | null>;
|
|
4077
|
+
waitNode<T extends Element | Element[]>(...args: any[]): Promise<T | null> {
|
|
4078
|
+
// 过滤掉undefined
|
|
4079
|
+
args = args.filter((arg) => arg !== void 0);
|
|
4080
|
+
let that = this;
|
|
4081
|
+
// 选择器
|
|
4082
|
+
let selector = args[0] as unknown as string | string[];
|
|
4083
|
+
// 父元素(监听的元素)
|
|
4084
|
+
let parent: Element = UtilsCore.document as any as Element;
|
|
4085
|
+
// 超时时间
|
|
4086
|
+
let timeout = 0;
|
|
4087
|
+
if (typeof args[0] !== "string" && !Array.isArray(args[0])) {
|
|
4088
|
+
throw new TypeError("Utils.waitNode 第一个参数必须是string|string[]");
|
|
4089
|
+
}
|
|
4090
|
+
if (args.length === 1) {
|
|
4091
|
+
// 上面已做处理
|
|
4092
|
+
} else if (args.length === 2) {
|
|
4093
|
+
let secondParam = args[1];
|
|
4094
|
+
if (typeof secondParam === "number") {
|
|
4095
|
+
// "div",10000
|
|
4096
|
+
timeout = secondParam;
|
|
4097
|
+
} else if (
|
|
4098
|
+
typeof secondParam === "object" &&
|
|
4099
|
+
secondParam instanceof Node
|
|
4100
|
+
) {
|
|
4101
|
+
// "div",document
|
|
4102
|
+
parent = secondParam as any as Element;
|
|
4103
|
+
} else {
|
|
4104
|
+
throw new TypeError("Utils.waitNode 第二个参数必须是number|Node");
|
|
4105
|
+
}
|
|
4106
|
+
} else if (args.length === 3) {
|
|
4107
|
+
// "div",document,10000
|
|
4108
|
+
// 第二个参数,parent
|
|
4109
|
+
let secondParam = args[1];
|
|
4110
|
+
// 第三个参数,timeout
|
|
4111
|
+
let thirdParam = args[2];
|
|
4112
|
+
if (typeof secondParam === "object" && secondParam instanceof Node) {
|
|
4113
|
+
parent = secondParam as any as Element;
|
|
4114
|
+
if (typeof thirdParam === "number") {
|
|
4115
|
+
timeout = thirdParam;
|
|
4116
|
+
} else {
|
|
4117
|
+
throw new TypeError("Utils.waitNode 第三个参数必须是number");
|
|
4118
|
+
}
|
|
4119
|
+
} else {
|
|
4120
|
+
throw new TypeError("Utils.waitNode 第二个参数必须是Node");
|
|
4121
|
+
}
|
|
4122
|
+
} else {
|
|
4123
|
+
throw new TypeError("Utils.waitNode 参数个数错误");
|
|
4124
|
+
}
|
|
4125
|
+
return new Promise((resolve) => {
|
|
4126
|
+
function getNode() {
|
|
4127
|
+
if (Array.isArray(selector)) {
|
|
4128
|
+
let result: T[] = [];
|
|
4129
|
+
for (let index = 0; index < selector.length; index++) {
|
|
4130
|
+
let node = parent.querySelector(selector[index]);
|
|
4131
|
+
if (node) {
|
|
4132
|
+
result.push(node as any);
|
|
4133
|
+
}
|
|
4134
|
+
}
|
|
4135
|
+
if (result.length === selector.length) {
|
|
4136
|
+
return result;
|
|
4137
|
+
}
|
|
4138
|
+
} else {
|
|
4139
|
+
return parent.querySelector(selector);
|
|
4140
|
+
}
|
|
4141
|
+
}
|
|
4142
|
+
var observer = that.mutationObserver(parent, {
|
|
4143
|
+
config: {
|
|
4144
|
+
subtree: true,
|
|
4145
|
+
childList: true,
|
|
4146
|
+
attributes: true,
|
|
4147
|
+
},
|
|
4148
|
+
callback() {
|
|
4149
|
+
let node = getNode();
|
|
4150
|
+
if (node) {
|
|
4151
|
+
// 取消观察器
|
|
4152
|
+
if (typeof observer?.disconnect === "function") {
|
|
4153
|
+
observer.disconnect();
|
|
4154
|
+
}
|
|
4155
|
+
resolve(node as any as T);
|
|
4156
|
+
return;
|
|
4157
|
+
}
|
|
4158
|
+
},
|
|
4159
|
+
immediate: true,
|
|
4160
|
+
});
|
|
4161
|
+
if (timeout > 0) {
|
|
4162
|
+
setTimeout(() => {
|
|
4163
|
+
// 取消观察器
|
|
4164
|
+
if (typeof observer?.disconnect === "function") {
|
|
4165
|
+
observer.disconnect();
|
|
4166
|
+
}
|
|
4167
|
+
resolve(null);
|
|
4168
|
+
}, timeout);
|
|
4169
|
+
}
|
|
4170
|
+
});
|
|
4171
|
+
}
|
|
4172
|
+
/**
|
|
4173
|
+
* 等待任意元素出现
|
|
4174
|
+
* @param selectorList CSS选择器数组
|
|
4175
|
+
* @param parent (可选)监听的父元素
|
|
4176
|
+
* @example
|
|
4177
|
+
* Utils.waitAnyNode(["div","div"]).then( $div =>{
|
|
4178
|
+
* console.log($div); // $div => HTMLDivELement 这里是第一个
|
|
4179
|
+
* })
|
|
4180
|
+
* Utils.waitAnyNode(["a","div"],document).then( $a =>{
|
|
4181
|
+
* console.log($a); // $a => HTMLAnchorElement 这里是第一个
|
|
4182
|
+
* })
|
|
4183
|
+
*/
|
|
4184
|
+
waitAnyNode<K extends keyof HTMLElementTagNameMap>(
|
|
4185
|
+
selectorList: K[],
|
|
4186
|
+
parent?: Node | Element | Document | HTMLElement
|
|
4187
|
+
): Promise<HTMLElementTagNameMap[K]>;
|
|
4188
|
+
waitAnyNode<T extends Element>(
|
|
4189
|
+
selectorList: string[],
|
|
4190
|
+
parent?: Node | Element | Document | HTMLElement
|
|
4191
|
+
): Promise<T>;
|
|
4192
|
+
/**
|
|
4193
|
+
* 等待任意元素出现
|
|
4194
|
+
* @param selectorList CSS选择器数组
|
|
4195
|
+
* @param parent 父元素,默认document
|
|
4196
|
+
* @param timeout 超时时间,默认0
|
|
4197
|
+
* @example
|
|
4198
|
+
* Utils.waitAnyNode(["div","div"],document,10000).then( $div =>{
|
|
4199
|
+
* console.log($div); // $div => HTMLDivELement | null
|
|
4200
|
+
* })
|
|
4201
|
+
*/
|
|
4202
|
+
waitAnyNode<K extends keyof HTMLElementTagNameMap>(
|
|
4203
|
+
selectorList: K[],
|
|
4204
|
+
parent: Node | Element | Document | HTMLElement,
|
|
4205
|
+
timeout: number
|
|
4206
|
+
): Promise<HTMLElementTagNameMap[K] | null>;
|
|
4207
|
+
waitAnyNode<T extends Element>(
|
|
4208
|
+
selectorList: string[],
|
|
4209
|
+
parent: Node | Element | Document | HTMLElement,
|
|
4210
|
+
timeout: number
|
|
4211
|
+
): Promise<T | null>;
|
|
4212
|
+
/**
|
|
4213
|
+
* 等待任意元素出现
|
|
4214
|
+
* @param selectorList CSS选择器数组
|
|
4215
|
+
* @param timeout 超时时间,默认0
|
|
4216
|
+
* @example
|
|
4217
|
+
* Utils.waitAnyNode(["div","div"],10000).then( $div =>{
|
|
4218
|
+
* console.log($div); // $div => HTMLDivELement | null
|
|
4219
|
+
* })
|
|
4220
|
+
*/
|
|
4221
|
+
waitAnyNode<K extends keyof HTMLElementTagNameMap>(
|
|
4222
|
+
selectorList: K[],
|
|
4223
|
+
timeout: number
|
|
4224
|
+
): Promise<HTMLElementTagNameMap[K] | null>;
|
|
4225
|
+
waitAnyNode<T extends Element>(
|
|
4226
|
+
selectorList: string[],
|
|
4227
|
+
timeout: number
|
|
4228
|
+
): Promise<T | null>;
|
|
4229
|
+
waitAnyNode<T extends Element>(...args: any[]): Promise<T | null> {
|
|
4230
|
+
// 过滤掉undefined
|
|
4231
|
+
args = args.filter((arg) => arg !== void 0);
|
|
4232
|
+
let that = this;
|
|
4233
|
+
// 选择器
|
|
4234
|
+
let selectorList = args[0] as unknown as string[];
|
|
4235
|
+
// 父元素(监听的元素)
|
|
4236
|
+
let parent: Element = UtilsCore.document as any as Element;
|
|
4237
|
+
// 超时时间
|
|
4238
|
+
let timeout = 0;
|
|
4239
|
+
if (typeof args[0] !== "object" && !Array.isArray(args[0])) {
|
|
4240
|
+
throw new TypeError("Utils.waitAnyNode 第一个参数必须是string[]");
|
|
4241
|
+
}
|
|
4242
|
+
if (args.length === 1) {
|
|
4243
|
+
// 上面已做处理
|
|
4244
|
+
} else if (args.length === 2) {
|
|
4245
|
+
let secondParam = args[1];
|
|
4246
|
+
if (typeof secondParam === "number") {
|
|
4247
|
+
// "div",10000
|
|
4248
|
+
timeout = secondParam;
|
|
4249
|
+
} else if (
|
|
4250
|
+
typeof secondParam === "object" &&
|
|
4251
|
+
secondParam instanceof Node
|
|
4252
|
+
) {
|
|
4253
|
+
// "div",document
|
|
4254
|
+
parent = secondParam as any as Element;
|
|
4255
|
+
} else {
|
|
4256
|
+
throw new TypeError("Utils.waitAnyNode 第二个参数必须是number|Node");
|
|
4257
|
+
}
|
|
4258
|
+
} else if (args.length === 3) {
|
|
4259
|
+
// "div",document,10000
|
|
4260
|
+
// 第二个参数,parent
|
|
4261
|
+
let secondParam = args[1];
|
|
4262
|
+
// 第三个参数,timeout
|
|
4263
|
+
let thirdParam = args[2];
|
|
4264
|
+
if (typeof secondParam === "object" && secondParam instanceof Node) {
|
|
4265
|
+
parent = secondParam as any as Element;
|
|
4266
|
+
if (typeof thirdParam === "number") {
|
|
4267
|
+
timeout = thirdParam;
|
|
4268
|
+
} else {
|
|
4269
|
+
throw new TypeError("Utils.waitAnyNode 第三个参数必须是number");
|
|
4270
|
+
}
|
|
4271
|
+
} else {
|
|
4272
|
+
throw new TypeError("Utils.waitAnyNode 第二个参数必须是Node");
|
|
4273
|
+
}
|
|
4274
|
+
} else {
|
|
4275
|
+
throw new TypeError("Utils.waitAnyNode 参数个数错误");
|
|
4276
|
+
}
|
|
4277
|
+
let promiseList = selectorList.map((selector) => {
|
|
4278
|
+
return that.waitNode<T>(selector, parent, timeout);
|
|
4279
|
+
});
|
|
4280
|
+
return Promise.any(promiseList);
|
|
4281
|
+
}
|
|
4282
|
+
|
|
4283
|
+
/**
|
|
4284
|
+
* 等待元素数组出现
|
|
4285
|
+
* @param selector CSS选择器
|
|
4286
|
+
* @param parent (可选)监听的父元素
|
|
4287
|
+
* @example
|
|
4288
|
+
* Utils.waitNodeList("div").then( $result =>{
|
|
4289
|
+
* console.log($result); // $result => NodeListOf<HTMLDivElement>
|
|
4290
|
+
* })
|
|
4291
|
+
* Utils.waitNodeList("div",document).then( $result =>{
|
|
4292
|
+
* console.log($result); // $result => NodeListOf<HTMLDivElement>
|
|
4293
|
+
* })
|
|
4294
|
+
*/
|
|
4295
|
+
waitNodeList<T extends keyof HTMLElementTagNameMap>(
|
|
4296
|
+
selector: T,
|
|
4297
|
+
parent?: Node | Element | Document | HTMLElement
|
|
4298
|
+
): Promise<NodeListOf<HTMLElementTagNameMap[T]>>;
|
|
4299
|
+
waitNodeList<T extends NodeListOf<Element>>(
|
|
4300
|
+
selector: string,
|
|
4301
|
+
parent?: Node | Element | Document | HTMLElement
|
|
4302
|
+
): Promise<T>;
|
|
4303
|
+
/**
|
|
4304
|
+
* 等待元素数组出现
|
|
4305
|
+
* @param selectorList CSS选择器数组
|
|
4306
|
+
* @param parent (可选)监听的父元素
|
|
4307
|
+
* @example
|
|
4308
|
+
* Utils.waitNodeList(["div"]).then( $result =>{
|
|
4309
|
+
* console.log($result); // $result => NodeListOf<HTMLDivElement>[]
|
|
4310
|
+
* })
|
|
4311
|
+
* Utils.waitNodeList(["div"],document).then( $result =>{
|
|
4312
|
+
* console.log($result); // $result => NodeListOf<HTMLDivElement>[]
|
|
4313
|
+
* })
|
|
4314
|
+
*/
|
|
4315
|
+
waitNodeList<K extends keyof HTMLElementTagNameMap>(
|
|
4316
|
+
selectorList: K[],
|
|
4317
|
+
parent?: Node | Element | Document | HTMLElement
|
|
4318
|
+
): Promise<NodeListOf<HTMLElementTagNameMap[K]>[]>;
|
|
4319
|
+
waitNodeList<T extends NodeListOf<Element>[]>(
|
|
4320
|
+
selectorList: string[],
|
|
4321
|
+
parent?: Node | Element | Document | HTMLElement
|
|
4322
|
+
): Promise<T>;
|
|
4323
|
+
/**
|
|
4324
|
+
* 等待元素数组出现
|
|
4325
|
+
* @param selector CSS选择器
|
|
4326
|
+
* @param parent 监听的父元素
|
|
4327
|
+
* @param timeout 超时时间,默认0
|
|
4328
|
+
* @example
|
|
4329
|
+
* Utils.waitNodeList("div",document,10000).then( $result =>{
|
|
4330
|
+
* console.log($result); // $result => NodeListOf<HTMLDivElement> | null
|
|
4331
|
+
* })
|
|
4332
|
+
*/
|
|
4333
|
+
waitNodeList<T extends NodeListOf<Element>>(
|
|
4334
|
+
selector: string,
|
|
4335
|
+
parent: Node | Element | Document | HTMLElement,
|
|
4336
|
+
timeout: number
|
|
4337
|
+
): Promise<T | null>;
|
|
4338
|
+
waitNodeList<K extends keyof HTMLElementTagNameMap>(
|
|
4339
|
+
selector: K,
|
|
4340
|
+
parent: Node | Element | Document | HTMLElement,
|
|
4341
|
+
timeout: number
|
|
4342
|
+
): Promise<NodeListOf<HTMLElementTagNameMap[K]> | null>;
|
|
4343
|
+
/**
|
|
4344
|
+
* 等待元素数组出现
|
|
4345
|
+
* @param selectorList CSS选择器数组
|
|
4346
|
+
* @param parent 监听的父元素
|
|
4347
|
+
* @param timeout 超时时间,默认0
|
|
4348
|
+
* @example
|
|
4349
|
+
* Utils.waitNodeList(["div"],document,10000).then( $result =>{
|
|
4350
|
+
* console.log($result); // $result => NodeListOf<HTMLDivElement>[] | null
|
|
4351
|
+
* })
|
|
4352
|
+
*/
|
|
4353
|
+
waitNodeList<K extends keyof HTMLElementTagNameMap>(
|
|
4354
|
+
selectorList: K[],
|
|
4355
|
+
parent: Node | Element | Document | HTMLElement,
|
|
4356
|
+
timeout: number
|
|
4357
|
+
): Promise<NodeListOf<HTMLElementTagNameMap[K]>[] | null>;
|
|
4358
|
+
waitNodeList<T extends NodeListOf<Element>[]>(
|
|
4359
|
+
selectorList: string[],
|
|
4360
|
+
parent: Node | Element | Document | HTMLElement,
|
|
4361
|
+
timeout: number
|
|
4362
|
+
): Promise<T | null>;
|
|
4363
|
+
/**
|
|
4364
|
+
* 等待元素数组出现
|
|
4365
|
+
* @param selector CSS选择器数组
|
|
4366
|
+
* @param timeout 超时时间,默认0
|
|
4367
|
+
* @example
|
|
4368
|
+
* Utils.waitNodeList("div",10000).then( $result =>{
|
|
4369
|
+
* console.log($result); // $result => NodeListOf<HTMLDivElement> | null
|
|
4370
|
+
* })
|
|
4371
|
+
*/
|
|
4372
|
+
waitNodeList<K extends keyof HTMLElementTagNameMap>(
|
|
4373
|
+
selector: K[],
|
|
4374
|
+
timeout: number
|
|
4375
|
+
): Promise<NodeListOf<HTMLElementTagNameMap[K]> | null>;
|
|
4376
|
+
waitNodeList<T extends NodeListOf<Element>>(
|
|
4377
|
+
selector: string[],
|
|
4378
|
+
timeout: number
|
|
4379
|
+
): Promise<T | null>;
|
|
4380
|
+
/**
|
|
4381
|
+
* 等待元素数组出现
|
|
4382
|
+
* @param selectorList CSS选择器数组
|
|
4383
|
+
* @param timeout 超时时间,默认0
|
|
4384
|
+
* @example
|
|
4385
|
+
* Utils.waitNodeList(["div"],10000).then( $result =>{
|
|
4386
|
+
* console.log($result); // $result => NodeListOf<HTMLDivElement>[] | null
|
|
4387
|
+
* })
|
|
4388
|
+
*/
|
|
4389
|
+
waitNodeList<K extends keyof HTMLElementTagNameMap>(
|
|
4390
|
+
selectorList: K[],
|
|
4391
|
+
timeout: number
|
|
4392
|
+
): Promise<NodeListOf<HTMLElementTagNameMap[K]>[] | null>;
|
|
4393
|
+
waitNodeList<T extends NodeListOf<Element>>(
|
|
4394
|
+
selectorList: string[],
|
|
4395
|
+
timeout: number
|
|
4396
|
+
): Promise<T[] | null>;
|
|
4397
|
+
waitNodeList<T extends NodeListOf<Element> | NodeListOf<Element>[]>(
|
|
4398
|
+
...args: any[]
|
|
4399
|
+
): Promise<T | null> {
|
|
4400
|
+
// 过滤掉undefined
|
|
4401
|
+
args = args.filter((arg) => arg !== void 0);
|
|
4402
|
+
let that = this;
|
|
4403
|
+
// 选择器数组
|
|
4404
|
+
let selector = args[0] as unknown as string | string[];
|
|
4405
|
+
// 父元素(监听的元素)
|
|
4406
|
+
let parent: Element = UtilsCore.document as any as Element;
|
|
4407
|
+
// 超时时间
|
|
4408
|
+
let timeout = 0;
|
|
4409
|
+
if (typeof args[0] !== "string" && !Array.isArray(args[0])) {
|
|
4410
|
+
throw new TypeError("Utils.waitNodeList 第一个参数必须是string|string[]");
|
|
4411
|
+
}
|
|
4412
|
+
if (args.length === 1) {
|
|
4413
|
+
// 上面已做处理
|
|
4414
|
+
} else if (args.length === 2) {
|
|
4415
|
+
let secondParam = args[1];
|
|
4416
|
+
if (typeof secondParam === "number") {
|
|
4417
|
+
// "div",10000
|
|
4418
|
+
timeout = secondParam;
|
|
4419
|
+
} else if (
|
|
4420
|
+
typeof secondParam === "object" &&
|
|
4421
|
+
secondParam instanceof Node
|
|
4422
|
+
) {
|
|
4423
|
+
// "div",document
|
|
4424
|
+
parent = secondParam as any as Element;
|
|
4425
|
+
} else {
|
|
4426
|
+
throw new TypeError("Utils.waitNodeList 第二个参数必须是number|Node");
|
|
4427
|
+
}
|
|
4428
|
+
} else if (args.length === 3) {
|
|
4429
|
+
// "div",document,10000
|
|
4430
|
+
// 第二个参数,parent
|
|
4431
|
+
let secondParam = args[1];
|
|
4432
|
+
// 第三个参数,timeout
|
|
4433
|
+
let thirdParam = args[2];
|
|
4434
|
+
if (typeof secondParam === "object" && secondParam instanceof Node) {
|
|
4435
|
+
parent = secondParam as any as Element;
|
|
4436
|
+
if (typeof thirdParam === "number") {
|
|
4437
|
+
timeout = thirdParam;
|
|
4438
|
+
} else {
|
|
4439
|
+
throw new TypeError("Utils.waitNodeList 第三个参数必须是number");
|
|
4440
|
+
}
|
|
4441
|
+
} else {
|
|
4442
|
+
throw new TypeError("Utils.waitNodeList 第二个参数必须是Node");
|
|
4443
|
+
}
|
|
4444
|
+
} else {
|
|
4445
|
+
throw new TypeError("Utils.waitNodeList 参数个数错误");
|
|
4446
|
+
}
|
|
4447
|
+
return new Promise((resolve) => {
|
|
4448
|
+
function getNodeList() {
|
|
4449
|
+
if (Array.isArray(selector)) {
|
|
4450
|
+
let result: T[] = [];
|
|
4451
|
+
for (let index = 0; index < selector.length; index++) {
|
|
4452
|
+
let nodeList = (parent as Element).querySelectorAll(
|
|
4453
|
+
selector[index]
|
|
4454
|
+
) as T;
|
|
4455
|
+
if (nodeList.length) {
|
|
4456
|
+
result.push(nodeList);
|
|
4457
|
+
}
|
|
4458
|
+
}
|
|
4459
|
+
if (result.length === selector.length) {
|
|
4460
|
+
return result;
|
|
4461
|
+
}
|
|
4462
|
+
} else {
|
|
4463
|
+
let nodeList = (parent as Element).querySelectorAll(selector) as T;
|
|
4464
|
+
if (nodeList.length) {
|
|
4465
|
+
return nodeList;
|
|
4466
|
+
}
|
|
4467
|
+
}
|
|
4468
|
+
}
|
|
4469
|
+
var observer = that.mutationObserver(parent, {
|
|
4470
|
+
config: {
|
|
4471
|
+
subtree: true,
|
|
4472
|
+
childList: true,
|
|
4473
|
+
attributes: true,
|
|
4474
|
+
},
|
|
4475
|
+
callback() {
|
|
4476
|
+
let node = getNodeList();
|
|
4477
|
+
if (node) {
|
|
4478
|
+
// 取消观察器
|
|
4479
|
+
try {
|
|
4480
|
+
observer.disconnect();
|
|
4481
|
+
} catch (error) {}
|
|
4482
|
+
resolve(node as T);
|
|
4483
|
+
return;
|
|
4484
|
+
}
|
|
4485
|
+
},
|
|
4486
|
+
immediate: true,
|
|
4487
|
+
});
|
|
4488
|
+
if (timeout > 0) {
|
|
4489
|
+
setTimeout(() => {
|
|
4490
|
+
// 取消观察器
|
|
4491
|
+
if (typeof observer?.disconnect === "function") {
|
|
4492
|
+
observer.disconnect();
|
|
4493
|
+
}
|
|
4494
|
+
resolve(null);
|
|
4495
|
+
}, timeout);
|
|
4496
|
+
}
|
|
4497
|
+
});
|
|
4498
|
+
}
|
|
4499
|
+
/**
|
|
4500
|
+
* 等待任意元素数组出现
|
|
4501
|
+
* @param selectorList CSS选择器数组
|
|
4502
|
+
* @param parent (可选)监听的父元素
|
|
4503
|
+
* @example
|
|
4504
|
+
* Utils.waitAnyNodeList(["div","a"]).then( $result =>{
|
|
4505
|
+
* console.log($result); // $result => NodeListOf<HTMLDivElement>
|
|
4506
|
+
* })
|
|
4507
|
+
* Utils.waitAnyNodeList(["div","a"],document).then( $result =>{
|
|
4508
|
+
* console.log($result); // $result => NodeListOf<HTMLDivElement>
|
|
4509
|
+
* })
|
|
4510
|
+
*/
|
|
4511
|
+
waitAnyNodeList<K extends keyof HTMLElementTagNameMap>(
|
|
4512
|
+
selectorList: K[],
|
|
4513
|
+
parent?: Node | Element | Document | HTMLElement
|
|
4514
|
+
): Promise<NodeListOf<HTMLElementTagNameMap[K]>>;
|
|
4515
|
+
waitAnyNodeList<T extends Element>(
|
|
4516
|
+
selectorList: string[],
|
|
4517
|
+
parent?: Node | Element | Document | HTMLElement
|
|
4518
|
+
): Promise<NodeListOf<T>>;
|
|
4519
|
+
/**
|
|
4520
|
+
* 等待任意元素数组出现
|
|
4521
|
+
* @param selectorList CSS选择器数组
|
|
4522
|
+
* @param parent 父元素,默认document
|
|
4523
|
+
* @param timeout 超时时间,默认0
|
|
4524
|
+
* @example
|
|
4525
|
+
* Utils.waitAnyNodeList(["div","a"],document,10000).then( $result =>{
|
|
4526
|
+
* console.log($result); // $result => NodeListOf<HTMLDivElement> | null
|
|
4527
|
+
* })
|
|
4528
|
+
*/
|
|
4529
|
+
waitAnyNodeList<K extends keyof HTMLElementTagNameMap>(
|
|
4530
|
+
selectorList: K[],
|
|
4531
|
+
parent: Node | Element | Document | HTMLElement,
|
|
4532
|
+
timeout: number
|
|
4533
|
+
): Promise<NodeListOf<HTMLElementTagNameMap[K]> | null>;
|
|
4534
|
+
waitAnyNodeList<T extends Element>(
|
|
4535
|
+
selectorList: string[],
|
|
4536
|
+
parent: Node | Element | Document | HTMLElement,
|
|
4537
|
+
timeout: number
|
|
4538
|
+
): Promise<NodeListOf<T> | null>;
|
|
4539
|
+
/**
|
|
4540
|
+
* 等待任意元素出现
|
|
4541
|
+
* @param selectorList CSS选择器数组
|
|
4542
|
+
* @param timeout 超时时间,默认0
|
|
4543
|
+
* @example
|
|
4544
|
+
* Utils.waitAnyNodeList(["div","div"],10000).then( $result =>{
|
|
4545
|
+
* console.log($result); // $result => NodeListOf<HTMLDivElement> | null
|
|
4546
|
+
* })
|
|
4547
|
+
*/
|
|
4548
|
+
waitAnyNodeList<K extends keyof HTMLElementTagNameMap>(
|
|
4549
|
+
selectorList: K[],
|
|
4550
|
+
timeout: number
|
|
4551
|
+
): Promise<NodeListOf<HTMLElementTagNameMap[K]> | null>;
|
|
4552
|
+
waitAnyNodeList<T extends Element>(
|
|
4553
|
+
selectorList: string[],
|
|
4554
|
+
timeout: number
|
|
4555
|
+
): Promise<NodeListOf<T> | null>;
|
|
4556
|
+
waitAnyNodeList<T extends Element>(
|
|
4557
|
+
...args: any[]
|
|
4558
|
+
): Promise<NodeListOf<T> | null> {
|
|
4559
|
+
// 过滤掉undefined
|
|
4560
|
+
args = args.filter((arg) => arg !== void 0);
|
|
4561
|
+
let that = this;
|
|
4562
|
+
// 选择器数组
|
|
4563
|
+
let selectorList = args[0] as unknown as string[];
|
|
4564
|
+
// 父元素(监听的元素)
|
|
4565
|
+
let parent: Element = UtilsCore.document as any as Element;
|
|
4566
|
+
// 超时时间
|
|
4567
|
+
let timeout = 0;
|
|
4568
|
+
if (!Array.isArray(args[0])) {
|
|
4569
|
+
throw new TypeError("Utils.waitAnyNodeList 第一个参数必须是string[]");
|
|
4570
|
+
}
|
|
4571
|
+
if (args.length === 1) {
|
|
4572
|
+
// 上面已做处理
|
|
4573
|
+
} else if (args.length === 2) {
|
|
4574
|
+
let secondParam = args[1];
|
|
4575
|
+
if (typeof secondParam === "number") {
|
|
4576
|
+
// "div",10000
|
|
4577
|
+
timeout = secondParam;
|
|
4578
|
+
} else if (
|
|
4579
|
+
typeof secondParam === "object" &&
|
|
4580
|
+
secondParam instanceof Node
|
|
4581
|
+
) {
|
|
4582
|
+
// "div",document
|
|
4583
|
+
parent = secondParam as any as Element;
|
|
4584
|
+
} else {
|
|
4585
|
+
throw new TypeError(
|
|
4586
|
+
"Utils.waitAnyNodeList 第二个参数必须是number|Node"
|
|
4587
|
+
);
|
|
4588
|
+
}
|
|
4589
|
+
} else if (args.length === 3) {
|
|
4590
|
+
// "div",document,10000
|
|
4591
|
+
// 第二个参数,parent
|
|
4592
|
+
let secondParam = args[1];
|
|
4593
|
+
// 第三个参数,timeout
|
|
4594
|
+
let thirdParam = args[2];
|
|
4595
|
+
if (typeof secondParam === "object" && secondParam instanceof Node) {
|
|
4596
|
+
parent = secondParam as any as Element;
|
|
4597
|
+
if (typeof thirdParam === "number") {
|
|
4598
|
+
timeout = thirdParam;
|
|
4599
|
+
} else {
|
|
4600
|
+
throw new TypeError("Utils.waitAnyNodeList 第三个参数必须是number");
|
|
4601
|
+
}
|
|
4602
|
+
} else {
|
|
4603
|
+
throw new TypeError("Utils.waitAnyNodeList 第二个参数必须是Node");
|
|
4604
|
+
}
|
|
4605
|
+
} else {
|
|
4606
|
+
throw new TypeError("Utils.waitAnyNodeList 参数个数错误");
|
|
4607
|
+
}
|
|
4608
|
+
|
|
4609
|
+
let promiseList = selectorList.map((selector) => {
|
|
4610
|
+
return that.waitNodeList<NodeListOf<T>>(selector, parent, timeout);
|
|
4611
|
+
});
|
|
4612
|
+
return Promise.any(promiseList);
|
|
4613
|
+
}
|
|
4614
|
+
|
|
4615
|
+
/**
|
|
4616
|
+
* 等待对象上的属性出现
|
|
4617
|
+
* @param checkObj 检查的对象
|
|
4618
|
+
* @param checkPropertyName 检查的对象的属性名
|
|
4619
|
+
* @example
|
|
4620
|
+
* await Utils.waitProperty(window,"test");
|
|
4621
|
+
* console.log("test success set");
|
|
4622
|
+
*
|
|
4623
|
+
* window.test = 1;
|
|
4624
|
+
* > "test success set"
|
|
4625
|
+
*
|
|
4626
|
+
*/
|
|
4627
|
+
waitProperty<T extends any>(
|
|
4628
|
+
checkObj: AnyObject | (() => AnyObject),
|
|
4629
|
+
checkPropertyName: string
|
|
4630
|
+
): Promise<T>;
|
|
4631
|
+
waitProperty<T extends any>(
|
|
4632
|
+
checkObj: AnyObject | (() => AnyObject),
|
|
4633
|
+
checkPropertyName: string
|
|
4634
|
+
): Promise<T> {
|
|
4635
|
+
return new Promise((resolve) => {
|
|
4636
|
+
let obj = checkObj;
|
|
4637
|
+
if (typeof checkObj === "function") {
|
|
4638
|
+
obj = checkObj();
|
|
4639
|
+
}
|
|
4640
|
+
if (Reflect.has(obj, checkPropertyName)) {
|
|
4641
|
+
resolve((obj as any)[checkPropertyName]);
|
|
4642
|
+
} else {
|
|
4643
|
+
Object.defineProperty(obj, checkPropertyName, {
|
|
4644
|
+
set: function (value) {
|
|
4645
|
+
try {
|
|
4646
|
+
resolve(value);
|
|
4647
|
+
} catch (error) {
|
|
4648
|
+
console.error("Error setting property:", error);
|
|
4649
|
+
}
|
|
4650
|
+
},
|
|
4651
|
+
});
|
|
4652
|
+
}
|
|
4653
|
+
});
|
|
4654
|
+
}
|
|
4655
|
+
/**
|
|
4656
|
+
* 在规定时间内等待对象上的属性出现
|
|
4657
|
+
* @param checkObj 检查的对象
|
|
4658
|
+
* @param checkPropertyName 检查的对象的属性名
|
|
4659
|
+
* @param intervalTimer (可选)检查间隔时间(ms),默认250ms
|
|
4660
|
+
* @param maxTime (可选)限制在多长时间内,默认-1(不限制时间)
|
|
4661
|
+
* @example
|
|
4662
|
+
* await Utils.waitPropertyByInterval(window,"test");
|
|
4663
|
+
* console.log("test success set");
|
|
4664
|
+
*/
|
|
4665
|
+
waitPropertyByInterval<T extends any>(
|
|
4666
|
+
checkObj: AnyObject | (() => AnyObject),
|
|
4667
|
+
checkPropertyName: string | ((obj: any) => boolean),
|
|
4668
|
+
intervalTimer?: number,
|
|
4669
|
+
maxTime?: number
|
|
4670
|
+
): Promise<T>;
|
|
4671
|
+
waitPropertyByInterval<T extends any>(
|
|
4672
|
+
checkObj: AnyObject | (() => AnyObject),
|
|
4673
|
+
checkPropertyName: string | ((obj: any) => boolean),
|
|
4674
|
+
intervalTimer: number = 250,
|
|
4675
|
+
maxTime: number = -1
|
|
4676
|
+
): Promise<T> {
|
|
4677
|
+
if (checkObj == null) {
|
|
4678
|
+
throw new TypeError("checkObj 不能为空对象 ");
|
|
4679
|
+
}
|
|
4680
|
+
let isResolve = false;
|
|
4681
|
+
return new Promise((resolve, reject) => {
|
|
4682
|
+
let interval = setInterval(() => {
|
|
4683
|
+
let obj = checkObj;
|
|
4684
|
+
if (typeof checkObj === "function") {
|
|
4685
|
+
obj = checkObj();
|
|
4686
|
+
}
|
|
4687
|
+
if (typeof obj !== "object") {
|
|
4688
|
+
return;
|
|
4689
|
+
}
|
|
4690
|
+
if (obj == null) {
|
|
4691
|
+
return;
|
|
4692
|
+
}
|
|
4693
|
+
if (
|
|
4694
|
+
(typeof checkPropertyName === "function" && checkPropertyName(obj)) ||
|
|
4695
|
+
Reflect.has(obj, checkPropertyName as string)
|
|
4696
|
+
) {
|
|
4697
|
+
isResolve = true;
|
|
4698
|
+
clearInterval(interval);
|
|
4699
|
+
resolve((obj as any)[checkPropertyName as string]);
|
|
4700
|
+
}
|
|
4701
|
+
}, intervalTimer);
|
|
4702
|
+
if (maxTime !== -1) {
|
|
4703
|
+
setTimeout(() => {
|
|
4704
|
+
if (!isResolve) {
|
|
4705
|
+
clearInterval(interval);
|
|
4706
|
+
reject();
|
|
4707
|
+
}
|
|
4708
|
+
}, maxTime);
|
|
4709
|
+
}
|
|
4710
|
+
});
|
|
4711
|
+
}
|
|
4712
|
+
/**
|
|
4713
|
+
* 在规定时间内等待元素上的__vue__属性或者__vue__属性上的某个值出现出现
|
|
4714
|
+
* @param element 目标元素
|
|
4715
|
+
* @param propertyName (可选)vue上的属性名或者传递一个获取属性的方法返回boolean
|
|
4716
|
+
* @param timer (可选)间隔时间(ms),默认:250(ms)
|
|
4717
|
+
* @param maxTime(可选) 限制在多长时间内,默认:-1(不限制时间)
|
|
4718
|
+
* @param vueName (可选)vue挂载的属性名,默认:__vue__
|
|
4719
|
+
* @example
|
|
4720
|
+
* await Utils.waitVueByInterval(
|
|
4721
|
+
* function(){
|
|
4722
|
+
* return document.querySelector("a.xx")
|
|
4723
|
+
* },
|
|
4724
|
+
* function(__vue__){
|
|
4725
|
+
* return Boolean(__vue__.xxx == null);
|
|
4726
|
+
* },
|
|
4727
|
+
* 250,
|
|
4728
|
+
* 10000,
|
|
4729
|
+
* "__vue__"
|
|
4730
|
+
* )
|
|
4731
|
+
*/
|
|
4732
|
+
waitVueByInterval(
|
|
4733
|
+
element: HTMLElement | (() => any),
|
|
4734
|
+
propertyName: string | ((__vue__: any) => boolean),
|
|
4735
|
+
timer?: number,
|
|
4736
|
+
maxTime?: number,
|
|
4737
|
+
vueName?: "__vue__" | string
|
|
4738
|
+
): Promise<boolean>;
|
|
4739
|
+
async waitVueByInterval(
|
|
4740
|
+
element: HTMLElement | (() => any),
|
|
4741
|
+
propertyName: string | ((__vue__: any) => boolean),
|
|
4742
|
+
timer = 250,
|
|
4743
|
+
maxTime = -1,
|
|
4744
|
+
vueName = "__vue__"
|
|
4745
|
+
) {
|
|
4746
|
+
if (element == null) {
|
|
4747
|
+
throw new Error("Utils.waitVueByInterval 参数element 不能为空");
|
|
4748
|
+
}
|
|
4749
|
+
let flag = false;
|
|
4750
|
+
let UtilsContext = this;
|
|
4751
|
+
try {
|
|
4752
|
+
await UtilsContext.waitPropertyByInterval(
|
|
4753
|
+
element,
|
|
4754
|
+
function (targetElement) {
|
|
4755
|
+
if (targetElement == null) {
|
|
4756
|
+
return false;
|
|
4757
|
+
}
|
|
4758
|
+
if (!(vueName in targetElement)) {
|
|
4759
|
+
return false;
|
|
4760
|
+
}
|
|
4761
|
+
if (propertyName == null) {
|
|
4762
|
+
return true;
|
|
4763
|
+
}
|
|
4764
|
+
let vueObject = targetElement[vueName];
|
|
4765
|
+
if (typeof propertyName === "string") {
|
|
4766
|
+
if (propertyName in vueObject) {
|
|
4767
|
+
flag = true;
|
|
4768
|
+
return true;
|
|
4769
|
+
}
|
|
4770
|
+
} else {
|
|
4771
|
+
/* Function */
|
|
4772
|
+
if (propertyName(vueObject)) {
|
|
4773
|
+
flag = true;
|
|
4774
|
+
return true;
|
|
4775
|
+
}
|
|
4776
|
+
}
|
|
4777
|
+
return false;
|
|
4778
|
+
},
|
|
4779
|
+
timer,
|
|
4780
|
+
maxTime
|
|
4781
|
+
);
|
|
4782
|
+
} catch (error) {
|
|
4783
|
+
return flag;
|
|
4784
|
+
}
|
|
4785
|
+
return flag;
|
|
4786
|
+
}
|
|
4787
|
+
/**
|
|
4788
|
+
* 观察对象的set、get
|
|
4789
|
+
* @param target 观察的对象
|
|
4790
|
+
* @param propertyName 观察的对象的属性名
|
|
4791
|
+
* @param getCallBack (可选)触发get的回调,可以自定义返回特定值
|
|
4792
|
+
* @param setCallBack (可选)触发set的回调,参数为将要设置的value
|
|
4793
|
+
* @example
|
|
4794
|
+
* Utils.watchObject(window,"test",()=>{return 111;},(value)=>{console.log("test出现,值是",value)});
|
|
4795
|
+
*
|
|
4796
|
+
* window.test = 1;
|
|
4797
|
+
* > test出现,值是 1
|
|
4798
|
+
* console.log(window.test);
|
|
4799
|
+
* > 111;
|
|
4800
|
+
*/
|
|
4801
|
+
watchObject(
|
|
4802
|
+
target: AnyObject,
|
|
4803
|
+
propertyName: string,
|
|
4804
|
+
getCallBack: (value: any) => void,
|
|
4805
|
+
setCallBack: (value: any) => void
|
|
4806
|
+
): void;
|
|
4807
|
+
watchObject(
|
|
4808
|
+
target: AnyObject,
|
|
4809
|
+
propertyName: string,
|
|
4810
|
+
getCallBack: (value: any) => void,
|
|
4811
|
+
setCallBack: (value: any) => void
|
|
4812
|
+
): void {
|
|
4813
|
+
if (
|
|
4814
|
+
typeof getCallBack !== "function" &&
|
|
4815
|
+
typeof setCallBack !== "function"
|
|
4816
|
+
) {
|
|
4817
|
+
return;
|
|
4818
|
+
}
|
|
4819
|
+
|
|
4820
|
+
if (typeof getCallBack === "function") {
|
|
4821
|
+
Object.defineProperty(target, propertyName, {
|
|
4822
|
+
get() {
|
|
4823
|
+
if (typeof getCallBack === "function") {
|
|
4824
|
+
return getCallBack(target[propertyName]);
|
|
4825
|
+
} else {
|
|
4826
|
+
return target[propertyName];
|
|
4827
|
+
}
|
|
4828
|
+
},
|
|
4829
|
+
});
|
|
4830
|
+
} else if (typeof setCallBack === "function") {
|
|
4831
|
+
Object.defineProperty(target, propertyName, {
|
|
4832
|
+
set(value) {
|
|
4833
|
+
if (typeof setCallBack === "function") {
|
|
4834
|
+
setCallBack(value);
|
|
4835
|
+
}
|
|
4836
|
+
},
|
|
4837
|
+
});
|
|
4838
|
+
} else {
|
|
4839
|
+
Object.defineProperty(target, propertyName, {
|
|
4840
|
+
get() {
|
|
4841
|
+
if (typeof getCallBack === "function") {
|
|
4842
|
+
return (getCallBack as any)(target[propertyName]);
|
|
4843
|
+
} else {
|
|
4844
|
+
return target[propertyName];
|
|
4845
|
+
}
|
|
4846
|
+
},
|
|
4847
|
+
set(value) {
|
|
4848
|
+
if (typeof setCallBack === "function") {
|
|
4849
|
+
(setCallBack as any)(value);
|
|
4850
|
+
}
|
|
4851
|
+
},
|
|
4852
|
+
});
|
|
4853
|
+
}
|
|
4854
|
+
}
|
|
4855
|
+
|
|
4856
|
+
/**
|
|
4857
|
+
* 创建一个新的Utils实例
|
|
4858
|
+
* @param option
|
|
4859
|
+
* @returns
|
|
4860
|
+
*/
|
|
4861
|
+
createUtils(option?: UtilsCoreOption) {
|
|
4862
|
+
return new Utils(option);
|
|
4863
|
+
}
|
|
4864
|
+
|
|
4865
|
+
/**
|
|
4866
|
+
* 将对象转换为FormData
|
|
4867
|
+
* @param data 待转换的对象
|
|
4868
|
+
* @param isEncode 是否对值为string进行编码转换(encodeURIComponent),默认false
|
|
4869
|
+
* @param valueAutoParseToStr 是否对值强制使用JSON.stringify()转换,默认false
|
|
4870
|
+
* @example
|
|
4871
|
+
* Utils.toFormData({
|
|
4872
|
+
* test: "1",
|
|
4873
|
+
* 666: 666,
|
|
4874
|
+
* })
|
|
4875
|
+
*/
|
|
4876
|
+
toFormData(
|
|
4877
|
+
data: { [key: string]: string | Blob | File | number },
|
|
4878
|
+
isEncode: boolean = false,
|
|
4879
|
+
valueAutoParseToStr: boolean = false
|
|
4880
|
+
) {
|
|
4881
|
+
const formData = new FormData();
|
|
4882
|
+
Object.keys(data).forEach((key) => {
|
|
4883
|
+
let value = data[key];
|
|
4884
|
+
if (valueAutoParseToStr) {
|
|
4885
|
+
value = JSON.stringify(value);
|
|
4886
|
+
}
|
|
4887
|
+
if (typeof value === "number") {
|
|
4888
|
+
value = value.toString();
|
|
4889
|
+
}
|
|
4890
|
+
if (isEncode && typeof value === "string") {
|
|
4891
|
+
value = encodeURIComponent(value);
|
|
4892
|
+
}
|
|
4893
|
+
if (value instanceof File) {
|
|
4894
|
+
formData.append(key, value, value.name);
|
|
4895
|
+
} else {
|
|
4896
|
+
formData.append(key, value);
|
|
4897
|
+
}
|
|
4898
|
+
});
|
|
4899
|
+
return formData;
|
|
4900
|
+
}
|
|
4901
|
+
/**
|
|
4902
|
+
* 将链接转为URL对象,自动补充URL的protocol或者origin
|
|
4903
|
+
* @param text 需要转换的链接字符串
|
|
4904
|
+
* @example
|
|
4905
|
+
* Utils.toUrl("//www.baidu.com/s?word=666");
|
|
4906
|
+
* Utils.toUrl("/s?word=666");
|
|
4907
|
+
*/
|
|
4908
|
+
toUrl(text: string) {
|
|
4909
|
+
if (typeof text !== "string") {
|
|
4910
|
+
throw new TypeError("toUrl: text must be string");
|
|
4911
|
+
}
|
|
4912
|
+
text = text.trim();
|
|
4913
|
+
if (text === "") {
|
|
4914
|
+
throw new TypeError("toUrl: text must not be empty");
|
|
4915
|
+
}
|
|
4916
|
+
if (text.startsWith("//")) {
|
|
4917
|
+
/* //www.baidu.com/xxxxxxx */
|
|
4918
|
+
/* 没有protocol,加上 */
|
|
4919
|
+
text = UtilsCore.globalThis.location.protocol + text;
|
|
4920
|
+
} else if (text.startsWith("/")) {
|
|
4921
|
+
/* /xxx/info?xxx=xxx */
|
|
4922
|
+
/* 没有Origin,加上 */
|
|
4923
|
+
text = UtilsCore.globalThis.location.origin + text;
|
|
4924
|
+
}
|
|
4925
|
+
return new URL(text);
|
|
4926
|
+
}
|
|
4927
|
+
/**
|
|
4928
|
+
* 生成uuid
|
|
4929
|
+
* @example
|
|
4930
|
+
* Utils.generateUUID()
|
|
4931
|
+
*/
|
|
4932
|
+
generateUUID = GenerateUUID;
|
|
4933
|
+
}
|
|
4934
|
+
|
|
4935
|
+
let utils = new Utils();
|
|
4936
|
+
|
|
4937
|
+
export { utils as Utils };
|