// ==UserScript== // @name Discuz! 引用内容快速查看 // @namespace http://tampermonkey.net/ // @version 0.9 // @description 在Discuz!论坛点击引用链接时显示被引用内容的悬浮框(修复图片懒加载问题) // @author mmm // @match *://*/forum.php?mod=viewthread&tid=* // @match *://*/thread-* // @match *://*/*/forum.php?mod=viewthread&tid=* // @match *://*/*/thread-* // @grant GM_xmlhttpRequest // @grant GM_addStyle // @connect * // @license MIT // @downloadURL https://update.greasyfork.icu/scripts/539116/Discuz%21%20%E5%BC%95%E7%94%A8%E5%86%85%E5%AE%B9%E5%BF%AB%E9%80%9F%E6%9F%A5%E7%9C%8B.user.js // @updateURL https://update.greasyfork.icu/scripts/539116/Discuz%21%20%E5%BC%95%E7%94%A8%E5%86%85%E5%AE%B9%E5%BF%AB%E9%80%9F%E6%9F%A5%E7%9C%8B.meta.js // ==/UserScript== (function() { 'use strict'; // 获取引用块基准样式 function getBaseStyles() { const quote = document.querySelector('.quote') || document.createElement('div'); const styles = getComputedStyle(quote); return { bg: styles.backgroundColor || 'rgb(245, 245, 245)', border: styles.borderColor || 'rgb(221, 221, 221)', text: styles.color || 'rgb(51, 51, 51)' }; } // 创建现代CSS样式(使用color-mix) GM_addStyle(` .quote-popup { position: fixed; top: 50%; left: 50%; transform: translate(-50%, -50%); background: color-mix(in srgb, ${getBaseStyles().bg} 85%, white); border: 1px solid color-mix(in srgb, ${getBaseStyles().border} 80%, white); color: ${getBaseStyles().text}; padding: 12px; max-width: min(80%, 800px); max-height: 80vh; z-index: 9999; box-shadow: 0 4px 20px rgba(0,0,0,0.15); border-radius: 8px; overflow: auto; display: none; font-size: 1.2em; } .quote-popup-content { max-height: calc(80vh - 60px); overflow-y: auto; padding-right: 8px; } .quote-popup-header { font-weight: bold; margin-bottom: 12px; padding-bottom: 6px; border-bottom: 1px solid color-mix(in srgb, ${getBaseStyles().border} 70%, white); display: flex; justify-content: space-between; align-items: center; } .quote-popup-close, .quote-popup-back { cursor: pointer; font-size: 18px; width: 24px; height: 24px; display: flex; align-items: center; justify-content: center; border-radius: 4px; transition: all 0.2s; } .quote-popup-close:hover, .quote-popup-back:hover { background: color-mix(in srgb, ${getBaseStyles().bg} 70%, white); } .quote-popup-back { margin-right: auto; } .quote-loading { padding: 20px; text-align: center; opacity: 0.7; } @media (max-width: 600px) { .quote-popup { width: 95%; max-width: none; } } `); // 创建悬浮框元素 const popup = document.createElement('div'); popup.className = 'quote-popup'; popup.innerHTML = `
引用内容
×
`; document.body.appendChild(popup); const backBtn = document.querySelector('.quote-popup-back'); const closeBtn = document.querySelector('.quote-popup-close'); const content = document.querySelector('.quote-popup-content'); // 页面缓存 const pageCache = new Map(); const CACHE_EXPIRY = 5 * 60 * 1000; // 5分钟缓存 // 历史记录栈 const historyStack = []; // 显示/隐藏悬浮框 function showPopup() { popup.style.display = 'block'; } function hidePopup() { popup.style.display = 'none'; historyStack.length = 0; backBtn.style.display = 'none'; } // 修复懒加载图片 function fixLazyLoadImages(html) { const tempDiv = document.createElement('div'); tempDiv.innerHTML = html; tempDiv.querySelectorAll('img[file]').forEach(img => { const fileValue = img.getAttribute('file'); if (fileValue) { img.setAttribute('src', fileValue); img.removeAttribute('file'); img.removeAttribute('onmouseover'); img.removeAttribute('onload'); } }); return tempDiv.innerHTML; } // 从URL中提取帖子ID和页码 function extractIdsFromUrl(url) { const pidMatch = url.match(/pid=(\d+)/); const ptidMatch = url.match(/ptid=(\d+)/); return { pid: pidMatch ? pidMatch[1] : null, ptid: ptidMatch ? ptidMatch[1] : null }; } // 检查当前页面是否包含目标元素 function checkCurrentPageForElement(pid) { const element = document.querySelector(`#pid${pid} .t_f`); return element ? fixLazyLoadImages(element.innerHTML) : null; } // 获取帖子内容 function fetchPostContent(url, callback, isBackAction = false) { content.innerHTML = '
加载中...
'; showPopup(); const ids = extractIdsFromUrl(url); // 优先检查当前页面 const currentPageContent = checkCurrentPageForElement(ids.pid); if (currentPageContent) { if (!isBackAction) { historyStack.push({ url, html: currentPageContent }); } callback(currentPageContent); backBtn.style.display = historyStack.length > 1 ? 'block' : 'none'; return; } // 检查缓存 const cachedData = pageCache.get(url); if (cachedData && (Date.now() - cachedData.timestamp < CACHE_EXPIRY)) { const fixedHtml = fixLazyLoadImages(cachedData.html); if (!isBackAction) { historyStack.push({ url, html: fixedHtml }); } callback(fixedHtml); backBtn.style.display = historyStack.length > 1 ? 'block' : 'none'; return; } // 从网络获取 GM_xmlhttpRequest({ method: "GET", url: url, onload: function(response) { const parser = new DOMParser(); const doc = parser.parseFromString(response.responseText, "text/html"); const quoteContent = doc.querySelector(`#pid${ids.pid} .t_f`); if (quoteContent) { const fixedHtml = fixLazyLoadImages(quoteContent.innerHTML); pageCache.set(url, { html: quoteContent.innerHTML, timestamp: Date.now() }); if (!isBackAction) { historyStack.push({ url, html: fixedHtml }); } callback(fixedHtml); backBtn.style.display = historyStack.length > 1 ? 'block' : 'none'; } else { callback("无法找到引用内容"); } }, onerror: function() { callback("加载引用内容失败"); } }); } // 处理返回按钮点击 function handleBackClick() { if (historyStack.length > 1) { historyStack.pop(); const previousItem = historyStack[historyStack.length - 1]; content.innerHTML = previousItem.html; content.querySelectorAll('.quote a font').forEach(newLink => { newLink.parentNode.addEventListener('click', handleQuoteClick); }); backBtn.style.display = historyStack.length > 1 ? 'block' : 'none'; } } // 处理引用链接点击 function handleQuoteClick(e) { e.preventDefault(); e.stopPropagation(); const link = e.currentTarget; const href = link.href; fetchPostContent(href, function(htmlContent) { content.innerHTML = htmlContent; content.querySelectorAll('.quote a font').forEach(newLink => { newLink.parentNode.addEventListener('click', handleQuoteClick); }); }); } // 事件绑定 closeBtn.addEventListener('click', hidePopup); backBtn.addEventListener('click', handleBackClick); document.addEventListener('click', function(e) { if (!popup.contains(e.target) && !e.target.closest('.quote a font')) { hidePopup(); } }); // 初始化引用链接 function initQuoteLinks() { document.querySelectorAll('.quote a font').forEach(link => { link.parentNode.addEventListener('click', handleQuoteClick); }); } // 启动 initQuoteLinks(); })();