BasicLib

This script contains some simple basic-functions.

当前为 2026-03-14 提交的版本,查看 最新版本

此脚本不应直接安装。它是供其他脚本使用的外部库,要使用该库请加入元指令 // @require https://update.greasyfork.org/scripts/547732/1774573/BasicLib.js

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴Greasemonkey 油猴子Violentmonkey 暴力猴,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴Violentmonkey 暴力猴,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴Userscripts ,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展后才能安装此脚本。

(我已经安装了用户脚本管理器,让我安装!)

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

(我已经安装了用户样式管理器,让我安装!)

// ==UserScript==
// @name           BasicLib
// @description    This script contains some simple basic-functions.
// @namespace      https://greasyfork.org/users/788550
// @version        1.1.0
// @author         Cyrano68
// @license        MIT
// @grant          none
// @run-at         document-start
// ==/UserScript==

// This is a IIFE (Immediately Invoked Function Expression).
(function()
{
    "use strict";

    // Verify if the object "console" exists and if it has the function "log".
    // Then use the "bind" method to ensure that the "log" function is always executed within the context of the "console" object.
    const logger = (window.console && console.log) ? console.log.bind(console) : function(){};

    let showLogToScreen = false;
    let maxNumScreenLogs = 200;

    const myVersion = "1.1.0";  // It must be the same value indicated in @version.
    consoleLog(`CY==> BasicLib: HELLO! Loading script (version: ${myVersion})...`);

    function logToScreen(now, text)
    {
        const DEBUG_CONTAINER_ID = "DebugContainer";
        const LOG_AREA_ID = `${DEBUG_CONTAINER_ID}-logArea`;

        let host = document.getElementById("my-proxy-host");
        let debugContainer;
        let logArea;

        if (!host)
        {
            // 1. Create the host.
            host = document.createElement("div");
            host.id = "my-proxy-host";
            document.body.appendChild(host);

            // 2. Create a "shadow DOM tree" and attach it to the "host" element.
            let shadow = host.attachShadow({ mode: "open" });

            // 3. Create the debug-container.
            debugContainer = document.createElement("div");
            debugContainer.id = DEBUG_CONTAINER_ID;

            debugContainer.style.cssText = `
                all: initial;
                position: fixed;
                top: 0;
                left: 0;
                right: 0;
                width: 100%;
                background: rgba(0, 0, 0, 0.95);
                color: #00ff00;
                font-family: monospace;
                font-size: 12px;
                z-index: 200000;
                border-bottom: 2px solid #444;
                box-shadow: 0 4px 15px rgba(0,0,0,0.5);
                pointer-events: auto;
                display: flex;
                flex-direction: column;
            `;

            // Create the LogArea for the messages (it is scrollable).
            logArea = document.createElement("div");
            logArea.id = LOG_AREA_ID;
            logArea.style.cssText = `
                max-height: 200px;
                overflow-y: auto;
                padding: 10px;
                flex-grow: 1;
            `;

            // Create a container for the buttons.
            const btnBar = document.createElement("div");
            btnBar.style.cssText = `
                display: flex;
                background: #222;
            `;

            // Helper-function for creating the buttons.
            const createBtn = (label, color, width) =>
            {
                const btn = document.createElement("button");
                btn.innerText = label;
                btn.style.cssText = `
                    ${width ? `width: ${width}` : 'flex: 1'};
                    background: ${color};
                    color: #fff;
                    border: none;
                    padding: 8px;
                    cursor: pointer;
                    font-size: 11px;
                    font-weight: bold;
                    text-transform: uppercase;
                    border-right: 1px solid #555;
                `;
                return btn;
            };

            // This is a "flex" button.
            const clearBtn = createBtn("CLEAR LOGS", "#444");
            clearBtn.onclick = function()
            {
                logArea.innerHTML = "";
            };

            // This is a "fixed-width" button.
            const scanBtn = createBtn("SCAN SHADOW", "#0055aa", "100px");
            scanBtn.onclick = () =>
            {
                consoleLog(`CY==> BasicLib: --- STARTING DEEP SHADOW-DOM SCAN ---`);

                // Recursive function.
                const scanDeep = (root) =>
                {
                    const elements = root.querySelectorAll('*');

                    elements.forEach(el =>
                    {
                        if (el.shadowRoot)
                        {
                            const tagName = el.tagName.toUpperCase();
                            const mode = el.shadowRoot.mode.toUpperCase();
                            consoleLog(`CY==> BasicLib: ShadowRoot FOUND: TagName='${tagName}', Mode='${mode}'`);

                            if (mode === "OPEN")
                            {
                                const internalElements = el.shadowRoot.querySelectorAll('[id]');
                                const internalIds = Array.from(internalElements).map(i => i.id).join(', ');

                                if (internalIds)
                                {
                                    consoleLog(`CY==> BasicLib:   => Internal IDs: ${internalIds}`);
                                }
                                else
                                {
                                    consoleLog(`CY==> BasicLib:   => NO Internal ID found`);
                                }

                                // Recursive call.
                                scanDeep(el.shadowRoot);
                            }
                            else
                            {
                                consoleLog(`CY==> BasicLib:   => Content of CLOSED ShadowRoot cannot be inspected`);
                            }
                        }
                    });
                };

                // Avvia la scansione dal documento principale
                scanDeep(document);

                consoleLog(`CY==> BasicLib: --- SHADOW-DOM SCAN COMPLETED ---`);
            };

            // This is a "fixed-width" button.
            const closeBtn = createBtn("HIDE", "#600", "100px");
            closeBtn.onclick = function()
            {
                debugContainer.style.display = "none";
            };

            btnBar.appendChild(clearBtn);
            btnBar.appendChild(scanBtn);
            btnBar.appendChild(closeBtn);

            debugContainer.appendChild(logArea);
            debugContainer.appendChild(btnBar);

            // 4. Add the DebugContainer to the "Shadow DOM", NOT to the body!
            //document.body.appendChild(debugContainer);
            shadow.appendChild(debugContainer);
        }
        else
        {
            // IMPORTANT: Here we are looking for elements into the "Shadow root", NOT into the document!
            let shadow = host.shadowRoot;
            debugContainer = shadow.getElementById(DEBUG_CONTAINER_ID);
            logArea = shadow.getElementById(LOG_AREA_ID);
        }

        if (debugContainer.style.display === "none")
        {
            debugContainer.style.display = "flex";
        }

        if (text !== undefined)
        {
            const newEntry = document.createElement("div");
            newEntry.style.cssText = `
                border-bottom: 1px solid #333;
                padding: 4px 0;
                white-space: pre-wrap;
                word-break: break-all;
            `;

            // Check if "text" is empty and check for html special characters.
            const newEntryText = (text === "" ? " " : text)
                .replace(/</g, "&lt;")  // Check "<"
                .replace(/>/g, "&gt;"); // Check ">"

            newEntry.innerHTML = `<span style="color: #888; font-size: 10px;">[${now}]</span> ${newEntryText}`;

            // The new entry is prepended to (i.e. inserted to the begin of) the LogArea.
            logArea.prepend(newEntry);

            if (logArea.childNodes.length > maxNumScreenLogs)
            {
                for (let i = 0; i < 10; ++i)
                {
                    if (logArea.lastChild)
                    {
                        logArea.removeChild(logArea.lastChild);
                    }
                }
            }
        }
    }

    function consoleLog(text, showLog = true)
    {
        if (showLog)
        {
            const dateNow = new Date();
            //const now = dateNow.toISOString();
            const now = dateNow.toLocaleString() + "." + dateNow.getMilliseconds().toString().padStart(3, "0");
            logger(`${now} ${text}`);

            if (showLogToScreen)
            {
                logToScreen(now, text);
            }
        }
    }

    function getMathRandomInteger(val1, val2)
    {
        let min = 0;
        let max = 100;

        const val1IsValid = ((val1 !== undefined) && (val1 !== undefined));
        const val2IsValid = ((val2 !== undefined) && (val2 !== undefined));
        consoleLog(`CY==> BasicLib: getMathRandomInteger - val1IsValid=${val1IsValid}, val2IsValid=${val2IsValid}`);

        if (val1IsValid || val2IsValid)
        {
            if (val1IsValid && val2IsValid)
            {
                min = val1;
                max = val2;
            }
            else
            {
                min = 0;
                max = (val1IsValid ? val1 : val2);
            }
        }

        if (max < min)
        {
            const tmp = min;
            min = max;
            max = tmp;
        }

        consoleLog(`CY==> BasicLib: getMathRandomInteger - min=${min}, max=${max}`);
        return Math.floor(Math.random() * (max - min)) + min;
    }

    function generateID()
    {
        // NOTE-1: The function "toString(36)" converts the number to a base-36 string (i.e. a string containing digits 0-9 and letters a-z).
        // NOTE-2: THe function "substring(2)" is used to remove the "0." from the start of the random number string.
        const ID = Date.now().toString(36) + "_" + Math.random().toString(36).substring(2);
        consoleLog(`CY==> BasicLib: generateID - ID='${ID}'`);
        return ID;
    }

    function setInterval2(callback, interval_ms, execCallbackNow)
    {
        // I defined a new "setInterval" function because I want to call the "setInterval" function and then
        // (if required) call immediately the callback function, instead of wait for the first timeout.
        //

        // Call the "setInterval" for the periodic timer.
        consoleLog(`CY==> BasicLib: setInterval2 - STARTING TIMER - interval_ms=${interval_ms}`);
        const timerId = setInterval(callback, interval_ms);
        consoleLog(`CY==> BasicLib: setInterval2 - TIMER STARTED - timerId=${timerId}`);

        if (execCallbackNow)
        {
            // Call immediately the callback function.
            callback(timerId);
        }

        return timerId;
    }

    function textMatchesArray(text, array, index)
    {
        // This function returns true if there is an element in the array that matches with "text".
        //
        // IMPORTANT: The input "index" must be an object with a field named "value". For example:
        //     let index = {value: -1};
        // At the end the "index.value" will contain the index of the element of the array that matched with "text" (or -1 if there is no match).
        //
        for (let i = 0; i < array.length; ++i)
        {
            if (text.startsWith(array[i]))
            {
                index.value = i;
                return true;
            }
        }

        index.value = -1;
        return false;
    }

    function setShowLogToScreen(showLogToScreenIn, maxNumScreenLogsIn)
    {
        showLogToScreen  = showLogToScreenIn;
        maxNumScreenLogs = ((maxNumScreenLogsIn !== undefined) && (maxNumScreenLogsIn !== null)) ? maxNumScreenLogsIn : 200;
        consoleLog(`CY==> BasicLib: setShowLogToScreen - showLogToScreen=${showLogToScreen}, maxNumScreenLogs=${maxNumScreenLogs}`);
    }

    function getVersion()
    {
        return myVersion;
    }

    // Expose the public interface by returning an object.
    window.BasicLib =
    {
        consoleLog:           consoleLog,
        getMathRandomInteger: getMathRandomInteger,
        generateID:           generateID,
        setInterval2:         setInterval2,
        textMatchesArray:     textMatchesArray,
        setShowLogToScreen:   setShowLogToScreen,
        getVersion:           getVersion
    };

    consoleLog("CY==> BasicLib: Script loaded");
})();