Dynamic Include Sites Script (Protocol-Independent)

Run on default & user-defined sites using wildcard patterns (ignores protocols), with full management features.

Fra og med 13.02.2025. Se den nyeste version.

Dette script bør ikke installeres direkte. Det er et bibliotek, som andre scripts kan inkludere med metadirektivet // @require https://update.greasyfork.org/scripts/526770/1536548/Dynamic%20Include%20Sites%20Script%20%28Protocol-Independent%29.js

// ==UserScript==
// @name         Dynamic Include Sites Script (Protocol-Independent)
// @namespace    http://tampermonkey.net/
// @version      3.2
// @description  Run on default & user-defined sites using wildcard patterns (ignores protocols), with full management features.
// @author       blvdmd
// @match        *://*/*
// @grant        GM_getValue
// @grant        GM_setValue
// @grant        GM_registerMenuCommand
// @grant        GM_download
// ==/UserScript==

(function () {
    'use strict';

    const STORAGE_KEY = "additionalSites"; // Key for storing additional site patterns

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

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

    // Load stored additional sites (default is an empty array)
    let additionalSites = GM_getValue(STORAGE_KEY, []);

    // Merge user-defined sites with default sites (protocols ignored)
    let mergedSites = [...new Set([...getDefaultList(), ...additionalSites])].map(normalizeUrl);

    // Add menu commands for user interaction
    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);

    const currentHost = window.location.hostname;
    const currentPath = window.location.pathname;
    const currentFullPath = normalizeUrl(`${window.location.href}`);

    if (shouldRunOnCurrentSite()) {
        console.log(`✅ Running script on: ${currentFullPath}`);
        runScript();
    }

    function runScript() {
        console.log("🚀 Script is running...");
        alert("Included site");

        // Your main logic here
    }

    function shouldRunOnCurrentSite() {
        return mergedSites.some(pattern => wildcardToRegex(normalizeUrl(pattern)).test(currentFullPath));
    }

    function addCurrentSiteMenu() {
        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 baseOptions = [
            { 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}`) },
        ];

        const customOption = { name: "Custom Wildcard Pattern", pattern: normalizeUrl(`${window.location.href}`) };

        const options = [...baseOptions, customOption];

        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}`);
            } else {
                alert(`⚠️ Pattern "${pattern}" is already in the list.`);
            }
        }
    }

    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])];
                alert("📥 Sites imported successfully.");
            };
            reader.readAsText(event.target.files[0]);
        };
        input.click();
    }

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