Site Filter (Protocol-Independent)

Manage allowed sites dynamically and reference this in other scripts.

Verzia zo dňa 13.02.2025. 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.greatest.deepsurf.us/scripts/526770/1536564/Site%20Filter%20%28Protocol-Independent%29.js

// ==UserScript==
// @name         Site Filter (Protocol-Independent)
// @namespace    http://tampermonkey.net/
// @version      1.0
// @description  Manage allowed sites dynamically and reference this in other scripts.
// @author       You
// @match        *://*/*
// @grant        GM_getValue
// @grant        GM_setValue
// @grant        GM_registerMenuCommand
// @grant        GM_download
// ==/UserScript==

(function () {
    'use strict';

    const STORAGE_KEY = "additionalSites"; 

    function getDefaultList() {
        return [
            "*.example.*",
            "*example2*"
        ];
    }

    function normalizeUrl(url) {
        return url.replace(/^https?:\/\//, ''); // Remove "http://" or "https://"
    }

    let additionalSites = GM_getValue(STORAGE_KEY, []);
    let mergedSites = [...new Set([...getDefaultList(), ...additionalSites])].map(normalizeUrl);

    GM_registerMenuCommand("➕ Add Current Site to Include List", addCurrentSiteMenu);
    GM_registerMenuCommand("📜 View Included Sites", viewIncludedSites);
    GM_registerMenuCommand("🗑️ Delete Specific Entries", deleteEntries);
    GM_registerMenuCommand("✏️ Edit an Entry", editEntry);
    GM_registerMenuCommand("🚨 Clear All Entries", clearAllEntries);
    GM_registerMenuCommand("📤 Export Site List as JSON", exportAdditionalSites);
    GM_registerMenuCommand("📥 Import Site List from JSON", importAdditionalSites);

    async function shouldRunOnThisSite() {
        const currentFullPath = normalizeUrl(`${window.location.href}`);
        return mergedSites.some(pattern => wildcardToRegex(normalizeUrl(pattern)).test(currentFullPath));
    }

    // function wildcardToRegex(pattern) {
    //     return new RegExp("^" + pattern.replace(/[.+?^${}()|[\]\\]/g, '\\$&').replace(/\*/g, '.*').replace(/\?/g, '.') + "$");
    // }

    /**
     * Convert a wildcard pattern (e.g., "*.example.com/index.php?/forums/*") into a regex.
     * - `*` → Matches any characters (`.*`)
     * - `?` → Matches a literal `?` (escaped as `\?`)
     * - `.` → Matches a literal `.` (escaped as `\.`)
     */
    function wildcardToRegex(pattern) {
        return new RegExp("^" + pattern
            .replace(/[-[\]{}()+.,^$|#\s]/g, '\\$&') // Escape special regex characters
            .replace(/\*/g, '.*') // Convert `*` to `.*` (match anything)
            .replace(/\\\?/g, '\\?') // Ensure `?` is treated as a literal question mark
        + "$");
    }

    function addCurrentSiteMenu() {
        const currentHost = window.location.hostname;
        const currentPath = window.location.pathname;
        const domainParts = currentHost.split('.');
        const baseDomain = domainParts.length > 2 ? domainParts.slice(-2).join('.') : domainParts.join('.');
        const secondLevelDomain = domainParts.length > 2 ? domainParts.slice(-2, -1)[0] : domainParts[0];

        const options = [
            { name: `Base Hostname (*.${baseDomain})`, pattern: `*.${baseDomain}` },
            { name: `Base Domain (*.${secondLevelDomain}.*)`, pattern: `*.${secondLevelDomain}.*` },
            { name: `Host Contains (*${secondLevelDomain}*)`, pattern: `*${secondLevelDomain}*` },
            { name: `Exact Path (${currentHost}${currentPath})`, pattern: normalizeUrl(`${window.location.href}`) },
            { name: "Custom Wildcard Pattern", pattern: normalizeUrl(`${window.location.href}`) }
        ];

        const userChoice = prompt(
            "Select an option to add the site:\n" +
            options.map((opt, index) => `${index + 1}. ${opt.name}`).join("\n") +
            "\nEnter a number or cancel."
        );

        if (!userChoice) return;
        const selectedIndex = parseInt(userChoice, 10) - 1;
        if (selectedIndex >= 0 && selectedIndex < options.length) {
            let pattern = normalizeUrl(options[selectedIndex].pattern);
            if (options[selectedIndex].name === "Custom Wildcard Pattern") {
                pattern = normalizeUrl(prompt("Edit custom wildcard pattern:", pattern));
                if (!pattern.trim()) return alert("Invalid pattern. Operation canceled.");
            }
            if (!additionalSites.includes(pattern)) {
                additionalSites.push(pattern);
                GM_setValue(STORAGE_KEY, additionalSites);
                mergedSites = [...new Set([...getDefaultList(), ...additionalSites])].map(normalizeUrl);
                alert(`✅ Added site with pattern: ${pattern}`);
            }
        }
    }

    function viewIncludedSites() {
        //alert(`🔍 Included Sites:\n${mergedSites.join("\n") || "No sites added yet."}`);
        alert(`🔍 Included Sites:\n${additionalSites.join("\n") || "No sites added yet."}`);
    }

    function deleteEntries() {
        if (additionalSites.length === 0) return alert("⚠️ No user-defined entries to delete.");
        const userChoice = prompt("Select entries to delete (comma-separated numbers):\n" +
            additionalSites.map((item, index) => `${index + 1}. ${item}`).join("\n"));
        if (!userChoice) return;
        const indicesToRemove = userChoice.split(',').map(num => parseInt(num.trim(), 10) - 1);
        additionalSites = additionalSites.filter((_, index) => !indicesToRemove.includes(index));
        GM_setValue(STORAGE_KEY, additionalSites);
        mergedSites = [...new Set([...getDefaultList(), ...additionalSites])].map(normalizeUrl);
        alert("✅ Selected entries have been deleted.");
    }

    function editEntry() {
        if (additionalSites.length === 0) return alert("⚠️ No user-defined entries to edit.");
        const userChoice = prompt("Select an entry to edit:\n" +
            additionalSites.map((item, index) => `${index + 1}. ${item}`).join("\n"));
        if (!userChoice) return;
        const selectedIndex = parseInt(userChoice, 10) - 1;
        if (selectedIndex < 0 || selectedIndex >= additionalSites.length) return alert("❌ Invalid selection.");
        const newPattern = normalizeUrl(prompt("Edit the pattern:", additionalSites[selectedIndex]));
        if (newPattern && newPattern.trim() && newPattern !== additionalSites[selectedIndex]) {
            additionalSites[selectedIndex] = newPattern.trim();
            GM_setValue(STORAGE_KEY, additionalSites);
            mergedSites = [...new Set([...getDefaultList(), ...additionalSites])].map(normalizeUrl);
            alert("✅ Entry updated.");
        }
    }

    function clearAllEntries() {
        if (additionalSites.length === 0) return alert("⚠️ No user-defined entries to clear.");
        if (confirm(`🚨 You have ${additionalSites.length} entries. Clear all?`)) {
            additionalSites = [];
            GM_setValue(STORAGE_KEY, additionalSites);
            mergedSites = [...getDefaultList()].map(normalizeUrl);
            alert("✅ All user-defined entries cleared.");
        }
    }

    function exportAdditionalSites() {
        GM_download("data:text/json;charset=utf-8," + encodeURIComponent(JSON.stringify(additionalSites, null, 2)), "additionalSites_backup.json");
        alert("📤 Additional sites exported as JSON.");
    }

    function importAdditionalSites() {
        const input = document.createElement("input");
        input.type = "file";
        input.accept = ".json";
        input.onchange = event => {
            const reader = new FileReader();
            reader.onload = e => {
                additionalSites = JSON.parse(e.target.result);
                GM_setValue(STORAGE_KEY, additionalSites);
                mergedSites = [...new Set([...getDefaultList(), ...additionalSites])].map(normalizeUrl);
                alert("📥 Sites imported successfully.");
            };
            reader.readAsText(event.target.files[0]);
        };
        input.click();
    }

    window.shouldRunOnThisSite = shouldRunOnThisSite;
})();