diff options
| author | somhairle <[email protected]> | 2026-04-21 20:40:27 +0800 |
|---|---|---|
| committer | Somhairle H. Marisol <[email protected]> | 2026-05-24 17:24:57 +0800 |
| commit | ad8f9f55ea79a4d587aecc69698808da82de3d62 (patch) | |
| tree | d25c0db712e6f015d850627ff957edea44c64f94 /themes/theme/source/js | |
| download | blog-ad8f9f55ea79a4d587aecc69698808da82de3d62.tar.gz | |
二〇二六年四月二十一日
Diffstat (limited to 'themes/theme/source/js')
| -rw-r--r-- | themes/theme/source/js/format.js | 23 | ||||
| -rw-r--r-- | themes/theme/source/js/menu.js | 27 | ||||
| -rw-r--r-- | themes/theme/source/js/mermaid-zoom.js | 169 | ||||
| -rw-r--r-- | themes/theme/source/js/search.js | 68 | ||||
| -rw-r--r-- | themes/theme/source/js/theme-switch.js | 31 |
5 files changed, 318 insertions, 0 deletions
diff --git a/themes/theme/source/js/format.js b/themes/theme/source/js/format.js new file mode 100644 index 00000000..9d79edce --- /dev/null +++ b/themes/theme/source/js/format.js @@ -0,0 +1,23 @@ +function detectTaskList() { + var taskListObjects = document.getElementsByTagName("input"); + for (var i = 0; i < taskListObjects.length; i++) { + var par = taskListObjects[i].parentNode; + par.classList.add("task-list-item"); + par.parentNode.classList.add("task-list"); + } +} + +function detectBlockTable() { + var tableListObjects = document.getElementsByTagName("thead"); + for (var i = 0; i < tableListObjects.length; i++) { + var par = tableListObjects[i].parentNode; + par.classList.add("block-table"); + } +} + +function detectors(){ + detectTaskList(); + detectBlockTable(); +} + + diff --git a/themes/theme/source/js/menu.js b/themes/theme/source/js/menu.js new file mode 100644 index 00000000..fd84fcc3 --- /dev/null +++ b/themes/theme/source/js/menu.js @@ -0,0 +1,27 @@ +function toggleMenu() { + var menuList = document.getElementsByClassName("menu-list")[0]; + var menuButton = document.getElementById("menu-btn"); + if (menuList.classList.contains("active")) { + menuList.classList.remove("active"); + menuButton.innerHTML = "菜单"; + } else { + menuList.classList.add("active"); + menuButton.innerHTML = "菜单"; + } +} + +function closeMenu() { + var menuList = document.getElementsByClassName("menu-list")[0]; + var menuButton = document.getElementById("menu-btn"); + if (menuList.classList.contains("active")) { + menuList.classList.remove("active"); + menuButton.innerHTML = "菜单"; + } +} + +document.addEventListener("DOMContentLoaded", function() { + var menuItems = document.querySelectorAll(".menu-item a"); + menuItems.forEach(function(item) { + item.addEventListener("click", closeMenu); + }); +});
\ No newline at end of file diff --git a/themes/theme/source/js/mermaid-zoom.js b/themes/theme/source/js/mermaid-zoom.js new file mode 100644 index 00000000..6b4f0e17 --- /dev/null +++ b/themes/theme/source/js/mermaid-zoom.js @@ -0,0 +1,169 @@ +// Mermaid 图表点击放大功能 + +(function() { + 'use strict'; + + // 等待 DOM 加载完成 + function initMermaidZoom() { + const mermaidElements = document.querySelectorAll('.mermaid'); + + if (mermaidElements.length === 0) return; + + // 检测是否为移动端 + const isMobile = window.innerWidth <= 688; + + // 移动端:为 mermaid 元素添加提示样式 + if (isMobile) { + mermaidElements.forEach(function(el) { + el.style.position = 'relative'; + // 创建提示元素 + const hint = document.createElement('span'); + hint.textContent = '点击放大'; + hint.style.cssText = 'position:absolute;bottom:4px;right:4px;font-size:10px;padding:2px 6px;background:rgba(0,0,0,0.5);color:#fff;border-radius:4px;pointer-events:none;'; + el.appendChild(hint); + }); + } + + // 创建模态框 + const modal = document.createElement('div'); + modal.className = 'mermaid-modal'; + modal.innerHTML = ` + <button class="mermaid-close" aria-label="关闭">×</button> + <div class="mermaid-content"></div> + `; + document.body.appendChild(modal); + + const closeBtn = modal.querySelector('.mermaid-close'); + const content = modal.querySelector('.mermaid-content'); + + // 关闭模态框函数 + function closeModal() { + modal.classList.remove('active'); + document.body.style.overflow = ''; + } + + // 为每个 mermaid 图表添加点击事件 + mermaidElements.forEach(function(el) { + el.addEventListener('click', function(e) { + // 等待 mermaid 渲染完成 + const svg = el.querySelector('svg'); + if (!svg) return; + + // 复制 SVG 到模态框 + content.innerHTML = ''; + const clonedSvg = svg.cloneNode(true); + + // 获取原始 SVG 的尺寸 + const originalWidth = svg.viewBox.baseVal.width || svg.width.baseVal.value || 800; + const originalHeight = svg.viewBox.baseVal.height || svg.height.baseVal.value || 600; + + // 检测是否为移动端 + const isMobile = window.innerWidth <= 688; + + // 计算放大比例 + let targetWidth; + if (isMobile) { + // 移动端:适配屏幕宽度,留出边距 + targetWidth = window.innerWidth - 32; // 16px padding × 2 + } else { + // 桌面端:85% 视口宽度,最大 1600px + targetWidth = Math.min(window.innerWidth * 0.85, 1600); + } + + const scale = targetWidth / originalWidth; + const newWidth = originalWidth * scale; + const newHeight = originalHeight * scale; + + // 设置放大后的尺寸 + clonedSvg.setAttribute('width', newWidth + 'px'); + clonedSvg.setAttribute('height', newHeight + 'px'); + clonedSvg.style.width = newWidth + 'px'; + clonedSvg.style.height = newHeight + 'px'; + clonedSvg.style.maxWidth = 'none'; + clonedSvg.style.maxHeight = 'none'; + + // 确保 viewBox 存在 + if (!clonedSvg.getAttribute('viewBox')) { + clonedSvg.setAttribute('viewBox', '0 0 ' + originalWidth + ' ' + originalHeight); + } + + content.appendChild(clonedSvg); + + // 显示模态框 + modal.classList.add('active'); + document.body.style.overflow = 'hidden'; + + // 移动端样式调整 + if (isMobile) { + content.style.maxWidth = '100vw'; + content.style.maxHeight = '100vh'; + content.style.padding = '12px'; + content.style.borderRadius = '0'; + modal.style.padding = '0'; + } else { + // 恢复桌面端样式 + content.style.maxWidth = ''; + content.style.maxHeight = ''; + content.style.padding = ''; + content.style.borderRadius = ''; + modal.style.padding = ''; + } + }); + }); + + // 关闭按钮事件 + closeBtn.addEventListener('click', closeModal); + + // 点击背景关闭 + modal.addEventListener('click', function(e) { + if (e.target === modal) { + closeModal(); + } + }); + + // ESC 键关闭 + document.addEventListener('keydown', function(e) { + if (e.key === 'Escape' && modal.classList.contains('active')) { + closeModal(); + } + }); + } + + // 等待 mermaid 渲染完成后再初始化 + function waitForMermaid() { + const mermaidElements = document.querySelectorAll('.mermaid'); + let renderedCount = 0; + + function checkRendered() { + let allRendered = true; + mermaidElements.forEach(function(el) { + if (!el.querySelector('svg')) { + allRendered = false; + } + }); + + if (allRendered) { + initMermaidZoom(); + } else { + // 如果还没渲染完,继续等待 + if (renderedCount < 50) { // 最多等待 5 秒 + renderedCount++; + setTimeout(checkRendered, 100); + } else { + // 超时,仍然初始化(可能有些图表渲染失败) + initMermaidZoom(); + } + } + } + + // 开始检查 + setTimeout(checkRendered, 500); + } + + // 页面加载完成后初始化 + if (document.readyState === 'loading') { + document.addEventListener('DOMContentLoaded', waitForMermaid); + } else { + waitForMermaid(); + } +})(); diff --git a/themes/theme/source/js/search.js b/themes/theme/source/js/search.js new file mode 100644 index 00000000..27abb4e0 --- /dev/null +++ b/themes/theme/source/js/search.js @@ -0,0 +1,68 @@ +// Search functionality for standalone search page + +(function () { + 'use strict'; + + let localSearch = null; + let isDataLoaded = false; + + function initSearch() { + const input = document.getElementById('search-input'); + const container = document.getElementById('search-results'); + + if (!input || !container || typeof LocalSearch === 'undefined') { + container.innerHTML = '<div class="search-result-message">搜索功能初始化失败</div>'; + return; + } + + localSearch = new LocalSearch({ + path: searchConfig.path, + top_n_per_article: searchConfig.top_n_per_article, + unescape: searchConfig.unescape + }); + + localSearch.fetchData(); + + window.addEventListener('search:loaded', function () { + isDataLoaded = true; + container.innerHTML = '<div class="search-result-message"></div>'; + }); + + function displaySearchResult() { + if (!isDataLoaded) { + container.innerHTML = '<div class="search-result-message">加载中...</div>'; + return; + } + + const searchText = input.value.trim().toLowerCase(); + + if (searchText.length === 0) { + container.innerHTML = '<div class="search-result-message"></div>'; + return; + } + + const keywords = searchText.split(/[-\s]+/); + const resultItems = localSearch.getResultItems(keywords); + + if (resultItems.length === 0) { + container.innerHTML = '<div class="search-result-message">未找到相关结果</div>'; + } else { + container.innerHTML = ` + <div class="search-result-message">找到 ${resultItems.length} 个结果</div> + <ul class="search-result-list"> + ${resultItems.map(result => result.item).join('')} + </ul> + `; + } + } + + input.addEventListener('input', displaySearchResult); + input.focus(); + } + + if (document.readyState === 'loading') { + document.addEventListener('DOMContentLoaded', initSearch); + } else { + initSearch(); + } +})(); diff --git a/themes/theme/source/js/theme-switch.js b/themes/theme/source/js/theme-switch.js new file mode 100644 index 00000000..4dddd304 --- /dev/null +++ b/themes/theme/source/js/theme-switch.js @@ -0,0 +1,31 @@ +document.addEventListener('DOMContentLoaded', () => { + const themeSwitch = document.getElementById('theme-switch-btn'); + const prefersDarkScheme = window.matchMedia('(prefers-color-scheme: dark)'); + + // 检查本地存储中的主题设置 + const currentTheme = localStorage.getItem('theme'); + if (currentTheme === 'dark') { + document.body.classList.add('dark-theme'); + themeSwitch.querySelector('i').classList.replace('icon-moon', 'icon-sun'); + } else if (currentTheme === 'light') { + document.body.classList.remove('dark-theme'); + } else if (prefersDarkScheme.matches) { + document.body.classList.add('dark-theme'); + themeSwitch.querySelector('i').classList.replace('icon-moon', 'icon-sun'); + } + + // 切换主题 + themeSwitch.addEventListener('click', (e) => { + e.preventDefault(); + + if (document.body.classList.contains('dark-theme')) { + document.body.classList.remove('dark-theme'); + localStorage.setItem('theme', 'light'); + themeSwitch.querySelector('i').classList.replace('icon-sun', 'icon-moon'); + } else { + document.body.classList.add('dark-theme'); + localStorage.setItem('theme', 'dark'); + themeSwitch.querySelector('i').classList.replace('icon-moon', 'icon-sun'); + } + }); +});
\ No newline at end of file |
