@whitesev/utils
Version:
一个常用的工具库
1,810 lines (1,791 loc) • 154 kB
text/typescript
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