UNPKG

@whitesev/utils

Version:

一个常用的工具库

5,225 lines 154 kB
import { ColorConversion } from "./ColorConversion";
import { GBKEncoder } from "./GBKEncoder";
import { UtilsGMCookie } from "./UtilsGMCookie";
import { AjaxHooker } from "./ajaxHooker/ajaxHooker.js";
import { AjaxHooker1_2_4 } from "./ajaxHooker/ajaxHooker1.2.4";
import { GMMenu } from "./UtilsGMMenu";
import { Hooks } from "./Hooks";
import { Httpx } from "./Httpx";
import { indexedDB } from "./indexedDB";
import { LockFunction } from "./LockFunction";
import { Log } from "./Log";
import { Progress } from "./Progress";
import { TryCatch } from "./TryCatch";
import { UtilsDictionary } from "./Dictionary";
import type { DOMUtils_EventType } from "./types/Event";
import type { UtilsAjaxHookResult } from "./types/ajaxHooker";
import { GenerateUUID } from "./UtilsCommon";
import { WindowApi } from "./WindowApi";
import { Vue } from "./Vue";
import {
	type ArgsType,
	type JSTypeNames,
	type UtilsOwnObject,
} from "./types/global";
import type { WindowApiOption } from "./types/WindowApi";
import { ModuleRaid } from "./ModuleRaid";

class Utils {
	private windowApi: typeof WindowApi.prototype;
	constructor(option?: WindowApiOption) {
		this.windowApi = new WindowApi(option);
	}
	/** 版本号 */
	version = "2025.4.11";

	/**
	 * 在页面中增加style元素,如果html节点存在子节点,添加子节点第一个,反之,添加到html节点的子节点最后一个
	 * @param cssText css字符串
	 * @returns 返回添加的CSS标签
	 * @example
	 * Utils.GM_addStyle("html{}");
	 * > <style type="text/css">html{}</style>
	 */
	addStyle(cssText: string): HTMLStyleElement;
	addStyle(cssText: string) {
		if (typeof cssText !== "string") {
			throw new Error("Utils.addStyle 参数cssText 必须为String类型");
		}
		let cssNode = this.windowApi.document.createElement("style");
		cssNode.setAttribute("type", "text/css");
		cssNode.innerHTML = cssText;
		if (this.windowApi.document.head) {
			/* 插入head最后 */
			this.windowApi.document.head.appendChild(cssNode);
		} else if (this.windowApi.document.body) {
			/* 插入body后 */
			this.windowApi.document.body.appendChild(cssNode);
		} else if (
			this.windowApi.document.documentElement.childNodes.length === 0
		) {
			/* 插入#html第一个元素后 */
			this.windowApi.document.documentElement.appendChild(cssNode);
		} else {
			/* 插入head前面 */
			this.windowApi.document.documentElement.insertBefore(
				cssNode,
				this.windowApi.document.documentElement.childNodes[0]
			);
		}
		return cssNode;
	}

	/** 
     * JSON数据从源端替换到目标端中,如果目标端存在该数据则替换,不添加,返回结果为目标端替换完毕的结果
     * @param target 目标数据
     * @param source 源数据
     * @param isAdd 是否可以追加键,默认false
     * @example
     * Utils.assign({"1":1,"2":{"3":3}}, {"2":{"3":4}});
     * > 
     * {
            "1": 1,
            "2": {
                "3": 4
            }
        }
     */
	assign<T1, T2 extends object, T3 extends boolean>(
		target: T1,
		source: T2,
		isAdd?: T3
	): T3 extends true ? T1 & T2 : T1;
	assign(target = {}, source = {}, isAdd = false) {
		let UtilsContext = this;
		if (Array.isArray(source)) {
			let canTraverse = source.filter((item) => {
				return typeof item === "object";
			});
			if (!canTraverse.length) {
				return source;
			}
		}
		if (source == null) {
			return target;
		}
		if (target == null) {
			target = {};
		}
		if (isAdd) {
			for (const sourceKeyName in source) {
				const targetKeyName = sourceKeyName;
				let targetValue = (target as any)[targetKeyName];
				let sourceValue = (source as any)[sourceKeyName];
				if (
					typeof sourceValue === "object" &&
					sourceValue != null &&
					sourceKeyName in target &&
					!UtilsContext.isDOM(sourceValue)
				) {
					/* 源端的值是object类型,且不是元素节点 */
					(target as any)[sourceKeyName] = UtilsContext.assign(
						targetValue,
						sourceValue,
						isAdd
					);
					continue;
				}
				(target as any)[sourceKeyName] = sourceValue;
			}
		} else {
			for (const targetKeyName in target) {
				if (targetKeyName in source) {
					let targetValue = (target as any)[targetKeyName];
					let sourceValue = (source as any)[targetKeyName];
					if (
						typeof sourceValue === "object" &&
						sourceValue != null &&
						!UtilsContext.isDOM(sourceValue) &&
						Object.keys(sourceValue).length
					) {
						/* 源端的值是object类型,且不是元素节点 */
						(target as any)[targetKeyName] = UtilsContext.assign(
							targetValue,
							sourceValue,
							isAdd
						);
						continue;
					}
					/* 直接赋值 */
					(target as any)[targetKeyName] = sourceValue;
				}
			}
		}

		return target;
	}
	/**
	 * 异步替换字符串
	 * @param string 需要被替换的目标字符串
	 * @param pattern 正则匹配模型
	 * @param asyncFn 异步获取的函数
	 */
	asyncReplaceAll(
		string: string,
		pattern: RegExp | string,
		asyncFn: (item: string) => Promise<string>
	): Promise<string>;
	async asyncReplaceAll(
		string: string,
		pattern: RegExp | string,
		asyncFn: (item: string) => Promise<string>
	) {
		let UtilsContext = this;
		if (typeof string !== "string") {
			throw new TypeError("string必须是字符串");
		}
		if (typeof asyncFn !== "function") {
			throw new TypeError("asyncFn必须是函数");
		}
		let reg;
		if (typeof pattern === "string") {
			reg = new RegExp(UtilsContext.parseStringToRegExpString(pattern), "g");
		} else if (pattern instanceof RegExp) {
			if (!pattern.global) {
				throw new TypeError("pattern必须是全局匹配");
			}
			reg = new RegExp(pattern);
		} else {
			throw new TypeError("pattern必须是正则对象");
		}
		let result = [];
		let match;
		let lastIndex = 0;
		while ((match = reg.exec(string)) !== null) {
			/* 异步获取匹配对应的字符串 */
			const item = asyncFn(match[0]);
			/* 获取该匹配项和上一个匹配项的中间的字符串 */
			const prefix = string.slice(lastIndex, match.index);
			lastIndex = match.index + match[0].length;
			result.push(item);
			result.push(prefix);
		}
		result.push(string.slice(lastIndex));
		/* 等待所有异步完成 */
		result = await Promise.all(result);
		return result.join("");
	}
	/**
	 * ajax劫持库,支持xhr和fetch劫持。
	 * + 来源:https://bbs.tampermonkey.net.cn/thread-3284-1-1.html
	 * + 作者:cxxjackie
	 * + 版本:1.4.4
	 * + 旧版本:1.2.4
	 * + 文档:https://scriptcat.org/zh-CN/script-show-page/637/
	 * @param useOldVersion 是否使用旧版本,默认false
	 */
	ajaxHooker = (useOldVersion: boolean = false): UtilsAjaxHookResult => {
		if (useOldVersion) {
			return AjaxHooker1_2_4();
		} else {
			return AjaxHooker();
		}
	};
	/**
	 * 根据坐标点击canvas元素的内部位置
	 * @param canvasElement 画布元素
	 * @param clientX X坐标,默认值0
	 * @param clientY Y坐标,默认值0
	 * @param view 触发的事件目标
	 */
	canvasClickByPosition(
		canvasElement: HTMLCanvasElement,
		clientX?: number | string,
		clientY?: number | string,
		view?: Window & typeof globalThis
	): void;
	canvasClickByPosition(
		canvasElement: HTMLCanvasElement,
		clientX = 0,
		clientY = 0,
		view = globalThis
	) {
		if (!(canvasElement instanceof HTMLCanvasElement)) {
			throw new Error(
				"Utils.canvasClickByPosition 参数canvasElement必须是canvas元素"
			);
		}
		clientX = parseInt(clientX.toString());
		clientY = parseInt(clientY.toString());
		const eventInit: MouseEventInit = {
			cancelBubble: true,
			cancelable: true,
			clientX: clientX,
			clientY: clientY,
			// @ts-ignore
			view: view,
			detail: 1,
		};
		canvasElement.dispatchEvent(new MouseEvent("mousedown", eventInit));
		canvasElement.dispatchEvent(new MouseEvent("mouseup", eventInit));
	}
	/**
	 * 【手机】检测点击的地方是否在该元素区域内
	 * @param element 需要检测的元素
	 * @returns
	 * + true 点击在元素上
	 * + false 未点击在元素上
	 * @example
	 * Utils.checkUserClickInNode(document.querySelector(".xxx"));
	 * > false
	 **/
	checkUserClickInNode(element: Element | Node | HTMLElement): boolean;
	checkUserClickInNode(element: Element | Node | HTMLElement) {
		let UtilsContext = this;
		if (!UtilsContext.isDOM(element)) {
			throw new Error(
				"Utils.checkUserClickInNode 参数 targetNode 必须为 Element|Node 类型"
			);
		}
		let clickEvent = UtilsContext.windowApi.window.event as PointerEvent;
		let touchEvent = UtilsContext.windowApi.window.event as TouchEvent;
		let $click = clickEvent?.composedPath()?.[0] as HTMLElement;

		// 点击的x坐标
		let clickPosX =
			clickEvent?.clientX != null
				? clickEvent.clientX
				: touchEvent.touches[0].clientX;
		// 点击的y坐标
		let clickPosY =
			clickEvent?.clientY != null
				? clickEvent.clientY
				: touchEvent.touches[0].clientY;
		let {
			/* 要检测的元素的相对屏幕的横坐标最左边 */
			left: elementPosXLeft,
			/* 要检测的元素的相对屏幕的横坐标最右边 */
			right: elementPosXRight,
			/* 要检测的元素的相对屏幕的纵坐标最上边 */
			top: elementPosYTop,
			/* 要检测的元素的相对屏幕的纵坐标最下边 */
			bottom: elementPosYBottom,
		} = (element as HTMLElement).getBoundingClientRect();
		if (
			clickPosX >= elementPosXLeft &&
			clickPosX <= elementPosXRight &&
			clickPosY >= elementPosYTop &&
			clickPosY <= elementPosYBottom
		) {
			return true;
		} else if (($click && element.contains($click)) || $click == element) {
			/* 这种情况是应对在界面中隐藏的元素,getBoundingClientRect获取的都是0 */
			return true;
		} else {
			return false;
		}
	}
	/**
	 * 复制formData数据
	 * @param formData 需要clone的数据
	 */
	cloneFormData<T extends FormData>(
		formData: T,
		filterFn?: (key: string, value: string | Blob) => boolean
	): T {
		let clonedFormData = new FormData() as T;
		for (let [key, value] of (formData as any).entries()) {
			let isFilter =
				typeof filterFn === "function" ? filterFn(key, value) : false;
			if (typeof isFilter === "boolean" && isFilter) {
				continue;
			}
			clonedFormData.append(key, value);
		}
		return clonedFormData;
	}
	/**
	 * 函数重载实现
	 * @example
	 * let getUsers = Utils.createOverload();
	 * getUsers.addImpl("",()=>{
	 *    console.log("无参数");
	 * });
	 *
	 * getUsers.addImpl("boolean",()=>{
	 *    console.log("boolean");
	 * });
	 *
	 * getUsers.addImpl("string",()=>{
	 *    console.log("string");
	 * });
	 *
	 * getUsers.addImpl("number","string",()=>{
	 *    console.log("number string");
	 * });
	 */
	createOverload(): {
		/**
		 * 前面的参数都是字符串,最后一个参数是函数
		 */
		addImpl<T extends JSTypeNames[]>(
			...args: [...T, (...args: ArgsType<T>) => any]
		): void;
	};
	createOverload(): {
		/**
		 * 前面的参数都是字符串,最后一个参数是函数
		 */
		addImpl<T extends JSTypeNames[]>(
			...args: [...T, (...args: ArgsType<T>) => any]
		): void;
	} {
		let fnMap = new Map();
		function overload(this: any, ...args: any[]) {
			let key = args.map((it) => typeof it).join(",");
			let fn = fnMap.get(key);
			if (!fn) {
				throw new TypeError("没有找到对应的实现");
			}
			return fn.apply(this, args);
		}
		overload.addImpl = function (...args: any[]) {
			let fn = args.pop();
			if (typeof fn !== "function") {
				throw new TypeError("最后一个参数必须是函数");
			}
			let key = args.join(",");
			fnMap.set(key, fn);
		};
		return overload;
	}
	/**
	 * 颜色转换
	 * @returns
	 */
	ColorConversion = ColorConversion;
	/**
	 * 深拷贝
	 * @param obj 对象
	 */
	deepClone<T extends object | undefined | null>(obj?: T): T;
	deepClone<T extends object | undefined | null>(obj?: T) {
		let UtilsContext = this;
		if (obj === void 0) return void 0;
		if (obj === null) return null;
		let clone = obj instanceof Array ? [] : {};
		for (const [key, value] of Object.entries(obj)) {
			(clone as any)[key] =
				typeof value === "object" ? UtilsContext.deepClone(value) : value;
		}
		return clone;
	}
	/**
	 * 防抖函数
	 * @param fn 需要触发的回调
	 * @param delay 防抖判定时间(毫秒),默认是0ms
	 */
	debounce<A extends any[], R>(
		fn: (...args: A) => R,
		delay?: number
	): (...args: A) => void;
	debounce<A extends any[], R>(fn: (...args: A) => R, delay = 0) {
		let timer: any = null as any;
		const context = this;
		return function (...args: A) {
			clearTimeout(timer);
			timer = setTimeout(function () {
				fn.apply(context, args);
			}, delay);
		};
	}
	/**
	 * 删除某个父元素,父元素可能在上层或上上层或上上上层...
	 * @param element 当前元素
	 * @param targetSelector 判断是否满足父元素,参数为当前处理的父元素,满足返回true,否则false
	 * @returns
	 * + true 已删除
	 * + false 未删除
	 * @example
	 * Utils.deleteParentNode(document.querySelector("a"),".xxx");
	 * > true
	 **/
	deleteParentNode(
		element: Node | HTMLElement | Element | null,
		targetSelector: string
	): boolean;
	deleteParentNode(
		element: Node | HTMLElement | Element | null,
		targetSelector: string
	) {
		let UtilsContext = this;
		if (element == null) {
			return;
		}
		if (!UtilsContext.isDOM(element)) {
			throw new Error(
				"Utils.deleteParentNode 参数 target 必须为 Node|HTMLElement 类型"
			);
		}
		if (typeof targetSelector !== "string") {
			throw new Error(
				"Utils.deleteParentNode 参数 targetSelector 必须为 string 类型"
			);
		}
		let result = false;
		let needRemoveDOM = (element as HTMLElement).closest(targetSelector);
		if (needRemoveDOM) {
			needRemoveDOM.remove();
			result = true;
		}
		return result;
	}

	/**
	 * 字典
	 * @example
	 * let dictionary = new Utils.Dictionary();
	 * let dictionary2 = new Utils.Dictionary();
	 * dictionary.set("test","111");
	 * dictionary.get("test");
	 * > '111'
	 * dictionary.has("test");
	 * > true
	 * dictionary.concat(dictionary2);
	 **/
	Dictionary = UtilsDictionary;
	/**
	 * 主动触发事件
	 * @param element 元素
	 * @param eventName 事件名称,可以是字符串,也可是字符串格式的列表
	 * @param details (可选)赋予触发的Event的额外属性
	 * + true 使用Proxy代理Event并设置获取isTrusted永远为True
	 * + false (默认) 不对Event进行Proxy代理
	 * @example
	 * Utils.dispatchEvent(document.querySelector("input","input"))
	 */
	dispatchEvent(
		element: HTMLElement | Document,
		eventName: DOMUtils_EventType | DOMUtils_EventType[],
		details?: any
	): void;
	/**
	 * 主动触发事件
	 * @param element 元素
	 * @param eventName 事件名称,可以是字符串,也可是字符串格式的列表
	 * @param details (可选)赋予触发的Event的额外属性
	 * + true 使用Proxy代理Event并设置获取isTrusted永远为True
	 * + false (默认) 不对Event进行Proxy代理
	 * @example
	 * Utils.dispatchEvent(document.querySelector("input","input"))
	 */
	dispatchEvent(
		element: HTMLElement | Document,
		eventName: string,
		details?: any
	): void;
	dispatchEvent(
		element: HTMLElement | Document,
		eventName: DOMUtils_EventType | DOMUtils_EventType[] | string,
		details?: any
	) {
		let eventNameList: string[] = [];
		if (typeof eventName === "string") {
			eventNameList = [eventName];
		}
		if (Array.isArray(eventName)) {
			eventNameList = [...eventName];
		}
		eventNameList.forEach((_eventName_) => {
			let event = new Event(_eventName_);
			if (details) {
				Object.assign(event, details);
			}
			element.dispatchEvent(event);
		});
	}
	/**
	 * 下载base64格式的数据
	 * @param base64Data	需要转换的base64数据
	 * @param fileName	需要保存的文件名
	 * @param isIFrame (可选)是否使用iframe进行下载
	 * @example
	 * Utils.downloadBase64("data:image/jpeg:base64/,xxxxxx");
	 **/
	downloadBase64(
		base64Data: string,
		fileName: string,
		isIFrame?: boolean
	): void;
	downloadBase64(base64Data: string, fileName: string, isIFrame = false) {
		if (typeof base64Data !== "string") {
			throw new Error(
				"Utils.downloadBase64 参数 base64Data 必须为 string 类型"
			);
		}
		if (typeof fileName !== "string") {
			throw new Error("Utils.downloadBase64 参数 fileName 必须为 string 类型");
		}
		if (isIFrame) {
			/* 使用iframe */
			const iframeElement = this.windowApi.document.createElement("iframe");
			iframeElement.style.display = "none";
			iframeElement.src = base64Data;
			this.windowApi.document.body.appendChild(iframeElement);
			setTimeout(() => {
				iframeElement!.contentWindow!.document.execCommand(
					"SaveAs",
					true,
					fileName
				);
				this.windowApi.document.body.removeChild(iframeElement);
			}, 100);
		} else {
			/* 使用A标签 */
			const linkElement = this.windowApi.document.createElement("a");
			linkElement.setAttribute("target", "_blank");
			linkElement.download = fileName;
			linkElement.href = base64Data;
			linkElement.click();
		}
	}

	/**
	 * 选中页面中的文字,类似Ctrl+F的选中
	 * @param str (可选)需要寻找的字符串,默认为空
	 * @param caseSensitive(可选)默认false
	 * + true 区分大小写
	 * + false (默认) 不区分大小写
	 * @returns
	 * + true 找到
	 * + false 未找到
	 * + undefined 不可使用该Api
	 * @example
	 * Utils.findWebPageVisibleText("xxxxx");
	 * > true
	 **/
	findWebPageVisibleText(str?: string, caseSensitive?: boolean): boolean | void;
	findWebPageVisibleText(str = "", caseSensitive = false) {
		let TRange = null;
		let strFound;
		if ((this.windowApi.globalThis as any).find) {
			/* CODE FOR BROWSERS THAT SUPPORT window.find */
			let windowFind = (this.windowApi.self as any).find;
			strFound = windowFind(str, caseSensitive, true, true, false);
			if (
				strFound &&
				this.windowApi.self.getSelection &&
				!this.windowApi.self.getSelection()!.anchorNode
			) {
				strFound = windowFind(str, caseSensitive, true, true, false);
			}
			if (!strFound) {
				strFound = windowFind(str, 0, 1);
				while (windowFind(str, 0, 1)) continue;
			}
		} else if (navigator.appName.indexOf("Microsoft") != -1) {
			/* EXPLORER-SPECIFIC CODE */
			if (TRange != null) {
				TRange = TRange as any;
				TRange.collapse(false);
				strFound = TRange.findText(str);
				if (strFound) TRange.select();
			}
			if (TRange == null || strFound == 0) {
				TRange = (this.windowApi.self.document.body as any).createTextRange();
				strFound = TRange.findText(str);
				if (strFound) TRange.select();
			}
		} else if (navigator.appName == "Opera") {
			alert("Opera browsers not supported, sorry...");
			return;
		}
		return strFound ? true : false;
	}
	/**
	 * 定位元素上的字符串,返回一个迭代器
	 * @param element 目标元素
	 * @param text 需要定位的字符串
	 * @param filter (可选)过滤器函数,返回值为true是排除该元素
	 * @example
	 * let textIterator = Utils.findElementsWithText(document.documentElement,"xxxx");
	 * textIterator.next();
	 * > {value: ?HTMLElement, done: boolean, next: Function}
	 */
	findElementsWithText<T extends HTMLElement | Element | Node>(
		element: T,
		text: string,
		filter?: (element: T) => boolean
	): Generator<HTMLElement | ChildNode, void, any>;
	*findElementsWithText<T extends HTMLElement | Element | Node>(
		element: T,
		text: string,
		filter?: (element: T) => boolean
	) {
		let that = this;
		if ((element as HTMLElement).outerHTML.includes(text)) {
			if ((element as HTMLElement).children.length === 0) {
				let filterResult =
					typeof filter === "function" ? filter(element) : false;
				if (!filterResult) {
					yield element as any;
				}
			} else {
				let textElement = Array.from(element.childNodes).filter(
					(ele) => ele.nodeType === Node.TEXT_NODE
				);
				for (let ele of textElement) {
					if ((ele as any).textContent.includes(text)) {
						let filterResult =
							typeof filter === "function" ? filter(element) : false;
						if (!filterResult) {
							yield ele;
						}
					}
				}
			}
		}

		for (
			let index = 0;
			index < (element as HTMLElement).children.length;
			index++
		) {
			let childElement = (element as HTMLElement).children[index] as any;
			yield* that.findElementsWithText(childElement, text, filter);
		}
	}
	/**
	 * 判断该元素是否可见,如果不可见,向上找它的父元素直至找到可见的元素
	 * @param element
	 * @example
	 * let visibleElement = Utils.findVisibleElement(document.querySelector("a.xx"));
	 * > <HTMLElement>
	 */
	findVisibleElement(element: HTMLElement | Element | Node) {
		let currentElement = element as HTMLElement;
		while (currentElement) {
			let elementRect = currentElement.getBoundingClientRect();
			if (Boolean((elementRect as any).length)) {
				return currentElement;
			}
			currentElement = currentElement.parentElement as any;
		}
		return null;
	}
	/**
	 * 格式化byte为KB、MB、GB、TB、PB、EB、ZB、YB、BB、NB、DB
	 * @param byteSize 字节
	 * @param addType (可选)是否添加单位
	 * + true (默认) 添加单位
	 * + false 不添加单位
	 * @returns
	 * + {string} 当addType为true时,且保留小数点末尾2位
	 * + {number} 当addType为false时,且保留小数点末尾2位
	 * @example
	 * Utils.formatByteToSize("812304");
	 * > '793.27KB'
	 * @example
	 * Utils.formatByteToSize("812304",false);
	 * > 793.27
	 **/
	formatByteToSize(byteSize: number | string): number;
	formatByteToSize<T extends boolean>(
		byteSize: number | string,
		addType?: T
	): T extends true ? string : number;
	formatByteToSize(byteSize: number | string, addType = true) {
		byteSize = parseInt(byteSize.toString());
		if (isNaN(byteSize)) {
			throw new Error("Utils.formatByteToSize 参数 byteSize 格式不正确");
		}
		let result = 0;
		let resultType = "KB";
		let sizeData: UtilsOwnObject<number> = {};
		sizeData.B = 1;
		sizeData.KB = 1024;
		sizeData.MB = sizeData.KB * sizeData.KB;
		sizeData.GB = sizeData.MB * sizeData.KB;
		sizeData.TB = sizeData.GB * sizeData.KB;
		sizeData.PB = sizeData.TB * sizeData.KB;
		sizeData.EB = sizeData.PB * sizeData.KB;
		sizeData.ZB = sizeData.EB * sizeData.KB;
		sizeData.YB = sizeData.ZB * sizeData.KB;
		sizeData.BB = sizeData.YB * sizeData.KB;
		sizeData.NB = sizeData.BB * sizeData.KB;
		sizeData.DB = sizeData.NB * sizeData.KB;
		for (let key in sizeData) {
			result = byteSize / (sizeData as any)[key];
			resultType = key;
			if (sizeData.KB >= result) {
				break;
			}
		}
		result = result.toFixed(2) as any;
		result = addType
			? result + resultType.toString()
			: (parseFloat(result.toString()) as any);
		return result;
	}
	/**
	 * 应用场景: 当你想要获取数组形式的元素时,它可能是其它的选择器,那么需要按照先后顺序填入参数
	 * 第一个是优先级最高的,依次下降,如果都没有,返回空列表
	 * 支持document.querySelectorAll、$("")、()=>{return document.querySelectorAll("")}
	 * @param NodeList
	 * @example
	 * Utils.getNodeListValue(
	 *  document.querySelectorAll("div.xxx"),
	 *  document.querySelectorAll("a.xxx")
	 * );
	 * > [...div,div,div]
	 * @example
	 * Utils.getNodeListValue(divGetFunction,aGetFunction);
	 * > [...div,div,div]
	 */
	getNodeListValue(...args: (NodeList | (() => HTMLElement))[]): HTMLElement[];
	getNodeListValue(...args: (NodeList | (() => HTMLElement))[]) {
		let resultArray: HTMLElement[] = [];
		for (let arg of args) {
			let value = arg as any;
			if (typeof arg === "function") {
				/* 方法 */
				value = arg();
			}
			if (value.length !== 0) {
				resultArray = [...value];
				break;
			}
		}
		return resultArray;
	}
	/**
	 * 自动判断N个参数,获取非空的值,如果都是空,返回最后一个值
	 */
	getNonNullValue(...args: any[]): any;
	getNonNullValue(...args: any[]) {
		let resultValue = args[args.length - 1];
		let UtilsContext = this;
		for (const argValue of args) {
			if (UtilsContext.isNotNull(argValue)) {
				resultValue = argValue;
				break;
			}
		}
		return resultValue;
	}
	/**
	 * 获取格式化后的时间
	 * @param text (可选)需要格式化的字符串或者时间戳,默认:new Date()
	 * @param formatType (可选)格式化成的显示类型,默认:yyyy-MM-dd HH:mm:ss
	 * + yyyy 年
	 * + MM 月
	 * + dd 天
	 * + HH 时 (24小时制)
	 * + hh 时 (12小时制)
	 * + mm 分
	 * + ss 秒
	 * @returns {string}	返回格式化后的时间
	 * @example
	 * Utils.formatTime("2022-08-21 23:59:00","HH:mm:ss");
	 * > '23:59:00'
	 * @example
	 * Utils.formatTime(1899187424988,"HH:mm:ss");
	 * > '15:10:13'
	 * @example
	 * Utils.formatTime()
	 * > '2023-1-1 00:00:00'
	 **/
	formatTime(text?: string | number | Date, formatType?: string): string;
	/**
	 * 获取格式化后的时间
	 * @param text (可选)需要格式化的字符串或者时间戳,默认:new Date()
	 * @param formatType (可选)格式化成的显示类型,默认:yyyy-MM-dd HH:mm:ss
	 * + yyyy 年
	 * + MM 月
	 * + dd 天
	 * + HH 时 (24小时制)
	 * + hh 时 (12小时制)
	 * + mm 分
	 * + ss 秒
	 * @returns {string}	返回格式化后的时间
	 * @example
	 * Utils.formatTime("2022-08-21 23:59:00","HH:mm:ss");
	 * > '23:59:00'
	 * @example
	 * Utils.formatTime(1899187424988,"HH:mm:ss");
	 * > '15:10:13'
	 * @example
	 * Utils.formatTime()
	 * > '2023-1-1 00:00:00'
	 **/
	formatTime(
		text?: string | number | Date,
		formatType?:
			| "yyyy-MM-dd HH:mm:ss"
			| "yyyy/MM/dd HH:mm:ss"
			| "yyyy_MM_dd_HH_mm_ss"
			| "yyyy年MM月dd日 HH时mm分ss秒"
			| "yyyy年MM月dd日 hh:mm:ss"
			| "yyyy年MM月dd日 HH:mm:ss"
			| "yyyy-MM-dd"
			| "yyyyMMdd"
			| "HH:mm:ss"
			| "yyyy"
			| "MM"
			| "dd"
			| "HH"
			| "mm"
			| "ss"
	): string;
	formatTime(text = new Date(), formatType = "yyyy-MM-dd HH:mm:ss") {
		let time = text == null ? new Date() : new Date(text);
		/**
		 * 校验时间补0
		 * @param timeNum
		 * @returns
		 */
		function checkTime(timeNum: number) {
			if (timeNum < 10) return "0" + timeNum;
			return timeNum;
		}

		/**
		 * 时间制修改 24小时制转12小时制
		 * @param hourNum 小时
		 * @returns
		 */
		function timeSystemChange(hourNum: number) {
			return hourNum > 12 ? hourNum - 12 : hourNum;
		}

		let timeRegexp = {
			yyyy: time.getFullYear(),
			/* 年 */
			MM: checkTime(time.getMonth() + 1),
			/* 月 */
			dd: checkTime(time.getDate()),
			/* 日 */
			HH: checkTime(time.getHours()),
			/* 时 (24小时制) */
			hh: checkTime(timeSystemChange(time.getHours())),
			/* 时 (12小时制) */
			mm: checkTime(time.getMinutes()),
			/* 分 */
			ss: checkTime(time.getSeconds()),
			/* 秒 */
		};
		Object.keys(timeRegexp).forEach(function (key) {
			let replaecRegexp = new RegExp(key, "g");
			formatType = formatType.replace(replaecRegexp, (timeRegexp as any)[key]);
		});
		return formatType;
	}
	/**
	 * 字符串格式的时间转时间戳
	 * @param text	字符串格式的时间,例如:
	 * + 2022-11-21 00:00:00
	 * + 00:00:00
	 * @returns 返回时间戳
	 * @example
	 * Utils.formatToTimeStamp("2022-11-21 00:00:00");
	 * > 1668960000000
	 **/
	formatToTimeStamp(text: string): number;
	formatToTimeStamp(text: string) {
		/* 把字符串格式的时间(完整,包括日期和时间)格式化成时间 */
		if (typeof text !== "string") {
			throw new Error("Utils.formatToTimeStamp 参数 text 必须为 string 类型");
		}
		if (text.length === 8) {
			/* 该字符串只有时分秒 */
			let today = new Date();
			text =
				today.getFullYear() +
				"-" +
				(today.getMonth() + 1) +
				"-" +
				today.getDate() +
				" " +
				text;
		}
		text = text.substring(0, 19);
		text = text.replace(/-/g, "/");
		let timestamp = new Date(text).getTime();
		return timestamp;
	}
	/**
	 * gbk格式的url编码,来自https://greasyfork.org/zh-CN/scripts/427726-gbk-url-js
	 * @example
	 * let gbkEncoder = new Utils.GBKEncoder();
	 * gbkEncoder.encode("测试");
	 * > '%B2%E2%CA%D4'
	 * gbkEncoder.decode("%B2%E2%CA%D4");
	 * > 测试
	 */
	GBKEncoder = GBKEncoder;
	/**
	 * 获取 transitionend 的在各个浏览器的兼容名
	 */
	getTransitionEndNameList() {
		return [
			"webkitTransitionEnd",
			"mozTransitionEnd",
			"MSTransitionEnd",
			"otransitionend",
			"transitionend",
		];
	}
	/**
	 * 获取 animationend 的在各个浏览器的兼容名
	 */
	getAnimationEndNameList() {
		return [
			"webkitAnimationEnd",
			"mozAnimationEnd",
			"MSAnimationEnd",
			"oanimationend",
			"animationend",
		];
	}
	/**
	 * 获取NodeList或Array对象中的最后一个的值
	 * @param targetObj
	 * @returns
	 * @example
	 * Utils.getArrayLastValue(document.querySelectorAll("div"));
	 * > div
	 * @example
	 * Utils.getArrayLastValue([1,2,3,4,5]);
	 * > 5
	 */
	getArrayLastValue<R extends any>(targetObj: NodeList | any[]): R;
	getArrayLastValue(targetObj: NodeList | any[]) {
		return targetObj[targetObj.length - 1];
	}
	/**
	 * 应用场景: 当想获取的元素可能是不同的选择器的时候,按顺序优先级获取
	 * 参数类型可以是Element或者是Function
	 * @returns 如果都没有的话,返回null
	 * @example
	 * // 如果a.aaa不存在的话,取a.bbb,这里假设a.aaa不存在
	 * Utils.getArrayRealValue(document.querySelector("a.aaa"),document.querySelector("a.bbb"));
	 * > a.bbb
	 * @example
	 * Utils.getArrayRealValue(()=>{return document.querySelector("a.aaa").href},()=>{document.querySelector("a.bbb").getAttribute("data-href")});
	 * > javascript:;
	 */
	getArrayRealValue(...args: (NodeList | (() => HTMLElement))[]): any;
	getArrayRealValue(...args: (NodeList | (() => HTMLElement))[]) {
		let result = null;
		for (let arg of args) {
			if (typeof arg === "function") {
				/* 方法 */
				(arg as any) = arg();
			}
			if (arg != null) {
				result = arg;
				break;
			}
		}
		return result;
	}
	/**
	 * 获取天数差异,如何获取某个时间与另一个时间相差的天数
	 * @param timestamp1 (可选)时间戳(毫秒|秒),不区分哪个更大,默认为:Date.now()
	 * @param timestamp2 (可选)时间戳(毫秒|秒),不区分哪个更大,默认为:Date.now()
	 * @param type (可选)返回的数字的表达的类型,比如:年、月、天、时、分、秒、auto,默认天
	 * @example
	 * Utils.getDaysDifference(new Date().getTime());
	 * > 0
	 * @example
	 * Utils.getDaysDifference(new Date().getTime(),undefined,"秒");
	 * > 0
	 */
	getDaysDifference(
		timestamp1?: number,
		timestamp2?: number,
		type?: "auto"
	): string;
	/**
	 * 获取天数差异,如何获取某个时间与另一个时间相差的天数
	 * @param timestamp1 (可选)时间戳(毫秒|秒),不区分哪个更大,默认为:Date.now()
	 * @param timestamp2 (可选)时间戳(毫秒|秒),不区分哪个更大,默认为:Date.now()
	 * @param type (可选)返回的数字的表达的类型,比如:年、月、天、时、分、秒、auto,默认天
	 * @example
	 * Utils.getDaysDifference(new Date().getTime());
	 * > 0
	 * @example
	 * Utils.getDaysDifference(new Date().getTime(),undefined,"秒");
	 * > 0
	 */
	getDaysDifference(
		timestamp1?: number,
		timestamp2?: number,
		type?: "年" | "月" | "天" | "时" | "分" | "秒"
	): number;
	getDaysDifference(
		timestamp1 = Date.now(),
		timestamp2 = Date.now(),
		type = "天"
	): number | string {
		type = type.trim();
		if (timestamp1.toString().length === 10) {
			timestamp1 = timestamp1 * 1000;
		}
		if (timestamp2.toString().length === 10) {
			timestamp2 = timestamp2 * 1000;
		}
		let smallTimeStamp = timestamp1 > timestamp2 ? timestamp2 : timestamp1;
		let bigTimeStamp = timestamp1 > timestamp2 ? timestamp1 : timestamp2;
		let oneSecond = 1000; /* 一秒的毫秒数 */
		let oneMinute = 60 * oneSecond; /* 一分钟的毫秒数 */
		let oneHour = 60 * oneMinute; /* 一小时的毫秒数 */
		let oneDay = 24 * oneHour; /* 一天的毫秒数 */
		let oneMonth = 30 * oneDay; /* 一个月的毫秒数(30天) */
		let oneYear = 12 * oneMonth; /* 一年的毫秒数 */
		let bigDate = new Date(bigTimeStamp);
		let smallDate = new Date(smallTimeStamp);
		let remainderValue = 1;
		if (type === "年") {
			remainderValue = oneYear;
		} else if (type === "月") {
			remainderValue = oneMonth;
		} else if (type === "天") {
			remainderValue = oneDay;
		} else if (type === "时") {
			remainderValue = oneHour;
		} else if (type === "分") {
			remainderValue = oneMinute;
		} else if (type === "秒") {
			remainderValue = oneSecond;
		}
		let diffValue = Math.round(
			Math.abs(((bigDate as any) - (smallDate as any)) / remainderValue)
		);
		if (type === "auto") {
			let timeDifference = bigTimeStamp - smallTimeStamp;
			diffValue = Math.floor(timeDifference / (24 * 3600 * 1000));
			if (diffValue > 0) {
				(diffValue as any) = diffValue + "天";
			} else {
				/* 计算出小时数 */
				let leave1 =
					timeDifference % (24 * 3600 * 1000); /* 计算天数后剩余的毫秒数 */
				let hours = Math.floor(leave1 / (3600 * 1000));
				if (hours > 0) {
					(diffValue as any) = hours + "小时";
				} else {
					/* 计算相差分钟数 */
					let leave2 = leave1 % (3600 * 1000); /* 计算小时数后剩余的毫秒数 */
					let minutes = Math.floor(leave2 / (60 * 1000));
					if (minutes > 0) {
						(diffValue as any) = minutes + "分钟";
					} else {
						/* 计算相差秒数 */
						let leave3 = leave2 % (60 * 1000); /* 计算分钟数后剩余的毫秒数 */
						let seconds = Math.round(leave3 / 1000);
						(diffValue as any) = seconds + "秒";
					}
				}
			}
		}
		return diffValue;
	}
	/**
	 * 获取元素的选择器字符串
	 * @param element
	 * @example
	 * Utils.getElementSelector(document.querySelector("a"))
	 * > '.....'
	 */
	getElementSelector(element: HTMLElement): string;
	getElementSelector(element: HTMLElement): string {
		let UtilsContext = this;
		// @ts-ignore
		if (!element) return;
		// @ts-ignore
		if (!element.parentElement) return;
		/* 如果元素有id属性,则直接返回id选择器 */
		if (element.id) return "#" + element.id;

		/* 递归地获取父元素的选择器 */
		let selector = UtilsContext.getElementSelector(element.parentElement);
		if (!selector) {
			return element.tagName.toLowerCase();
		}
		/* 如果有多个相同类型的兄弟元素,则需要添加索引 */
		if (element.parentElement.querySelectorAll(element.tagName).length > 1) {
			let index =
				Array.prototype.indexOf.call(element.parentElement.children, element) +
				1;
			selector +=
				" > " + element.tagName.toLowerCase() + ":nth-child(" + index + ")";
		} else {
			selector += " > " + element.tagName.toLowerCase();
		}
		return selector;
	}
	/**
	 * 获取最大值
	 * @example
	 * Utils.getMaxValue(1,3,5,7,9)
	 * > 9
	 */
	getMaxValue(...args: number[]): number;
	/**
	 * 获取最大值
	 * @example
	 * Utils.getMaxValue([1,3,5])
	 * > 5
	 */
	getMaxValue(val: number[]): number;
	/**
	 * 获取最大值
	 * @example
	 * Utils.getMaxValue({1:123,2:345,3:456},(key,value)=>{return parseInt(value)})
	 * > 456
	 */
	getMaxValue(
		val: UtilsOwnObject<number>,
		handler: (key: any, value: any) => number
	): number;
	/**
	 * 获取最大值
	 * @example
	 * Utils.getMaxValue([{1:123},{2:345},{3:456}],(index,value)=>{return parseInt(index)})
	 * > 2
	 */
	getMaxValue(...args: any[]): number {
		let result = [...args];
		let newResult: number[] = [];
		if (result.length === 0) {
			// @ts-ignore
			return;
		}
		if (result.length > 1) {
			if (
				result.length === 2 &&
				typeof result[0] === "object" &&
				typeof result[1] === "function"
			) {
				let data = result[0];
				let handleDataFunc = result[1];
				Object.keys(data).forEach((keyName) => {
					newResult = [...newResult, handleDataFunc(keyName, data[keyName])];
				});
			} else {
				result.forEach((item) => {
					if (!isNaN(parseFloat(item))) {
						newResult = [...newResult, parseFloat(item)];
					}
				});
			}
			return Math.max(...newResult);
		} else {
			result[0].forEach((item: any) => {
				if (!isNaN(parseFloat(item))) {
					newResult = [...newResult, parseFloat(item)];
				}
			});
			return Math.max(...newResult);
		}
	}
	/**
	 * 获取页面中最大的z-index的元素信息
	 * @param deviation 获取最大的z-index值的偏移,默认是1
	 * @param node 进行判断的元素,默认是document
	 * @param ignoreCallBack 执行元素处理时调用的函数,返回false可忽略不想要处理的元素
	 * @example
	 * Utils.getMaxZIndexNodeInfo();
	 * > {
	 *   node: ...,
	 *   zIndex: 1001
	 * }
	 **/
	getMaxZIndexNodeInfo(
		deviation?: number,
		target?: Element | ShadowRoot | Document,
		ignoreCallBack?: (
			$ele: Element | HTMLElement | ShadowRoot
		) => boolean | void
	): {
		node: Element;
		zIndex: number;
	};
	getMaxZIndexNodeInfo(
		deviation = 1,
		target: Element | ShadowRoot | Document = this.windowApi.document,
		ignoreCallBack?: (
			$ele: Element | HTMLElement | ShadowRoot
		) => boolean | void
	): {
		node: Element;
		zIndex: number;
	} {
		deviation = Number.isNaN(deviation) ? 1 : deviation;
		const UtilsContext = this;
		// 最大值 2147483647
		// const maxZIndex = Math.pow(2, 31) - 1;
		// 比较值 2000000000
		const maxZIndexCompare = 2 * Math.pow(10, 9);
		// 当前页面最大的z-index
		let zIndex = 0;
		// 当前的最大z-index的元素,调试使用
		// @ts-ignore
		let maxZIndexNode: Element = null;
		/**
		 * 元素是否可见
		 * @param $css
		 */
		function isVisibleNode($css: CSSStyleDeclaration): boolean {
			return $css.position !== "static" && $css.display !== "none";
		}
		/**
		 * 查询元素的z-index
		 * 并比较值是否是已获取的最大值
		 * @param $ele
		 */
		function queryMaxZIndex($ele: Element) {
			if (typeof ignoreCallBack === "function") {
				let ignoreResult = ignoreCallBack($ele);
				if (typeof ignoreResult === "boolean" && !ignoreResult) {
					return;
				}
			}
			/** 元素的样式 */
			const nodeStyle = UtilsContext.windowApi.window.getComputedStyle($ele);
			/* 不对position为static和display为none的元素进行获取它们的z-index */
			if (isVisibleNode(nodeStyle)) {
				let nodeZIndex = parseInt(nodeStyle.zIndex);
				if (!isNaN(nodeZIndex)) {
					if (nodeZIndex > zIndex) {
						// 赋值到全局
						zIndex = nodeZIndex;
						maxZIndexNode = $ele;
					}
				}
				// 判断shadowRoot
				if ($ele.shadowRoot != null && $ele instanceof ShadowRoot) {
					$ele.shadowRoot.querySelectorAll("*").forEach(($shadowEle) => {
						queryMaxZIndex($shadowEle);
					});
				}
			}
		}
		target.querySelectorAll("*").forEach(($ele, index) => {
			queryMaxZIndex($ele);
		});
		zIndex += deviation;
		if (zIndex >= maxZIndexCompare) {
			// 最好不要超过最大值
			zIndex = maxZIndexCompare;
		}
		return {
			node: maxZIndexNode,
			zIndex: zIndex,
		};
	}
	/**
	 * 获取页面中最大的z-index
	 * @param deviation 获取最大的z-index值的偏移,默认是1
	 * @param node 进行判断的元素,默认是document
	 * @param ignoreCallBack 执行元素处理时调用的函数,返回false可忽略不想要处理的元素
	 * @example
	 * Utils.getMaxZIndex();
	 * > 1001
	 **/
	getMaxZIndex(
		deviation?: number,
		target?: Element | DocumentOrShadowRoot | Document,
		ignoreCallBack?: (
			$ele: Element | HTMLElement | ShadowRoot
		) => boolean | void
	): number;
	getMaxZIndex(
		deviation = 1,
		target: Element | ShadowRoot | Document = this.windowApi.document,
		ignoreCallBack?: (
			$ele: Element | HTMLElement | ShadowRoot
		) => boolean | void
	): number {
		return this.getMaxZIndexNodeInfo(deviation, target, ignoreCallBack).zIndex;
	}
	/**
	 * 获取最小值
	 * @example
	 * Utils.getMinValue(1,3,5,7,9)
	 * > 1
	 */
	getMinValue(...args: number[]): number;
	/**
	 * 获取最小值
	 * @example
	 * Utils.getMinValue([1,3,5])
	 * > 1
	 */
	getMinValue(val: number[]): number;
	/**
	 * 获取最小值
	 * @example
	 * Utils.getMinValue({1:123,2:345,3:456},(key,value)=>{return parseInt(value)})
	 * > 123
	 */
	getMinValue(
		val: UtilsOwnObject<number>,
		handler: (key: any, value: any) => number
	): number;
	/**
	 * 获取最小值
	 * @example
	 * Utils.getMinValue([{1:123},{2:345},{3:456}],(index,value)=>{return parseInt(index)})
	 * > 0
	 */
	getMinValue(
		val: UtilsOwnObject<number>[],
		handler: (index: number, value: any) => number
	): number;
	getMinValue(...args: any[]): number {
		let result = [...args];
		let newResult: number[] = [];
		if (result.length === 0) {
			// @ts-ignore
			return;
		}
		if (result.length > 1) {
			if (
				result.length === 2 &&
				typeof result[0] === "object" &&
				typeof result[1] === "function"
			) {
				let data = result[0];
				let handleDataFunc = result[1];
				Object.keys(data).forEach((keyName) => {
					newResult = [...newResult, handleDataFunc(keyName, data[keyName])];
				});
			} else {
				result.forEach((item) => {
					if (!isNaN(parseFloat(item))) {
						newResult = [...newResult, parseFloat(item)];
					}
				});
			}
			return Math.min(...newResult);
		} else {
			result[0].forEach((item: any) => {
				if (!isNaN(parseFloat(item))) {
					newResult = [...newResult, parseFloat(item)];
				}
			});
			return Math.min(...newResult);
		}
	}
	/**
	 * 获取随机的安卓手机User-Agent
	 * @example
	 * Utils.getRandomAndroidUA();
	 * > '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'
	 **/
	getRandomAndroidUA(): string {
		let UtilsContext = this;
		let mobileNameList = [
			"LDN-LX3",
			"RNE-L03",
			"ASUS_X00ID Build/NMF26F",
			"WAS-LX3",
			"PRA-LX3",
			"MYA-L03",
			"Moto G Play",
			"Moto C Build/NRD90M.063",
			"Redmi Note 4 Build/NRD90M",
			"HUAWEI VNS-L21 Build/HUAWEIVNS-L21",
			"VTR-L09",
			"TRT-LX3",
			"M2003J15SC Build/RP1A.200720.011; wv",
			"MI 13 Build/OPR1.170623.027; wv",
		];
		/* 安卓版本 */
		let androidVersion = UtilsContext.getRandomValue(12, 14);
		/* 手机型号 */
		let randomMobile = UtilsContext.getRandomValue(mobileNameList);
		/* chrome大版本号 */
		let chromeVersion1 = UtilsContext.getRandomValue(120, 132);
		let chromeVersion2 = UtilsContext.getRandomValue(0, 0);
		let chromeVersion3 = UtilsContext.getRandomValue(2272, 6099);
		let chromeVersion4 = UtilsContext.getRandomValue(1, 218);
		return `Mozilla/5.0 (Linux; Android ${androidVersion}; ${randomMobile}) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/${chromeVersion1}.${chromeVersion2}.${chromeVersion3}.${chromeVersion4} Mobile Safari/537.36`;
	}
	/**
	 * 获取随机的电脑端User-Agent
	 * + Mozilla/5.0:以前用于Netscape浏览器,目前大多数浏览器UA都会带有
	 * + Windows NT 13:代表Window11系统
	 * + Windows NT 10.0:代表Window10系统
	 * + Windows NT 6.1:代表windows7系统
	 * + WOW64:Windows-on-Windows 64-bit,32位的应用程序运行于此64位处理器上
	 * + Win64:64位
	 * + AppleWebKit/537.36:浏览器内核
	 * + KHTML:HTML排版引擎
	 * + like Gecko:这不是Geckeo 浏览器,但是运行起来像Geckeo浏览器
	 * + Chrome/106.0.5068.19:Chrome版本号
	 * + Safari/537.36:宣称自己是Safari?
	 * @returns 返回随机字符串
	 * @example
	 * Utils.getRandomPCUA();
	 * > 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/106.0.5068.19 Safari/537.36'
	 **/
	getRandomPCUA(): string {
		let UtilsContext = this;
		/* chrome大版本号 */
		let chromeVersion1 = UtilsContext.getRandomValue(120, 132);
		let chromeVersion2 = UtilsContext.getRandomValue(0, 0);
		let chromeVersion3 = UtilsContext.getRandomValue(2272, 6099);
		let chromeVersion4 = UtilsContext.getRandomValue(1, 218);
		return `Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/${chromeVersion1}.${chromeVersion2}.${chromeVersion3}.${chromeVersion4} Safari/537.36`;
	}
	/**
	 * 获取随机值
	 * @example
	 * Utils.getRandomValue(1,9,6,99)
	 * > 6
	 */
	getRandomValue<T extends any>(...args: T[]): T;
	/**
	 * 获取随机值
	 * @example
	 * Utils.getRandomValue([1,2,3])
	 * > 3
	 * @example
	 * Utils.getRandomValue({1:"结果1",2:"结果2",3:"结果3"}})
	 * > 结果2
	 */
	getRandomValue<T extends any>(val: T[] | UtilsOwnObject<T>): T;
	/**
	 * 获取两个数之间随机值
	 * @example
	 * Utils.getRandomValue(1,9)
	 * > 6
	 */
	getRandomValue(val_1: number, val_2: number): number;
	/**
	 * 获取随机值
	 * @example
	 * Utils.getRandomValue({1:1},{2:2})
	 * > {1: 1}
	 */
	getRandomValue<T extends any>(
		val_1: UtilsOwnObject<T>,
		val_2: UtilsOwnObject<T>
	): T;
	getRandomValue(...args: any[]): any {
		let result = [...args];
		if (result.length > 1) {
			if (
				result.length === 2 &&
				typeof result[0] === "number" &&
				typeof result[1] === "number"
			) {
				let leftNumber = result[0] > result[1] ? result[1] : result[0];
				let rightNumber = result[0] > result[1] ? result[0] : result[1];
				return (
					Math.round(Math.random() * (rightNumber - leftNumber)) + leftNumber
				);
			} else {
				return result[Math.floor(Math.random() * result.length)];
			}
		} else if (result.length === 1) {
			let paramData = result[0];
			if (Array.isArray(paramData)) {
				return paramData[Math.floor(Math.random() * paramData.length)];
			} else if (
				typeof paramData === "object" &&
				Object.keys(paramData).length > 0
			) {
				let paramObjDataKey =
					Object.keys(paramData)[
						Math.floor(Math.random() * Object.keys(paramData).length)
					];
				return paramData[paramObjDataKey];
			} else {
				return paramData;
			}
		}
	}
	/**
	 * 获取元素上的使用React框架的实例属性,目前包括reactFiber、reactProps、reactEvents、reactEventHandlers、reactInternalInstance
	 * @param element 需要获取的目标元素
	 * @returns
	 * @example
	 * Utils.getReactObj(document.querySelector("input"))?.reactProps?.onChange({target:{value:"123"}});
	 */
	getReactObj(element: HTMLElement | Element): {
		reactFiber?: any;
		reactProps?: any;
		reactEvents?: any;
		reactEventHandlers?: any;
		reactInternalInstance?: any;
		reactContainer?: any;
	} {
		let result = {};
		Object.keys(element).forEach((domPropsName) => {
			if (domPropsName.startsWith("__react")) {
				let propsName = domPropsName.replace(/__(.+)\$.+/i, "$1");
				if (propsName in result) {
					new Error("重复属性 " + domPropsName);
				} else {
					(result as any)[propsName] = (element as any)[domPropsName];
				}
			}
		});
		return result;
	}
	/**
	 * 获取对象上的Symbol属性,如果没设置keyName,那么返回一个对象,对象是所有遍历到的Symbol对象
	 * @param target 目标对象
	 * @param keyName (可选)Symbol名或者Symbol对象
	 */
	getSymbol(target: any, keyName?: string | symbol) {
		if (typeof target !== "object") {
			throw new TypeError("target不是一个对象");
		}
		let objectsSymbols = Object.getOwnPropertySymbols(target);
		if (typeof keyName === "string") {
			let findSymbol = objectsSymbols.find((key) => {
				return key.toString() === keyName;
			});
			if (findSymbol) {
				return target[findSymbol];
			}
		} else if (typeof keyName === "symbol") {
			let findSymbol = objectsSymbols.find((key) => {
				return key === keyName;
			});
			if (findSymbol) {
				return (target as any)[findSymbol];
			}
		} else {
			let result = {};
			objectsSymbols.forEach((item) => {
				(result as any)[item] = target[item];
			});
			return result;
		}
	}
	/**
	 * 获取文本的字符长度
	 * @param text
	 * @example
	 * Utils.getTextLength("测试文本")
	 * > 12
	 */
	getTextLength(text: string): number {
		let encoder = new TextEncoder();
		let bytes = encoder.encode(text);
		return bytes.length;
	}
	/**
	 * 获取文本占据的空间大小,返回自动的单位,如12 Kb,14 K,20 MB,1 GB
	 * @param text 目标字符串
	 * @param addType (可选)是否添加单位
	 * + true (默认) 自动添加单位
	 * + false 不添加单位
	 * @example
	 * Utils.getTextStorageSize("测试文本");
	 * > '12.00B'
	 */
	getTextStorageSize<T extends boolean>(
		text: string,
		addType?: T
	): T extends true ? string : number;
	getTextStorageSize(text: string, addType = true) {
		let UtilsContext = this;
		return UtilsContext.formatByteToSize(
			UtilsContext.getTextLength(text),
			addType
		);
	}
	/**
	 * 获取迅雷协议的Url
	 * @param url Url链接或者其它信息
	 */
	getThunderUrl(url: string): string;
	getThunderUrl(url: string): string {
		if (url == null) {
			throw new TypeError("url不能为空");
		}
		if (typeof url !== "string") {
			throw new TypeError("url必须是string类型");
		}
		if (url.trim() === "") {
			throw new TypeError("url不能为空字符串或纯空格");
		}
		return `thunder://${this.windowApi.globalThis.btoa("AA" + url + "ZZ")}`;
	}
	/**
     * 对于GM_cookie的兼容写法,当无法使用GM_cookie时可以使用这个,但是并不完全兼容,有些写不出来且限制了httponly是无法访问的
     * @example
      let GM_cookie = new Utils.GM_Cookie();
      GM_cookie.list({name:"xxx_cookie_xxx"},function(cookies,error){
          if (!error) {
              console.log(cookies);
              console.log(cookies.value);
          } else {
              console.error(error);
          }
      });
      GM_cookie.set({name:"xxx_cookie_test_xxx",value:"这是Cookie测试值"},function(error){
          if (error) {
              console.error(error);
          } else {
              console.log('Cookie set successfully.');
          }
      })
      GM_cookie.delete({name:"xxx_cookie_test_xxx"},function(error){
          if (error) {
              console.error(error);
          } else {
              console.log('Cookie set successfully.');
          }
      })
     **/
	GM_Cookie = UtilsGMCookie;
	/**
     * 注册油猴菜单,要求本地存储的键名不能存在其它键名`GM_Menu_Local_Map`会冲突/覆盖
     * @example
      let GM_Menu = new Utils.GM_Menu({
        data: [
          {
            menu_key: "menu_key",
            text: "测试按钮",
            enable: true,
            accessKey: "a",
            autoClose: false,
            showText(text, enable) {
              return "[" + (enable ? "√" : "×") + "]" + text;
            },
            callback(data) {
              console.log("点击菜单,值修改为", data.enable);
            },
          },
        ],
        autoReload: false,
        GM_getValue,
        GM_setValue,
        GM_registerMenuCommand,
        GM_unregisterMenuCommand,
      });
  
  
      // 获取某个菜单项的值
      GM_Menu.get("menu_key");
      > true
  
      // 获取某个菜单项的开启/关闭后显示的文本
      GM_Menu.getShowTextValue("menu_key");
      > √测试按钮
  
      // 添加键为menu_key2的菜单项
      GM_Menu.add({
        key:"menu_key2",
        text: "测试按钮2",
        enable: false,
        showText(text,enable){
          return "[" + (enable ? "√" : "×") + "]" + text;
        },
        callback(data){
          console.log("点击菜单,值修改为",data.enable);
        }
      });
      // 使用数组的方式添加多个菜单,如menu_key3、menu_key4
      GM_Menu.add([
        {
          key:"menu_key3",
          text: "测试按钮3",
          enable: false,
          showText(text,enable){
            return "[" + (enable ? "√" : "×") + "]" + text;
          },
          callback(data){
            console.log("点击菜单,值修改为",data.enable);
          }
        },
        {
          key:"menu_key4",
          text: "测试按钮4",
          enable: false,
          showText(text,enable){
            return "[" + (enable ? "√" : "×") + "]" + text;
          },
          callback(data){
            console.log("点击菜单,值修改为",data.enable);
          }
        }
      ]);
  
      // 更新键为menu_key的显示文字和点击回调
      GM_Menu.update({
        menu_key:{
          text: "更新后的测试按钮",
          enable: true,
          showText(text,enable){
            return "[" + (enable ? "√" : "×") + "]" + text;
          },
          callback(data){
            console.log("点击菜单更新后的测试按钮,新值修改为",data.enable);
          }
        }
      });
  
      // 删除键为menu_key的菜单
      GM_Menu.delete("menu_key");
     **/
	GM_Menu = GMMenu;
	/**
     * 基于Function prototype,能够勾住和释放任何函数
     * 
     * .hook
     * + realFunc {string} 用于保存原始函数的函数名称,用于unHook
     * + hookFunc {string} 替换的hook函数
     * + context {object} 目标函数所在对象,用于hook非window对象下的函数,如String.protype.slice,carInstance1
     * + methodName {string} 匿名函数需显式传入目标函数名eg:this.Begin = function(){....};}
     * 
     * .unhook
     * + realFunc {string} 用于保存原始函数的函数名称,用于unHook
     * + funcName {string} 被Hook的函数名称
     * + context {object} 目标函数所在对象,用于hook非window对象下的函数,如String.protype.slice,carInstance1
     * @example
      let hook = new Utils.Hooks();
      hook.initEnv();
      function myFunction(){
          console.log("我自己需要执行的函数");
      }
      function testFunction(){
          console.log("正常执行的函数");
      }
      testFunction.hook(testFunction,myFunction,window);
     **/
	Hooks = Hooks;

	/**
     * 为减少代码量和回调,把GM_xmlhttpRequest封装
     * 文档地址: https://www.tampermonkey.net/documentation.php?ext=iikm
     * 其中onloadstart、onprogress、onreadystatechange是回调形式,onabort、ontimeout、onerror可以设置全局回调函数
     * @param _GM_xmlHttpRequest_ 油猴中的GM_xmlhttpRequest
     * @example
      let httpx = new Utils.Httpx(GM_xmlhttpRequest);
      let postResp = await httpx.post({
        url:url,
        data:JSON.stringify({
          test:1
        }),
        timeout: 5000
      });
      console.log(postResp);
      > {
        status: true,
        data: {responseText: "...", response: xxx,...},
        msg: "请求完毕",
        type: "onload",
      }
  
      if(postResp === "onload" && postResp.status){
      // onload
      }else if(postResp === "ontimeout"){
      // ontimeout
      }
     * @example
      // 也可以先配置全局参数
      let httpx = new Utils.Httpx(GM_xmlhttpRequest);
      httpx.config({
        timeout: 5000,
        async: false,
        responseType: "html",
        redirect: "follow",
      })
      // 优先级为 默认details < 全局details < 单独的details
     */
	Httpx = Httpx;
	/**
     * 浏览器端的indexedDB操作封装
     * @example
      let db = new Utils.indexedDB('web_DB', 'nav_text')
      let data = {name:'管理员', roleId: 1, type: 1};
      db.save('list',data).then((resolve)=>{
          console.log(resolve,'存储成功')
      })
  
      db.get('list').then((resolve)=>{
          console.log(resolve,'查询成功')
      })
  
      db.getPaging('list',20,10).then((resolve)=>{
          console.log(resolve,'查询分页偏移第20,一共10行成功');
      })
  
      db.delete('list').then(resolve=>{
          console.log(resolve,'删除成功---->>>>>>name')
      })
  
      db.deleteAll().then(resolve=>{
          console.log(resolve,'清除数据库---->>>>>>name')
      })
     **/
	indexedDB = indexedDB;
	/**
	 * 判断目标函数是否是Native Code
	 * @param target
	 * @returns
	 * + true 是Native
	 * + false 不是Native
	 * @example
	 * Utils.isNativeFunc(window.location.assign)
	 * > true
	 */
	isNativeFunc(target: Function): boolean;
	isNativeFunc(target: Function): boolean {
		return Boolean(
			target.toString().match(/^function .*\(\) { \[native code\] }$/)
		);
	}
	/**
	 * 判断当前的位置是否位于页面底部附近
	 * @param nearBottomHeight (可选)判断在底部的误差值,默认:50
	 * @returns
	 * + true 在底部附近
	 * + false 不在底部附近
	 */
	isNearBottom(nearBottomHeight?: number): boolean;
	/**
	 * 判断元素内当前的位置是否位于元素内底部附近
	 * @param target 需要判断的元素
	 * @param nearBottomHeight (可选)判断在底部的误差值,默认:50
	 */
	isNearBottom(target: HTMLElement, nearBottomHeight?: number): boolean;
	isNearBottom(...args: any[]): boolean {
		let nearBottomHeight = 50;

		let checkWindow = () => {
			// 已滚动的距离
			let scrollTop: number =
				this.windowApi.window.pageYOffset ||
				this.windowApi.document.documentElement.scrollTop;
			// 视窗高度
			let viewportHeight: number =
				this.windowApi.window.innerHeight ||
				this.windowApi.document.documentElement.clientHeight;
			// 最大滚动距离
			let maxScrollHeight: number =
				this.windowApi.document.documentElement.scrollHeight - nearBottomHeight;

			return scrollTop + viewportHeight >= maxScrollHeight;
		};

		let checkNode = ($ele: HTMLElement) => {
			// 已滚动的距离
			let scrollTop: number = $ele.scrollTop;
			// 视窗高度
			let viewportHeight: number = $ele.clientHeight;
			// 最大滚动距离
			let maxScrollHeight: number =
				$ele.scrollHeight - viewportHeight - nearBottomHeight;

			return scrollTop >= maxScrollHeight;
		};

		let firstArg = args[0];
		if (args.length === 0 || typeof args[0] === "number") {
			// nearBottomHeight
			//
			return checkWindow();
		} else if (typeof args[0] === "object" && args[0] instanceof HTMLElement) {
			// target
			// target,nearBottomHeight
			if (typeof args[1] === "number" && !Number.isNaN(args[1])) {
				nearBottomHeight = args[1];
			}
			return checkNode(args[0]);
		} else {
			throw new TypeError("参数1类型错误" + typeof firstArg);
		}
	}
	/**
	 * 判断对象是否是元素
	 * @param target
	 * @returns
	 * + true 是元素
	 * + false 不是元素
	 * @example
	 * Utils.isDOM(document.querySelector("a"))
	 * > true
	 */
	isDOM(target: any): boolean;
	isDOM(target: any): boolean {
		return target instanceof Node;
	}
	/**
	 * 判断浏览器是否支持全屏
	 */
	isFullscreenEnabled(): boolean;
	isFullscreenEnabled(): boolean {
		return !!(
			(this.windowApi.document as any).fullscreenEnabled ||
			(this.windowApi.document as any).webkitFullScreenEnabled ||
			(this.windowApi.document as any).mozFullScreenEnabled ||
			(this.windowApi.document as any).msFullScreenEnabled
		);
	}
	/**
	 * 判断对象是否是jQuery对象
	 * @param target
	 * @returns
	 * + true 是jQuery对象
	 * + false 不是jQuery对象
	 * @example
	 * Utils.isJQuery($("a"))
	 * > true
	 */
	isJQuery(target: any): boolean;
	isJQuery(target: any): boolean {
		let result = false;
		// @ts-ignore
		if (typeof jQuery === "object" && target instanceof jQuery) {
			result = true;
		}
		if (target == null) {
			return false;
		}
		if (typeof target === "object") {
			/* 也有种可能,这个jQuery对象是1.8.3版本的,页面中的jQuery是3.4.1版本的 */
			let jQueryProps = [
				"add",
				"addBack",
				"addClass",
				"after",
				"ajaxComplete",
				"ajaxError",
				"ajaxSend",
				"ajaxStart",
				"ajaxStop",
				"ajaxSuccess",
				"animate",
				"append",
				"appendTo",
				"attr",
				"before",
				"bind",
				"blur",
				"change",
				"children",
				"clearQueue",
				"click",
				"clone",
				"closest",
				"constructor",
				"contents",
				"contextmenu",
				"css",
				"data",
				"dblclick",
				"delay",
				"delegate",
				"dequeue",
				"each",
				"empty",
				"end",
				"eq",
				"extend",
				"fadeIn",
				"fadeOut",
				"fadeTo",
				"fadeToggle",
				"filter",
				"find",
				"first",
				"focus",
				"focusin",
				"focusout",
				"get",
				"has",
				"hasClass",
				"height",
				"hide",
				"hover",
				"html",
				"index",
				"init",
				"innerHeight",
				"innerWidth",
				"insertAfter",
				"insertBefore",
				"is",
				"jquery",
				"keydown",
				"keypress",
				"keyup",
				"last",
				"load",
				"map",
				"mousedown",
				"mouseenter",
				"mouseleave",
				"mousemove",
				"mouseout",
				"mouseover",
				"mouseup",
				"next",
				"nextAll",
				"not",
				"off",
				"offset",
				"offsetParent",
				"on",
				"one",
				"outerHeight",
				"outerWidth",
				"parent",
				"parents",
				"position",
				"prepend",
				"prependTo",
				"prev",
				"prevAll",
				"prevUntil",
				"promise",
				"prop",
				"pushStack",
				"queue",
				"ready",
				"remove",
				"removeAttr",
				"removeClass",
				"removeData",
				"removeProp",
				"replaceAll",
				"replaceWith",
				"resize",
				"scroll",
				"scrollLeft",
				"scrollTop",
				"select",
				"show",
				"siblings",
				"slice",
				"slideDown",
				"slideToggle",
				"slideUp",
				"sort",
				"splice",
				"text",
				"toArray",
				"toggle",
				"toggleClass",
				"trigger",
				"triggerHandler",
				"unbind",
				"width",
				"wrap",
			];
			for (const jQueryPropsName of jQueryProps) {
				if (!(jQueryPropsName in target)) {
					result = false;
					/* console.log(jQueryPropsName); */
					break;
				} else {
					result = true;
				}
			}
		}
		return result;
	}
	/**
	 * 判断当前设备是否是移动端
	 * @param userAgent (可选)UA字符串,默认使用当前的navigator.userAgent
	 * @returns
	 * + true 是移动端
	 * + false 不是移动端
	 * @example
	 * Utils.isPhone();
	 * > true
	 **/
	isPhone(userAgent?: string): boolean;
	isPhone(userAgent: string = navigator.userAgent): boolean {
		return Boolean(/(iPhone|iPad|iPod|iOS|Android|Mobile)/i.test(userAgent));
	}
	/**
	 * 判断传递的字符串是否是由相同的字符组成
	 * @param targetStr 需要判断的字符串,长度(.length)需要≥2
	 * @param coefficient 系数(默认:1),某个字符重复的系数大于它那么就是返回true,默认全部
	 */
	isSameChars(targetStr: string, coefficient?: number): boolean;
	isSameChars(targetStr: string, coefficient: number = 1): boolean {
		if (typeof targetStr !== "string") {
			throw new TypeError("参数 str 必须是 string 类型");
		}
		if (targetStr.length < 2) {
			return false;
		}
		targetStr = targetStr.toLowerCase();
		const targetCharMap: UtilsOwnObject<string> = {};
		let targetStrLength = 0;
		for (const char of targetStr) {
			if (Reflect.has(targetCharMap, char)) {
				(targetCharMap as any)[char]++;
			} else {
				(targetCharMap as any)[char] = 1;
			}
			targetStrLength++;
		}
		let result = false;
		for (const char in targetCharMap) {
			if ((targetCharMap as any)[char] / targetStrLength >= coefficient) {
				result = true;
				break;
			}
		}
		return result;
	}
	/**
	 * 判断对象是否不为空
	 * @returns {boolean}
	 * + true 不为空
	 * + false 为空
	 * @example
	 * Utils.isNotNull("123");
	 * > true
	 */
	isNotNull<T>(value: T | null | undefined): value is T;
	isNotNull(...args: any[]): boolean;
	isNotNull(...args: any[]): boolean {
		let UtilsContext = this;
		return !UtilsContext.isNull.apply(this, args);
	}
	/**
     * 判断对象或数据是否为空
     * + `String`判空的值,如 ""、"null"、"undefined"、"   "
     * + `Number`判空的值,如 0
     * + `Object`判空的值,如 {}、null、undefined
     * + `Array`(存在属性Symbol.iterator)判空的值,如 []
     * + `Boolean`判空的值,如false
     * + `Function`判空的值,如()=>{}、(xxx="")=>{}、function(){}、function(xxx=""){}
     * @returns
     * + true 为空
     * + false 不为空
     * @example
      Utils.isNull({});
      > true
     * @example
      Utils.isNull([]);
      > true
     * @example
      Utils.isNull(" ");
      > true
     * @example
      Utils.isNull(function(){});
      > true
     * @example
      Utils.isNull(()=>{}));
      > true
     * @example
      Utils.isNull("undefined");
      > true
     * @example
      Utils.isNull("null");
      > true
     * @example
      Utils.isNull(" ", false);
      > true
     * @example
      Utils.isNull([1],[]);
      > false
     * @example
      Utils.isNull([],[1]);
      > false
     * @example
      Utils.isNull(false,[123]);
      > false
     **/
	isNull<T>(value: T | undefined | null): value is null | undefined;
	isNull(...args: any[]): boolean;
	isNull(...args: any[]): boolean {
		let result = true;
		let checkList = [...args];
		for (const objItem of checkList) {
			let itemResult = false;
			if (objItem === null || objItem === undefined) {
				itemResult = true;
			} else {
				switch (typeof objItem) {
					case "object":
						if (typeof objItem[Symbol.iterator] === "function") {
							/* 可迭代 */
							itemResult = objItem.length === 0;
						} else {
							itemResult = Object.keys(objItem).length === 0;
						}
						break;
					case "number":
						itemResult = objItem === 0;
						break;
					case "string":
						itemResult =
							objItem.trim() === "" ||
							objItem === "null" ||
							objItem === "undefined";
						break;
					case "boolean":
						itemResult = !objItem;
						break;
					case "function":
						let funcStr = objItem.toString().replace(/\s/g, "");
						/* 排除()=>{}、(xxx="")=>{}、function(){}、function(xxx=""){} */
						itemResult = Boolean(
							funcStr.match(/^\(.*?\)=>\{\}$|^function.*?\(.*?\)\{\}$/)
						);
						break;
				}
			}
			result = result && itemResult;
		}

		return result;
	}

	/**
	 * 判断浏览器主题是否是暗黑|深色模式
	 */
	isThemeDark(): boolean;
	isThemeDark(): boolean {
		return this.windowApi.globalThis.matchMedia("(prefers-color-scheme: dark)")
			.matches;
	}
	/**
	 * 判断元素是否在页面中可见
	 * @param element 需要检查的元素,可以是普通元素|数组形式的元素|通过querySelectorAll获取的元素数组
	 * @param inView
	 * + true 在窗口可视区域
	 * + false 不在窗口可视区域
	 * @returns
	 * + true 可见
	 * + false 不可见
	 * @example
	 * Utils.isVisible(document.documentElement)
	 * > true
	 */
	isVisible(
		element: HTMLElement | HTMLElement[] | Element | NodeList,
		inView: boolean = false
	): boolean {
		let needCheckDomList = [];
		if (element instanceof Array || element instanceof NodeList) {
			element = element as HTMLElement[];
			needCheckDomList = [...element];
		} else {
			needCheckDomList = [element];
		}
		let result = true;
		for (const domItem of needCheckDomList) {
			let domDisplay = this.windowApi.window.getComputedStyle(domItem);
			if (domDisplay.display === "none") {
				result = false;
			} else {
				let domClientRect = domItem.getBoundingClientRect();
				if (inView) {
					let viewportWidth =
						this.windowApi.window.innerWidth ||
						this.windowApi.document.documentElement.clientWidth;
					let viewportHeight =
						this.windowApi.window.innerHeight ||
						this.windowApi.document.documentElement.clientHeight;
					result = !(
						domClientRect.right < 0 ||
						domClientRect.left > viewportWidth ||
						domClientRect.bottom < 0 ||
						domClientRect.top > viewportHeight
					);
				} else {
					result = Boolean(domItem.getClientRects().length);
				}
			}
			if (!result) {
				/* 有一个不可见就退出循环 */
				break;
			}
		}
		return result;
	}

	/**
	 * 判断是否是Via浏览器环境
	 * @returns
	 * + true 是Via
	 * + false 不是Via
	 * @example
	 * Utils.isWebView_Via()
	 * > false
	 */
	isWebView_Via(): boolean;
	isWebView_Via(): boolean {
		let result = true;
		let UtilsContext = this;
		if (typeof (this.windowApi.top.window as any).via === "object") {
			for (const key in Object.values((this.windowApi.top.window as any).via)) {
				if (Reflect.has((this.windowApi.top.window as any).via, key)) {
					let objValueFunc = (this.windowApi.top.window as any).via[key];
					if (
						typeof objValueFunc === "function" &&
						UtilsContext.isNativeFunc(objValueFunc)
					) {
						result = true;
					} else {
						result = false;
						break;
					}
				}
			}
		} else {
			result = false;
		}
		return result;
	}
	/**
	 * 判断是否是X浏览器环境
	 * @returns
	 * + true 是X浏览器
	 * + false 不是X浏览器
	 * @example
	 * Utils.isWebView_X()
	 * > false
	 */
	isWebView_X(): boolean;
	isWebView_X(): boolean {
		let result = true;
		let UtilsContext = this;
		if (typeof (this.windowApi.top.window as any).mbrowser === "object") {
			for (const key in Object.values(
				(this.windowApi.top.window as any).mbrowser
			)) {
				if (Reflect.has((this.windowApi.top.window as any).mbrowser, key)) {
					let objValueFunc = (this.windowApi.top.window as any).mbrowser[key];
					if (
						typeof objValueFunc === "function" &&
						UtilsContext.isNativeFunc(objValueFunc)
					) {
						result = true;
					} else {
						result = false;
						break;
					}
				}
			}
		} else {
			result = false;
		}
		return result;
	}
	/**
	 * 把对象内的value值全部取出成数组
	 * @param target 目标对象
	 * @returns 返回数组
	 * @example
	 * Utils.parseObjectToArray({"工具类":"jsonToArray","return","Array"});
	 * > ['jsonToArray', 'Array']
	 **/
	parseObjectToArray<T extends any>(target: T): T[keyof T][];
	parseObjectToArray<T extends any>(target: T) {
		if (typeof target !== "object") {
			throw new Error(
				"Utils.parseObjectToArray 参数 target 必须为 object 类型"
			);
		}
		let result: T[keyof T][] = [];
		Object.keys(target!).forEach(function (keyName) {
			result = result.concat(target![keyName as any as keyof T]);
		});
		return result;
	}
	/**
     * 自动锁对象,用于循环判断运行的函数,在循环外new后使用,注意,如果函数内部存在异步操作,需要使用await
     * @example
      let lock = new Utils.LockFunction(()=>{console.log(1)}))
      lock.run();
      > 1
     * @example
      let lock = new Utils.LockFunction(()=>{console.log(1)}),true) -- 异步操作
      await lock.run();
      > 1
     **/
	LockFunction = LockFunction;
	/**
     * 日志对象
     * @param _GM_info_ 油猴管理器的API GM_info,或者是一个对象,如{"script":{name:"Utils.Log"}}
     * @example
      let log = new Utils.Log(GM_info);
      log.info("普通输出");
      > 普通输出
  
      log.success("成功输出");
      > 成功输出
  
      log.error("错误输出");
      > 错误输出
      
      log.warn("警告输出");
      > 警告输出
  
      log.tag = "自定义tag信息";
      log.info("自定义info的颜色","#e0e0e0");
      > 自定义info的颜色
  
      log.config({
        successColor: "#31dc02",
        errorColor: "#e02d2d",
        infoColor: "black",
      })
      log.success("颜色为#31dc02");
      > 颜色为#31dc02
     */
	Log = Log;
	/**
	 * 合并数组内的JSON的值字符串
	 * @param data 需要合并的数组
	 * @param handleFunc 处理的函数|JSON的key
	 * @example
	 * Utils.mergeArrayToString([{"name":"数组内数据部分字段合并成字符串->"},{"name":"mergeToString"}],(item)=>{return item["name"]});
	 * > '数组内数据部分字段合并成字符串->mergeToString'
	 **/
	mergeArrayToString<T extends any>(
		data: T[],
		handleFunc?: ((val: T) => T) | keyof T
	): string;
	mergeArrayToString<T extends any>(
		data: T[],
		handleFunc?: ((val: T) => T) | keyof T
	): string {
		if (!(data instanceof Array)) {
			throw new Error("Utils.mergeArrayToString 参数 data 必须为 Array 类型");
		}
		let content = "";
		if (typeof handleFunc === "function") {
			data.forEach((item) => {
				content += handleFunc(item);
			});
		} else if (typeof handleFunc === "string") {
			data.forEach((item) => {
				content += item[handleFunc];
			});
		} else {
			data.forEach((item) => {
				Object.values(item as any)
					.filter((item2) => typeof item2 === "string")
					.forEach((item3) => {
						content += item3;
					});
			});
		}
		return content;
	}
	/**
     * 监听页面元素改变并处理
     * @param target 需要监听的元素,如果不存在,可以等待它出现
     * @param observer_config MutationObserver的配置
     * @example
      Utils.mutationObserver(document.querySelector("div.xxxx"),{
        "callback":(mutations, observer)=>{},
        "config":{childList:true,attributes:true}
      });
     * @example
      Utils.mutationObserver(document.querySelectorAll("div.xxxx"),{
        "callback":(mutations, observer)=>{},
        "config":{childList:true,attributes:true}}
      );
     * @example
      Utils.mutationObserver($("div.xxxx"),{
      "callback":(mutations, observer)=>{},
      "config":{childList:true,attributes:true}}
      );
     **/
	mutationObserver(
		target: HTMLElement | Node | NodeList | Document,
		observer_config: {
			/**
			 * observer的配置
			 */
			config?: MutationObserverInit;
			/**
			 * 是否主动触发一次
			 */
			immediate?: boolean;
			/**
			 * 触发的回调函数
			 */
			callback: MutationCallback;
		}
	): MutationObserver;
	mutationObserver(
		target: HTMLElement | Node | NodeList | Document,
		observer_config: {
			/**
			 * observer的配置
			 */
			config?: MutationObserverInit;
			/**
			 * 是否主动触发一次
			 */
			immediate?: boolean;
			/**
			 * 触发的回调函数
			 */
			callback: MutationCallback;
		}
	): MutationObserver {
		let UtilsContext = this;

		let default_obverser_config = {
			/* 监听到元素有反馈,需执行的函数 */
			callback: () => {},
			config: <MutationObserverInit>{
				/**
				 * + true 监听以 target 为根节点的整个子树。包括子树中所有节点的属性,而不仅仅是针对 target
				 * + false (默认) 不生效
				 */
				subtree: void 0 as any as boolean,
				/**
				 * + true 监听 target 节点中发生的节点的新增与删除(同时,如果 subtree 为 true,会针对整个子树生效)
				 * + false (默认) 不生效
				 */
				childList: void 0 as any as boolean,
				/**
				 * + true 观察所有监听的节点属性值的变化。默认值为 true,当声明了 attributeFilter 或 attributeOldValue
				 * + false (默认) 不生效
				 */
				attributes: void 0 as any as boolean,
				/**
				 * 一个用于声明哪些属性名会被监听的数组。如果不声明该属性,所有属性的变化都将触发通知
				 */
				attributeFilter: void 0 as any as string[],
				/**
				 * + true 记录上一次被监听的节点的属性变化;可查阅 MutationObserver 中的 Monitoring attribute values 了解关于观察属性变化和属性值记录的详情
				 * + false (默认) 不生效
				 */
				attributeOldValue: void 0 as any as boolean,
				/**
				 * + true 监听声明的 target 节点上所有字符的变化。默认值为 true,如果声明了 characterDataOldValue
				 * + false (默认) 不生效
				 */
				characterData: void 0 as any as boolean,
				/**
				 * + true 记录前一个被监听的节点中发生的文本变化
				 * + false (默认) 不生效
				 */
				characterDataOldValue: void 0 as any as boolean,
			},
			immediate: false,
		};
		observer_config = UtilsContext.assign(
			default_obverser_config,
			observer_config
		);
		let windowMutationObserver =
			this.windowApi.window.MutationObserver ||
			(this.windowApi.window as any).webkitMutationObserver ||
			(this.windowApi.window as any).MozMutationObserver;
		// 观察者对象
		let mutationObserver = new windowMutationObserver(function (
			mutations: MutationRecord[],
			observer: MutationObserver
		) {
			if (typeof observer_config.callback === "function") {
				observer_config.callback(mutations, observer);
			}
		});

		if (Array.isArray(target) || target instanceof NodeList) {
			// 传入的是数组或者元素数组
			target.forEach((item) => {
				mutationObserver.observe(item, observer_config.config);
			});
		} else if (UtilsContext.isJQuery(target)) {
			/* 传入的参数是jQuery对象 */
			(target as any).each((index: any, item: any) => {
				mutationObserver.observe(item, observer_config.config);
			});
		} else {
			mutationObserver.observe(target, observer_config.config);
		}
		if (observer_config.immediate) {
			/* 主动触发一次 */
			if (typeof observer_config.callback === "function") {
				observer_config.callback([], mutationObserver);
			}
		}
		return mutationObserver;
	}
	/**
	 * 使用观察器观察元素出现在视图内,出现的话触发回调
	 * @param target 目标元素
	 * @param callback 触发的回调
	 * @param options 观察器配置
	 * @example
	 * Utils.mutationVisible(document.querySelector("div.xxxx"),(entries,observer)=>{
	 *     console.log("该元素出现在视图内");
	 * }))
	 */
	mutationVisible(
		target: Element | Element[],
		callback: (
			entries: IntersectionObserverEntry[],
			observer: IntersectionObserver
		) => void,
		options?: IntersectionObserverInit
	) {
		if (typeof IntersectionObserver === "undefined") {
			throw new TypeError("IntersectionObserver is not defined");
		}
		if (target == null) {
			throw new TypeError("mutatuinVisible target is null");
		}
		let defaultOptions: IntersectionObserverInit = {
			root: null,
			rootMargin: "0px 0px 0px 0px",
			threshold: [0.01, 0.99],
		};
		defaultOptions = this.assign(defaultOptions, options || {});
		let intersectionObserver = new IntersectionObserver((entries, observer) => {
			if (entries[0].isIntersecting) {
				if (typeof callback === "function") {
					callback(entries, observer);
				}
			}
		}, defaultOptions);
		if (Array.isArray(target)) {
			target.forEach((item) => {
				intersectionObserver.observe(item);
			});
		} else {
			intersectionObserver.observe(target);
		}
	}
	/**
	 * 去除全局window下的Utils,返回控制权
	 * @example
	 * let utils = Utils.noConflict();
	 * > ...
	 */
	noConflict() {
		if ((this.windowApi.window as any).Utils) {
			Reflect.deleteProperty(this.windowApi.window as any, "Utils");
		}
		(this.windowApi.window as any).Utils = utils;
		return utils;
	}
	/**
     * 恢复/释放该对象内的为function,让它无效/有效
     * @param needReleaseObject 需要操作的对象
     * @param needReleaseName 需要操作的对象的名字
     * @param functionNameList (可选)需要释放的方法,默认:全部方法
     * @param release (可选)
     * + true (默认) 释放该对象下的某些方法
     * + false 恢复该对象下的某些方法
     * @example
      // 释放该方法
      Utils.noConflictFunc(console,"console",["log"],true);
      console.log;
      > () => {}
  
     * @example
      // 恢复该方法
      Utils.noConflictFunc(console,"console",["log"],false);
      console.log;
      > ƒ log() { [native code] }
  
     * @example
      // 释放所有方法
      Utils.noConflictFunc(console,"console",[],true);
      console.debug;
      > () => {}
  
     * @example
      // 恢复所有方法
      Utils.noConflictFunc(console,"console",[],false);
      console.debug;
      > ƒ log() { [native code] }
     **/
	noConflictFunc(
		needReleaseObject: object,
		needReleaseName: string,
		functionNameList?: any[],
		release?: boolean
	): void;
	noConflictFunc(
		needReleaseObject: object,
		needReleaseName: string,
		functionNameList: any[] = [],
		release: boolean = true
	): void {
		let UtilsContext = this;
		if (typeof needReleaseObject !== "object") {
			throw new Error(
				"Utils.noConflictFunc 参数 needReleaseObject 必须为 object 类型"
			);
		}
		if (typeof needReleaseName !== "string") {
			throw new Error(
				"Utils.noConflictFunc 参数 needReleaseName 必须为 string 类型"
			);
		}
		if (!Array.isArray(functionNameList)) {
			throw new Error(
				"Utils.noConflictFunc 参数 functionNameList 必须为 Array 类型"
			);
		}
		let needReleaseKey = "__" + needReleaseName;
		/**
		 * 释放所有
		 */
		function releaseAll() {
			if (
				typeof (UtilsContext.windowApi.window as any)[needReleaseKey] !==
				"undefined"
			) {
				/* 已存在 */
				return;
			}
			(UtilsContext.windowApi.window as any)[needReleaseKey] =
				UtilsContext.deepClone(needReleaseObject);
			Object.values(needReleaseObject).forEach((value) => {
				if (typeof value === "function") {
					(needReleaseObject as any)[value.name] = () => {};
				}
			});
		}
		/**
		 * 释放单个
		 */
		function releaseOne() {
			Array.from(functionNameList).forEach((item) => {
				Object.values(needReleaseObject).forEach((value) => {
					if (typeof value === "function") {
						if (
							typeof (UtilsContext.windowApi.window as any)[needReleaseKey] ===
							"undefined"
						) {
							(UtilsContext.windowApi.window as any)[needReleaseKey] = {};
						}
						if (item === value.name) {
							(UtilsContext.windowApi.window as any)[needReleaseKey][
								value.name
							] = (needReleaseObject as any)[value.name];
							(needReleaseObject as any)[value.name] = () => {};
						}
					}
				});
			});
		}
		/**
		 * 恢复所有
		 */
		function recoveryAll() {
			if (
				typeof (UtilsContext.windowApi.window as any)[needReleaseKey] ===
				"undefined"
			) {
				/* 未存在 */
				return;
			}
			Object.assign(
				needReleaseObject,
				(UtilsContext.windowApi.window as any)[needReleaseKey]
			);
			Reflect.deleteProperty(
				UtilsContext.windowApi.window as any,
				"needReleaseKey"
			);
		}

		/**
		 * 恢复单个
		 */
		function recoveryOne() {
			if (
				typeof (UtilsContext.windowApi.window as any)[needReleaseKey] ===
				"undefined"
			) {
				/* 未存在 */
				return;
			}
			Array.from(functionNameList).forEach((item) => {
				if ((UtilsContext.windowApi.window as any)[needReleaseKey][item]) {
					(needReleaseObject as any)[item] = (
						UtilsContext.windowApi.window as any
					)[needReleaseKey][item];
					Reflect.deleteProperty(
						(UtilsContext.windowApi.window as any)[needReleaseKey],
						item
					);
					if (
						Object.keys((UtilsContext.windowApi.window as any)[needReleaseKey])
							.length === 0
					) {
						Reflect.deleteProperty(window, needReleaseKey);
					}
				}
			});
		}
		if (release) {
			/* 释放 */
			if (functionNameList.length === 0) {
				releaseAll();
			} else {
				/* 对单个进行操作 */
				releaseOne();
			}
		} else {
			/* 恢复 */
			if (functionNameList.length === 0) {
				recoveryAll();
			} else {
				/* 对单个进行操作 */
				recoveryOne();
			}
		}
	}
	/**
	 * base64转blob
	 * @param dataUri base64的数据
	 * @returns blob的链接
	 * @example
	 * Utils.parseBase64ToBlob("data:image/jpeg;base64,.....");
	 * > blob://xxxxxxx
	 **/
	parseBase64ToBlob(dataUri: string): Blob;
	parseBase64ToBlob(dataUri: string): Blob {
		if (typeof dataUri !== "string") {
			throw new Error(
				"Utils.parseBase64ToBlob 参数 dataUri 必须为 string 类型"
			);
		}
		let dataUriSplit = dataUri.split(","),
			dataUriMime = (dataUriSplit[0] as any).match(/:(.*?);/)[1],
			dataUriBase64Str = atob(dataUriSplit[1]),
			dataUriLength = dataUriBase64Str.length,
			u8arr = new Uint8Array(dataUriLength);
		while (dataUriLength--) {
			u8arr[dataUriLength] = dataUriBase64Str.charCodeAt(dataUriLength);
		}
		return new Blob([u8arr], {
			type: dataUriMime,
		});
	}
	/**
	 * base64转File对象
	 * @param dataUri base64的数据
	 * @param fileName (可选)文件名,默认:example
	 * @returns blob的链接
	 * @example
	 * Utils.parseBase64ToFile("data:image/jpeg;base64,.....","测试文件");
	 * > object
	 **/
	parseBase64ToFile(dataUri: string, fileName?: string): File;
	parseBase64ToFile(dataUri: string, fileName = "example") {
		if (typeof dataUri !== "string") {
			throw new Error(
				"Utils.parseBase64ToFile 参数 dataUri 必须为 string 类型"
			);
		}
		if (typeof fileName !== "string") {
			throw new Error(
				"Utils.parseBase64ToFile 参数 fileName 必须为 string 类型"
			);
		}
		let dataUriSplit = dataUri.split(","),
			dataUriMime = (dataUriSplit[0] as any).match(/:(.*?);/)[1],
			dataUriBase64Str = atob(dataUriSplit[1]),
			dataUriLength = dataUriBase64Str.length,
			u8arr = new Uint8Array(dataUriLength);
		while (dataUriLength--) {
			u8arr[dataUriLength] = dataUriBase64Str.charCodeAt(dataUriLength);
		}
		return new File([u8arr], fileName, {
			type: dataUriMime,
		});
	}
	/**
	 * 将正则匹配到的结果取出最后一个值并转换成int格式
	 * @param matchList 正则匹配的列表
	 * @param defaultValue 正则匹配的列表为空时,或者正则匹配的列表最后一项不为Int,返回该默认值0
	 * @example
	 * Utils.parseInt(["dadaadada123124","123124"],0);
	 * > 123124
	 *
	 * @example
	 * Utils.parseInt(null,0);
	 * > 0
	 * @example
	 * Utils.parseInt(["aaaaaa"]);
	 * > 0
	 *
	 * @example
	 * Utils.parseInt(["aaaaaa"],"66");
	 * > 66
	 *
	 * @example
	 * Utils.parseInt(["aaaaaaa"],"aa");
	 * > NaN
	 **/
	parseInt(
		matchList?: any[] | null | undefined | RegExpMatchArray,
		defaultValue?: number
	): number;
	parseInt(matchList: any[] = [], defaultValue = 0): number {
		if (matchList == null) {
			return parseInt(defaultValue.toString());
		}
		let parseValue = parseInt(matchList[matchList.length - 1]);
		if (isNaN(parseValue)) {
			parseValue = parseInt(defaultValue.toString());
		}
		return parseValue;
	}
	/**
	 * blob转File对象
	 * @param blobUrl 需要转换的blob的链接
	 * @param fileName (可选)转换成的File对象的文件名称,默认:example
	 * @example
	 * Utils.parseBlobToFile("blob://xxxxx");
	 * > object
	 **/
	parseBlobToFile(blobUrl: string, fileName?: string): Promise<File | Error>;
	async parseBlobToFile(
		blobUrl: string,
		fileName: string = "example"
	): Promise<File | Error> {
		return new Promise((resolve, reject) => {
			fetch(blobUrl)
				.then((response) => response.blob())
				.then((blob) => {
					const file = new File([blob], fileName, { type: blob.type });
					resolve(file);
				})
				.catch((error) => {
					console.error("Error:", error);
					reject(error);
				});
		});
	}
	/**
	 * 解析CDATA格式的内容字符串
	 * @param text 传入CDATA字符串
	 * @returns 返回解析出的内容
	 * @example
	 * let xml = "<root><![CDATA[This is some CDATA content.]]></root>";
	 * console.log(Utils.parseCDATA(xml));
	 * > This is some CDATA content.
	 */
	parseCDATA(text: string): string;
	parseCDATA(text: string = ""): string {
		let result = "";
		let cdataRegexp = /<\!\[CDATA\[([\s\S]*)\]\]>/;
		let cdataMatch = cdataRegexp.exec(text.trim());
		if (cdataMatch && cdataMatch.length > 1) {
			result = cdataMatch[cdataMatch.length - 1];
		}
		return result;
	}
	/**
	 * 【异步函数】File对象转base64
	 * @param fileObj 需要转换的File对象
	 * @example
	 * await Utils.parseFileToBase64(object);
	 * > 'data:image/jpeg:base64/,xxxxxx'
	 **/
	parseFileToBase64(fileObj: File): Promise<string>;
	async parseFileToBase64(fileObj: File): Promise<string> {
		let reader = new FileReader();
		reader.readAsDataURL(fileObj);
		return new Promise((resolve) => {
			reader.onload = function (event: any) {
				resolve(event.target.result as string);
			};
		});
	}
	/**
	 * 解析字符串
	 * @param text 要解析的 DOMString。它必须包含 HTML、xml、xhtml+xml 或 svg 文档。
	 * @param mimeType (可选)解析成的类型
	 * + (默认)text/html
	 * + text/xml
	 * + application/xml
	 * + application/xhtml+xml
	 * + image/svg+xml
	 * @example
	 * Utils.parseFromString("<p>123<p>");
	 * > #document
	 */
	parseFromString(
		text: string,
		mimeType?:
			| "text/html"
			| "text/xml"
			| "application/xml"
			| "application/xhtml+xml"
			| "image/svg+xml"
	): HTMLElement | XMLDocument | SVGElement;
	parseFromString(
		text: string,
		mimeType:
			| "text/html"
			| "text/xml"
			| "application/xml"
			| "application/xhtml+xml"
			| "image/svg+xml" = "text/html"
	): HTMLElement | XMLDocument | SVGElement {
		let parser = new DOMParser();
		return parser.parseFromString(text, mimeType);
	}
	/**
	 * 将字符串进行正则转义
	 * 例如:^替换$
	 * 转换:\^替换\$
	 */
	parseStringToRegExpString(text: string): string;
	parseStringToRegExpString(text: string): string {
		if (typeof text !== "string") {
			throw new TypeError("string必须是字符串");
		}
		let regString = text.replace(/[.*+\-?^${}()|[\]\\]/g, "\\$&");
		return regString;
	}
	/**
	 * 阻止事件传递
	 * @param element 要进行处理的元素
	 * @param eventNameList (可选)要阻止的事件名|列表
	 * @param capture (可选)是否捕获,默认false
	 * @example
	 * Utils.preventEvent(document.querySelector("a"),"click")
	 * @example
	 * Utils.preventEvent(event);
	 */
	preventEvent(event: Event): boolean;
	/**
	 * 阻止事件传递
	 * @param element 要进行处理的元素
	 * @param eventNameList (可选)要阻止的事件名|列表
	 * @param capture (可选)是否捕获,默认false
	 * @example
	 * Utils.preventEvent(document.querySelector("a"),"click")
	 * @example
	 * Utils.preventEvent(event);
	 */
	preventEvent(
		element: HTMLElement,
		eventNameList?: string | string[],
		capture?: boolean
	): boolean;
	preventEvent(
		element: HTMLElement | Event,
		eventNameList: string | string[] = [],
		capture?: boolean
	): boolean | undefined {
		function stopEvent(event: Event) {
			/* 阻止事件的默认行为发生。例如,当点击一个链接时,浏览器会默认打开链接的URL */
			event?.preventDefault();
			/* 停止事件的传播,阻止它继续向更上层的元素冒泡,事件将不会再传播给其他的元素 */
			event?.stopPropagation();
			/* 阻止事件传播,并且还能阻止元素上的其他事件处理程序被触发 */
			event?.stopImmediatePropagation();
			return false;
		}
		if (arguments.length === 1) {
			/* 直接阻止事件 */
			return stopEvent(arguments[0]);
		} else {
			/* 添加对应的事件来阻止触发 */
			if (typeof eventNameList === "string") {
				eventNameList = [eventNameList];
			}
			eventNameList.forEach((eventName) => {
				(element as HTMLElement).addEventListener(eventName, stopEvent, {
					capture: Boolean(capture),
				});
			});
		}
	}
	/**
     * 在canvas元素节点上绘制进度圆圈
     * @example
      let progress = new Utils.Process({canvasNode:document.querySelector("canvas")});
      progress.draw();
     * **/
	Progress = Progress;
	/**
	 * 劫持Event的isTrust为true,注入时刻,ducument-start
	 * @param isTrustValue (可选)让isTrusted为true
	 * @param filter (可选)过滤出需要的事件名,true为需要,false为不需要
	 * @example
	 * Utils.registerTrustClickEvent()
	 */
	registerTrustClickEvent(
		isTrustValue?: boolean,
		filter?: (typeName: string) => boolean
	): void;
	registerTrustClickEvent(
		isTrustValue: boolean = true,
		filter?: (typeName: string) => boolean
	): void {
		function trustEvent(event: Event) {
			return new Proxy(event, {
				get: function (target, property) {
					if (property === "isTrusted") {
						return isTrustValue;
					} else {
						return Reflect.get(target, property);
					}
				},
			});
		}
		if (filter == null) {
			filter = function (typeName) {
				return typeName === "click";
			};
		}
		const originalListener = EventTarget.prototype.addEventListener;
		EventTarget.prototype.addEventListener = function (...args) {
			let type = args[0];
			let callback = args[1];
			// @ts-ignore
			let options = args[2];
			if (filter(type)) {
				if (typeof callback === "function") {
					args[1] = function (event) {
						callback.call(this, trustEvent(event));
					};
				} else if (
					typeof callback === "object" &&
					"handleEvent" in (callback as any)
				) {
					let oldHandleEvent = (callback as any)["handleEvent"];

					(args[1] as any)["handleEvent"] = function (event: Event) {
						if (event == null) {
							return;
						}
						try {
							/* Proxy对象使用instanceof会报错 */
							event instanceof Proxy;
							oldHandleEvent.call(this, trustEvent(event));
						} catch (error) {
							// @ts-ignore
							event["isTrusted"] = isTrustValue;
						}
					};
				}
			}
			return originalListener.apply(this, args);
		};
	}
	/**
	 * 将数字进行正/负转换
	 * @param num 需要进行转换的数字
	 */
	reverseNumber(num: number): number;
	reverseNumber(num: number): number {
		let reversedNum = 0;
		let isNegative = false;

		if (num < 0) {
			isNegative = true;
			num = Math.abs(num);
		}

		while (num > 0) {
			reversedNum = reversedNum * 10 + (num % 10);
			num = Math.floor(num / 10);
		}

		return isNegative ? -reversedNum : reversedNum;
	}
	/**
	 * 将元素上的文本或元素使用光标进行选中
	 *
	 * 注意,如果设置startIndex和endIndex,且元素上并无可选则的坐标,那么会报错
	 * @param element 目标元素
	 * @param childTextNode 目标元素下的#text元素
	 * @param startIndex (可选)开始坐标,可为空
	 * @param endIndex (可选)结束坐标,可为空
	 */
	selectElementText(
		element: HTMLElement | Element | Node,
		childTextNode?: ChildNode,
		startIndex?: number,
		endIndex?: number
	): void;
	selectElementText(
		element: HTMLElement | Element | Node,
		childTextNode?: ChildNode,
		startIndex?: number,
		endIndex?: number
	): void {
		let range = this.windowApi.document.createRange();
		range.selectNodeContents(element);
		if (childTextNode) {
			if (childTextNode.nodeType !== Node.TEXT_NODE) {
				throw new TypeError("childTextNode必须是#text元素");
			}
			if (startIndex != null && endIndex != null) {
				range.setStart(childTextNode, startIndex);
				range.setEnd(childTextNode, endIndex);
			}
		}

		let selection = this.windowApi.globalThis.getSelection();
		if (selection) {
			selection.removeAllRanges();
			selection.addRange(range);
		}
	}
	/**
	 * 复制到剪贴板
	 * @param data 需要复制到剪贴板的文本
	 * @param info (可选)默认:text/plain
	 * @example
	 * Utils.setClip({1:2});
	 * > {"1":2}
	 * @example
	 * Utils.setClip( ()=>{
	 *   console.log(1)
	 * });
	 * > ()=>{console.log(1)}
	 * @example
	 * Utils.setClip("xxxx");
	 * > xxxx
	 * @example
	 * Utils.setClip("xxxx","html");
	 * > xxxx
	 * @example
	 * Utils.setClip("xxxx","text/plain");
	 * > xxxx
	 **/
	setClip(
		data: any,
		info?:
			| {
					type: string;
					mimetype: string;
			  }
			| string
	): Promise<boolean>;
	setClip(
		data: any,
		info:
			| {
					type: string;
					mimetype: string;
			  }
			| string = {
			type: "text",
			mimetype: "text/plain",
		}
	): Promise<boolean> {
		if (typeof data === "object") {
			if (data instanceof Element) {
				data = data.outerHTML;
			} else {
				data = JSON.stringify(data);
			}
		} else if (typeof data !== "string") {
			data = data.toString();
		}
		let textType = typeof info === "object" ? info.type : (info as string);
		if (textType.includes("html")) {
			textType = "text/html";
		} else {
			textType = "text/plain";
		}
		let UtilsContext = this;
		class UtilsClipboard {
			#resolve;
			#copyData;
			#copyDataType;
			constructor(
				resolve: (value: boolean | PromiseLike<boolean>) => void,
				copyData: any,
				copyDataType: string
			) {
				this.#resolve = resolve;
				this.#copyData = copyData;
				this.#copyDataType = copyDataType;
			}
			async init() {
				let copyStatus = false;
				// @ts-ignore
				let requestPermissionStatus = await this.requestClipboardPermission();
				if (
					this.hasClipboard() &&
					(this.hasClipboardWrite() || this.hasClipboardWriteText())
				) {
					try {
						copyStatus = await this.copyDataByClipboard();
					} catch (error) {
						console.error("复制失败,使用第二种方式,error👉", error);
						copyStatus = this.copyTextByTextArea();
					}
				} else {
					copyStatus = this.copyTextByTextArea();
				}
				this.#resolve(copyStatus);
				this.destroy();
			}
			destroy() {
				// @ts-ignore
				this.#resolve = null;
				// @ts-ignore
				this.#copyData = null;
				// @ts-ignore
				this.#copyDataType = null;
			}
			isText() {
				return this.#copyDataType.includes("text");
			}
			hasClipboard() {
				return navigator?.clipboard != null;
			}
			hasClipboardWrite() {
				return navigator?.clipboard?.write != null;
			}
			hasClipboardWriteText() {
				return navigator?.clipboard?.writeText != null;
			}
			/**
			 * 使用textarea和document.execCommand("copy")来复制文字
			 */
			copyTextByTextArea() {
				try {
					let copyElement =
						UtilsContext.windowApi.document.createElement("textarea");
					copyElement.value = this.#copyData;
					copyElement.setAttribute("type", "text");
					copyElement.setAttribute("style", "opacity:0;position:absolute;");
					copyElement.setAttribute("readonly", "readonly");
					UtilsContext.windowApi.document.body.appendChild(copyElement);
					copyElement.select();
					UtilsContext.windowApi.document.execCommand("copy");
					UtilsContext.windowApi.document.body.removeChild(copyElement);
					return true;
				} catch (error) {
					console.error("复制失败,error👉", error);
					return false;
				}
			}
			/**
			 * 申请剪贴板权限
			 */
			requestClipboardPermission() {
				return new Promise((resolve, reject) => {
					if (navigator.permissions && navigator.permissions.query) {
						navigator.permissions
							.query({
								// @ts-ignore
								name: "clipboard-write",
							})
							.then((permissionStatus) => {
								resolve(true);
							})
							.catch((error: TypeError) => {
								console.error([
									"申请剪贴板权限失败,尝试直接写入👉",
									error.message ?? error.name ?? error.stack,
								]);
								resolve(false);
							});
					} else {
						resolve(false);
					}
				});
			}
			/**
			 * 使用clipboard直接写入数据到剪贴板
			 */
			copyDataByClipboard(): Promise<boolean> {
				return new Promise((resolve, reject) => {
					if (this.isText()) {
						/* 只复制文字 */
						navigator.clipboard
							.writeText(this.#copyData)
							.then(() => {
								resolve(true);
							})
							.catch((error) => {
								reject(error);
							});
					} else {
						/* 可复制对象 */
						let textBlob = new Blob([this.#copyData], {
							type: this.#copyDataType,
						});
						navigator.clipboard
							.write([
								new ClipboardItem({
									[this.#copyDataType]: textBlob,
								}),
							])
							.then(() => {
								resolve(true);
							})
							.catch((error) => {
								reject(error);
							});
					}
				});
			}
		}
		return new Promise((resolve) => {
			const utilsClipboard = new UtilsClipboard(resolve, data, textType);
			if (UtilsContext.windowApi.document.hasFocus()) {
				utilsClipboard.init();
			} else {
				UtilsContext.windowApi.window.addEventListener(
					"focus",
					() => {
						utilsClipboard.init();
					},
					{ once: true }
				);
			}
		});
	}
	/**
	 * 【异步函数】等待N秒执行函数
	 * @param callback 待执行的函数(字符串)
	 * @param delayTime (可选)延时时间(ms),默认:0
	 * @example
	 * await Utils.setTimeout(()=>{}, 2500);
	 * > ƒ tryCatchObj() {}
	 * @example
	 * await Utils.setTimeout("()=>{console.log(12345)}", 2500);
	 * > ƒ tryCatchObj() {}
	 **/
	setTimeout(callback: (() => void) | string, delayTime?: number): Promise<any>;
	setTimeout(
		callback: (() => void) | string,
		delayTime: number = 0
	): Promise<any> {
		let UtilsContext = this;
		if (typeof callback !== "function" && typeof callback !== "string") {
			throw new TypeError(
				"Utils.setTimeout 参数 callback 必须为 function|string 类型"
			);
		}
		if (typeof delayTime !== "number") {
			throw new TypeError("Utils.setTimeout 参数 delayTime 必须为 number 类型");
		}
		return new Promise((resolve) => {
			setTimeout(() => {
				resolve(UtilsContext.tryCatch().run(callback));
			}, delayTime);
		});
	}
	/**
	 * 【异步函数】延迟xxx毫秒
	 * @param delayTime (可选)延时时间(ms),默认:0
	 * @example
	 * await Utils.sleep(2500)
	 **/
	sleep(delayTime?: number): Promise<void>;
	sleep(delayTime: number = 0): Promise<void> {
		if (typeof delayTime !== "number") {
			throw new Error("Utils.sleep 参数 delayTime 必须为 number 类型");
		}
		return new Promise((resolve) => {
			setTimeout(() => {
				resolve(void 0);
			}, delayTime);
		});
	}
	/**
	 * 向右拖动滑块
	 * @param selector 选择器|元素
	 * @param offsetX (可选)水平拖动长度,默认:window.innerWidth
	 * @example
	 * Utils.dragSlider("#xxxx");
	 * @example
	 * Utils.dragSlider("#xxxx",100);
	 */
	dragSlider(selector: string | Element | Node, offsetX?: number): void;
	dragSlider(
		selector: string | Element | Node,
		offsetX: number = this.windowApi.window.innerWidth
	): void {
		let UtilsContext = this;
		function initMouseEvent(
			eventName: string,
			offSetX: number,
			offSetY: number
		) {
			// @ts-ignore
			let win = typeof unsafeWindow === "undefined" ? globalThis : unsafeWindow;
			let mouseEvent =
				UtilsContext.windowApi.document.createEvent("MouseEvents");
			mouseEvent.initMouseEvent(
				eventName,
				true,
				true,
				win,
				0,
				offSetX,
				offSetY,
				offSetX,
				offSetY,
				false,
				false,
				false,
				false,
				0,
				null
			);
			return mouseEvent;
		}
		let sliderElement =
			typeof selector === "string"
				? this.windowApi.document.querySelector(selector)
				: selector;
		if (
			!(sliderElement instanceof Node) ||
			!(sliderElement instanceof Element)
		) {
			throw new Error("Utils.dragSlider 参数selector 必须为Node/Element类型");
		}
		let rect = sliderElement.getBoundingClientRect(),
			x0 = rect.x || rect.left,
			y0 = rect.y || rect.top,
			x1 = x0 + offsetX,
			y1 = y0;
		sliderElement.dispatchEvent(initMouseEvent("mousedown", x0, y0));
		sliderElement.dispatchEvent(initMouseEvent("mousemove", x1, y1));
		sliderElement.dispatchEvent(initMouseEvent("mouseleave", x1, y1));
		sliderElement.dispatchEvent(initMouseEvent("mouseout", x1, y1));
	}
	/**
	 * 使目标元素进入全屏
	 * @param element (可选)目标元素,默认:document.documentElement
	 * @param options (可选)配置,一般不用
	 * @example
	 * Utils.enterFullScreen();
	 */
	enterFullScreen(element: HTMLElement, options?: FullscreenOptions): void;
	enterFullScreen(
		element: HTMLElement = this.windowApi.document.documentElement,
		options?: FullscreenOptions
	): void {
		try {
			if (element.requestFullscreen) {
				element.requestFullscreen(options);
			} else if ((element as any).webkitRequestFullScreen) {
				(element as any).webkitRequestFullScreen();
			} else if ((element as any).mozRequestFullScreen) {
				(element as any).mozRequestFullScreen();
			} else if ((element as any).msRequestFullscreen) {
				(element as any).msRequestFullscreen();
			} else {
				throw new TypeError("该浏览器不支持全屏API");
			}
		} catch (err) {
			console.error(err);
		}
	}
	/**
	 * 使浏览器退出全屏
	 * @param element (可选)目标元素,默认:document.documentElement
	 * @example
	 * Utils.exitFullScreen();
	 */
	exitFullScreen(element?: HTMLElement): Promise<void>;
	exitFullScreen(
		element: HTMLElement = this.windowApi.document.documentElement
	): Promise<void> {
		if (this.windowApi.document.exitFullscreen) {
			return this.windowApi.document.exitFullscreen();
		} else if ((this.windowApi.document as any).msExitFullscreen) {
			return (this.windowApi.document as any).msExitFullscreen();
		} else if ((this.windowApi.document as any).mozCancelFullScreen) {
			return (this.windowApi.document as any).mozCancelFullScreen();
		} else if ((this.windowApi.document as any).webkitCancelFullScreen) {
			return (this.windowApi.document as any).webkitCancelFullScreen();
		} else {
			return new Promise((resolve, reject) => {
				reject(new TypeError("该浏览器不支持全屏API"));
			});
		}
	}
	/**
	 * 数组按照内部某个值的大小比对排序,如[{"time":"2022-1-1"},{"time":"2022-2-2"}]
	 * @param data 数据|获取数据的方法
	 * @param getPropertyValueFunc 数组内部项的某个属性的值的方法,参数为这个项
	 * @param sortByDesc (可选)排序方式
	 * + true (默认)倒序(值最大排第一个,如:6、5、4、3...)
	 * + false 升序(值最小排第一个,如:1、2、3、4...)
	 * @returns 返回比较排序完成的数组
	 * @example
	 * Utils.sortListByProperty([{"time":"2022-1-1"},{"time":"2022-2-2"}],(item)=>{return item["time"]})
	 * > [{time: '2022-2-2'},{time: '2022-1-1'}]
	 * @example
	 * Utils.sortListByProperty([{"time":"2022-1-1"},{"time":"2022-2-2"}],(item)=>{return item["time"]},false)
	 * > [{time: '2022-1-1'},{time: '2022-2-2'}]
	 **/
	sortListByProperty<T extends any>(
		data: T[],
		getPropertyValueFunc: string | ((value: T) => any),
		sortByDesc?: boolean
	): T[];
	sortListByProperty<T extends any>(
		data: T[],
		getPropertyValueFunc: string | ((value: T) => any),
		sortByDesc: boolean = true
	): T[] {
		let UtilsContext = this;
		if (
			typeof getPropertyValueFunc !== "function" &&
			typeof getPropertyValueFunc !== "string"
		) {
			throw new Error(
				"Utils.sortListByProperty 参数 getPropertyValueFunc 必须为 function|string 类型"
			);
		}
		if (typeof sortByDesc !== "boolean") {
			throw new Error(
				"Utils.sortListByProperty 参数 sortByDesc 必须为 boolean 类型"
			);
		}
		let getObjValue = function (obj: any) {
			return typeof getPropertyValueFunc === "string"
				? obj[getPropertyValueFunc]
				: getPropertyValueFunc(obj);
		};
		/**
		 * 排序方法
		 * @param {any} after_obj
		 * @param {any} before_obj
		 * @returns
		 */
		let sortFunc = function (after_obj: any, before_obj: any) {
			let beforeValue = getObjValue(before_obj); /*  前 */
			let afterValue = getObjValue(after_obj); /* 后 */
			if (sortByDesc) {
				if (afterValue > beforeValue) {
					return -1;
				} else if (afterValue < beforeValue) {
					return 1;
				} else {
					return 0;
				}
			} else {
				if (afterValue < beforeValue) {
					return -1;
				} else if (afterValue > beforeValue) {
					return 1;
				} else {
					return 0;
				}
			}
		};
		/**
		 * 排序元素方法
		 * @param  nodeList 元素列表
		 * @param getNodeListFunc 获取元素列表的函数
		 */
		let sortNodeFunc = function (
			nodeList: NodeListOf<HTMLElement>,
			getNodeListFunc: () => NodeListOf<HTMLElement>
		) {
			let nodeListLength = nodeList.length;
			for (let i = 0; i < nodeListLength - 1; i++) {
				for (let j = 0; j < nodeListLength - 1 - i; j++) {
					let beforeNode = nodeList[j];
					let afterNode = nodeList[j + 1];
					let beforeValue = getObjValue(beforeNode); /*  前 */
					let afterValue = getObjValue(afterNode); /* 后 */
					if (
						(sortByDesc == true && beforeValue < afterValue) ||
						(sortByDesc == false && beforeValue > afterValue)
					) {
						/* 升序/降序 */
						/* 相邻元素两两对比 */
						let temp = beforeNode.nextElementSibling;
						afterNode.after(beforeNode);
						if (temp == null) {
							/* 如果为空,那么是最后一个元素,使用append */
							(temp as any).parentNode.appendChild(afterNode);
						} else {
							/* 不为空,使用before */
							temp.before(afterNode);
						}
						nodeList = getNodeListFunc();
					}
				}
			}
		};
		let result = data;
		let getDataFunc = null;
		if (data instanceof Function) {
			getDataFunc = data;
			data = (data as any)();
		}
		if (Array.isArray(data)) {
			data.sort(sortFunc);
		} else if (
			(data as any) instanceof NodeList ||
			UtilsContext.isJQuery(data)
		) {
			sortNodeFunc(data as any, getDataFunc as any);
			result = (getDataFunc as any)();
		} else {
			throw new Error(
				"Utils.sortListByProperty 参数 data 必须为 Array|NodeList|jQuery 类型"
			);
		}
		return result;
	}
	/**
	 * 字符串转正则,用于把字符串中不规范的字符进行转义
	 * @param targetString 需要进行转换的字符串
	 * @param flags 正则标志
	 */
	stringToRegular(
		targetString: string | RegExp,
		flags?: "g" | "i" | "m" | "u" | "y" | string
	): RegExp;
	stringToRegular(
		targetString: string | RegExp,
		flags: "g" | "i" | "m" | "u" | "y" | string = "ig"
	): RegExp {
		let reg;
		// @ts-ignore
		flags = flags.toLowerCase();
		if (typeof targetString === "string") {
			reg = new RegExp(
				targetString.replace(/[.*+\-?^${}()|[\]\\]/g, "\\$&"),
				flags
			);
		} else if ((targetString as any) instanceof RegExp) {
			reg = targetString;
		} else {
			throw new Error(
				"Utils.stringToRegular 参数targetString必须是string|Regexp类型"
			);
		}
		return reg;
	}
	/**
	 * 字符串首字母转大写
	 * @param targetString 目标字符串
	 * @param otherStrToLowerCase (可选)剩余部分字符串转小写,默认false
	 */
	stringTitleToUpperCase(
		targetString: string,
		otherStrToLowerCase?: boolean
	): string;
	stringTitleToUpperCase(
		targetString: string,
		otherStrToLowerCase: boolean = false
	): string {
		let newTargetString = targetString.slice(0, 1).toUpperCase();
		if (otherStrToLowerCase) {
			newTargetString = newTargetString + targetString.slice(1).toLowerCase();
		} else {
			newTargetString = newTargetString + targetString.slice(1);
		}
		return newTargetString;
	}
	/**
	 * 判断目标字符串是否是以xxx开始
	 *
	 * 如果searchString是字符串数组,那么判断的结果则是字符串数组中的任意字符匹配到返回true
	 * @param target 目标字符串
	 * @param searchString 需要搜索的字符串
	 * @param position (可选)目标字符串的判断起点,要求≥0,默认为0
	 */
	startsWith(
		target: string,
		searchString: string | RegExp | string[],
		position?: number
	): boolean;
	startsWith(
		target: string,
		searchString: string | RegExp | string[],
		position: number = 0
	): boolean {
		let UtilsContext = this;
		if (position > target.length) {
			/* 超出目标字符串的长度 */
			return false;
		}
		if (position !== 0) {
			target = target.slice(position, target.length);
		}
		let searchStringRegexp = searchString;
		if (typeof searchString === "string") {
			searchStringRegexp = new RegExp(`^${searchString}`);
		} else if (Array.isArray(searchString)) {
			let flag = false;
			for (const searcStr of searchString) {
				if (!UtilsContext.startsWith(target, searcStr, position)) {
					flag = true;
					break;
				}
			}
			return flag;
		}
		return Boolean(target.match(searchStringRegexp as RegExp));
	}
	/**
	 * 字符串首字母转小写
	 * @param targetString 目标字符串
	 * @param otherStrToLowerCase (可选)剩余部分字符串转大写,默认false
	 */
	stringTitleToLowerCase(
		targetString: string,
		otherStrToUpperCase?: boolean
	): string;
	stringTitleToLowerCase(
		targetString: string,
		otherStrToUpperCase: boolean = false
	): string {
		let newTargetString = targetString.slice(0, 1).toLowerCase();
		if (otherStrToUpperCase) {
			newTargetString = newTargetString + targetString.slice(1).toUpperCase();
		} else {
			newTargetString = newTargetString + targetString.slice(1);
		}
		return newTargetString;
	}
	/**
	 * 字符串转Object对象,类似'{"test":""}' => {"test":""}
	 * @param data
	 * @param errorCallBack (可选)错误回调
	 * @example
	 * Utils.toJSON("{123:123}")
	 * > {123:123}
	 */
	toJSON<T = any>(
		data: string | null,
		errorCallBack?: (error: Error) => void
	): T;
	toJSON<T = any>(
		data: string | null,
		errorCallBack?: (error: Error) => void
	): T {
		let UtilsContext = this;
		let result: any = {};
		if (typeof data === "object") {
			return data as any;
		}
		UtilsContext.tryCatch()
			.config({ log: false })
			.error((error: Error) => {
				UtilsContext.tryCatch()
					.error(() => {
						try {
							result = (UtilsContext.windowApi.window as any).eval(
								"(" + data + ")"
							);
						} catch (error2: any) {
							if (typeof errorCallBack === "function") {
								errorCallBack(error2);
							}
						}
					})
					.run(() => {
						if (
							data &&
							/^[\],:{}\s]*$/.test(
								data
									.replace(/\\(?:["\\\/bfnrt]|u[0-9a-fA-F]{4})/g, "@")
									.replace(
										/"[^"\\\n\r]*"|true|false|null|-?\d+(?:\.\d*)?(?:[eE][+\-]?\d+)?/g,
										"]"
									)
									.replace(/(?:^|:|,)(?:\s*\[)+/g, "")
							)
						) {
							result = new Function("return " + data)();
						} else {
							if (typeof errorCallBack === "function") {
								errorCallBack(new Error("target is not a JSON"));
							}
						}
					});
			})
			.run(() => {
				data = (data as string).trim();
				result = JSON.parse(data);
			});
		return result as any;
	}
	/**
	 * 对象转为UrlSearchParams格式的字符串
	 * @param obj 目标对象,可以是对象组成的数组
	 * @param addPrefix 是否添加前缀?
	 * @example
	 * Utils.toSearchParamsStr({
	 *   "test": 1,
	 *   "test2": 2
	 * })
	 * > test=1&test2=2
	 * @example
	 * Utils.toSearchParamsStr([{
	 *   "test": 1,
	 *   "test2": 2
	 * },
	 * {
	 *   "test3": 3
	 * }
	 * ])
	 * > test=1&test2=2&test3=3
	 */
	toSearchParamsStr(obj: object | object[], addPrefix?: boolean): string;
	toSearchParamsStr(obj: object | object[], addPrefix?: boolean): string {
		let UtilsContext = this;
		let searhParamsStr = "";
		if (Array.isArray(obj)) {
			obj.forEach((item) => {
				if (searhParamsStr === "") {
					searhParamsStr += UtilsContext.toSearchParamsStr(item);
				} else {
					searhParamsStr += "&" + UtilsContext.toSearchParamsStr(item);
				}
			});
		} else {
			searhParamsStr = new URLSearchParams(Object.entries(obj)).toString();
		}
		if (addPrefix && !searhParamsStr.startsWith("?")) {
			searhParamsStr = "?" + searhParamsStr;
		}
		return searhParamsStr;
	}
	/**
	 * 将UrlSearchParams格式的字符串转为对象
	 */
	searchParamStrToObj<T extends any>(
		searhParamsStr?: string | null | undefined
	): T {
		if (typeof searhParamsStr !== "string") {
			// @ts-ignore
			return {};
		}
		// @ts-ignore
		return Object.fromEntries(new URLSearchParams(searhParamsStr));
	}
	/**
	 * 提供一个封装了 try-catch 的函数,可以执行传入的函数并捕获其可能抛出的错误,并通过传入的错误处理函数进行处理。
	 * @example
	 * Utils.tryCatch().error().run(()=>{console.log(1)});
	 * > 1
	 * @example
	 * Utils.tryCatch().config({log:true}).error((error)=>{console.log(error)}).run(()=>{throw new Error('测试错误')});
	 * > ()=>{throw new Error('测试错误')}出现错误
	 */
	tryCatch = TryCatch;
	/**
	 * 数组去重,去除重复的值
	 * @param uniqueArrayData 需要去重的数组
	 * @param compareArrayData 用来比较的数组
	 * @param compareFun 数组比较方法,如果值相同,去除该数据
	 * @returns 返回去重完毕的数组
	 * @example
	 * Utils.uniqueArray([1,2,3],[1,2],(item,item2)=>{return item===item2 ? true:false});
	 * > [3]
	 *
	 * @example
	 * Utils.uniqueArray([1,2,3],[1,2]);
	 * > [3]
	 *
	 * @example
	 * Utils.uniqueArray([{"key":1,"value":2},{"key":2}],[{"key":1}],(item,item2)=>{return item["key"] === item2["key"] ? true:false});
	 * > [{"key": 2}]
	 **/
	uniqueArray<T extends any, TT extends any>(
		uniqueArrayData?: T[],
		compareArrayData?: TT[],
		compareFun?: (item1: T, item2: TT) => boolean
	): any[];
	/**
	 * 数组去重,去除不需要的值
	 * @param uniqueArrayData 需要过滤的数组
	 * @param getIdentfierValue 获取用于确定唯一性的值
	 * @example
	 * Utils.uniqueArray([{name:"1",host:"baidu.com"},{name:"2",host:"baidu.com"},{name:"3",host:"baidu.com"}]);
	 * > [{name:"1",host:"baidu.com"}]
	 */
	uniqueArray<T>(
		uniqueArrayData: T[],
		getIdentfierValue: (itemValue: T) => any
	): T[];
	uniqueArray<T, T2>(
		uniqueArrayData: T[] = [],
		compareArrayData: any,
		compareFun: any = (item: any, item2: any) => {
			return item === item2;
		}
	): any[] {
		if (typeof compareArrayData === "function") {
			const compareFn = compareArrayData;
			const seen = new Set();

			const result: T[] = [];
			for (const item of uniqueArrayData) {
				// 使用compareFn函数来获取当前对象的唯一标识
				const identfier = compareFn(item);
				// 如果Set中还没有这个标识,则添加到结果数组中,并将其标识存入Set
				if (!seen.has(identfier)) {
					seen.add(identfier);
					result.push(item);
				}
			}
			return result;
		} else {
			return Array.from(uniqueArrayData).filter(
				(item) =>
					!Array.from(compareArrayData).some(function (item2) {
						return compareFun(item, item2);
					})
			);
		}
	}
	/**
	 * 等待函数数组全部执行完毕,注意,每个函数的顺序不是同步
	 * @param data 需要遍历的数组
	 * @param handleFunc 对该数组进行操作的函数,该函数的参数为数组格式的参数,[数组下标,数组项]
	 * @example
	 * await Utils.waitArrayLoopToEnd([callback,callback,callback],xxxcallback);
	 **/
	waitArrayLoopToEnd(
		data: any[] | HTMLElement[],
		handleFunc: Function
	): Promise<void[]>;
	waitArrayLoopToEnd(
		data: any[] | HTMLElement[],
		handleFunc: Function
	): Promise<void[]> {
		let UtilsContext = this;
		if (typeof handleFunc !== "function" && typeof handleFunc !== "string") {
			throw new Error(
				"Utils.waitArrayLoopToEnd 参数 handleDataFunction 必须为 function|string 类型"
			);
		}
		return Promise.all(
			Array.from(data).map(async (item, index) => {
				await UtilsContext.tryCatch(index, item).run(handleFunc);
			})
		);
	}
	/**
	 * 等待任意事件成立
	 *
	 * 运行方式为根据页面元素的改变而触发回调
	 * @param checkFn 检测的函数
	 * @param timeout 超时时间,默认0
	 * @param parent (可选)父元素,默认document
	 * @example
	 * Utils.wait(()=> {
	 *   let $test = document.querySelector("#test");
	 *   return {
	 *     success: $test !== null,
	 *     data:  $test
	 *   }
	 * })
	 */
	wait<T extends any>(
		checkFn: (...args: any[]) => {
			/**
			 * 是否检测成功
			 */
			success: boolean;
			/**
			 * 返回的值
			 */
			data: T;
		},
		timeout?: null | undefined,
		parent?: Node | Element | Document | HTMLElement
	): Promise<T>;
	wait<T extends any>(
		checkFn: (...args: any[]) => {
			/**
			 * 是否检测成功
			 */
			success: boolean;
			/**
			 * 返回的值
			 */
			data: T;
		},
		timeout?: number,
		parent?: Node | Element | Document | HTMLElement
	): Promise<T | null>;
	wait<T extends any>(
		checkFn: (...args: any[]) => {
			/**
			 * 是否检测成功
			 */
			success: boolean;
			/**
			 * 返回的值
			 */
			data: T;
		},
		timeout?: number | null | undefined,
		parent?: Node | Element | Document | HTMLElement
	): Promise<T | null> {
		const UtilsContext = this;
		let __timeout__ = typeof timeout === "number" ? timeout : 0;
		return new Promise((resolve) => {
			let observer = UtilsContext.mutationObserver(
				parent || UtilsContext.windowApi.document,
				{
					config: {
						subtree: true,
						childList: true,
						attributes: true,
					},
					immediate: true,
					callback(mutations, __observer__) {
						let result = checkFn();
						if (result.success) {
							// 取消观察器
							if (typeof __observer__?.disconnect === "function") {
								__observer__.disconnect();
							}
							resolve(result.data);
						}
					},
				}
			);
			if (__timeout__ > 0) {
				setTimeout(() => {
					// 取消观察器
					if (typeof observer?.disconnect === "function") {
						observer.disconnect();
					}
					resolve(null as T);
				}, __timeout__);
			}
		});
	}
	/**
	 * 等待元素出现
	 * @param selectorFn 获取元素的函数
	 * @param timeout 超时时间,默认0
	 * @example
	 * Utils.waitNode(()=>document.querySelector("div"), 1000).then( $div =>{
	 *  console.log($div); // $div => HTMLDivELement | null
	 * })
	 */
	waitNode<K extends any>(selectorFn: () => K | null | undefined): Promise<K>;
	waitNode<K extends any>(
		selectorFn: () => K | null | undefined,
		timeout: number
	): Promise<K | null | undefined>;
	/**
	 * 等待元素出现
	 * @param selector CSS选择器
	 * @param parent (可选)父元素,默认document
	 * @example
	 * Utils.waitNode("div").then( $div =>{
	 *  console.log($div); // div => HTMLDivELement
	 * })
	 * Utils.waitNode("div", document).then( $div =>{
	 *  console.log($div); // div => HTMLDivELement
	 * })
	 */
	waitNode<K extends keyof HTMLElementTagNameMap>(
		selector: K,
		parent?: Node | Element | Document | HTMLElement
	): Promise<HTMLElementTagNameMap[K]>;
	waitNode<T extends Element>(
		selector: string,
		parent?: Node | Element | Document | HTMLElement
	): Promise<T>;
	/**
	 * 等待元素出现
	 * @param selectorList CSS选择器数组
	 * @param parent (可选)父元素,默认document
	 * @example
	 * Utils.waitNode(["div"]).then( ([$div]) =>{
	 *  console.log($div); // div => HTMLDivELement[]
	 * })
	 * Utils.waitNode(["div"], document).then( ([$div]) =>{
	 *  console.log($div); // div => HTMLDivELement[]
	 * })
	 */
	waitNode<K extends keyof HTMLElementTagNameMap>(
		selectorList: K[],
		parent?: Node | Element | Document | HTMLElement
	): Promise<HTMLElementTagNameMap[K][]>;
	waitNode<T extends Element[]>(
		selectorList: string[],
		parent?: Node | Element | Document | HTMLElement
	): Promise<T>;
	/**
	 * 等待元素出现
	 * @param selector CSS选择器
	 * @param parent 父元素,默认document
	 * @param timeout 超时时间,默认0
	 * @example
	 * Utils.waitNode("div", document, 1000).then( $div =>{
	 *  console.log($div); // $div => HTMLDivELement | null
	 * })
	 */
	waitNode<K extends keyof HTMLElementTagNameMap>(
		selector: K,
		parent: Node | Element | Document | HTMLElement,
		timeout: number
	): Promise<HTMLElementTagNameMap[K] | null>;
	waitNode<T extends Element>(
		selector: string,
		parent: Node | Element | Document | HTMLElement,
		timeout: number
	): Promise<T | null>;
	/**
	 * 等待元素出现
	 * @param selectorList CSS选择器数组
	 * @param parent 父元素,默认document
	 * @param timeout 超时时间,默认0
	 * @example
	 * Utils.waitNode(["div"], document, 1000).then( ([$div]) =>{
	 *  console.log($div); // $div => HTMLDivELement[] | null
	 * })
	 */
	waitNode<K extends keyof HTMLElementTagNameMap>(
		selectorList: K[],
		parent: Node | Element | Document | HTMLElement,
		timeout: number
	): Promise<HTMLElementTagNameMap[K] | null>;
	waitNode<T extends Element[]>(
		selectorList: string[],
		parent: Node | Element | Document | HTMLElement,
		timeout: number
	): Promise<T | null>;
	/**
	 * 等待元素出现
	 * @param selector CSS选择器
	 * @param timeout 超时时间,默认0
	 * @example
	 * Utils.waitNode("div", 1000).then( $div =>{
	 *  console.log($div); // $div => HTMLDivELement | null
	 * })
	 */
	waitNode<K extends keyof HTMLElementTagNameMap>(
		selector: K,
		timeout: number
	): Promise<HTMLElementTagNameMap[K] | null>;
	waitNode<T extends Element>(
		selector: string,
		timeout: number
	): Promise<T | null>;
	/**
	 * 等待元素出现
	 * @param selectorList CSS选择器数组
	 * @param timeout 超时时间,默认0
	 * @example
	 * Utils.waitNode(["div"], 1000).then( [$div] =>{
	 *  console.log($div); // $div => HTMLDivELement[] | null
	 * })
	 */
	waitNode<K extends keyof HTMLElementTagNameMap>(
		selectorList: K[],
		timeout: number
	): Promise<HTMLElementTagNameMap[K] | null>;
	waitNode<T extends Element[]>(
		selectorList: string[],
		timeout: number
	): Promise<T | null>;
	waitNode<T extends Element | Element[]>(...args: any[]): Promise<T | null> {
		// 过滤掉undefined
		args = args.filter((arg) => arg !== void 0);
		let UtilsContext = this;
		// 选择器
		let selector = args[0] as unknown as string | string[] | Function;
		// 父元素(监听的元素)
		let parent: Element = UtilsContext.windowApi.document as any as Element;
		// 超时时间
		let timeout = 0;
		if (
			typeof args[0] !== "string" &&
			!Array.isArray(args[0]) &&
			typeof args[0] !== "function"
		) {
			throw new TypeError(
				"Utils.waitNode 第一个参数必须是string|string[]|Function"
			);
		}
		if (args.length === 1) {
			// 上面已做处理
		} else if (args.length === 2) {
			let secondParam = args[1];
			if (typeof secondParam === "number") {
				// "div",10000
				timeout = secondParam;
			} else if (
				typeof secondParam === "object" &&
				secondParam instanceof Node
			) {
				// "div",document
				parent = secondParam as any as Element;
			} else {
				throw new TypeError("Utils.waitNode 第二个参数必须是number|Node");
			}
		} else if (args.length === 3) {
			// "div",document,10000
			// 第二个参数,parent
			let secondParam = args[1];
			// 第三个参数,timeout
			let thirdParam = args[2];
			if (typeof secondParam === "object" && secondParam instanceof Node) {
				parent = secondParam as any as Element;
				if (typeof thirdParam === "number") {
					timeout = thirdParam;
				} else {
					throw new TypeError("Utils.waitNode 第三个参数必须是number");
				}
			} else {
				throw new TypeError("Utils.waitNode 第二个参数必须是Node");
			}
		} else {
			throw new TypeError("Utils.waitNode 参数个数错误");
		}
		function getNode() {
			if (Array.isArray(selector)) {
				let result: T[] = [];
				for (let index = 0; index < selector.length; index++) {
					let node = parent.querySelector(selector[index]);
					if (node) {
						result.push(node as any);
					}
				}
				if (result.length === selector.length) {
					return result;
				}
			} else if (typeof selector === "function") {
				return selector();
			} else {
				return parent.querySelector(selector);
			}
		}
		return UtilsContext.wait(
			() => {
				let node = getNode();
				if (node) {
					return {
						success: true,
						data: node,
					};
				} else {
					return {
						success: false,
						data: node,
					};
				}
			},
			timeout,
			parent
		);
	}
	/**
	 * 等待任意元素出现
	 * @param selectorList CSS选择器数组
	 * @param parent (可选)监听的父元素
	 * @example
	 * Utils.waitAnyNode(["div","div"]).then( $div =>{
	 *  console.log($div); // $div => HTMLDivELement 这里是第一个
	 * })
	 * Utils.waitAnyNode(["a","div"], document).then( $a =>{
	 *  console.log($a); // $a => HTMLAnchorElement 这里是第一个
	 * })
	 */
	waitAnyNode<K extends keyof HTMLElementTagNameMap>(
		selectorList: K[],
		parent?: Node | Element | Document | HTMLElement
	): Promise<HTMLElementTagNameMap[K]>;
	waitAnyNode<T extends Element>(
		selectorList: string[],
		parent?: Node | Element | Document | HTMLElement
	): Promise<T>;
	/**
	 * 等待任意元素出现
	 * @param selectorList CSS选择器数组
	 * @param parent 父元素,默认document
	 * @param timeout 超时时间,默认0
	 * @example
	 * Utils.waitAnyNode(["div","div"], document, 10000).then( $div =>{
	 *  console.log($div); // $div => HTMLDivELement | null
	 * })
	 */
	waitAnyNode<K extends keyof HTMLElementTagNameMap>(
		selectorList: K[],
		parent: Node | Element | Document | HTMLElement,
		timeout: number
	): Promise<HTMLElementTagNameMap[K] | null>;
	waitAnyNode<T extends Element>(
		selectorList: string[],
		parent: Node | Element | Document | HTMLElement,
		timeout: number
	): Promise<T | null>;
	/**
	 * 等待任意元素出现
	 * @param selectorList CSS选择器数组
	 * @param timeout 超时时间,默认0
	 * @example
	 * Utils.waitAnyNode(["div","div"], 10000).then( $div =>{
	 *  console.log($div); // $div => HTMLDivELement | null
	 * })
	 */
	waitAnyNode<K extends keyof HTMLElementTagNameMap>(
		selectorList: K[],
		timeout: number
	): Promise<HTMLElementTagNameMap[K] | null>;
	waitAnyNode<T extends Element>(
		selectorList: string[],
		timeout: number
	): Promise<T | null>;
	waitAnyNode<T extends Element>(...args: any[]): Promise<T | null> {
		// 过滤掉undefined
		args = args.filter((arg) => arg !== void 0);
		let UtilsContext = this;
		// 选择器
		let selectorList = args[0] as unknown as string[];
		// 父元素(监听的元素)
		let parent: Element = UtilsContext.windowApi.document as any as Element;
		// 超时时间
		let timeout = 0;
		if (typeof args[0] !== "object" && !Array.isArray(args[0])) {
			throw new TypeError("Utils.waitAnyNode 第一个参数必须是string[]");
		}
		if (args.length === 1) {
			// 上面已做处理
		} else if (args.length === 2) {
			let secondParam = args[1];
			if (typeof secondParam === "number") {
				// "div",10000
				timeout = secondParam;
			} else if (
				typeof secondParam === "object" &&
				secondParam instanceof Node
			) {
				// "div",document
				parent = secondParam as any as Element;
			} else {
				throw new TypeError("Utils.waitAnyNode 第二个参数必须是number|Node");
			}
		} else if (args.length === 3) {
			// "div",document,10000
			// 第二个参数,parent
			let secondParam = args[1];
			// 第三个参数,timeout
			let thirdParam = args[2];
			if (typeof secondParam === "object" && secondParam instanceof Node) {
				parent = secondParam as any as Element;
				if (typeof thirdParam === "number") {
					timeout = thirdParam;
				} else {
					throw new TypeError("Utils.waitAnyNode 第三个参数必须是number");
				}
			} else {
				throw new TypeError("Utils.waitAnyNode 第二个参数必须是Node");
			}
		} else {
			throw new TypeError("Utils.waitAnyNode 参数个数错误");
		}
		let promiseList = selectorList.map((selector) => {
			return UtilsContext.waitNode<T>(selector, parent, timeout);
		});
		return Promise.any(promiseList);
	}
	/**
	 * 等待元素数组出现
	 * @param selector CSS选择器
	 * @param parent (可选)监听的父元素
	 * @example
	 * Utils.waitNodeList("div").then( $result =>{
	 *  console.log($result); // $result => NodeListOf<HTMLDivElement>
	 * })
	 * Utils.waitNodeList("div", document).then( $result =>{
	 *  console.log($result); // $result => NodeListOf<HTMLDivElement>
	 * })
	 */
	waitNodeList<T extends keyof HTMLElementTagNameMap>(
		selector: T,
		parent?: Node | Element | Document | HTMLElement
	): Promise<NodeListOf<HTMLElementTagNameMap[T]>>;
	waitNodeList<T extends NodeListOf<Element>>(
		selector: string,
		parent?: Node | Element | Document | HTMLElement
	): Promise<T>;
	/**
	 * 等待元素数组出现
	 * @param selectorList CSS选择器数组
	 * @param parent (可选)监听的父元素
	 * @example
	 * Utils.waitNodeList(["div"]).then( $result =>{
	 *  console.log($result); // $result => NodeListOf<HTMLDivElement>[]
	 * })
	 * Utils.waitNodeList(["div"], document).then( $result =>{
	 *  console.log($result); // $result => NodeListOf<HTMLDivElement>[]
	 * })
	 */
	waitNodeList<K extends keyof HTMLElementTagNameMap>(
		selectorList: K[],
		parent?: Node | Element | Document | HTMLElement
	): Promise<NodeListOf<HTMLElementTagNameMap[K]>[]>;
	waitNodeList<T extends NodeListOf<Element>[]>(
		selectorList: string[],
		parent?: Node | Element | Document | HTMLElement
	): Promise<T>;
	/**
	 * 等待元素数组出现
	 * @param selector CSS选择器
	 * @param parent 监听的父元素
	 * @param timeout 超时时间,默认0
	 * @example
	 * Utils.waitNodeList("div", document, 10000).then( $result =>{
	 *  console.log($result); // $result => NodeListOf<HTMLDivElement> | null
	 * })
	 */
	waitNodeList<T extends NodeListOf<Element>>(
		selector: string,
		parent: Node | Element | Document | HTMLElement,
		timeout: number
	): Promise<T | null>;
	waitNodeList<K extends keyof HTMLElementTagNameMap>(
		selector: K,
		parent: Node | Element | Document | HTMLElement,
		timeout: number
	): Promise<NodeListOf<HTMLElementTagNameMap[K]> | null>;
	/**
	 * 等待元素数组出现
	 * @param selectorList CSS选择器数组
	 * @param parent 监听的父元素
	 * @param timeout 超时时间,默认0
	 * @example
	 * Utils.waitNodeList(["div"], document, 10000).then( $result =>{
	 *  console.log($result); // $result => NodeListOf<HTMLDivElement>[] | null
	 * })
	 */
	waitNodeList<K extends keyof HTMLElementTagNameMap>(
		selectorList: K[],
		parent: Node | Element | Document | HTMLElement,
		timeout: number
	): Promise<NodeListOf<HTMLElementTagNameMap[K]>[] | null>;
	waitNodeList<T extends NodeListOf<Element>[]>(
		selectorList: string[],
		parent: Node | Element | Document | HTMLElement,
		timeout: number
	): Promise<T | null>;
	/**
	 * 等待元素数组出现
	 * @param selector CSS选择器数组
	 * @param timeout 超时时间,默认0
	 * @example
	 * Utils.waitNodeList("div", 10000).then( $result =>{
	 *  console.log($result); // $result => NodeListOf<HTMLDivElement> | null
	 * })
	 */
	waitNodeList<K extends keyof HTMLElementTagNameMap>(
		selector: K[],
		timeout: number
	): Promise<NodeListOf<HTMLElementTagNameMap[K]> | null>;
	waitNodeList<T extends NodeListOf<Element>>(
		selector: string[],
		timeout: number
	): Promise<T | null>;
	/**
	 * 等待元素数组出现
	 * @param selectorList CSS选择器数组
	 * @param timeout 超时时间,默认0
	 * @example
	 * Utils.waitNodeList(["div"], 10000).then( $result =>{
	 *  console.log($result); // $result => NodeListOf<HTMLDivElement>[] | null
	 * })
	 */
	waitNodeList<K extends keyof HTMLElementTagNameMap>(
		selectorList: K[],
		timeout: number
	): Promise<NodeListOf<HTMLElementTagNameMap[K]>[] | null>;
	waitNodeList<T extends NodeListOf<Element>>(
		selectorList: string[],
		timeout: number
	): Promise<T[] | null>;
	waitNodeList<T extends NodeListOf<Element> | NodeListOf<Element>[]>(
		...args: any[]
	): Promise<T | null> {
		// 过滤掉undefined
		args = args.filter((arg) => arg !== void 0);
		let UtilsContext = this;
		// 选择器数组
		let selector = args[0] as unknown as string | string[];
		// 父元素(监听的元素)
		let parent: Element = UtilsContext.windowApi.document as any as Element;
		// 超时时间
		let timeout = 0;
		if (typeof args[0] !== "string" && !Array.isArray(args[0])) {
			throw new TypeError("Utils.waitNodeList 第一个参数必须是string|string[]");
		}
		if (args.length === 1) {
			// 上面已做处理
		} else if (args.length === 2) {
			let secondParam = args[1];
			if (typeof secondParam === "number") {
				// "div",10000
				timeout = secondParam;
			} else if (
				typeof secondParam === "object" &&
				secondParam instanceof Node
			) {
				// "div",document
				parent = secondParam as any as Element;
			} else {
				throw new TypeError("Utils.waitNodeList 第二个参数必须是number|Node");
			}
		} else if (args.length === 3) {
			// "div",document,10000
			// 第二个参数,parent
			let secondParam = args[1];
			// 第三个参数,timeout
			let thirdParam = args[2];
			if (typeof secondParam === "object" && secondParam instanceof Node) {
				parent = secondParam as any as Element;
				if (typeof thirdParam === "number") {
					timeout = thirdParam;
				} else {
					throw new TypeError("Utils.waitNodeList 第三个参数必须是number");
				}
			} else {
				throw new TypeError("Utils.waitNodeList 第二个参数必须是Node");
			}
		} else {
			throw new TypeError("Utils.waitNodeList 参数个数错误");
		}
		function getNodeList() {
			if (Array.isArray(selector)) {
				let result: T[] = [];
				for (let index = 0; index < selector.length; index++) {
					let nodeList = (parent as Element).querySelectorAll(
						selector[index]
					) as T;
					if (nodeList.length) {
						result.push(nodeList);
					}
				}
				if (result.length === selector.length) {
					return result;
				}
			} else {
				let nodeList = (parent as Element).querySelectorAll(selector) as T;
				if (nodeList.length) {
					return nodeList;
				}
			}
		}
		return UtilsContext.wait<any>(
			() => {
				let node = getNodeList();
				if (node) {
					return {
						success: true,
						data: node,
					};
				} else {
					return {
						success: false,
						data: node,
					};
				}
			},
			timeout,
			parent
		);
	}
	/**
	 * 等待任意元素数组出现
	 * @param selectorList CSS选择器数组
	 * @param parent (可选)监听的父元素
	 * @example
	 * Utils.waitAnyNodeList(["div","a"]).then( $result =>{
	 *  console.log($result); // $result => NodeListOf<HTMLDivElement>
	 * })
	 * Utils.waitAnyNodeList(["div","a"], document).then( $result =>{
	 *  console.log($result); // $result => NodeListOf<HTMLDivElement>
	 * })
	 */
	waitAnyNodeList<K extends keyof HTMLElementTagNameMap>(
		selectorList: K[],
		parent?: Node | Element | Document | HTMLElement
	): Promise<NodeListOf<HTMLElementTagNameMap[K]>>;
	waitAnyNodeList<T extends Element>(
		selectorList: string[],
		parent?: Node | Element | Document | HTMLElement
	): Promise<NodeListOf<T>>;
	/**
	 * 等待任意元素数组出现
	 * @param selectorList CSS选择器数组
	 * @param parent 父元素,默认document
	 * @param timeout 超时时间,默认0
	 * @example
	 * Utils.waitAnyNodeList(["div","a"], document, 10000).then( $result =>{
	 *  console.log($result); // $result => NodeListOf<HTMLDivElement> | null
	 * })
	 */
	waitAnyNodeList<K extends keyof HTMLElementTagNameMap>(
		selectorList: K[],
		parent: Node | Element | Document | HTMLElement,
		timeout: number
	): Promise<NodeListOf<HTMLElementTagNameMap[K]> | null>;
	waitAnyNodeList<T extends Element>(
		selectorList: string[],
		parent: Node | Element | Document | HTMLElement,
		timeout: number
	): Promise<NodeListOf<T> | null>;
	/**
	 * 等待任意元素出现
	 * @param selectorList CSS选择器数组
	 * @param timeout 超时时间,默认0
	 * @example
	 * Utils.waitAnyNodeList(["div","div"], 10000).then( $result =>{
	 *  console.log($result); // $result => NodeListOf<HTMLDivElement> | null
	 * })
	 */
	waitAnyNodeList<K extends keyof HTMLElementTagNameMap>(
		selectorList: K[],
		timeout: number
	): Promise<NodeListOf<HTMLElementTagNameMap[K]> | null>;
	waitAnyNodeList<T extends Element>(
		selectorList: string[],
		timeout: number
	): Promise<NodeListOf<T> | null>;
	waitAnyNodeList<T extends Element>(
		...args: any[]
	): Promise<NodeListOf<T> | null> {
		// 过滤掉undefined
		args = args.filter((arg) => arg !== void 0);
		let UtilsContext = this;
		// 选择器数组
		let selectorList = args[0] as unknown as string[];
		// 父元素(监听的元素)
		let parent: Element = UtilsContext.windowApi.document as any as Element;
		// 超时时间
		let timeout = 0;
		if (!Array.isArray(args[0])) {
			throw new TypeError("Utils.waitAnyNodeList 第一个参数必须是string[]");
		}
		if (args.length === 1) {
			// 上面已做处理
		} else if (args.length === 2) {
			let secondParam = args[1];
			if (typeof secondParam === "number") {
				// "div",10000
				timeout = secondParam;
			} else if (
				typeof secondParam === "object" &&
				secondParam instanceof Node
			) {
				// "div",document
				parent = secondParam as any as Element;
			} else {
				throw new TypeError(
					"Utils.waitAnyNodeList 第二个参数必须是number|Node"
				);
			}
		} else if (args.length === 3) {
			// "div",document,10000
			// 第二个参数,parent
			let secondParam = args[1];
			// 第三个参数,timeout
			let thirdParam = args[2];
			if (typeof secondParam === "object" && secondParam instanceof Node) {
				parent = secondParam as any as Element;
				if (typeof thirdParam === "number") {
					timeout = thirdParam;
				} else {
					throw new TypeError("Utils.waitAnyNodeList 第三个参数必须是number");
				}
			} else {
				throw new TypeError("Utils.waitAnyNodeList 第二个参数必须是Node");
			}
		} else {
			throw new TypeError("Utils.waitAnyNodeList 参数个数错误");
		}

		let promiseList = selectorList.map((selector) => {
			return UtilsContext.waitNodeList<NodeListOf<T>>(
				selector,
				parent,
				timeout
			);
		});
		return Promise.any(promiseList);
	}

	/**
	 * 等待对象上的属性出现
	 * @param checkObj 检查的对象
	 * @param checkPropertyName 检查的对象的属性名
	 * @example
	 * await Utils.waitProperty(window,"test");
	 * console.log("test success set");
	 *
	 * window.test = 1;
	 * > "test success set"
	 *
	 */
	waitProperty<T extends any>(
		checkObj: any | (() => any),
		checkPropertyName: string
	): Promise<T>;
	waitProperty<T extends any>(
		checkObj: any | (() => any),
		checkPropertyName: string
	): Promise<T> {
		return new Promise((resolve) => {
			let obj = checkObj;
			if (typeof checkObj === "function") {
				obj = checkObj();
			}
			if (Reflect.has(obj, checkPropertyName)) {
				resolve((obj as any)[checkPropertyName]);
			} else {
				Object.defineProperty(obj, checkPropertyName, {
					set: function (value) {
						try {
							resolve(value);
						} catch (error) {
							console.error("Error setting property:", error);
						}
					},
				});
			}
		});
	}
	/**
	 * 在规定时间内等待对象上的属性出现
	 * @param checkObj 检查的对象
	 * @param checkPropertyName 检查的对象的属性名
	 * @param intervalTimer (可选)检查间隔时间(ms),默认250ms
	 * @param maxTime (可选)限制在多长时间内,默认-1(不限制时间)
	 * @example
	 * await Utils.waitPropertyByInterval(window,"test");
	 * console.log("test success set");
	 */
	waitPropertyByInterval<T extends any>(
		checkObj: any | (() => any),
		checkPropertyName: string | ((obj: any) => boolean),
		intervalTimer?: number,
		maxTime?: number
	): Promise<T>;
	waitPropertyByInterval<T extends any>(
		checkObj: any | (() => any),
		checkPropertyName: string | ((obj: any) => boolean),
		intervalTimer: number = 250,
		maxTime: number = -1
	): Promise<T> {
		if (checkObj == null) {
			throw new TypeError("checkObj 不能为空对象 ");
		}
		let isResolve = false;
		return new Promise((resolve, reject) => {
			let interval = setInterval(() => {
				let obj = checkObj;
				if (typeof checkObj === "function") {
					obj = checkObj();
				}
				if (typeof obj !== "object") {
					return;
				}
				if (obj == null) {
					return;
				}
				if (
					(typeof checkPropertyName === "function" && checkPropertyName(obj)) ||
					Reflect.has(obj, checkPropertyName as string)
				) {
					isResolve = true;
					clearInterval(interval);
					resolve((obj as any)[checkPropertyName as string]);
				}
			}, intervalTimer);
			if (maxTime !== -1) {
				setTimeout(() => {
					if (!isResolve) {
						clearInterval(interval);
						reject();
					}
				}, maxTime);
			}
		});
	}
	/**
	 * 在规定时间内等待元素上的__vue__属性或者__vue__属性上的某个值出现出现
	 * @param element 目标元素
	 * @param propertyName (可选)vue上的属性名或者传递一个获取属性的方法返回boolean
	 * @param timer (可选)间隔时间(ms),默认:250(ms)
	 * @param maxTime(可选) 限制在多长时间内,默认:-1(不限制时间)
	 * @param vueName (可选)vue挂载的属性名,默认:__vue__
	 * @example
	 * await Utils.waitVueByInterval(
	 * function(){
	 *    return document.querySelector("a.xx")
	 * },
	 * function(__vue__){
	 *    return Boolean(__vue__.xxx == null);
	 * },
	 * 250,
	 * 10000,
	 * "__vue__"
	 * )
	 */
	waitVueByInterval(
		element: HTMLElement | (() => any),
		propertyName: string | ((__vue__: any) => boolean),
		timer?: number,
		maxTime?: number,
		vueName?: "__vue__" | string
	): Promise<boolean>;
	async waitVueByInterval(
		element: HTMLElement | (() => any),
		propertyName: string | ((__vue__: any) => boolean),
		timer = 250,
		maxTime = -1,
		vueName = "__vue__"
	) {
		if (element == null) {
			throw new Error("Utils.waitVueByInterval 参数element 不能为空");
		}
		let flag = false;
		let UtilsContext = this;
		try {
			await UtilsContext.waitPropertyByInterval(
				element,
				function (targetElement) {
					if (targetElement == null) {
						return false;
					}
					if (!(vueName in targetElement)) {
						return false;
					}
					if (propertyName == null) {
						return true;
					}
					let vueObject = targetElement[vueName];
					if (typeof propertyName === "string") {
						if (propertyName in vueObject) {
							flag = true;
							return true;
						}
					} else {
						/* Function */
						if (propertyName(vueObject)) {
							flag = true;
							return true;
						}
					}
					return false;
				},
				timer,
				maxTime
			);
		} catch (error) {
			return flag;
		}
		return flag;
	}
	/**
	 * 观察对象的set、get
	 * @param target 观察的对象
	 * @param propertyName 观察的对象的属性名
	 * @param getCallBack (可选)触发get的回调,可以自定义返回特定值
	 * @param setCallBack (可选)触发set的回调,参数为将要设置的value
	 * @example
	 * Utils.watchObject(window,"test",()=>{return 111;},(value)=>{console.log("test出现,值是",value)});
	 *
	 * window.test = 1;
	 * > test出现,值是 1
	 * console.log(window.test);
	 * > 111;
	 */
	watchObject(
		target: any,
		propertyName: string,
		getCallBack: (value: any) => void,
		setCallBack: (value: any) => void
	): void;
	watchObject(
		target: any,
		propertyName: string,
		getCallBack: (value: any) => void,
		setCallBack: (value: any) => void
	): void {
		if (
			typeof getCallBack !== "function" &&
			typeof setCallBack !== "function"
		) {
			return;
		}

		if (typeof getCallBack === "function") {
			Object.defineProperty(target, propertyName, {
				get() {
					if (typeof getCallBack === "function") {
						return getCallBack(target[propertyName]);
					} else {
						return target[propertyName];
					}
				},
			});
		} else if (typeof setCallBack === "function") {
			Object.defineProperty(target, propertyName, {
				set(value) {
					if (typeof setCallBack === "function") {
						setCallBack(value);
					}
				},
			});
		} else {
			Object.defineProperty(target, propertyName, {
				get() {
					if (typeof getCallBack === "function") {
						return (getCallBack as any)(target[propertyName]);
					} else {
						return target[propertyName];
					}
				},
				set(value) {
					if (typeof setCallBack === "function") {
						(setCallBack as any)(value);
					}
				},
			});
		}
	}

	/**
	 * 深度获取对象属性
	 * @param target 待获取的对象
	 * @param handler 获取属性的回调
	 */
	queryProperty(
		target: any,
		handler: (target: any) => {
			/**
			 * 是否是需要的属性
			 * + true 将目标值赋值给data
			 * + false 不是需要的,data为下一个处理的对象
			 */
			isFind: boolean;
			/**
			 * 对象/目标值
			 */
			data: any;
		}
	): any {
		if (target == null) {
			return;
		}
		let handleResult = handler(target);
		if (
			handleResult &&
			typeof handleResult.isFind === "boolean" &&
			handleResult.isFind
		) {
			return handleResult.data;
		}
		return this.queryProperty(handleResult.data, handler);
	}

	/**
	 * 创建一个新的Utils实例
	 * @param option
	 * @returns
	 */
	createUtils(option?: WindowApiOption) {
		return new Utils(option);
	}

	/**
	 * 将对象转换为FormData
	 * @param data 待转换的对象
	 * @param isEncode 是否对值为string进行编码转换(encodeURIComponent),默认false
	 * @param valueAutoParseToStr 是否对值强制使用JSON.stringify()转换,默认false
	 * @example
	 * Utils.toFormData({
	 * 	test: "1",
	 *  666: 666,
	 * })
	 */
	toFormData(
		data: { [key: string]: string | Blob | File | number },
		isEncode: boolean = false,
		valueAutoParseToStr: boolean = false
	) {
		const formData = new FormData();
		Object.keys(data).forEach((key) => {
			let value = data[key];
			if (valueAutoParseToStr) {
				value = JSON.stringify(value);
			}
			if (typeof value === "number") {
				value = value.toString();
			}
			if (isEncode && typeof value === "string") {
				value = encodeURIComponent(value);
			}
			if (value instanceof File) {
				formData.append(key, value, value.name);
			} else {
				formData.append(key, value);
			}
		});
		return formData;
	}
	/**
	 * 将链接转为URL对象,自动补充URL的protocol或者origin
	 * @param text 需要转换的链接字符串
	 * @example
	 * Utils.toUrl("//www.baidu.com/s?word=666");
	 * Utils.toUrl("/s?word=666");
	 */
	toUrl(text: string) {
		if (typeof text !== "string") {
			throw new TypeError("toUrl: text must be string");
		}
		text = text.trim();
		if (text === "") {
			throw new TypeError("toUrl: text must not be empty");
		}
		if (text.startsWith("//")) {
			/* //www.baidu.com/xxxxxxx */
			/* 没有protocol,加上 */
			text = this.windowApi.globalThis.location.protocol + text;
		} else if (text.startsWith("/")) {
			/* /xxx/info?xxx=xxx */
			/* 没有Origin,加上 */
			text = this.windowApi.globalThis.location.origin + text;
		}
		return new URL(text);
	}
	/**
	 * 生成uuid
	 * @example
	 * Utils.generateUUID()
	 */
	generateUUID = GenerateUUID;
	/**
	 * 自定义的动态响应对象
	 * @example
	 * let vue = new Utils.Vue();
	 * let reactive = new vue.reactive({});
	 * vue.watch(()=>reactive["name"], (newValue, oldValue)=>{
	 *     console.log("newValue ==> " + newValue);
	 *     console.log("oldValue ==> " + oldValue);
	 * })
	 * vue["name"] = "测试";
	 * > "测试"
	 */
	Vue = Vue;
	ModuleRaid = ModuleRaid;
}

let utils = new Utils();

export { utils as Utils };