您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
Manage allowed sites dynamically and reference this in other scripts.
当前为
此脚本不应直接安装,它是供其他脚本使用的外部库。如果您需要使用该库,请在脚本元属性加入:// @require https://updategreasyfork.deno.dev/scripts/526770/1536607/Site%20Filter%20%28Protocol-Independent%29.js
// ==UserScript== // @name Site Filter (Protocol-Independent) // @namespace http://tampermonkey.net/ // @version 2.0 // @description Manage allowed sites dynamically and reference this in other scripts. // @author blvdmd // @match *://*/* // @grant GM_getValue // @grant GM_setValue // @grant GM_registerMenuCommand // @grant GM_download // @run-at document-start // ==/UserScript== (function () { 'use strict'; // ✅ Wait for `SCRIPT_STORAGE_KEY` to be set function waitForScriptStorageKey(maxWait = 1000) { return new Promise(resolve => { const startTime = Date.now(); const interval = setInterval(() => { if (typeof window.SCRIPT_STORAGE_KEY !== 'undefined') { clearInterval(interval); resolve(window.SCRIPT_STORAGE_KEY); } else if (Date.now() - startTime > maxWait) { clearInterval(interval); console.error("🚨 SCRIPT_STORAGE_KEY is not set! Make sure your script sets it **before** @require."); resolve(null); } }, 50); }); } (async function initialize() { async function waitForDocumentReady() { if (document.readyState === "complete") return; return new Promise(resolve => { window.addEventListener("load", resolve, { once: true }); }); } // ✅ Wait for the script storage key const key = await waitForScriptStorageKey(); if (!key) return; // ✅ Ensure the document is fully loaded before setting `shouldRunOnThisSite` await waitForDocumentReady(); const STORAGE_KEY = `additionalSites_${key}`; // Unique per script function getDefaultList() { return [ "*.example.*", "*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); 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 valid regex. * - `*` → Matches any characters (`.*`) * - `?` → Treated as a **literal question mark** (`\?`) * - `.` → Treated as a **literal dot** (`\.`) */ function wildcardToRegex(pattern) { return new RegExp("^" + pattern .replace(/[-[\]{}()+^$|#\s]/g, '\\$&') // Escape regex special characters (EXCEPT `.` and `?`) .replace(/\./g, '\\.') // Ensure `.` is treated as a literal dot .replace(/\?/g, '\\?') // Ensure `?` is treated as a literal question mark .replace(/\*/g, '.*') // Convert `*` to `.*` (match any sequence) + "$"); } 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: `Preferred Domain Match (*${secondLevelDomain}.*)`, pattern: `*${secondLevelDomain}.*` }, { 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}`); } 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])].map(normalizeUrl); alert("📥 Sites imported successfully."); }; reader.readAsText(event.target.files[0]); }; input.click(); } window.shouldRunOnThisSite = shouldRunOnThisSite; })(); })();