coofoUtils

一些工具

ეს სკრიპტი არ უნდა იყოს პირდაპირ დაინსტალირებული. ეს ბიბლიოთეკაა, სხვა სკრიპტებისთვის უნდა ჩართეთ მეტა-დირექტივაში // @require https://update.greasyfork.org/scripts/442002/1541573/coofoUtils.js.

You will need to install an extension such as Tampermonkey, Greasemonkey or Violentmonkey to install this script.

You will need to install an extension such as Tampermonkey or Violentmonkey to install this script.

You will need to install an extension such as Tampermonkey or Violentmonkey to install this script.

You will need to install an extension such as Tampermonkey or Userscripts to install this script.

You will need to install an extension such as Tampermonkey to install this script.

You will need to install a user script manager extension to install this script.

(I already have a user script manager, let me install it!)

You will need to install an extension such as Stylus to install this style.

You will need to install an extension such as Stylus to install this style.

You will need to install an extension such as Stylus to install this style.

You will need to install a user style manager extension to install this style.

You will need to install a user style manager extension to install this style.

You will need to install a user style manager extension to install this style.

(I already have a user style manager, let me install it!)

// ==UserScript==
// @name         coofoUtils
// @namespace    https://github.com/coofo/someScript
// @version      0.3.4
// @license      MIT License
// @description  一些工具
// @author       coofo
// @downloadURL  https://github.com/coofo/someScript/raw/main/tampermonkey/coofoUtils.user.js
// @supportURL   https://github.com/coofo/someScript/issues
// @grant        GM_download
// @grant        GM_xmlhttpRequest
// ==/UserScript==

(function () {
    'use strict';
    window.coofoUtils = {
        commonUtils: {
            format: {
                num: {
                    fullNum: function (num, length) {
                        return (Array(length).join('0') + num).slice(-length);
                    },
                    toThousands: function (value, seperator, digitNum) {
                        if ((value = ((value = value + "").replace(/^\s*|\s*$|,*/g, ''))).match(/^\d*\.?\d*$/) == null)
                            return value;
                        value = digitNum >= 0 ? (Number(value).toFixed(digitNum) + "") : value;
                        let r = [],
                            tl = value.split(".")[0],
                            tr = value.split(".")[1];
                        tr = typeof tr !== "undefined" ? tr : "";
                        if (seperator != null && seperator !== "") {
                            while (tl.length >= 3) {
                                r.push(tl.substring(tl.length - 3));
                                tl = tl.substring(0, tl.length - 3);
                            }
                            if (tl.length > 0)
                                r.push(tl);
                            r.reverse();
                            r = r.join(seperator);
                            return tr === "" ? r : r + "." + tr;
                        }
                        return value;
                    },
                    percentAutoDigitNum: function (num, total, maxDigitNum) {
                        let standard = 100;
                        let digitNum = 0;
                        while (standard > total && maxDigitNum < digitNum) {
                            standard *= 10;
                            digitNum++;
                        }
                        return this.toThousands(num / total * 100, null, digitNum) + "%";
                    }
                },
                file: {
                    getSuffix: function (name) {
                        let index = name.lastIndexOf('.');
                        if (index < 0) {
                            return "";
                        } else {
                            return name.substring(index + 1);
                        }
                    }
                },
                string: {
                    byMap: function (str, map, preprocessing) {
                        let reg = new RegExp('\\${([a-z][a-zA-Z0-9_.]+)}', 'g');
                        return str.replace(reg, function (match, pos, originalText) {
                            let key = match.replace(reg, '$1');
                            let value = map[key];
                            if (value === null || value === undefined) {
                                value = match;
                            }
                            if (typeof preprocessing === "function") {
                                value = preprocessing(value, key, map);
                            }
                            return value;
                        });
                    },
                    filePathByMap: function (str, map) {
                        let preprocessing = function (value, key, map) {
                            let match = key.match(/^(.*)_([a-zA-Z0-9]+)$/);
                            let ext = null;
                            if (match != null) {
                                let rKey = match[1];
                                ext = match[2];
                                let rValue = map[rKey];
                                if (rValue !== null && rValue !== undefined) {
                                    value = rValue;
                                } else {
                                    value = "";
                                }
                            }

                            if (typeof value === "string") {
                                value = value.replace(/[\/:?"<>*|~]/g, function (match, pos, originalText) {
                                    switch (match) {
                                        case "\\":
                                            return "\";
                                        case "/":
                                            return "/";
                                        case ":":
                                            return ":";
                                        case "?":
                                            return "?";
                                        case '"':
                                            return '"';
                                        case '<':
                                            return '<';
                                        case '>':
                                            return '>';
                                        case '*':
                                            return '*';
                                        case '|':
                                            return '|';
                                        case '~':
                                            return '~';
                                    }
                                });
                            }

                            if (ext !== null && value !== "") {
                                switch (ext) {
                                    case "empty":
                                        break;
                                    case "path":
                                        value += '/';
                                        break;
                                    case "parenthesis":
                                        value = "(" + value + ")";
                                        break;
                                    case "squareBracket":
                                        value = "[" + value + "]";
                                        break;
                                    case "curlyBracket":
                                        value = "{" + value + "}";
                                        break;
                                    default:
                                        let indexMatch = ext.match(/index([0-9]+)/);
                                        if (indexMatch !== null && (typeof value === "number" || ('' + value).match(/^\d+$/))) {
                                            value = coofoUtils.commonUtils.format.num.fullNum(value, Number(indexMatch[1]));
                                        }
                                        break;
                                }
                            }

                            return value;
                        };
                        return coofoUtils.commonUtils.format.string.byMap(str, map, preprocessing);
                    }
                },
                url: {
                    fullUrl: function (url) {
                        if (url.match(/^[a-zA-Z0-9]+:\/\//) !== null) {
                            return url;
                        } else if (url.match(/^\/\/[a-zA-Z0-9]+/) !== null) {
                            return window.location.protocol + url;
                        } else if (url.match(/^\/[a-zA-Z0-9]+/) !== null) {
                            return window.location.origin + url;
                        } else {
                            return url;
                        }
                    }
                }
            },
            assert: {
                isTrue: function (value, message) {
                    if (true !== value) {
                        console.error(message);
                        console.error(value);
                        throw message;
                    }
                },
                isNull: function (value, message) {
                    if (value !== null) {
                        console.error(message);
                        console.error(value);
                        throw message;
                    }
                },
                notNull: function (value, message) {
                    if (value === null) {
                        console.error(message);
                        console.error(value);
                        throw message;
                    }
                },
                hasLength: function (value, message) {
                    if (!(value !== null && value.length > 0)) {
                        console.error(message);
                        console.error(value);
                        throw message;
                    }
                },
            },
            downloadHelp: {
                toBlob: {},
                toUser: {
                    asTagA4Url: function (url, fileName) {
                        let aLink = document.createElement('a');
                        if (fileName) {
                            aLink.download = fileName;
                        } else {
                            aLink.download = url.substring(url.lastIndexOf('/') + 1);
                        }
                        aLink.className = 'download-temp-node';
                        aLink.target = "_blank";
                        aLink.style = "display:none;";
                        aLink.href = url;
                        document.body.appendChild(aLink);
                        if (document.all) {
                            aLink.click(); //IE
                        } else {
                            let evt = document.createEvent("MouseEvents");
                            evt.initEvent("click", true, true);
                            aLink.dispatchEvent(evt); // 其它浏览器
                        }
                        document.body.removeChild(aLink);
                    },
                    asTagA4Blob: function (content, fileName) {
                        if ('msSaveOrOpenBlob' in navigator) {
                            navigator.msSaveOrOpenBlob(content, fileName);
                        } else {
                            let aLink = document.createElement('a');
                            aLink.className = 'download-temp-node';
                            aLink.download = fileName;
                            aLink.style = "display:none;";
                            let blob = new Blob([content], {type: content.type});
                            aLink.href = window.URL.createObjectURL(blob);
                            document.body.appendChild(aLink);
                            if (document.all) {
                                aLink.click(); //IE
                            } else {
                                let evt = document.createEvent("MouseEvents");
                                evt.initEvent("click", true, true);
                                aLink.dispatchEvent(evt); // 其它浏览器
                            }
                            window.URL.revokeObjectURL(aLink.href);
                            document.body.removeChild(aLink);
                        }
                    },
                    asHref4Blob: function (content, fileName) {
                        let blob = new Blob([content], {type: content.type});
                        let file = new File([blob], fileName, {type: blob.type});
                        let url = window.URL.createObjectURL(file);
                        window.open(url, "_self")
                    },
                    asForm: function (url, method, data = null) {
                        //新建form表单
                        let form = document.createElement("form");
                        form.id = "eform";
                        form.name = "eform";
                        form.target = "_blank";
                        document.body.appendChild(form);
                        //添加参数
                        if (data != null) {
                            Object.keys(data).forEach(function (key) {
                                let sdate_input = document.createElement("input");
                                sdate_input.type = "text";
                                sdate_input.name = key;
                                sdate_input.value = data[key];
                                form.appendChild(sdate_input);
                            });
                        }
                        form.method = method;
                        form.action = url;
                        form.submit();
                        document.body.removeChild(form);
                    }
                },

            },
            xss: {
                htmlEscape: function (text) {
                    if (!text) {
                        return text;
                    }
                    text = text + "";
                    return text.replace(/[<>"&']/g, function (match, pos, originalText) {
                        switch (match) {
                            case "<":
                                return "&lt;";
                            case ">":
                                return "&gt;";
                            case "&":
                                return "&amp;";
                            case "\"":
                                return "&quot;";
                            case "'":
                                return "&#39;";
                        }
                    })
                }
            },
            url: {
                //获取url中的参数
                getQueryVariable: function (variableKey, defaultValue = null) {
                    let query = window.location.search.substring(1);
                    let vars = query.split("&");
                    for (let i = 0; i < vars.length; i++) {
                        let pair = vars[i].split("=");
                        if (pair[0] === variableKey) {
                            return decodeURIComponent(pair[1]);
                        }
                    }
                    return defaultValue;
                },
                //在url中添加参数keyValue格式[{key: xx, value: xx}]
                addVariable: function (url, keyValues) {
                    if (!keyValues || keyValues.length === 0) return url;
                    let add = (u) => {
                        if (u.lastIndexOf("?") !== -1) {
                            u += "&";
                        } else {
                            u += "?";
                        }
                        for (let i = 0; i < keyValues.length; i++) {
                            if (!(keyValues[i].key || keyValues[i].value)) continue;
                            u += keyValues[i].key + "=" + encodeURIComponent(keyValues[i].value);
                            if (i !== keyValues.length - 1) {
                                u += "&"
                            }
                        }
                        return u;
                    };
                    //处理有url有hash的情况
                    let index = url.indexOf("#");
                    if (index !== -1) {
                        let realUrl = url.substr(0, index);
                        let hash = url.substr(index);
                        url = add(realUrl) + hash;
                    } else {
                        url = add(url);
                    }
                    return url;
                },
                addVariableByData: function (url, data) {
                    let keyValues = [];
                    let i = 0;
                    Object.keys(data).forEach(function (key) {
                        keyValues[i] = {
                            key: key,
                            value: data[key]
                        };
                        i++;
                    });
                    return coofoUtils.commonUtils.url.addVariable(url, keyValues);
                },
                getBaseUrl: function () {
                    return window.location.protocol + '//' + window.location.host;
                }
            },
            browser: {
                type: function () {
                    let u = navigator.userAgent;
                    let app = navigator.appVersion;
                    let name = {};

                    name.isAndroid = /Android/i.test(u);
                    name.isiPhone = /iPhone/i.test(u);
                    name.isiPad = /iPad/i.test(u);
                    name.isWindowsPc = /Windows/i.test(u);
                    name.isWindowsPhone = /Windows Phone/i.test(u);
                    return name;
                }(),
            },
        },
        service: {
            //可自动重试的Promise
            retryablePromise: {
                create: function (exec, retryTimes) {
                    let taskInfo = {
                        resolve: null,
                        reject: null,
                        retryTimes: retryTimes + 1
                    };
                    let p = new Promise((res, rej) => {
                        taskInfo.resolve = res;
                        taskInfo.reject = rej;
                    });

                    let doTask = function () {
                        new Promise((res, rej) => {
                            exec(res, rej);
                        }).then(
                            r => taskInfo.resolve(r),
                            r => {
                                taskInfo.retryTimes--;
                                if (taskInfo.retryTimes > 0) {
                                    doTask();
                                } else {
                                    taskInfo.reject(r);
                                }
                            }
                        );
                    };
                    doTask();

                    return p;
                }
            },
            task: {
                create: function ()  {
                    let task = {
                        runtime: {taskList: [], executing: [], nowExec: false},
                        api: {
                            addTask: function (exec, lastRetryTimes) {
                                if (task.runtime.nowExec) {
                                    return;
                                }
                                let taskItem = {
                                    complete: false,
                                    lastFinishTime: 0,
                                    lastRetryTimes: lastRetryTimes + 1,
                                    exec: exec,
                                    success: null,
                                    failed: null
                                };
                                task.runtime.taskList.push(taskItem);
                            },
                            exec: async function (poolLimit) {
                                if (task.runtime.nowExec) {
                                    return;
                                }
                                const executing = task.runtime.executing;
                                for (const taskItem of task.runtime.taskList) {
                                    let createPromise = function (taskItem) {
                                        let p = new Promise((resolve, reject) => {
                                            taskItem.success = resolve;
                                            taskItem.failed = reject;
                                            taskItem.exec(taskItem)
                                        }).then(() => {
                                                taskItem.complete = true;
                                                executing.splice(executing.indexOf(p), 1)
                                            },
                                            () => {
                                                taskItem.lastFinishTime = Date.now();
                                                taskItem.lastRetryTimes--;
                                                if (taskItem.lastRetryTimes > 0) {
                                                    executing.splice(executing.indexOf(p), 1, createPromise(taskItem));
                                                } else {
                                                    executing.splice(executing.indexOf(p), 1)
                                                }
                                            }
                                        );
                                        return p;
                                    };
                                    executing.push(createPromise(taskItem));
                                    while (executing.length >= poolLimit) {
                                        await Promise.race(executing);
                                    }
                                }
                                while (executing.length > 0) {
                                    await Promise.race(executing);
                                }
                                let completeNum = 0;
                                let retryTimesOutNum = 0;
                                for (const taskItem of task.runtime.taskList) {
                                    if (taskItem.complete) {
                                        completeNum++;
                                    } else {
                                        retryTimesOutNum++;
                                    }
                                }
                                return {completeNum: completeNum, retryTimesOutNum: retryTimesOutNum};
                            }
                        }
                    };
                    return task;
                }
            },
            taskCount: {
                create: function () {
                    let taskListInfo = {
                        size: 0,
                        finished: 0,
                    };
                    let callback = null;
                    let callCallback = function () {
                        try {
                            if (typeof callback === 'function') callback(taskListInfo);
                        } catch (e) {
                            console.error(e);
                        }
                    };
                    return {
                        addTask: function (runnable) {
                            taskListInfo.size++;
                            let pending = true;
                            callCallback();
                            return function (resolve, reject) {
                                let taskResolve = function (o) {
                                    if (!pending) {
                                        throw "taskCount task can not exec again"
                                    }
                                    pending = false;
                                    taskListInfo.finished++;
                                    callCallback();
                                    resolve(o);
                                };
                                let taskReject = function (o) {
                                    taskListInfo.finished++;
                                    callCallback();
                                    reject(o);
                                };
                                runnable(taskResolve, taskReject);
                            }
                        },
                        setStatusCallback: function (thisCallback) {
                            callback = thisCallback;
                        },
                        getInfo: function () {
                            return taskListInfo;
                        }
                    };
                }
            },
            //线程池
            threadPoolTaskExecutor: {
                create: function (size) {
                    let executing = [];
                    let pending = [];

                    let execOne = function () {
                        if (executing.length < size && pending.length > 0) {
                            let pendingItem = pending.shift();
                            let e = new Promise((r, s) => {
                                pendingItem.runnable(r, s);
                            }).then(r => {
                                executing.splice(executing.indexOf(e), 1);
                                execOne();
                                pendingItem.resolve(r);
                            }, r => {
                                executing.splice(executing.indexOf(e), 1);
                                execOne();
                                pendingItem.reject(r);
                            });
                            executing.push(e);
                        }
                    };

                    return {
                        execute: function (runnable) {
                            let thisPendingItem = {runnable: runnable};
                            let p = new Promise((resolve, reject) => {
                                thisPendingItem.resolve = resolve;
                                thisPendingItem.reject = reject;
                            });
                            pending.push(thisPendingItem);

                            execOne();
                            return p;
                        },
                        cancelAll: function () {
                            pending.splice(0);
                        }
                    };
                }
            }
        }
    };
})();