BasicLib

This script contains some simple basic-functions.

Verzia zo dňa 14.03.2026. Pozri najnovšiu verziu.

Tento skript by nemal byť nainštalovaný priamo. Je to knižnica pre ďalšie skripty, ktorú by mali používať cez meta príkaz // @require https://update.greasyfork.org/scripts/547732/1774573/BasicLib.js

Na nainštalovanie skriptu si budete musieť nainštalovať rozšírenie, ako napríklad Tampermonkey, Greasemonkey alebo Violentmonkey.

Na inštaláciu tohto skriptu je potrebné nainštalovať rozšírenie, ako napríklad Tampermonkey.

Na nainštalovanie skriptu si budete musieť nainštalovať rozšírenie, ako napríklad Tampermonkey, % alebo Violentmonkey.

Na nainštalovanie skriptu si budete musieť nainštalovať rozšírenie, ako napríklad Tampermonkey alebo Userscripts.

Na inštaláciu tohto skriptu je potrebné nainštalovať rozšírenie, ako napríklad Tampermonkey.

Na inštaláciu tohto skriptu je potrebné nainštalovať rozšírenie správcu používateľských skriptov.

(Už mám správcu používateľských skriptov, nechajte ma ho nainštalovať!)

Na inštaláciu tohto štýlu je potrebné nainštalovať rozšírenie, ako napríklad Stylus.

Na inštaláciu tohto štýlu je potrebné nainštalovať rozšírenie, ako napríklad Stylus.

Na inštaláciu tohto štýlu je potrebné nainštalovať rozšírenie, ako napríklad Stylus.

Na inštaláciu tohto štýlu je potrebné nainštalovať rozšírenie správcu používateľských štýlov.

Na inštaláciu tohto štýlu je potrebné nainštalovať rozšírenie správcu používateľských štýlov.

Na inštaláciu tohto štýlu je potrebné nainštalovať rozšírenie správcu používateľských štýlov.

(Už mám správcu používateľských štýlov, nechajte ma ho nainštalovať!)

// ==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");
})();