您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
在妖火论坛的回帖楼层中显示被引用楼层的内容,并可直接跳转到引用楼层
当前为
// ==UserScript== // @name 妖火回帖引用显示(他到底说了啥) // @namespace https://www.yaohuo.me/bbs/userinfo.aspx?touserid=20740 // @version 1.1.0 // @description 在妖火论坛的回帖楼层中显示被引用楼层的内容,并可直接跳转到引用楼层 // @author SiXi // @match https://www.yaohuo.me/bbs* // @match https://yaohuo.me/bbs* // @icon https://www.yaohuo.me/css/favicon.ico // @license Apache 2 // @grant none // ==/UserScript== (function() { 'use strict'; const loadedFloors = new Map(); let isLoading = false; let loadAttempts = 0; const MAX_ATTEMPTS = 5; function log(message, data = '') { console.log(`[引用显示] ${message}`, data); } function throttle(func, limit) { let inThrottle; return function(...args) { if (!inThrottle) { func.apply(this, args); inThrottle = true; setTimeout(() => inThrottle = false, limit); } }; } function getFloorNumber(text) { const floorMap = { "沙发": 1, "椅子": 2, "板凳": 3 }; return floorMap[text] || parseInt(text); } function sleep(ms) { return new Promise(resolve => setTimeout(resolve, ms)); } async function waitForNewContent() { const startCount = document.querySelectorAll('.forum-post, .list-reply').length; let attempts = 0; while (attempts < 5) { await sleep(800); const currentCount = document.querySelectorAll('.forum-post, .list-reply').length; if (currentCount > startCount) return true; attempts++; } return false; } async function getQuotedContent(floorNumber, retryCount = 0) { if (loadedFloors.has(floorNumber)) return loadedFloors.get(floorNumber); if (isLoading) { if (retryCount < 3) { await sleep(1000); return getQuotedContent(floorNumber, retryCount + 1); } return { text: '加载超时', isError: true }; } const findFloorContent = () => { const floors = document.querySelectorAll('.forum-post, .list-reply'); for (const floor of floors) { const floorInfo = floor.querySelector('.floor-info, .floornumber'); if (!floorInfo) continue; const currentFloor = getFloorNumber(floorInfo.textContent.replace(/[楼\s]/g, '')); if (currentFloor === floorNumber) { const contentElement = floor.querySelector('.retext') || floor.querySelector('.post-content'); let content = contentElement ? contentElement.innerHTML : ''; const tempDiv = document.createElement('div'); tempDiv.innerHTML = content; const quoteElement = tempDiv.querySelector('.replay-other, .reother'); if (quoteElement) quoteElement.remove(); content = tempDiv.innerHTML.trim(); const userNick = floor.querySelector('.user-nick a, .renick a')?.textContent || '未知用户'; const userId = floor.querySelector('.user-id a, .reidlink .renickid')?.textContent.replace(/[()]/g, '') || '未知ID'; return { userNick, userId, floorNumber: currentFloor, content, text: `<strong>${userNick}(${userId}) ${currentFloor}楼:</strong>${content}` }; } } return null; }; let content = findFloorContent(); if (content) { loadedFloors.set(floorNumber, content); return content; } if (loadAttempts >= MAX_ATTEMPTS) return { text: '加载失败:已达到最大尝试次数', isError: true }; const loadMoreBtn = document.querySelector('#KL_show_tip, #YH_show_tip'); if (!loadMoreBtn || loadMoreBtn.textContent.includes('没有了')) { return { text: '未找到该楼层内容', isError: true }; } log('点击加载更多按钮'); isLoading = true; loadAttempts++; try { loadMoreBtn.click(); const hasNewContent = await waitForNewContent(); isLoading = false; return hasNewContent ? await getQuotedContent(floorNumber) : { text: '加载失败', isError: true }; } catch (error) { console.error('加载失败:', error); return { text: '加载失败', isError: true }; } finally { isLoading = false; } } function addAnchorsToFloors() { document.querySelectorAll('.forum-post, .list-reply').forEach(floor => { const floorInfo = floor.querySelector('.floor-info, .floornumber'); if (!floorInfo) return; const floorText = floorInfo.textContent.replace(/[楼\s]/g, ''); const floorNumber = getFloorNumber(floorText); if (!floor.id && !isNaN(floorNumber)) { floor.id = `floor-${floorNumber}`; } }); } function scrollToFloor(floorNumber) { const targetId = `floor-${floorNumber}`; const targetFloor = document.getElementById(targetId); if (targetFloor) { targetFloor.scrollIntoView({ behavior: 'smooth', block: 'center', inline: 'nearest' }); const originalBg = targetFloor.style.backgroundColor; targetFloor.style.transition = 'background-color 1s ease'; targetFloor.style.backgroundColor = '#fff3cd'; setTimeout(() => { targetFloor.style.backgroundColor = originalBg; setTimeout(() => { targetFloor.style.transition = ''; }, 1000); }, 2000); return true; } console.warn(`未找到目标楼层: ${targetId}`); return false; } function createImageOverlay(img) { const overlay = document.createElement('div'); overlay.style.cssText = ` position: fixed; top: 0; left: 0; width: 100vw; height: 100vh; background: rgba(0,0,0,0.8); z-index: 999999; display: flex; align-items: center; justify-content: center; cursor: zoom-out; `; const overlayImg = new Image(); overlayImg.src = img.src; overlayImg.style.cssText = ` max-width: 90vw; max-height: 90vh; object-fit: contain; cursor: default; `; overlay.appendChild(overlayImg); overlay.addEventListener('click', (e) => { if (e.target === overlay) { document.body.removeChild(overlay); } }); return overlay; } function processImage(img) { const MAX_HEIGHT = 250; const TARGET_WIDTH = 120; const TARGET_HEIGHT = 200; const applyScaling = () => { if (img.naturalHeight <= MAX_HEIGHT) return; // 计算缩放比例 const ratio = Math.min( TARGET_WIDTH / img.naturalWidth, TARGET_HEIGHT / img.naturalHeight ); // 应用缩放样式 img.style.cssText = ` width: ${img.naturalWidth * ratio}px; height: ${img.naturalHeight * ratio}px; object-fit: contain; display: inline-block; `; }; if (img.complete) { applyScaling(); } else { img.addEventListener('load', applyScaling); } } function processImages(contentDiv) { contentDiv.querySelectorAll('img').forEach(processImage); } function createQuoteBox(quoteData) { const box = document.createElement('div'); box.className = 'quoted-content'; box.style.cssText = ` margin: 5px 0; padding: 8px; background-color: #f5f5f5; border-left: 3px solid #4CAF50; border-radius: 3px; font-size: 14px; color: #666; display: flex; align-items: flex-start; justify-content: space-between; margin-bottom: 10px; `; const contentDiv = document.createElement('div'); contentDiv.style.flex = '1'; const userInfo = typeof quoteData === 'string' ? '' : `<strong>${quoteData.userNick}(${quoteData.userId}) ${quoteData.floorNumber}楼:</strong>`; const mainContent = typeof quoteData === 'string' ? quoteData : quoteData.content; contentDiv.innerHTML = userInfo + mainContent; const textContent = contentDiv.textContent; if (textContent.length > 70 && !quoteData.isError) { const mainContentText = typeof quoteData === 'string' ? quoteData : quoteData.content; const truncatedContent = mainContentText.substring(0, 70) + '...'; contentDiv.innerHTML = userInfo + truncatedContent; const expandBtn = document.createElement('button'); expandBtn.className = 'bt4'; expandBtn.textContent = '展开'; expandBtn.style.marginLeft = '5px'; expandBtn.style.cursor = 'pointer'; expandBtn.style.width = '50px'; expandBtn.addEventListener('click', () => { contentDiv.innerHTML = userInfo + mainContent; processImages(contentDiv); expandBtn.remove(); }); contentDiv.appendChild(expandBtn); } processImages(contentDiv); const jumpBtn = document.createElement('a'); jumpBtn.innerHTML = '跳转'; jumpBtn.style.cssText = ` margin-left: 10px; color: #4CAF50; cursor: pointer; white-space: nowrap; font-size: 12px; text-decoration: none; `; if (quoteData.isError) jumpBtn.style.display = 'none'; jumpBtn.addEventListener('click', async (e) => { e.preventDefault(); if (quoteData.floorNumber) { if (!scrollToFloor(quoteData.floorNumber)) { const loadBtn = document.querySelector('#KL_show_tip, #YH_show_tip'); if (loadBtn && !loadBtn.textContent.includes('没有了')) { alert('正在加载更多内容...'); loadBtn.click(); await waitForNewContent(); let retry = 0; while (retry++ < 3 && !scrollToFloor(quoteData.floorNumber)) { await sleep(800); } if (!scrollToFloor(quoteData.floorNumber)) alert('未找到目标楼层'); } } } }); box.appendChild(contentDiv); box.appendChild(jumpBtn); return box; } const handleQuoteReplies = throttle(async () => { addAnchorsToFloors(); document.querySelectorAll('.forum-post, .list-reply').forEach(reply => { if (reply.dataset.processed) return; const quoteElement = reply.querySelector('.replay-other, .reother'); if (!quoteElement) return; const floorMatch = quoteElement.textContent.match(/回复(\d+)楼|回复(沙发|椅子|板凳)/); if (!floorMatch) return; reply.dataset.processed = 'true'; const floorNumber = getFloorNumber(floorMatch[1] || floorMatch[2]); getQuotedContent(floorNumber).then(content => { if (!reply.querySelector('.quoted-content')) { const replyContent = reply.querySelector('.retext') || reply.querySelector('.post-content'); if (replyContent) replyContent.prepend(createQuoteBox(content)); } }); }); }, 500); function unifyFontColor() { document.querySelectorAll('.recontent font[color]').forEach(text => { text.style.color = '#000'; }); } const observer = new MutationObserver(mutations => { mutations.forEach(mutation => { if (mutation.addedNodes.length) { setTimeout(() => { addAnchorsToFloors(); handleQuoteReplies(); unifyFontColor(); }, 300); } }); }); observer.observe(document.body, { childList: true, subtree: true, attributes: false, characterData: false }); handleQuoteReplies(); unifyFontColor(); })();