// ==UserScript== // @name 阅图标记 (边框标记版) // @namespace RANRAN // @version 1.0 // @description 可配合【阅图标记 (Visited Image Marker)】使用 // @author Gemini // @match http://*/* // @match https://*/* // @grant GM_addStyle // @grant GM_getValue // @grant GM_setValue // @grant GM_registerMenuCommand // @grant GM_deleteValue // @downloadURL none // ==/UserScript== (function() { 'use strict'; // --- 默认设置与常量 --- const STORAGE_KEY_VISITED = 'visitedLinks'; const STORAGE_KEY_SETTINGS = 'readimage_settings'; const READ_STATE_CLASS = 'readimage-visited-link'; const DEFAULTS = { unreadWidth: '5px', unreadColor: 'rgba(211, 211, 211, 0.7)', readWidth: '5px', readColor: 'tomato', matchingMode: 'blacklist', matchingList: [ 'google.com', 'bing.com', 'baidu.com' ] }; // --- 加载设置 --- let settings = { ...DEFAULTS, ...JSON.parse(GM_getValue(STORAGE_KEY_SETTINGS, '{}')) }; if (!Array.isArray(settings.matchingList)) { settings.matchingList = DEFAULTS.matchingList; } // --- 核心逻辑:检查黑白名单 --- function shouldScriptRun() { const currentUrl = window.location.href; const { matchingMode, matchingList } = settings; // v5.1 优化的匹配逻辑:不再使用严格的正则表达式,而是使用更宽容的字符串包含检查 // 这样用户输入 "example.com" 就能匹配 "https://www.example.com/page" const isMatch = matchingList.some(pattern => { if (!pattern) return false; // 忽略空行 // 将 ".*" 形式的简单通配符转为真正的通配符,其他则直接检查是否包含 if (pattern.includes('*')) { const regex = new RegExp(pattern.replace(/\./g, '\\.').replace(/\*/g, '.*'), 'i'); return regex.test(currentUrl); } return currentUrl.includes(pattern); }); if (matchingMode === 'whitelist') { return isMatch; } else { return !isMatch; } } if (!shouldScriptRun()) { return; // 如果不应运行,则停止脚本 } // --- 脚本主要功能 (与之前版本相同) --- let visitedLinks = JSON.parse(GM_getValue(STORAGE_KEY_VISITED, '{}')); function applyStyles() { const styleId = 'readimage-dynamic-styles'; let styleElement = document.getElementById(styleId); if (!styleElement) { styleElement = document.createElement('style'); styleElement.id = styleId; document.head.appendChild(styleElement); } styleElement.textContent = ` a img { border: ${settings.unreadWidth} solid ${settings.unreadColor} !important; box-sizing: border-box; } a.${READ_STATE_CLASS} img { border-width: ${settings.readWidth} !important; border-color: ${settings.readColor} !important; } `; } function markVisitedLinks() { document.querySelectorAll('a:has(img)').forEach(link => { if (link.href && visitedLinks[link.href]) { link.classList.add(READ_STATE_CLASS); } }); } document.body.addEventListener('click', (event) => { const link = event.target.closest('a'); if (link && link.href && link.querySelector('img')) { if (!visitedLinks[link.href]) { visitedLinks[link.href] = true; link.classList.add(READ_STATE_CLASS); GM_setValue(STORAGE_KEY_VISITED, JSON.stringify(visitedLinks)); } } }, true); // --- 可视化UI模块 (与之前版本相同) --- const UI = { init() { GM_addStyle(` #readimage-settings-panel { position: fixed; top: 50%; left: 50%; transform: translate(-50%, -50%); z-index: 99999; background: #f0f0f0; border: 1px solid #ccc; border-radius: 8px; box-shadow: 0 5px 15px rgba(0,0,0,0.3); font-family: Arial, sans-serif; font-size: 14px; color: #333; width: 420px; } #readimage-settings-panel .ri-header { padding: 10px 15px; background: #e0e0e0; font-weight: bold; border-bottom: 1px solid #ccc; border-radius: 8px 8px 0 0; cursor: move; position: relative; } #readimage-settings-panel .ri-close-btn { position: absolute; top: 5px; right: 10px; font-size: 20px; font-weight: bold; cursor: pointer; color: #888; } #readimage-settings-panel .ri-close-btn:hover { color: #000; } #readimage-settings-panel .ri-body { padding: 15px; max-height: 70vh; overflow-y: auto; } #readimage-settings-panel fieldset { border: 1px solid #ccc; border-radius: 4px; padding: 10px; margin-bottom: 15px; } #readimage-settings-panel legend { font-weight: bold; padding: 0 5px; } #readimage-settings-panel .ri-row { display: flex; align-items: center; margin-bottom: 8px; } #readimage-settings-panel .ri-row label { width: 50px; } #readimage-settings-panel .ri-row input[type="text"] { flex-grow: 1; border: 1px solid #ccc; border-radius: 4px; padding: 5px; } #readimage-settings-panel .ri-row input[type="color"] { margin-left: 10px; border: 1px solid #ccc; padding: 2px; border-radius: 4px; width: 40px; height: 30px; cursor: pointer; } #readimage-settings-panel .ri-footer { padding: 10px 15px; background: #e0e0e0; text-align: right; border-top: 1px solid #ccc; border-radius: 0 0 8px 8px; } #readimage-settings-panel .ri-footer button { margin-left: 10px; padding: 5px 15px; border: 1px solid #999; border-radius: 4px; cursor: pointer; background: #fff; } #readimage-settings-panel .ri-footer button#ri-save-btn { background: #4CAF50; color: white; border-color: #4CAF50; font-weight: bold; } #readimage-settings-panel .ri-note { font-size: 12px; color: #666; margin: 5px 0 10px 0; } #readimage-settings-panel textarea { width: 95%; min-height: 80px; resize: vertical; padding: 5px; border: 1px solid #ccc; border-radius: 4px; font-family: monospace; } `); }, create() { if (document.getElementById('readimage-settings-panel')) return; const panel = document.createElement('div'); panel.id = 'readimage-settings-panel'; panel.innerHTML = `