window.WebAssembly = undefined; const url = new URL(window.location.href); const pathParts = url.pathname.split('/'); const UUID = pathParts.pop() || pathParts.pop(); // DOM元素 const sidebar = document.getElementById('sidebar'); const sidebarToggle = document.getElementById('sidebar-toggle'); const overlay = document.getElementById('overlay'); const sidebarSearch = document.getElementById('sidebar-search'); const themeToggle = document.getElementById('theme-toggle'); const articleContainer = document.getElementById('article-container'); const sidebarNav = document.getElementById('sidebar-nav'); const globalSearch = document.getElementById('global-search'); const searchButton = document.getElementById('search-button'); const headerTitle = document.getElementById('header-title'); // 初始化Marked.js if (window.marked) marked.setOptions({ highlight: function (code, lang) { if (lang && hljs.getLanguage(lang)) { return hljs.highlight(code, { language: lang }).value; } return hljs.highlightAuto(code).value; }, breaks: true, }); // 渲染侧边栏导航 function renderSidebar() { sidebarNav.innerHTML = ''; tableOfContents.chapters.forEach(chapter => { const chapterEl = document.createElement('div'); chapterEl.className = 'mb-1'; const chapterHeader = document.createElement('div'); chapterHeader.className = 'flex items-center justify-between p-2 rounded-lg hover:bg-gray-100 cursor-pointer transition-all duration-300'; chapterHeader.innerHTML = ` ${chapter.title} `; const chapterContent = document.createElement('div'); chapterContent.className = 'pl-4 mt-1 space-y-1 overflow-hidden max-h-0 transition-all duration-300'; // 添加章节链接 const chapterLink = document.createElement('a'); chapterLink.href = `#${chapter.id}`; chapterLink.className = 'block p-2 rounded-lg hover:bg-gray-100 text-sm transition-all duration-300'; chapterLink.textContent = chapter.title; chapterLink.addEventListener('click', (e) => { e.preventDefault(); loadArticleAsync(chapter.id); closeSidebar(); highlightActiveLink(chapter.id); }); chapterContent.appendChild(chapterLink); // 添加子章节链接 if (chapter.sections && chapter.sections.length > 0) { chapter.sections.forEach(section => { const sectionLink = document.createElement('a'); sectionLink.href = `#${section.id}`; sectionLink.className = 'block p-2 rounded-lg hover:bg-gray-100 text-sm transition-all duration-300'; sectionLink.innerHTML = ` ${section.title} `; sectionLink.addEventListener('click', (e) => { e.preventDefault(); loadArticleAsync(section.id); closeSidebar(); highlightActiveLink(section.id); }); chapterContent.appendChild(sectionLink); }); } chapterHeader.addEventListener('click', () => { const isExpanded = chapterContent.classList.contains('expanded'); if (isExpanded) { chapterContent.classList.remove('expanded'); chapterContent.style.maxHeight = '0'; chapterHeader.querySelector('.fa-chevron-down').style.transform = 'rotate(0deg)'; } else { chapterContent.classList.add('expanded'); chapterContent.style.maxHeight = chapterContent.scrollHeight + 'px'; chapterHeader.querySelector('.fa-chevron-down').style.transform = 'rotate(180deg)'; } }); chapterEl.appendChild(chapterHeader); chapterEl.appendChild(chapterContent); sidebarNav.appendChild(chapterEl); }); } // 异步加载文章内容 async function loadArticleAsync(id, password = null) { // 显示现代化加载动画 showLoadingAnimation(); // 找到侧边栏中对应的链接并模拟点击 const link = document.querySelector(`#sidebar-nav a[href="#${id}"]`); let apiUrl = `/API/GetDoc/${id}`; if (password) { apiUrl += `?Pwd=${password}`; } try { // 使用fetch API进行异步请求 const response = await fetch(apiUrl); if (!response.ok) { throw new Error(`HTTP error! status: ${response.status}`); } const content = await response.json(); if (content.DocDecrypt === false) { showPasswordPrompt(content.DocContent, id); } else if (content) { renderArticle(content); } else { showError("未找到文章内容"); } } catch (error) { console.error('Fetch error:', error); showError("加载文章失败,请稍后再试"); } } // 显示加载动画 function showLoadingAnimation() { articleContainer.innerHTML = `

正在加载文章内容...

`; } // 显示错误信息 function showError(message) { articleContainer.innerHTML = `

${message}

请尝试刷新页面或联系管理员

`; } function sanitizeInput(input) { if (typeof input !== 'string') return ''; // 1. 移除 script 标签 let sanitized = input.replace(/]*>([\s\S]*?)<\/script>/gi, ''); // 2. 移除所有事件处理器(包括 onerror) sanitized = sanitized.replace(/\bon\w+\s*=\s*(?:"[^"]*"|'[^']*'|[^"'>\s]+)/gi, ''); // 3. 移除 javascript: 和 data: 协议 sanitized = sanitized.replace(/(javascript|data|vbscript):/gi, ''); // 4. 编码特殊字符 sanitized = sanitized .replace(/\bonerror\s*=\s*"[^"]*"/gi, '') .replace(/\bonerror\s*=\s*'[^']*'/gi, '') .replace(/\bonerror\s*=\s*[^"'>\s]+/gi, ''); return sanitized; } function sanitizeHTML(input) { const parser = new DOMParser(); const doc = parser.parseFromString(input, 'text/html'); const elements = doc.body.getElementsByTagName('*'); for (let element of elements) { const attributes = element.attributes; for (let i = attributes.length - 1; i >= 0; i--) { const attr = attributes[i]; if (attr.name.startsWith('on') || attr.name.match(/^on[a-z]+/i) || ['src', 'href', 'xlink:href'].includes(attr.name.toLowerCase())) { element.removeAttribute(attr.name); } } if (['iframe', 'script', 'object', 'embed'].includes(element.tagName.toLowerCase())) { element.remove(); } } return doc.body.innerHTML; } // 渲染文章内容 function renderArticle(content) { // 解析Markdown并渲染 if (content.DocContent.substring(0, 5) === "`; const IFcontent = document.getElementById("IFcontent"); IFcontent.srcdoc = content.DocContent; IFcontent.onload = function () { IFcontent.style.height = IFcontent.contentWindow.document.body.scrollHeight + 100 + 'px'; IFcontent.contentDocument.body.style.color = document.documentElement.classList.contains('dark') ? '#FFFFFF' : '#000000'; } } else if (content.DocPermission) { articleContainer.innerHTML = marked.parse(content.DocContent); } else { articleContainer.innerHTML = marked.parse(sanitizeInput(content.DocContent)); } // 渲染数学公式 document.querySelectorAll('pre code.language-math, pre code.language-katex, pre code.language-latex').forEach((block) => { const parentPre = block.parentNode; const displayMode = true; const mathDiv = document.createElement('div'); mathDiv.className = 'math'; mathDiv.textContent = block.textContent; katex.render(mathDiv.textContent, mathDiv, { throwOnError: false, displayMode }); parentPre.parentNode.replaceChild(mathDiv, parentPre); }); // 渲染流程图和序列图 document.querySelectorAll('pre code.language-flow, pre code.language-seq').forEach((block) => { const parentPre = block.parentNode; const diagram = document.createElement('div'); diagram.className = 'mermaid'; diagram.textContent = block.textContent; parentPre.parentNode.replaceChild(diagram, parentPre); }); if (window.mermaid) mermaid.init(); // 为普通代码块添加行号、标题、复制按钮和演示按钮 document.querySelectorAll('pre code').forEach((block) => { const lang = block.getAttribute('class')?.replace('hljs', '').trim(); const supportedLanguages = ['javascript', 'html', 'plaintext']; // 定义支持高亮的语言 // 只对支持的语言进行高亮处理 if (supportedLanguages.includes(lang)) { hljs.highlightElement(block); } // 跳过数学公式和图表代码块 if (['math', 'katex', 'latex', 'flow', 'seq'].includes(lang)) { return; } // 添加行号 if (!block.classList.contains('nohighlight')) { const lines = block.textContent.split('\n').length; let lineNumbersHTML = ''; for (let i = 0; i < lines; i++) { lineNumbersHTML += ''; } const lineNumbersWrapper = document.createElement('div'); lineNumbersWrapper.className = 'line-numbers'; lineNumbersWrapper.innerHTML = lineNumbersHTML; block.className += ' line-numbers-rows'; block.parentNode.insertBefore(lineNumbersWrapper, block); } // 添加代码块标题 const codeTitle = document.createElement('div'); codeTitle.className = 'code-title'; codeTitle.textContent = lang || 'plaintext'; // 添加复制按钮 const copyButton = document.createElement('button'); copyButton.className = 'copy-btn'; copyButton.innerHTML = ''; copyButton.addEventListener('click', () => { const text = block.textContent; navigator.clipboard.writeText(text).then(() => { copyButton.innerHTML = ''; setTimeout(() => { copyButton.innerHTML = ''; }, 1000); }).catch((err) => { console.error('复制失败: ', err); }); }); codeTitle.appendChild(copyButton); // 添加演示按钮 if (lang === 'javascript' || lang === 'html') { const demoButton = document.createElement('button'); demoButton.className = 'demo-btn'; demoButton.innerHTML = ''; demoButton.addEventListener('click', () => { const code = block.textContent; const newWindow = window.open('', '_blank'); if (lang === 'javascript') { newWindow.document.write(``); } else if (lang === 'html') { newWindow.document.write(code); } }); codeTitle.appendChild(demoButton); } block.parentNode.insertBefore(codeTitle, block); }); if (window.renderMathInElement) renderMathInElement(document.body); // 添加淡入动画 articleContainer.style.opacity = '0'; setTimeout(() => { articleContainer.style.opacity = '1'; articleContainer.style.transition = 'opacity 0.3s ease'; }, 50); } // 高亮活动链接 function highlightActiveLink(id) { document.querySelectorAll('#sidebar-nav a').forEach(link => { if (link.getAttribute('href') === `#${id}`) { link.classList.add('bg-primary/10', 'dark:bg-primary/20', 'text-primary'); } else { link.classList.remove('bg-primary/10', 'dark:bg-primary/20', 'text-primary'); } }); } // 打开侧边栏 function openSidebar() { sidebar.classList.remove('-translate-x-full'); overlay.classList.remove('hidden'); setTimeout(() => { overlay.classList.add('opacity-100'); }, 10); } // 关闭侧边栏 function closeSidebar() { sidebar.classList.add('-translate-x-full'); overlay.classList.remove('opacity-100'); setTimeout(() => { overlay.classList.add('hidden'); }, 300); } // 切换主题 function toggleTheme() { document.documentElement.classList.toggle('dark'); IFcontent = document.getElementById("IFcontent"); if (IFcontent) { iframeDoc = IFcontent.contentDocument || IFcontent.contentWindow.document; iframeDoc.body.style.color = document.documentElement.classList.contains('dark') ? '#FFF' : '#000'; iframeDoc.documentElement.classList.toggle('dark'); } localStorage.setItem('theme', document.documentElement.classList.contains('dark') ? 'dark' : 'light'); } // 侧边栏搜索功能 function setupSidebarSearch() { sidebarSearch.addEventListener('input', (e) => { const searchTerm = e.target.value.toLowerCase().trim(); const links = document.querySelectorAll('#sidebar-nav a'); links.forEach(link => { const text = link.textContent.toLowerCase(); const isVisible = text.includes(searchTerm); link.style.display = isVisible ? 'block' : 'none'; // 如果章节下所有子项都不可见,则隐藏章节 const parent = link.closest('div'); const visibleItems = parent.querySelectorAll('a:not([style*="display: none"])').length; const chapterHeader = parent.previousElementSibling; if (chapterHeader && visibleItems === 0) { parent.style.display = 'none'; chapterHeader.style.display = 'none'; } else if (chapterHeader) { parent.style.display = ''; chapterHeader.style.display = ''; } }); }); } // 搜索功能 function setupSearch() { searchButton.addEventListener('click', performSearch); globalSearch.addEventListener('keydown', (e) => { if (e.key === 'Enter') { performSearch(); } }); } // 执行搜索 async function performSearch() { const searchTerm = globalSearch.value.toLowerCase().trim(); if (!searchTerm) return; showLoadingAnimation(); try { // 获取所有文章ID const allIds = []; tableOfContents.chapters.forEach(chapter => { allIds.push(chapter.id); if (chapter.sections) { chapter.sections.forEach(section => { allIds.push(section.id); }); } }); // 并行请求所有文章内容 const results = await Promise.allSettled( allIds.map(id => fetch(`/API/GetDoc/${id}`) .then(response => response.json()) .then(content => ({ id, content })) ) ); // 处理搜索结果 const searchResults = []; results.forEach(result => { if (result.status === 'fulfilled' && result.value.content) { const { id, content } = result.value; c = content.DocContent; if (c.toLowerCase().includes(searchTerm)) { const title = tableOfContents.chapters.flatMap(chapter => [ { id: chapter.id, title: chapter.title }, ...chapter.sections.map(section => ({ id: section.id, title: section.title })) ]).find(item => item.id === id)?.title || '未知标题'; const excerpt = getExcerpt(c, searchTerm); searchResults.push({ id, title, excerpt }); } } }); // 渲染搜索结果 renderSearchResults(searchResults); } catch (error) { console.error('搜索失败:', error); showError("搜索过程中发生错误"); } } // 渲染搜索结果 function renderSearchResults(results) { if (results.length > 0) { let resultsHTML = '
'; resultsHTML += `

搜索结果: ${results.length} 个匹配项

`; results.forEach(result => { resultsHTML += ` `; }); resultsHTML += '
'; articleContainer.innerHTML = resultsHTML; } else { showError("未找到匹配的结果"); } } // 获取摘要(带转义和搜索词高亮) function getExcerpt(content, searchTerm) { // 转义HTML特殊字符 const escapeHtml = (str) => { return str.replace(/[&<>'"]/g, tag => ({ '&': '&', '<': '<', '>': '>', "'": ''', '"': '"' }[tag])); }; // 转义内容和搜索词 const escapedContent = escapeHtml(content); const escapedSearchTerm = escapeHtml(searchTerm); // 查找位置(不区分大小写) const index = escapedContent.toLowerCase().indexOf(escapedSearchTerm.toLowerCase()); if (index === -1) return ''; // 没找到搜索词 // 计算摘要范围 const start = Math.max(0, index - 100); const end = Math.min(escapedContent.length, index + escapedSearchTerm.length + 100); // 构建摘要 let excerpt = escapedContent.substring(start, end); if (start > 0) excerpt = '...' + excerpt; if (end < escapedContent.length) excerpt = excerpt + '...'; // 高亮搜索词(使用转义后的搜索词) const regex = new RegExp(escapedSearchTerm.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'), 'gi'); excerpt = excerpt.replace(regex, match => `${match}`); return excerpt; } // 展开所有侧边栏章节 function expandAllSidebarSections() { const chapterContents = document.querySelectorAll('#sidebar-nav .pl-4.mt-1.space-y-1'); const chapterHeaders = document.querySelectorAll('#sidebar-nav .flex.items-center.justify-between.p-2.rounded-lg'); chapterContents.forEach((chapterContent) => { chapterContent.style.maxHeight = 'none'; const height = chapterContent.scrollHeight * 100; chapterContent.style.maxHeight = '0'; requestAnimationFrame(() => { chapterContent.classList.add('expanded'); chapterContent.style.maxHeight = height + 'px'; }); }); chapterHeaders.forEach((chapterHeader) => { const chevron = chapterHeader.querySelector('.fa-chevron-down'); chevron.style.transform = 'rotate(180deg)'; }); } // 显示密码输入提示 function showPasswordPrompt(prompt, id) { articleContainer.innerHTML = `

${prompt}

`; const passwordInput = document.getElementById('password-input'); const passwordSubmit = document.getElementById('password-submit'); const passwordError = document.getElementById('password-error'); passwordSubmit.addEventListener('click', () => { const password = passwordInput.value; loadArticleAsync(id, password).then(() => { if (articleContainer.innerHTML.includes('密码错误')) { passwordError.classList.remove('hidden'); } else { passwordError.classList.add('hidden'); } }); }); passwordInput.addEventListener('keydown', (e) => { if (e.key === 'Enter') { const password = passwordInput.value; loadArticleAsync(id, password).then(() => { if (articleContainer.innerHTML.includes('密码错误')) { passwordError.classList.remove('hidden'); } else { passwordError.classList.add('hidden'); } }); } }); } // 初始化 function init() { fetch(`/API/GetSerieDirectory/${UUID}`) .then(response => response.json()) .then(data => { window.tableOfContents = data; headerTitle.innerText = data.title; document.title = `${data.title} - 章节阅读器`; renderSidebar(); setupSearch(); setupSidebarSearch(); expandAllSidebarSections(); if (data.document) { loadArticleAsync(UUID); // 展开,懒得写了 highlightActiveLink(UUID); } else { loadArticleAsync(`${UUID}_index`); highlightActiveLink(`${UUID}_index`); } }) .catch(error => { // 错误处理保持不变 console.error('获取目录失败:', error); articleContainer.innerHTML = `

加载目录失败

请检查网络连接或刷新页面

`; }); // 恢复主题设置 //const savedTheme = localStorage.getItem('theme'); //if (savedTheme === 'dark') { // document.documentElement.classList.add('dark'); //} sidebarToggle.addEventListener('click', openSidebar); overlay.addEventListener('click', closeSidebar); themeToggle.addEventListener('click', toggleTheme); } window.onload = init;