// ==UserScript== // @name NodeSeek Addon // @namespace http://tampermonkey.net/ // @version 0.2 // @description NodeSeek 插件 // @author lauvinson // @match https://www.nodeseek.com/* // @match http://www.nodeseek.com/* // @grant none // @license GNU GPLv3 // @downloadURL none // ==/UserScript== (function() { 'use strict'; // 主题控制函数 function applyTheme() { // 检查当前主题 const isDarkMode = document.body.classList.contains('dark-layout'); // 更新主题相关的CSS变量 const themeStyles = document.createElement('style'); themeStyles.id = 'ns-theme-styles'; // 移除已存在的主题样式 const existingStyles = document.getElementById('ns-theme-styles'); if (existingStyles) { existingStyles.remove(); } // 设置主题相关的CSS变量 themeStyles.textContent = ` :root { --ns-bg-color: ${isDarkMode ? '#272727' : '#FFFFFF'}; --ns-text-color: ${isDarkMode ? '#f0f0f0' : '#333333'}; --ns-border-color: ${isDarkMode ? '#444444' : '#dddddd'}; --ns-hover-bg-color: ${isDarkMode ? '#383838' : '#f5f5f5'}; --ns-card-bg-color: ${isDarkMode ? 'rgba(39, 39, 39, 0.95)' : 'rgba(255, 255, 255, 0.95)'}; --ns-card-bg-hover: ${isDarkMode ? 'rgba(45, 45, 45, 0.95)' : 'rgba(255, 255, 255, 0.95)'}; --ns-card-shadow: ${isDarkMode ? '0 4px 15px rgba(0, 0, 0, 0.5)' : '0 4px 15px rgba(0, 0, 0, 0.2)'}; --ns-card-text-color: ${isDarkMode ? '#e0e0e0' : '#333'}; --ns-card-secondary-text: ${isDarkMode ? '#a0a0a0' : '#666'}; --ns-overlay-bg: ${isDarkMode ? 'rgba(0, 0, 0, 0.8)' : 'rgba(0, 0, 0, 0.7)'}; --ns-loader-bg: ${isDarkMode ? '#272727' : 'white'}; --ns-loader-border: ${isDarkMode ? '#444444' : '#f3f3f3'}; --ns-emoji-results-bg: ${isDarkMode ? '#333333' : '#f9f9f9'}; --ns-emoji-item-bg: ${isDarkMode ? '#3a3a3a' : 'white'}; --ns-emoji-item-border: ${isDarkMode ? '#555555' : '#eeeeee'}; --ns-debug-bg: ${isDarkMode ? '#333333' : '#f8f9fa'}; --ns-debug-border: ${isDarkMode ? '#444444' : '#dddddd'}; } `; document.head.appendChild(themeStyles); console.log(`NodeSeek Addon: 应用${isDarkMode ? '深色' : '浅色'}主题`); } // 监听主题变化 function observeThemeChanges() { // 使用MutationObserver监听body的class变化 const observer = new MutationObserver((mutations) => { mutations.forEach((mutation) => { if (mutation.attributeName === 'class') { applyTheme(); } }); }); // 开始观察body的class变化 observer.observe(document.body, { attributes: true }); } // 样式 const style = document.createElement('style'); style.textContent = ` .ns-iframe-container { position: fixed; top: 0; left: 0; width: 100%; height: 100%; background: var(--ns-overlay-bg); z-index: 9999; display: flex; justify-content: flex-end; /* 调整为靠右对齐 */ align-items: center; } .ns-iframe-wrapper { position: relative; width: 70%; height: 100%; background: var(--ns-bg-color); border-radius: 8px; overflow: hidden; } /* 添加左侧信息卡片样式 */ .ns-sidebar-info { position: fixed; left: 15%; top: 50%; transform: translate(-50%, -50%); width: 280px; background: var(--ns-card-bg-color); border-radius: 8px; padding: 20px; box-shadow: var(--ns-card-shadow); z-index: 9998; display: none; /* 初始状态为隐藏 */ flex-direction: column; align-items: center; text-align: center; } /* 添加前后帖子导航卡片样式 */ .ns-post-nav-card { position: fixed; left: 5%; width: 250px; background: var(--ns-card-bg-color); border-radius: 8px; padding: 15px; box-shadow: var(--ns-card-shadow); z-index: 9997; display: none; /* 初始状态为隐藏 */ flex-direction: column; align-items: center; text-align: center; cursor: pointer; transition: all 0.3s ease; opacity: 0.7; } .ns-post-nav-card:hover { opacity: 1; background: var(--ns-card-bg-hover); box-shadow: 0 4px 12px rgba(0, 0, 0, 0.25); } .ns-post-nav-card.prev { top: 25%; transform: translateY(-50%); } .ns-post-nav-card.next { top: 75%; transform: translateY(-50%); } .ns-post-nav-card .nav-label { display: block; font-size: 13px; color: var(--ns-card-secondary-text); margin-bottom: 5px; } .ns-post-nav-card .nav-title { font-size: 15px; color: var(--ns-card-text-color); font-weight: 500; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; width: 100%; } .ns-sidebar-info .avatar-container { margin-bottom: 15px; } .ns-sidebar-info .avatar-container img { width: 60px; height: 60px; border-radius: 50%; object-fit: cover; border: 2px solid #2ea44f; } .ns-sidebar-info .title { font-size: 18px; font-weight: bold; margin: 0 0 15px 0; color: var(--ns-card-text-color); } .ns-sidebar-info .user-info { margin-bottom: 10px; color: var(--ns-card-secondary-text); font-size: 14px; } /* 添加媒体查询,小屏幕使用100%宽度 */ @media screen and (max-width: 800px) { .ns-iframe-wrapper { width: 100%; border-radius: 0; } .ns-iframe-container { justify-content: center; } .ns-sidebar-info { display: none; /* 小屏幕隐藏左侧信息 */ } } .ns-iframe { width: 100%; height: 100%; border: none; opacity: 0; /* 初始隐藏iframe */ transition: opacity 0.3s ease; } .ns-close-btn { position: absolute; top: 10px; right: 10px; background: #f44336; color: white; border: none; border-radius: 50%; width: 30px; height: 30px; font-size: 16px; cursor: pointer; display: flex; justify-content: center; align-items: center; z-index: 10; } /* 回到顶部和底部按钮样式 */ .ns-to-top-btn, .ns-to-bottom-btn { position: absolute; right: 20px; width: 40px; height: 40px; background: rgba(46, 164, 79, 0.8); color: white; border: none; border-radius: 50%; font-size: 20px; cursor: pointer; display: flex; justify-content: center; align-items: center; z-index: 100; opacity: 0; transition: opacity 0.3s ease, background-color 0.2s ease; box-shadow: 0 2px 5px rgba(0,0,0,0.2); } .ns-to-top-btn:hover, .ns-to-bottom-btn:hover { background: rgba(46, 164, 79, 1); } .ns-to-top-btn { bottom: 70px; } .ns-to-bottom-btn { bottom: 20px; } /* 新增按钮显示动画 */ .ns-btn-show { opacity: 1; } /* 新增样式 - 使列表项可点击并添加悬停效果 */ .post-list-item { cursor: pointer; transition: background-color 0.2s ease; } .post-list-item:hover { background-color: var(--ns-hover-bg-color); } /* 加载指示器 */ .ns-loader { position: absolute; top: 50%; left: 50%; transform: translate(-50%, -50%); width: 50px; height: 50px; border: 2px solid var(--ns-loader-border); border-top: 2px solid #2ea44f; border-radius: 50%; animation: spin 1s linear infinite; z-index: 5; } @keyframes spin { 0% { transform: translate(-50%, -50%) rotate(0deg); } 100% { transform: translate(-50%, -50%) rotate(360deg); } } /* 表情包搜索样式 */ .ns-emoji-search { position: relative; margin-top: 5px; margin-bottom: 5px; padding: 0 1% 0 1%; width: 98%; display: flex; align-items: center; } .ns-emoji-search input { flex: 1; padding: 6px 10px; border: 1px solid var(--ns-border-color); border-radius: 4px; font-size: 14px; margin-right: 5px; background-color: var(--ns-bg-color); color: var(--ns-text-color); } /* 搜索按钮样式 - 保留用于自定义exp-item */ .exp-item.ns-emoji-btn { cursor: pointer; background-color: #2ea44f !important; color: white !important; transition: background-color 0.2s; } .exp-item.ns-emoji-btn:hover { background-color: #2c974b !important; opacity: 0.9; } .ns-emoji-results { position: absolute; bottom: 100%; left: 0; right: 0; margin-bottom: 5px; display: flex; flex-wrap: wrap; gap: 10px; max-height: 300px; overflow-y: auto; padding: 10px; background-color: var(--ns-emoji-results-bg); border: 1px solid var(--ns-border-color); border-radius: 4px; box-shadow: 0 -2px 10px rgba(0,0,0,0.1); z-index: 100; } .ns-emoji-results-header { width: 100%; text-align: center; margin-bottom: 10px; font-size: 14px; color: var(--ns-card-secondary-text); } .ns-emoji-item { width: 80px; height: 80px; border-radius: 4px; cursor: pointer; overflow: hidden; transition: transform 0.2s; border: 1px solid var(--ns-emoji-item-border); background-color: var(--ns-emoji-item-bg); flex-shrink: 0; } .ns-emoji-item:hover { transform: scale(1.1); box-shadow: 0 2px 5px rgba(0,0,0,0.2); border-color: #2ea44f; z-index: 1; } .ns-emoji-item img { width: 100%; height: 100%; object-fit: contain; } /* 调试信息样式 */ .ns-debug-info { background-color: var(--ns-debug-bg); border: 1px solid var(--ns-debug-border); border-radius: 4px; padding: 8px; margin-top: 5px; font-family: monospace; font-size: 12px; max-height: 100px; overflow-y: auto; white-space: pre-wrap; word-break: break-all; color: var(--ns-text-color); } `; document.head.appendChild(style); // 主函数 function init() { // 应用当前主题并监听主题变化 applyTheme(); observeThemeChanges(); // 找到所有帖子列表项 const postItems = document.querySelectorAll('.post-list-item'); // 创建帖子列表映射,用于获取前后帖子 const postMap = new Map(); let postOrder = []; // 将所有帖子信息存入映射 postItems.forEach((item, index) => { const postLink = item.querySelector('.post-title a[href^="/post-"]'); if (postLink) { const href = postLink.href; const title = postLink.textContent.trim(); postMap.set(href, { title: title, element: item, index: index }); postOrder.push(href); } }); // 为每个帖子列表项添加点击事件处理 postItems.forEach(item => { item.addEventListener('click', function(e) { // 检查点击的是否是头像或头像的容器 const isAvatarClick = e.target.classList.contains('avatar') || e.target.classList.contains('avatar-normal') || e.target.closest('a[href^="/space/"]') !== null || e.target.closest('.avatar-wrapper') !== null; // 如果点击的是头像相关元素,不阻止默认行为,允许正常跳转 if (isAvatarClick) { return; // 直接返回,不阻止事件,允许正常导航 } // 找到当前列表项中的帖子链接 const postLink = item.querySelector('.post-title a[href^="/post-"]'); if (postLink) { // 阻止事件冒泡和默认行为 e.preventDefault(); e.stopPropagation(); // 收集帖子信息 - 直接使用原有DOM元素 const postTitle = postLink.textContent.trim(); const avatarImg = item.querySelector('.avatar-normal, img.avatar'); const userElement = item.querySelector('.author-name, .post-username, .username'); const postInfoElement = item.querySelector('.post-info, .info'); // 获取前后帖子信息 const currentPostUrl = postLink.href; const currentIndex = postMap.get(currentPostUrl)?.index; let prevPost = null; let nextPost = null; if (currentIndex !== undefined) { // 获取前一篇帖子 if (currentIndex > 0) { const prevUrl = postOrder[currentIndex - 1]; const prevInfo = postMap.get(prevUrl); if (prevInfo) { prevPost = { url: prevUrl, title: prevInfo.title }; } } // 获取后一篇帖子 if (currentIndex < postOrder.length - 1) { const nextUrl = postOrder[currentIndex + 1]; const nextInfo = postMap.get(nextUrl); if (nextInfo) { nextPost = { url: nextUrl, title: nextInfo.title }; } } } // 在iframe中打开帖子,传入原始DOM元素和前后帖子信息 openInIframe(postLink.href, { title: postTitle, avatarElement: avatarImg, userElement: userElement, infoElement: postInfoElement, prevPost: prevPost, nextPost: nextPost, postMap: postMap, postOrder: postOrder }); } }); }); } // 表情包搜索API async function searchEmojis(query) { // 使用提供的API const url = `https://oiapi.net/API/EmoticonPack/?keyword=${encodeURIComponent(query)}`; try { console.log('搜索表情包,请求URL:', url); const response = await fetch(url); console.log('API响应状态:', response.status); if (!response.ok) { console.error('API响应非200:', response.status, response.statusText); return { error: `API响应错误: ${response.status}`, data: [] }; } const data = await response.json(); console.log('API返回数据:', data); // 检查是否有数据 - 注意这里API返回的成功code是1而不是200 if (data.code === 1 && Array.isArray(data.data) && data.data.length > 0) { console.log(`找到${data.data.length}个表情包`); return { error: null, data: data.data.map(item => ({ url: item.url, preview: item.url, width: item.width, height: item.height, type: item.type, size: item.size })) }; } else { console.log('API返回成功但无数据:', data); return { error: '未找到相关表情包', data: [] }; } } catch (error) { console.error('搜索表情包出错:', error); return { error: '请求出错: ' + error.message, data: [] }; } } // 在iframe中打开链接 function openInIframe(url, domElements = null) { // 禁用父页面滚动 const originalBodyOverflow = document.body.style.overflow; document.body.style.overflow = 'hidden'; // 创建容器 const container = document.createElement('div'); container.className = 'ns-iframe-container'; // 创建iframe包装器 const wrapper = document.createElement('div'); wrapper.className = 'ns-iframe-wrapper'; // 创建加载指示器容器 const loaderContainer = document.createElement('div'); loaderContainer.className = 'ns-loader-container'; loaderContainer.style.cssText = ` position: absolute; top: 0; left: 0; width: 100%; height: 100%; display: flex; flex-direction: column; justify-content: center; align-items: center; background-color: var(--ns-loader-bg); z-index: 10; `; // 如果有帖子信息,创建并添加左侧信息卡片 if (domElements) { // 创建左侧信息卡片 const sidebarInfo = document.createElement('div'); sidebarInfo.className = 'ns-sidebar-info'; // 创建并添加前一篇帖子导航卡片(如果有) if (domElements.prevPost) { const prevNav = document.createElement('div'); prevNav.className = 'ns-post-nav-card prev'; prevNav.innerHTML = `
`; prevNav.setAttribute('data-url', domElements.prevPost.url); prevNav.addEventListener('click', function() { loadNewPost(this.getAttribute('data-url'), domElements); }); container.appendChild(prevNav); } // 添加帖子标题 if (domElements.title) { const titleElement = document.createElement('div'); titleElement.className = 'title'; titleElement.textContent = domElements.title; titleElement.style.cssText = ` font-size: 18px; font-weight: bold; margin: 0 0 15px 0; text-align: center; color: var(--ns-card-text-color); width: 100%; `; sidebarInfo.appendChild(titleElement); } // 添加头像 if (domElements.avatarElement) { const avatarContainer = document.createElement('div'); avatarContainer.className = 'avatar-container'; // 克隆原始头像并添加样式 const avatarClone = domElements.avatarElement.cloneNode(true); avatarContainer.appendChild(avatarClone); sidebarInfo.appendChild(avatarContainer); } // 添加用户名和其他信息 if (domElements.userElement) { const userInfo = document.createElement('div'); userInfo.className = 'user-info'; const usernameClone = domElements.userElement.cloneNode(true); userInfo.appendChild(usernameClone); sidebarInfo.appendChild(userInfo); } // 添加帖子信息 if (domElements.infoElement) { const postInfo = document.createElement('div'); postInfo.className = 'post-info'; const infoClone = domElements.infoElement.cloneNode(true); postInfo.appendChild(infoClone); sidebarInfo.appendChild(postInfo); } // 将信息卡片添加到容器中 container.appendChild(sidebarInfo); // 创建并添加下一篇帖子导航卡片(如果有) if (domElements.nextPost) { const nextNav = document.createElement('div'); nextNav.className = 'ns-post-nav-card next'; nextNav.innerHTML = ` `; nextNav.setAttribute('data-url', domElements.nextPost.url); nextNav.addEventListener('click', function() { loadNewPost(this.getAttribute('data-url'), domElements); }); container.appendChild(nextNav); } // 将信息卡片添加到容器中 container.appendChild(sidebarInfo); // 创建简单信息容器用于加载界面 const infoContainer = document.createElement('div'); infoContainer.style.cssText = ` display: flex; flex-direction: column; align-items: center; width: 100%; max-width: 600px; padding: 10px 20px 20px; margin-bottom: 20px; text-align: center; `; // 直接复制头像元素,保留原有类名和样式,放在第一行居中 if (domElements.avatarElement) { const avatarContainer = document.createElement('div'); avatarContainer.style.cssText = ` display: flex; justify-content: center; margin-bottom: 15px; width: 100%; `; const avatarClone = domElements.avatarElement.cloneNode(true); avatarContainer.appendChild(avatarClone); infoContainer.appendChild(avatarContainer); } // 添加标题,放在第二行居中 if (domElements.title) { const titleElement = document.createElement('div'); titleElement.textContent = domElements.title; titleElement.style.cssText = ` font-size: 18px; font-weight: bold; margin: 0 0 15px 0; text-align: center; color: var(--ns-card-text-color); width: 100%; `; infoContainer.appendChild(titleElement); } // 用户名和帖子信息,放在第三行居中 const infoWrapper = document.createElement('div'); infoWrapper.style.cssText = ` display: flex; flex-direction: column; align-items: center; width: 100%; margin-bottom: 20px; `; // 直接复制用户名元素,保留原有类名和样式 if (domElements.userElement) { const usernameContainer = document.createElement('div'); usernameContainer.style.cssText = ` display: flex; justify-content: center; width: 100%; margin-bottom: 5px; `; const usernameClone = domElements.userElement.cloneNode(true); usernameContainer.appendChild(usernameClone); infoWrapper.appendChild(usernameContainer); } // 直接复制帖子信息元素,保留原有类名和样式 if (domElements.infoElement) { const infoElementContainer = document.createElement('div'); infoElementContainer.style.cssText = ` display: flex; justify-content: center; width: 100%; `; const infoClone = domElements.infoElement.cloneNode(true); infoElementContainer.appendChild(infoClone); infoWrapper.appendChild(infoElementContainer); } infoContainer.appendChild(infoWrapper); // 创建加载指示器 const loader = document.createElement('div'); loader.className = 'ns-loader'; loader.style.cssText = ` width: 40px; height: 40px; margin: 100px 0; border: 2px solid var(--ns-loader-border); border-top: 2px solid #2ea44f; border-radius: 50%; animation: spin 1s linear infinite; `; infoContainer.appendChild(loader); loaderContainer.appendChild(infoContainer); } else { // 如果没有元素信息,只显示加载指示器 const loader = document.createElement('div'); loader.className = 'ns-loader'; loader.style.cssText = ` width: 40px; height: 40px; border: 2px solid var(--ns-loader-border); border-top: 2px solid #2ea44f; border-radius: 50%; animation: spin 1s linear infinite; `; loaderContainer.appendChild(loader); } // 创建关闭按钮 const closeBtn = document.createElement('button'); closeBtn.className = 'ns-close-btn'; closeBtn.innerHTML = '✕'; closeBtn.title = 'ESC'; // 添加悬停提示,显示ESC快捷键 closeBtn.onclick = function(e) { e.stopPropagation(); // 防止事件传递到container closeIframe(); }; // 创建iframe const iframe = document.createElement('iframe'); iframe.className = 'ns-iframe'; iframe.src = url; // 防止滚动穿透 iframe.addEventListener('load', function() { try { // 尝试阻止iframe内部滚动穿透到父页面 const iframeDoc = iframe.contentDocument || iframe.contentWindow.document; const iframeBody = iframeDoc.body; // 在iframe中滚动到底部或顶部时,阻止继续滚动影响父页面 iframeDoc.addEventListener('wheel', function(e) { const scrollingElement = iframeDoc.scrollingElement || iframeBody; const scrollTop = scrollingElement.scrollTop; const scrollHeight = scrollingElement.scrollHeight; const clientHeight = scrollingElement.clientHeight; // 如果已经滚动到底部,并且继续向下滚动 if (scrollTop + clientHeight >= scrollHeight && e.deltaY > 0) { e.preventDefault(); } // 如果已经滚动到顶部,并且继续向上滚动 if (scrollTop <= 0 && e.deltaY < 0) { e.preventDefault(); } }, { passive: false }); } catch (error) { console.error('无法阻止iframe滚动穿透:', error); } }); // iframe加载完成后处理页面内容 iframe.onload = function() { try { // 获取iframe文档对象 const iframeDoc = iframe.contentDocument || iframe.contentWindow.document; // 移除header和footer const header = iframeDoc.querySelector('header'); const footer = iframeDoc.querySelector('footer'); if (header) header.style.display = 'none'; if (footer) footer.style.display = 'none'; // 移除右侧面板 const rightPanel = iframeDoc.getElementById('nsk-right-panel-container'); if (rightPanel) rightPanel.style.display = 'none'; // 检测iframe内的主题并应用相同主题 const isDarkMode = document.body.classList.contains('dark-layout'); const iframeIsDark = iframeDoc.body.classList.contains('dark-layout'); // 如果主题不一致,同步iframe内部主题与父页面 if (isDarkMode !== iframeIsDark) { if (isDarkMode) { iframeDoc.body.classList.add('dark-layout'); iframeDoc.body.classList.remove('light-layout'); } else { iframeDoc.body.classList.add('light-layout'); iframeDoc.body.classList.remove('dark-layout'); } } // 添加自定义样式到iframe内部 const styleElement = iframeDoc.createElement('style'); styleElement.textContent = ` /* 隐藏可能的导航和页脚 */ header, .header, #header, footer, .footer, #footer, nav, .nav, #nav, .site-header, .site-footer, .main-header, .main-footer, .page-header, .page-footer, #nsk-right-panel-container { display: none !important; } /* 调整主要内容区域 */ body { padding-top: 0 !important; margin-top: 0 !important; } /* 让内容区域占满整个空间 */ .container, .main-content, .content, #content, .page-content, .site-content, main, .main, #main { padding-top: 10px !important; margin-top: 0 !important; max-width: 100% !important; } /* 调整文章内容宽度,右侧面板被移除后 */ .post-detail-card { width: 100% !important; max-width: 100% !important; } /* 强制设置nsk-container的margin为0 */ .nsk-container { margin: 0 !important; } /* 平滑滚动效果 */ html { scroll-behavior: smooth; } `; iframeDoc.head.appendChild(styleElement); // 添加回到顶部和底部按钮 const toTopBtn = document.createElement('button'); toTopBtn.className = 'ns-to-top-btn'; toTopBtn.innerHTML = '↑'; toTopBtn.title = '回到顶部'; toTopBtn.onclick = () => { iframeDoc.documentElement.scrollTop = 0; }; const toBottomBtn = document.createElement('button'); toBottomBtn.className = 'ns-to-bottom-btn'; toBottomBtn.innerHTML = '↓'; toBottomBtn.title = '到达底部'; toBottomBtn.onclick = () => { iframeDoc.documentElement.scrollTop = iframeDoc.documentElement.scrollHeight; }; wrapper.appendChild(toTopBtn); wrapper.appendChild(toBottomBtn); // 滚动时显示/隐藏按钮 let scrollTimer; const handleScroll = () => { // 清除之前的定时器 clearTimeout(scrollTimer); const scrollTop = iframeDoc.documentElement.scrollTop; const scrollHeight = iframeDoc.documentElement.scrollHeight; const clientHeight = iframeDoc.documentElement.clientHeight; // 如果滚动位置超过100px,显示回到顶部按钮 if (scrollTop > 100) { toTopBtn.classList.add('ns-btn-show'); } else { toTopBtn.classList.remove('ns-btn-show'); } // 如果距离底部超过200px,显示回到底部按钮 if (scrollHeight - scrollTop - clientHeight > 200) { toBottomBtn.classList.add('ns-btn-show'); } else { toBottomBtn.classList.remove('ns-btn-show'); } // 设置5秒后自动隐藏按钮 scrollTimer = setTimeout(() => { toTopBtn.classList.remove('ns-btn-show'); toBottomBtn.classList.remove('ns-btn-show'); }, 5000); }; // 初始检查滚动位置 handleScroll(); // 监听滚动事件 iframeDoc.addEventListener('scroll', handleScroll); // 添加表情包搜索功能到评论框 addEmojiSearchToCommentBox(iframeDoc); // 处理翻页链接,拦截点击实现无刷新翻页 setupPagination(iframeDoc, iframe); // 处理完成后移除加载指示器并显示iframe wrapper.removeChild(loaderContainer); iframe.style.opacity = 1; // 显示左侧信息卡片(仅在iframe加载完成后) const sidebarInfo = container.querySelector('.ns-sidebar-info'); if (sidebarInfo) { sidebarInfo.style.display = 'flex'; } // 显示前后帖子导航卡片 const prevNavCard = container.querySelector('.ns-post-nav-card.prev'); if (prevNavCard) { prevNavCard.style.display = 'flex'; } const nextNavCard = container.querySelector('.ns-post-nav-card.next'); if (nextNavCard) { nextNavCard.style.display = 'flex'; } // 监听iframe内部主题变化并同步到外部UI const iframeObserver = new MutationObserver((mutations) => { mutations.forEach((mutation) => { if (mutation.attributeName === 'class') { // 检查iframe内的主题 const iframeIsDark = iframeDoc.body.classList.contains('dark-layout'); const parentIsDark = document.body.classList.contains('dark-layout'); // 如果主题不一致,同步父页面主题与iframe if (iframeIsDark !== parentIsDark) { if (iframeIsDark) { document.body.classList.add('dark-layout'); document.body.classList.remove('light-layout'); } else { document.body.classList.add('light-layout'); document.body.classList.remove('dark-layout'); } // 应用新主题 applyTheme(); } } }); }); // 开始观察iframe内body的class变化 iframeObserver.observe(iframeDoc.body, { attributes: true }); } catch (error) { console.error('无法修改iframe内容:', error); // 出错时也显示iframe,确保用户能看到内容 wrapper.removeChild(loaderContainer); iframe.style.opacity = 1; // 即使出错也显示左侧信息卡片 const sidebarInfo = container.querySelector('.ns-sidebar-info'); if (sidebarInfo) { sidebarInfo.style.display = 'flex'; } // 即使出错也显示前后帖子导航卡片 const prevNavCard = container.querySelector('.ns-post-nav-card.prev'); if (prevNavCard) { prevNavCard.style.display = 'flex'; } const nextNavCard = container.querySelector('.ns-post-nav-card.next'); if (nextNavCard) { nextNavCard.style.display = 'flex'; } } }; // 组装元素 wrapper.appendChild(closeBtn); wrapper.appendChild(loaderContainer); // 先添加加载指示器容器 wrapper.appendChild(iframe); container.appendChild(wrapper); document.body.appendChild(container); // 点击遮罩层关闭iframe container.addEventListener('click', function(e) { // 只有点击遮罩层本身才关闭,点击iframe内部不触发 if (e.target === container) { closeIframe(); } }); // 阻止iframe包装器的点击事件冒泡 wrapper.addEventListener('click', function(e) { e.stopPropagation(); }); // 关闭iframe的函数 function closeIframe() { document.body.removeChild(container); document.removeEventListener('keydown', escListener); // 恢复父页面滚动 document.body.style.overflow = originalBodyOverflow; } // 按ESC键关闭iframe function escListener(e) { if (e.key === 'Escape') { closeIframe(); } } document.addEventListener('keydown', escListener); // 加载新帖子的函数 - 移到openInIframe内部以便访问container变量 function loadNewPost(url, currentDomElements) { // 获取当前的postMap和postOrder const postMap = currentDomElements.postMap; const postOrder = currentDomElements.postOrder; if (!postMap || !postOrder) return; // 获取新帖子的信息 const newPostInfo = postMap.get(url); if (!newPostInfo) return; const newPostIndex = newPostInfo.index; const newPostElement = newPostInfo.element; if (!newPostElement) return; // 获取新帖子的详细信息 const postLink = newPostElement.querySelector('.post-title a[href^="/post-"]'); if (!postLink) return; const postTitle = postLink.textContent.trim(); const avatarImg = newPostElement.querySelector('.avatar-normal, img.avatar'); const userElement = newPostElement.querySelector('.author-name, .post-username, .username'); const postInfoElement = newPostElement.querySelector('.post-info, .info'); // 获取新帖子的前后帖子信息 let prevPost = null; let nextPost = null; // 获取前一篇帖子 if (newPostIndex > 0) { const prevUrl = postOrder[newPostIndex - 1]; const prevInfo = postMap.get(prevUrl); if (prevInfo) { prevPost = { url: prevUrl, title: prevInfo.title }; } } // 获取后一篇帖子 if (newPostIndex < postOrder.length - 1) { const nextUrl = postOrder[newPostIndex + 1]; const nextInfo = postMap.get(nextUrl); if (nextInfo) { nextPost = { url: nextUrl, title: nextInfo.title }; } } // 更新左侧信息卡片 const sidebarInfo = container.querySelector('.ns-sidebar-info'); if (sidebarInfo) { // 临时隐藏侧边栏,避免旧内容闪烁 sidebarInfo.style.display = 'none'; // 清空现有内容 sidebarInfo.innerHTML = ''; // 添加帖子标题 if (postTitle) { const titleElement = document.createElement('div'); titleElement.className = 'title'; titleElement.textContent = postTitle; titleElement.style.cssText = ` font-size: 18px; font-weight: bold; margin: 0 0 15px 0; text-align: center; color: var(--ns-card-text-color); width: 100%; `; sidebarInfo.appendChild(titleElement); } // 添加头像 if (avatarImg) { const avatarContainer = document.createElement('div'); avatarContainer.className = 'avatar-container'; // 克隆原始头像并添加样式 const avatarClone = avatarImg.cloneNode(true); avatarContainer.appendChild(avatarClone); sidebarInfo.appendChild(avatarContainer); } // 添加用户名和其他信息 if (userElement) { const userInfo = document.createElement('div'); userInfo.className = 'user-info'; const usernameClone = userElement.cloneNode(true); userInfo.appendChild(usernameClone); sidebarInfo.appendChild(userInfo); } // 添加帖子信息 if (postInfoElement) { const postInfo = document.createElement('div'); postInfo.className = 'post-info'; const infoClone = postInfoElement.cloneNode(true); postInfo.appendChild(infoClone); sidebarInfo.appendChild(postInfo); } } // 更新前后帖子导航卡片 // 移除旧的导航卡片 const oldPrevCard = container.querySelector('.ns-post-nav-card.prev'); if (oldPrevCard) { container.removeChild(oldPrevCard); } const oldNextCard = container.querySelector('.ns-post-nav-card.next'); if (oldNextCard) { container.removeChild(oldNextCard); } // 添加新的前一篇导航卡片(如果有) if (prevPost) { const prevNav = document.createElement('div'); prevNav.className = 'ns-post-nav-card prev'; prevNav.innerHTML = ` `; prevNav.setAttribute('data-url', prevPost.url); prevNav.addEventListener('click', function() { loadNewPost(this.getAttribute('data-url'), { postMap: postMap, postOrder: postOrder }); }); container.appendChild(prevNav); } // 添加新的下一篇导航卡片(如果有) if (nextPost) { const nextNav = document.createElement('div'); nextNav.className = 'ns-post-nav-card next'; nextNav.innerHTML = ` `; nextNav.setAttribute('data-url', nextPost.url); nextNav.addEventListener('click', function() { loadNewPost(this.getAttribute('data-url'), { postMap: postMap, postOrder: postOrder }); }); container.appendChild(nextNav); } // 显示加载指示器 const newLoaderContainer = document.createElement('div'); newLoaderContainer.className = 'ns-loader-container'; newLoaderContainer.style.cssText = ` position: absolute; top: 0; left: 0; width: 100%; height: 100%; display: flex; flex-direction: column; justify-content: center; align-items: center; background-color: var(--ns-loader-bg); z-index: 10; `; // 添加帖子信息到加载指示器,与初始加载时保持一致 if (postTitle || avatarImg || userElement || postInfoElement) { // 创建简单信息容器用于加载界面 const infoContainer = document.createElement('div'); infoContainer.style.cssText = ` display: flex; flex-direction: column; align-items: center; width: 100%; max-width: 600px; padding: 10px 20px 20px; margin-bottom: 20px; text-align: center; `; // 添加头像元素,放在第一行居中 if (avatarImg) { const avatarContainer = document.createElement('div'); avatarContainer.style.cssText = ` display: flex; justify-content: center; margin-bottom: 15px; width: 100%; `; const avatarClone = avatarImg.cloneNode(true); avatarContainer.appendChild(avatarClone); infoContainer.appendChild(avatarContainer); } // 添加标题,放在第二行居中 if (postTitle) { const titleElement = document.createElement('div'); titleElement.textContent = postTitle; titleElement.style.cssText = ` font-size: 18px; font-weight: bold; margin: 0 0 15px 0; text-align: center; color: var(--ns-card-text-color); width: 100%; `; infoContainer.appendChild(titleElement); } // 用户名和帖子信息,放在第三行居中 const infoWrapper = document.createElement('div'); infoWrapper.style.cssText = ` display: flex; flex-direction: column; align-items: center; width: 100%; margin-bottom: 20px; `; // 添加用户名元素 if (userElement) { const usernameContainer = document.createElement('div'); usernameContainer.style.cssText = ` display: flex; justify-content: center; width: 100%; margin-bottom: 5px; `; const usernameClone = userElement.cloneNode(true); usernameContainer.appendChild(usernameClone); infoWrapper.appendChild(usernameContainer); } // 添加帖子信息元素 if (postInfoElement) { const infoElementContainer = document.createElement('div'); infoElementContainer.style.cssText = ` display: flex; justify-content: center; width: 100%; `; const infoClone = postInfoElement.cloneNode(true); infoElementContainer.appendChild(infoClone); infoWrapper.appendChild(infoElementContainer); } infoContainer.appendChild(infoWrapper); // 创建加载指示器 const loader = document.createElement('div'); loader.className = 'ns-loader'; loader.style.cssText = ` width: 40px; height: 40px; margin: 100px 0; border: 2px solid var(--ns-loader-border); border-top: 2px solid #2ea44f; border-radius: 50%; animation: spin 1s linear infinite; `; infoContainer.appendChild(loader); newLoaderContainer.appendChild(infoContainer); } else { // 如果没有帖子信息,只显示加载指示器 const loader = document.createElement('div'); loader.className = 'ns-loader'; loader.style.cssText = ` width: 40px; height: 40px; border: 2px solid var(--ns-loader-border); border-top: 2px solid #2ea44f; border-radius: 50%; animation: spin 1s linear infinite; `; newLoaderContainer.appendChild(loader); } // 更新iframe的src iframe.style.opacity = 0; wrapper.appendChild(newLoaderContainer); iframe.src = url; // iframe加载完成后,移除加载指示器并显示左侧信息卡片 iframe.onload = function() { try { // 原有的iframe加载完成处理代码... const iframeDoc = iframe.contentDocument || iframe.contentWindow.document; // 移除header和footer const header = iframeDoc.querySelector('header'); const footer = iframeDoc.querySelector('footer'); if (header) header.style.display = 'none'; if (footer) footer.style.display = 'none'; // 移除右侧面板 const rightPanel = iframeDoc.getElementById('nsk-right-panel-container'); if (rightPanel) rightPanel.style.display = 'none'; // 添加自定义样式到iframe内部 const styleElement = iframeDoc.createElement('style'); styleElement.textContent = ` /* 隐藏可能的导航和页脚 */ header, .header, #header, footer, .footer, #footer, nav, .nav, #nav, .site-header, .site-footer, .main-header, .main-footer, .page-header, .page-footer, #nsk-right-panel-container { display: none !important; } /* 调整主要内容区域 */ body { padding-top: 0 !important; margin-top: 0 !important; } /* 让内容区域占满整个空间 */ .container, .main-content, .content, #content, .page-content, .site-content, main, .main, #main { padding-top: 10px !important; margin-top: 0 !important; max-width: 100% !important; } /* 调整文章内容宽度,右侧面板被移除后 */ .post-detail-card { width: 100% !important; max-width: 100% !important; } /* 强制设置nsk-container的margin为0 */ .nsk-container { margin: 0 !important; } /* 平滑滚动效果 */ html { scroll-behavior: smooth; } `; iframeDoc.head.appendChild(styleElement); // 添加表情包搜索功能到评论框 addEmojiSearchToCommentBox(iframeDoc); // 处理翻页链接,拦截点击实现无刷新翻页 setupPagination(iframeDoc, iframe); // 处理完成后移除加载指示器并显示iframe wrapper.removeChild(newLoaderContainer); iframe.style.opacity = 1; // 显示左侧信息卡片 if (sidebarInfo) { sidebarInfo.style.display = 'flex'; } // 显示前后帖子导航卡片 const prevNavCard = container.querySelector('.ns-post-nav-card.prev'); if (prevNavCard) { prevNavCard.style.display = 'flex'; } const nextNavCard = container.querySelector('.ns-post-nav-card.next'); if (nextNavCard) { nextNavCard.style.display = 'flex'; } } catch (error) { console.error('无法修改iframe内容:', error); // 出错时也显示iframe,确保用户能看到内容 wrapper.removeChild(newLoaderContainer); iframe.style.opacity = 1; // 即使出错也显示左侧信息卡片 if (sidebarInfo) { sidebarInfo.style.display = 'flex'; } // 即使出错也显示前后帖子导航卡片 const prevNavCard = container.querySelector('.ns-post-nav-card.prev'); if (prevNavCard) { prevNavCard.style.display = 'flex'; } const nextNavCard = container.querySelector('.ns-post-nav-card.next'); if (nextNavCard) { nextNavCard.style.display = 'flex'; } } }; } } // 添加表情包搜索功能到评论框 function addEmojiSearchToCommentBox(doc) { // 监听评论框加载 const checkCommentBox = setInterval(() => { const commentBox = doc.querySelector('.md-editor'); if (commentBox) { clearInterval(checkCommentBox); // 找到表情选择区域 const expressionArea = commentBox.querySelector('.expression'); if (!expressionArea) return; // 检查是否已经存在搜索框,避免重复添加 const existingSearchContainer = expressionArea.parentNode.querySelector('.ns-emoji-search'); if (existingSearchContainer) return; // 创建表情包搜索容器 const searchContainer = doc.createElement('div'); searchContainer.className = 'ns-emoji-search'; searchContainer.style.position = 'relative'; searchContainer.style.display = 'flex'; // 直接显示 // 创建搜索输入框 const searchInput = doc.createElement('input'); searchInput.type = 'text'; searchInput.placeholder = '搜索表情包...'; // 创建结果显示区域 const resultsContainer = doc.createElement('div'); resultsContainer.className = 'ns-emoji-results'; resultsContainer.style.display = 'none'; // 创建调试信息区域 const debugContainer = doc.createElement('div'); debugContainer.className = 'ns-debug-info'; debugContainer.style.display = 'none'; // 组装元素 searchContainer.appendChild(searchInput); searchContainer.appendChild(resultsContainer); searchContainer.appendChild(debugContainer); // 将搜索容器直接添加到expression元素后面 expressionArea.parentNode.insertBefore(searchContainer, expressionArea.nextSibling); // 获取编辑器实例 const editorArea = commentBox.querySelector('#code-mirror-editor'); let cmEditor = null; // 尝试获取CodeMirror编辑器实例 if (editorArea) { const cmElement = editorArea.querySelector('.CodeMirror'); if (cmElement && cmElement.CodeMirror) { cmEditor = cmElement.CodeMirror; } } // 回车键搜索 searchInput.addEventListener('keydown', async (e) => { if (e.key === 'Enter') { const query = searchInput.value.trim(); if (!query) return; // 显示"正在搜索"提示 resultsContainer.innerHTML = '