// ==UserScript== // @name ✨超星学习通智能阅读助手(URL检测自动跳转版) // @version 1.0.1 // @description 超星学习通智能阅读增强工具 📚,支持逐段 / 整页双模式自动阅读 ⏩,结合实时监听与定时检查实现 URL 变化精准检测 🔍,自动识别并跳转下一章节 ➡️,提供全键盘控制(K 开始 / Z 暂停 / S 设置 / R 跳转)⌨️ 及个性化配置中心 ⚙️,适配超星全平台课程页面,提升阅读效率与学习体验 ✨ // @author 伏黑甚而 // @match *://*.chaoxing.com/* // @match *://mooc1-*.chaoxing.com/* // @grant GM_registerMenuCommand // @grant GM_notification // @grant GM_xmlhttpRequest // @grant GM_getValue // @grant GM_setValue // @grant unsafeWindow // @connect chaoxing.com // @run-at document-end // @icon http://pan-yz.chaoxing.com/favicon.ico // ==/UserScript== (function() { 'use strict'; // 全局配置 const CONFIG = { scrollSpeed: parseFloat(GM_getValue('scrollSpeed', 0.5)), scrollMode: GM_getValue('scrollMode', 'paragraph'), autoStart: GM_getValue('autoStart', true), restartAfterFinish: GM_getValue('restartAfterFinish', true), showTips: GM_getValue('showTips', true), debugMode: GM_getValue('debugMode', true), autoRedirect: GM_getValue('autoRedirect', true), maxIframeDepth: 10, urlChangeDetection: true, urlChangeDebounce: 500, monitorNextButton: true, nextButtonSelector: '#right1', urlCheckInterval: 1000, // 新增:URL检查间隔(ms) }; // 状态管理 const STATE = { isRunning: false, isPaused: false, currentChapter: 0, totalChapters: 0, contentElements: [], currentElementIndex: 0, lastButton: null, lastUrl: window.location.href, urlChangeTimer: null, urlCheckTimer: null, // 新增:URL检查定时器 nextButtonObserver: null, }; // 日志与通知 function log(message, level = 'info') { if (!CONFIG.debugMode && level !== 'error') return; console.log(`[超星阅读助手] [${level.toUpperCase()}] ${message}`); } function notify(message, type = 'info') { const icons = { info: 'ℹ️', success: '✅', warning: '⚠️', error: '❌' }; GM_notification({ title: `${icons[type]} 超星阅读助手`, text: message, silent: true, timeout: 5000 }); log(message, type); } // DOM工具 function waitForElement(selector, timeout = 15000) { return new Promise(resolve => { const interval = setInterval(() => { const el = document.querySelector(selector); if (el) { clearInterval(interval); resolve(el); } }, 100); setTimeout(() => clearInterval(interval), timeout); }); } // 页面操作类 class PageOperator { constructor() { this.init(); this.setupUrlChangeDetection(); this.setupNextButtonMonitoring(); this.startUrlCheckTimer(); // 新增:启动URL定时检查 } init() { this.bindEvents(); this.detectPageType(); log(`脚本初始化,版本:${GM_info.script.version}`); } // 新增:启动URL定时检查 startUrlCheckTimer() { if (STATE.urlCheckTimer) clearInterval(STATE.urlCheckTimer); STATE.urlCheckTimer = setInterval(() => { if (window.location.href !== STATE.lastUrl) { log('定时检测到URL变化'); this.handleUrlChange(); } }, CONFIG.urlCheckInterval); log(`URL定时检查已启动,间隔:${CONFIG.urlCheckInterval}ms`); } setupNextButtonMonitoring() { if (!CONFIG.monitorNextButton) return; this.findAndBindNextButton(); const observer = new MutationObserver(() => { this.findAndBindNextButton(); }); observer.observe(document.body, { childList: true, subtree: true, attributes: true }); STATE.nextButtonObserver = observer; log('下一节按钮监控已启用'); } findAndBindNextButton() { try { const nextButton = document.querySelector(CONFIG.nextButtonSelector); if (nextButton && !nextButton._hasClickHandler) { log('找到下一节按钮,绑定点击事件'); const originalOnClick = nextButton.getAttribute('onclick'); nextButton.addEventListener('click', (e) => { log('检测到下一节按钮点击'); if (originalOnClick) { eval(originalOnClick); } this.onNextButtonClicked(); }); nextButton._hasClickHandler = true; log('下一节按钮点击事件绑定成功'); } } catch (error) { log(`绑定下一节按钮失败: ${error.message}`, 'error'); } } onNextButtonClicked() { notify('检测到章节切换,准备加载新内容...'); const wasRunning = STATE.isRunning; const wasPaused = STATE.isPaused; this.resetState(); this.showModal(`

超星阅读助手

正在加载下一章内容...

请稍候...

`); setTimeout(() => { this.hideModal(); this.detectPageType(); if (wasRunning && !wasPaused) { setTimeout(() => { if (this.isReadingPage()) { this.startAutoRead(); } }, 2000); } }, 3000); } // 增强URL变化检测 setupUrlChangeDetection() { if (!CONFIG.urlChangeDetection) return; // 监听hashchange事件 window.addEventListener('hashchange', () => { log('检测到hashchange事件'); this.handleUrlChange(); }); // 监听popstate事件 window.addEventListener('popstate', () => { log('检测到popstate事件'); this.handleUrlChange(); }); // 使用MutationObserver监听DOM变化 const observer = new MutationObserver(() => { if (window.location.href !== STATE.lastUrl) { log('MutationObserver检测到URL变化'); this.handleUrlChange(); } }); observer.observe(document.body, { childList: true, subtree: true, attributes: true }); log('URL变化检测已启用(实时监听+定时检查)'); } handleUrlChange() { const newUrl = window.location.href; if (newUrl !== STATE.lastUrl) { log(`URL变化: ${STATE.lastUrl} → ${newUrl}`); STATE.lastUrl = newUrl; clearTimeout(STATE.urlChangeTimer); STATE.urlChangeTimer = setTimeout(() => { this.resetState(); this.detectPageType(); if (this.isReadingPage() && CONFIG.autoStart) { setTimeout(() => this.startAutoRead(), 2000); } }, CONFIG.urlChangeDebounce); } } resetState() { STATE.isRunning = false; STATE.isPaused = false; STATE.contentElements = []; STATE.currentElementIndex = 0; log('状态已重置'); } bindEvents() { document.addEventListener('keydown', e => { if (e.key === 'k') this.startAutoRead(); if (e.key === 'z') this.pauseAutoRead(); if (e.key === 's') this.showSettings(); if (e.key === 'r') this.toggleAutoRedirect(); }); } detectPageType() { const oldTips = document.querySelector('#auto-read-tips'); if (oldTips) oldTips.remove(); if (this.isReadingPage()) { log('检测到阅读页面'); this.initReading(); } else if (this.isCoursePage()) { log('检测到课程主页'); this.addAutoRedirectButton(); CONFIG.autoRedirect && this.detectReadingTasks(); } else if (location.href.includes('/mooc-ans/course/')) { log('检测到课程目录/任务列表页面 (新)'); this.handleCourseCatalogPage(); } else if (this.isTaskPage()) { log('检测到任务页面 (旧)'); this.handleGenericTaskPage(); } } isReadingPage() { return location.href.includes('/ztnodedetailcontroller/visitnodedetail'); } isCoursePage() { return location.href.includes('/mooc-ans/mycourse/studentstudy'); } isTaskPage() { return location.href.includes('pageHeader=0'); } initReading() { this.showUsageTips(); this.detectChapterInfo(); this.collectContentElements(); if (CONFIG.autoStart) { setTimeout(() => { if (this.isReadingPage()) { this.startAutoRead(); } }, 2000); } } startAutoRead() { if (STATE.isRunning && !STATE.isPaused) return; STATE.isRunning = true; STATE.isPaused = false; if (CONFIG.scrollMode === 'paragraph') { this.startParagraphScroll(); } else { this.startPageScroll(); } this.showStatus(); notify(`开始阅读 (${CONFIG.scrollSpeed}秒/${CONFIG.scrollMode === 'paragraph' ? '段落' : '页'})`); } pauseAutoRead() { if (!STATE.isRunning) return; STATE.isPaused = true; clearTimeout(this.scrollTimer); this.showStatus(); notify('阅读已暂停'); } showStatus() { const status = STATE.isPaused ? '已暂停' : '阅读中'; const progress = STATE.totalChapters > 0 ? `第 ${STATE.currentChapter}/${STATE.totalChapters} 章` : '章节信息未知'; this.showModal(`

超星阅读助手

状态: ${status}

${progress}

模式: ${CONFIG.scrollMode === 'paragraph' ? '段落阅读' : '页面阅读'}

速度: ${CONFIG.scrollSpeed.toFixed(1)}秒/${CONFIG.scrollMode === 'paragraph' ? '段落' : '页'}

按 Z 键暂停 / 按 K 键继续

`); setTimeout(() => this.hideModal(), 3000); } startParagraphScroll() { this.collectContentElements(); if (STATE.contentElements.length === 0) { notify('未检测到段落,临时切换整页模式...', 'warning'); CONFIG.scrollMode = 'page'; this.startPageScroll(); // 启动内容监听器 const observer = new MutationObserver(() => { this.collectContentElements(); if (STATE.contentElements.length > 0) { observer.disconnect(); notify('检测到段落内容,恢复逐段模式', 'success'); CONFIG.scrollMode = 'paragraph'; if (!STATE.isPaused) { this.startParagraphScroll(); } } }); observer.observe(document.body, { childList: true, subtree: true, attributes: true }); return; } this.scrollToNextElement(); } collectContentElements() { const selectors = [ 'p', 'h1', 'h2', 'h3', 'h4', 'h5', 'h6', 'img', 'video', 'iframe', '.content', '.text-block', '.article-content' ]; STATE.contentElements = []; selectors.forEach(selector => { const elements = document.querySelectorAll(selector); elements.forEach(el => { if (el.offsetHeight > 20 && el.offsetWidth > 20) { STATE.contentElements.push(el); } }); }); STATE.contentElements.sort((a, b) => { return a.getBoundingClientRect().top - b.getBoundingClientRect().top; }); log(`找到 ${STATE.contentElements.length} 个内容元素`); } scrollToNextElement() { if (STATE.isPaused) return; if (STATE.currentElementIndex >= STATE.contentElements.length) { this.onChapterComplete(); return; } const element = STATE.contentElements[STATE.currentElementIndex]; if (CONFIG.debugMode) { element.style.outline = '2px solid red'; setTimeout(() => element.style.outline = '', 1000); } element.scrollIntoView({ behavior: 'smooth', block: 'center' }); const baseTime = parseFloat(CONFIG.scrollSpeed) * 1000; let waitTime = element.tagName === 'IMG' || element.tagName === 'VIDEO' ? baseTime * 1.5 : baseTime; STATE.currentElementIndex++; this.scrollTimer = setTimeout(() => this.scrollToNextElement(), waitTime); } startPageScroll() { const scrollSpeed = parseFloat(CONFIG.scrollSpeed); const totalHeight = document.documentElement.scrollHeight - window.innerHeight; const scrollStep = totalHeight / (scrollSpeed * 10); let currentTop = 0; clearInterval(this.scrollTimer); this.scrollTimer = setInterval(() => { if (STATE.isPaused) return; currentTop += scrollStep; if (currentTop >= totalHeight) { clearInterval(this.scrollTimer); this.onChapterComplete(); } else { window.scrollTo({ top: currentTop, behavior: 'smooth' }); } }, 100); } onChapterComplete() { clearTimeout(this.scrollTimer); STATE.currentElementIndex = 0; STATE.contentElements = []; this.findNextButton() .then(nextButton => { if (nextButton) { notify('正在加载下一章...'); nextButton.style.outline = '3px solid green'; setTimeout(() => nextButton.style.outline = '', 2000); nextButton.click(); STATE.currentChapter++; } else { if (CONFIG.restartAfterFinish) { notify('已到达最后一章,即将从头开始', 'warning'); setTimeout(() => this.goToFirstChapter(), 3000); } else { STATE.isRunning = false; notify('全部阅读完成!', 'success'); } } }) .catch(error => { notify(`章节切换错误: ${error.message}`, 'error'); log(error.stack, 'error'); }); } findNextButton() { return new Promise(resolve => { const selectors = [ CONFIG.nextButtonSelector, '.nodeItem.r i', '.next-page-btn', 'a:contains("下一章")', 'button:contains("下一章")', 'a[title="下一章"]', '.reader__control--next', '.uxp-pager-next', 'button[aria-label*="下一章"]', '.next-btn:visible' ]; for (const selector of selectors) { try { let element = document.querySelector(selector); if (!element && selector.includes(':contains(')) { const text = selector.match(/:contains\("(.*)"\)/)[1]; const allElements = document.querySelectorAll('a, button'); element = Array.from(allElements).find(el => el.textContent.includes(text)); } if (element) { log(`找到下一章按钮: ${selector}`); return resolve(element); } } catch (error) { // 继续尝试下一个选择器 } } log('未找到下一章按钮,尝试通用选择器', 'warning'); const genericElements = document.querySelectorAll('*'); for (const el of genericElements) { if (el.textContent.includes('下一章') && el.offsetWidth > 0 && el.offsetHeight > 0) { log('找到下一章按钮(通用选择器)'); return resolve(el); } } log('未找到下一章按钮', 'warning'); resolve(null); }); } goToFirstChapter() { try { const firstChapter = document.querySelector('.course_section .chapterText, .catalog-item:first-child'); if (firstChapter) { firstChapter.click(); setTimeout(() => { STATE.currentChapter = 1; if (CONFIG.scrollMode === 'paragraph') { this.collectContentElements(); } if (!STATE.isPaused) { this.scrollToNextElement(); } }, 3000); } else { notify('未找到目录,无法重新开始', 'error'); STATE.isRunning = false; } } catch (error) { notify(`跳转错误: ${error.message}`, 'error'); STATE.isRunning = false; } } // 自动跳转相关方法 async detectReadingTasks() { log('开始检测阅读任务'); const found = await this.detectIframes(document, 0); if (!found) { await new Promise(resolve => setTimeout(resolve, 500)); if (!STATE.lastButton) { notify('未找到阅读任务', 'warning'); } } else { log('成功找到并处理阅读按钮', 'info'); } } detectIframes(parentDoc, depth) { return new Promise(async resolve => { if (depth > CONFIG.maxIframeDepth) { log(`达到最大iframe深度(${CONFIG.maxIframeDepth})`, 'info'); return resolve(false); } log(`检测第${depth}层文档`); try { await waitForElement('#chapterContent, .content, .text-block, .article-content, body', 15000); log(`第${depth}层文档内容加载完毕,准备查找按钮`); await new Promise(resolve => setTimeout(resolve, 500)); } catch (error) { log(`等待第${depth}层文档内容加载超时: ${error.message}`, 'warning'); } const buttons = this.findReadingButtons(parentDoc); if (buttons.length > 0) { log(`在第${depth}层找到${buttons.length}个阅读按钮`); const success = await this.triggerButtonClick(buttons[0], parentDoc); if (success) { return resolve(true); } } else { log(`在第${depth}层文档未找到阅读按钮`, 'info'); } const iframes = parentDoc.querySelectorAll('iframe'); for (const iframe of iframes) { try { const iframeDoc = iframe.contentDocument || iframe.contentWindow.document; if (iframeDoc) { const foundInIframe = await this.detectIframes(iframeDoc, depth + 1); if (foundInIframe) { return resolve(true); } } else { log('无法获取iframe内容', 'warning'); } } catch (error) { log(`访问iframe失败: ${error.message}`, 'error'); } } resolve(false); }); } findReadingButtons(doc) { const buttonTexts = ['去阅读', '开始阅读', '立即阅读', '阅读全文', '进入阅读']; const buttonElements = []; // 先尝试精确匹配 buttonTexts.forEach(text => { const elements = doc.querySelectorAll('a, button, span, div'); elements.forEach(el => { if (el.textContent.trim() === text && el.offsetWidth > 0 && el.offsetHeight > 0 && getComputedStyle(el).display !== 'none') { buttonElements.push(el); } }); }); // 如果没找到,尝试模糊匹配 if (buttonElements.length === 0) { buttonTexts.forEach(text => { const elements = doc.querySelectorAll('a, button, span, div'); elements.forEach(el => { if (el.textContent.includes(text) && el.offsetWidth > 0 && el.offsetHeight > 0 && getComputedStyle(el).display !== 'none') { buttonElements.push(el); } }); }); } return buttonElements; } triggerButtonClick(button, doc) { return new Promise(async resolve => { if (!button) { log('没有提供按钮元素', 'error'); return resolve(false); } try { button.style.outline = '3px solid red'; button.style.transition = 'outline 0.3s ease'; STATE.lastButton = button; button.scrollIntoView({ block: 'center', behavior: 'smooth' }); await new Promise(resolve => setTimeout(resolve, 500)); button.click(); log('尝试点击按钮', 'info'); notify('已尝试点击阅读按钮'); resolve(true); } catch (error) { log(`点击按钮失败: ${error.message}`, 'error'); notify('点击按钮失败', 'error'); resolve(false); } finally { setTimeout(() => { if (STATE.lastButton) STATE.lastButton.style.outline = ''; }, 2000); } }); } _extractHref(element) { try { if (element.href && element.href.includes('chaoxing.com')) { return element.href; } const parentLink = element.closest('a'); if (parentLink && parentLink.href && parentLink.href.includes('chaoxing.com')) { return parentLink.href; } const dataHref = element.getAttribute('data-href'); if (dataHref && dataHref.includes('chaoxing.com')) { return dataHref; } const onclick = element.getAttribute('onclick'); if (onclick) { const hrefMatch = onclick.match(/window\.location\.href=['"]([^'"]+)['"]/); if (hrefMatch && hrefMatch[1] && hrefMatch[1].includes('chaoxing.com')) { return hrefMatch[1]; } const goToMatch = onclick.match(/goTo\(['"]([^'"]+)['"]\)/); if (goToMatch && goToMatch[1] && goToMatch[1].includes('chaoxing.com')) { return goToMatch[1]; } } return null; } catch (error) { log(`提取链接失败: ${error.message}`, 'error'); return null; } } // 自动跳转控制按钮 addAutoRedirectButton() { try { const controlButton = document.createElement('div'); controlButton.id = 'auto-redirect-control'; controlButton.style.cssText = ` position: fixed; top: 20px; right: 20px; background: rgba(0,0,0,0.7); color: white; padding: 10px; border-radius: 5px; z-index: 9999; font-family: Arial, sans-serif; font-size: 14px; cursor: pointer; display: flex; align-items: center; gap: 5px; `; const updateButtonState = () => { controlButton.innerHTML = ` ${CONFIG.autoRedirect ? '🟢' : '⚪'} 自动跳转: ${CONFIG.autoRedirect ? '开启' : '关闭'} `; }; updateButtonState(); controlButton.addEventListener('click', () => { CONFIG.autoRedirect = !CONFIG.autoRedirect; GM_setValue('autoRedirect', CONFIG.autoRedirect); updateButtonState(); notify(`自动跳转已${CONFIG.autoRedirect ? '开启' : '关闭'}`); if (CONFIG.autoRedirect) { setTimeout(() => this.detectReadingTasks(), 1000); } }); document.body.appendChild(controlButton); } catch (error) { log(`添加自动跳转控制按钮失败: ${error.message}`, 'error'); } } // 键盘事件初始化 initKeyboardEvents() { if (!window.location.href.includes('/ztnodedetailcontroller/visitnodedetail')) return; document.addEventListener('keydown', (e) => { if (e.key.toLowerCase() === 'k') { e.preventDefault(); this.startAutoRead(); } if (e.key.toLowerCase() === 'z') { e.preventDefault(); this.pauseAutoRead(); } if (e.key.toLowerCase() === 's') { e.preventDefault(); this.showSettings(); } if (e.key.toLowerCase() === 'r') { e.preventDefault(); CONFIG.autoRedirect = !CONFIG.autoRedirect; GM_setValue('autoRedirect', CONFIG.autoRedirect); notify(`自动跳转已${CONFIG.autoRedirect ? '开启' : '关闭'}`); this.showUsageTips(); } }); } // 设置菜单 showSettings() { const html = `

超星阅读助手设置

生活不易,猪猪叹气 —— 赏口饲料,让我少气!🐷✨

支持 0.1 - 30 秒(如:0.5 秒/页,1.2 秒/段落)
检测嵌套iframe的最大层数
自动检测章节切换
单位: 毫秒
专门监控特定"下一节"按钮
CSS选择器,默认为#right1
单位: 毫秒,值越小检测越频繁
`; this.showModal(html); document.getElementById('save-settings').addEventListener('click', () => { CONFIG.scrollSpeed = parseFloat(document.getElementById('scroll-speed').value).toFixed(1); CONFIG.scrollMode = document.getElementById('scroll-mode').value; CONFIG.autoStart = document.getElementById('auto-start').checked; CONFIG.restartAfterFinish = document.getElementById('restart-after-finish').checked; CONFIG.showTips = document.getElementById('show-tips').checked; CONFIG.debugMode = document.getElementById('debug-mode').checked; CONFIG.autoRedirect = document.getElementById('auto-redirect').checked; CONFIG.maxIframeDepth = parseInt(document.getElementById('iframe-depth').value); CONFIG.urlChangeDetection = document.getElementById('url-change-detection').checked; CONFIG.urlChangeDebounce = parseInt(document.getElementById('url-change-debounce').value); CONFIG.monitorNextButton = document.getElementById('monitor-next-button').checked; CONFIG.nextButtonSelector = document.getElementById('next-button-selector').value; CONFIG.urlCheckInterval = parseInt(document.getElementById('url-check-interval').value); GM_setValue('scrollSpeed', CONFIG.scrollSpeed.toString()); GM_setValue('scrollMode', CONFIG.scrollMode); GM_setValue('autoStart', CONFIG.autoStart); GM_setValue('restartAfterFinish', CONFIG.restartAfterFinish); GM_setValue('showTips', CONFIG.showTips); GM_setValue('debugMode', CONFIG.debugMode); GM_setValue('autoRedirect', CONFIG.autoRedirect); GM_setValue('maxIframeDepth', CONFIG.maxIframeDepth); GM_setValue('urlChangeDetection', CONFIG.urlChangeDetection); GM_setValue('urlChangeDebounce', CONFIG.urlChangeDebounce); GM_setValue('monitorNextButton', CONFIG.monitorNextButton); GM_setValue('nextButtonSelector', CONFIG.nextButtonSelector); GM_setValue('urlCheckInterval', CONFIG.urlCheckInterval); // 更新URL检查定时器 this.startUrlCheckTimer(); notify('设置已保存'); this.hideModal(); }); document.getElementById('close-settings').addEventListener('click', () => { this.hideModal(); }); } // 弹窗相关方法 createModal() { if (document.getElementById('auto-read-modal')) return; const modal = document.createElement('div'); modal.id = 'auto-read-modal'; modal.style.cssText = ` position: fixed; top: 50%; left: 50%; transform: translate(-50%, -50%); background: white; padding: 20px; border-radius: 8px; box-shadow: 0 4px 20px rgba(0,0,0,0.2); z-index: 9999; min-width: 300px; max-width: 80%; display: none; font-family: Arial, sans-serif; `; document.body.appendChild(modal); } showModal(content) { this.createModal(); const modal = document.getElementById('auto-read-modal'); modal.innerHTML = content; modal.style.display = 'block'; } hideModal() { const modal = document.getElementById('auto-read-modal'); if (modal) modal.style.display = 'none'; } // 显示操作提示 showUsageTips() { if (!CONFIG.showTips) return; if (!location.href.includes("/ztnodedetailcontroller/visitnodedetail")) return; const oldTips = document.querySelector('#auto-read-tips'); if (oldTips) oldTips.remove(); const tips = `

超星阅读助手快捷键:

• K: 开始/继续阅读

• Z: 暂停阅读

• S: 显示设置

• R: 切换自动跳转 (${CONFIG.autoRedirect ? '开启' : '关闭'})

`; document.body.insertAdjacentHTML('beforeend', tips); } // 添加快速启动按钮 addQuickStartButton() { try { const courseTitle = document.querySelector('.course-title') || document.querySelector('.course-name') || document.querySelector('h1'); if (courseTitle) { const startButton = document.createElement('button'); startButton.textContent = '🚀 开始自动阅读'; startButton.style.cssText = ` background-color: #4CAF50; color: white; border: none; padding: 8px 16px; margin-left: 10px; border-radius: 4px; cursor: pointer; font-size: 14px; `; startButton.addEventListener('click', () => { const firstChapter = document.querySelector('.course_section .chapterText, .catalog-item:first-child'); if (firstChapter) { firstChapter.click(); setTimeout(() => { this.detectChapterInfo(); this.startAutoRead(); }, 3000); } else { notify('未找到章节列表', 'error'); } }); courseTitle.parentNode.insertBefore(startButton, courseTitle.nextSibling); } } catch (error) { log(`添加快速启动按钮失败: ${error.message}`, 'error'); } } // 处理课程目录页面 async handleCourseCatalogPage() { try { log('尝试查找并跳转到第一个阅读章节链接'); const readingLinkSelector = 'a[href*="/ztnodedetailcontroller/visitnodedetail"]'; const firstReadingLink = await waitForElement(readingLinkSelector, 15000); if (firstReadingLink) { log(`找到第一个阅读章节链接: ${firstReadingLink.href}`); notify('正在跳转到第一个阅读章节...'); window.location.href = firstReadingLink.href; } else { notify('在课程目录/任务列表页面未找到阅读章节链接', 'warning'); } } catch (error) { notify(`处理课程目录页面失败: ${error.message}`, 'error'); } } // 处理通用任务页面 async handleGenericTaskPage() { try { log('处理通用任务页面'); if (top === window) { const chapterLink = await waitForElement('a[title="章节"], a:contains("章节")', 5000); if (chapterLink) { await new Promise(resolve => setTimeout(resolve, 1000)); chapterLink.click(); notify('已自动切换到章节列表页面,点击任意章节即可开始自动学习!'); } else { notify('未找到章节列表按钮', 'warning'); } } } catch (error) { notify(`处理通用任务页面失败: ${error.message}`, 'error'); } } // 检测章节信息 detectChapterInfo() { try { const chapterTitle = document.querySelector('.node-title, .chapter-title, h1')?.textContent || '未知章节'; const chapterElements = document.querySelectorAll('.course_section .chapterText, .catalog-item'); const currentChapterElement = document.querySelector('.course_section .chapterText.active, .catalog-item.active'); if (chapterElements.length > 0) { STATE.totalChapters = chapterElements.length; if (currentChapterElement) { const chapterArray = Array.from(chapterElements); STATE.currentChapter = chapterArray.indexOf(currentChapterElement) + 1; log(`当前章节: ${STATE.currentChapter}/${STATE.totalChapters} - ${chapterTitle}`); } else { log(`总章节数: ${STATE.totalChapters} - 无法确定当前章节`); } } else { log('无法获取章节信息', 'warning'); } } catch (error) { log(`检测章节信息失败: ${error.message}`, 'error'); } } // 切换自动跳转状态 toggleAutoRedirect() { CONFIG.autoRedirect = !CONFIG.autoRedirect; GM_setValue('autoRedirect', CONFIG.autoRedirect); notify(`自动跳转已${CONFIG.autoRedirect ? '开启' : '关闭'}`); const controlButton = document.getElementById('auto-redirect-control'); if (controlButton) { const statusText = controlButton.querySelector('span:last-child'); if (statusText) { statusText.textContent = `自动跳转: ${CONFIG.autoRedirect ? '开启' : '关闭'}`; } } } } // 初始化页面操作类 new PageOperator(); // 添加菜单项 GM_registerMenuCommand("开始自动阅读", () => { const pageOperator = new PageOperator(); if (pageOperator.isReadingPage()) { pageOperator.startAutoRead(); } else { notify('请先导航到阅读页面', 'warning'); } }); GM_registerMenuCommand("显示设置", () => { const pageOperator = new PageOperator(); pageOperator.showSettings(); }); log('脚本已加载 v' + GM_info.script.version); notify('脚本已加载 v' + GM_info.script.version); })();