// 判断是否为移动端 function isMobile() { if (window.navigator.userAgent.match(/(phone|pad|pod|iPhone|iPod|ios|iPad|Android|Mobile|BlackBerry|IEMobile|MQQBrowser|JUC|Fennec|wOSBrowser|BrowserNG|WebOS|Symbian|Windows Phone)/i)) { return true; // 移动端 } else { return false; // PC端 } } // 文章图片灯箱 window.ViewImage && ViewImage.init('.post-content img'); // Pure JavaScript Back to Top (High Performance) document.addEventListener('DOMContentLoaded', function() { var backToTopButton = document.getElementById('back-to-top'); if (backToTopButton) { var isVisible = false; var ticking = false; function update() { if (window.scrollY > 300) { if (!isVisible) { backToTopButton.classList.add('show'); isVisible = true; } } else { if (isVisible) { backToTopButton.classList.remove('show'); isVisible = false; } } ticking = false; } window.addEventListener('scroll', function() { if (!ticking) { window.requestAnimationFrame(update); ticking = true; } }, { passive: true }); backToTopButton.addEventListener('click', function(e) { e.preventDefault(); window.scrollTo({ top: 0, behavior: 'smooth' }); }); } }); // loader定时器 $(document).ready(function () { setTimeout(function () { $("#loader").addClass("hide"); }, 30000); }); // 点击进入按钮 $("#closeloader").click(function () { $("#loader").addClass("hide"); }); // nav栏样式调整 $('#libabys').first().attr("style", "margin-left: -5px"); $('#libabys').eq(1).attr("style", "margin-left: -5px"); // 友链列表版样式调整 $('.itp-index:odd').attr("style", "float: right;width:48%;"); //文章图片懒加载 if (typeof lazyLoadInit === 'function') { lazyLoadInit({ // coverColor: "white", // coverDiv: "
优世界·一个记录生活点滴的小站
", offsetBottom: 800, offsetTopm: 800, showTime: 1100 }); } else { console.warn('lazyLoadInit is not defined'); } // 异步初始化 Artalk 并获取评论数据 (Moved to barrage.js) async function fetchHitokoto() { try { const response = await fetch('https://v1.hitokoto.cn/?c=d&c=i'); // 获取句子 const data = await response.json(); return data.hitokoto; } catch (error) { console.error('获取一言失败:', error); return null; } } // 代码块复制功能 function initCodeCopy() { const codeBlocks = document.querySelectorAll('.post-content pre code'); codeBlocks.forEach(codeBlock => { // 检查是否已经添加了复制按钮,避免重复添加 if (codeBlock.parentNode.querySelector('.copy-code-btn')) return; const pre = codeBlock.parentNode; pre.style.position = 'relative'; const copyBtn = document.createElement('button'); copyBtn.className = 'copy-code-btn'; copyBtn.innerHTML = ''; // 使用 FontAwesome 图标 copyBtn.title = '复制代码'; // 样式 (建议放到 CSS 文件中) copyBtn.style.position = 'absolute'; copyBtn.style.top = '5px'; copyBtn.style.right = '5px'; copyBtn.style.border = 'none'; copyBtn.style.background = 'transparent'; copyBtn.style.color = '#999'; copyBtn.style.cursor = 'pointer'; copyBtn.style.padding = '5px'; copyBtn.style.borderRadius = '4px'; copyBtn.style.transition = 'all 0.3s'; // Hover 效果 copyBtn.addEventListener('mouseenter', () => { copyBtn.style.color = '#333'; copyBtn.style.background = 'rgba(0,0,0,0.1)'; }); copyBtn.addEventListener('mouseleave', () => { copyBtn.style.color = '#999'; copyBtn.style.background = 'transparent'; }); copyBtn.addEventListener('click', () => { const code = codeBlock.innerText; navigator.clipboard.writeText(code).then(() => { copyBtn.innerHTML = ''; setTimeout(() => { copyBtn.innerHTML = ''; }, 2000); Qmsg.success("代码复制成功"); }).catch(err => { console.error('复制失败:', err); copyBtn.innerHTML = ''; Qmsg.error("复制失败"); }); }); pre.appendChild(copyBtn); }); } // 页面加载完成后初始化 (Merged into main DOMContentLoaded listener) // document.addEventListener('DOMContentLoaded', function() { // initCodeCopy(); // initDescriptionHitokoto(); // }); // 初始化个人描述一言切换 function initDescriptionHitokoto() { const descEl = document.querySelector('.yiyandes'); if (!descEl) return; descEl.title = '点击切换一言'; descEl.addEventListener('click', async () => { if (descEl.dataset.loading === 'true') return; descEl.dataset.loading = 'true'; const originalText = descEl.innerText; descEl.innerText = '正在获取灵感...'; const hitokoto = await fetchHitokoto(); if (hitokoto) { typeWriter(descEl, hitokoto); } else { descEl.innerText = originalText; descEl.dataset.loading = 'false'; } }); } // 打字机效果 function typeWriter(element, text, speed = 100) { element.innerText = ''; element.classList.add('typing'); let i = 0; function type() { if (i < text.length) { element.innerText += text.charAt(i); i++; setTimeout(type, speed); } else { element.dataset.loading = 'false'; element.classList.remove('typing'); } } type(); } // 友链信息一键复制 function copySzhContent() { var szhElement = document.getElementById('szh'); if (szhElement) { var textToCopy = szhElement.innerText; navigator.clipboard.writeText(textToCopy).then(function() { Qmsg.success("复制成功"); }).catch(function(err) { console.error('Could not copy text: ', err); Qmsg.error("复制失败"); }); } } // Theme Toggle Logic function toggleTheme() { const currentTheme = document.documentElement.getAttribute('data-theme'); const newTheme = currentTheme === 'dark' ? 'light' : 'dark'; document.documentElement.setAttribute('data-theme', newTheme); localStorage.setItem('theme', newTheme); // Notify Artalk if present if (window.Artalk) { Artalk.setDarkMode(newTheme === 'dark'); } } // Listen for system theme changes try { const mediaQuery = window.matchMedia('(prefers-color-scheme: dark)'); const handleSystemThemeChange = (e) => { // Only follow system if user hasn't manually set a preference if (!localStorage.getItem('theme')) { const newTheme = e.matches ? 'dark' : 'light'; document.documentElement.setAttribute('data-theme', newTheme); if (window.Artalk) { Artalk.setDarkMode(newTheme === 'dark'); } } }; // Modern browsers if (mediaQuery.addEventListener) { mediaQuery.addEventListener('change', handleSystemThemeChange); } else if (mediaQuery.addListener) { // Old Safari/IE mediaQuery.addListener(handleSystemThemeChange); } } catch (e) { console.warn('System theme detection not supported', e); } // Update Floating Tools Visibility based on current page function updateFloatingToolsVisibility() { const tools = document.querySelector('.floating-tools'); if (!tools) return; // Hide on Homepage const isHome = window.location.pathname === '/' || window.location.pathname === '/index.html'; if (isHome) { tools.style.display = 'none'; } else { tools.style.display = 'flex'; } // Hide Comment button if no comments section const commentBtn = document.getElementById('to-comments'); if (commentBtn) { const commentsSection = document.getElementById('Comments'); commentBtn.style.display = commentsSection ? 'flex' : 'none'; } } // Floating Tools Initialization function initFloatingTools() { // Initial visibility check updateFloatingToolsVisibility(); // Prevent multiple event bindings if (window.floatingToolsInitialized) return; window.floatingToolsInitialized = true; // Use Event Delegation for robust handling document.addEventListener('click', function(e) { // Theme Toggle const toggleBtn = e.target.closest('#theme-toggle'); if (toggleBtn) { e.preventDefault(); toggleTheme(); } // Jump to Comments const commentBtn = e.target.closest('#to-comments'); if (commentBtn) { e.preventDefault(); const commentsSection = document.getElementById('Comments'); if (commentsSection) { commentsSection.scrollIntoView({ behavior: 'smooth' }); } } }); // Theme Toggle (Avatar) const avatar = document.querySelector('.head_author_img'); if (avatar) { avatar.addEventListener('click', function(e) { e.preventDefault(); toggleTheme(); }); } } // Listen for PJAX complete to update visibility document.addEventListener('pjax:complete', function() { updateFloatingToolsVisibility(); hideSelectionPopup(); // PJAX 跳转后重新加载弹幕 (Moved to barrage.js) }); document.addEventListener('DOMContentLoaded', function() { initFloatingTools(); initSelectionPopup(); initCodeCopy(); initDescriptionHitokoto(); // 页面首次加载时启动弹幕 (Moved to barrage.js) }); // Selection Popup Logic let selectionPopup = null; let selectionTimeout; function initSelectionPopup() { if (document.getElementById('selection-popup')) return; selectionPopup = document.createElement('div'); selectionPopup.id = 'selection-popup'; selectionPopup.innerHTML = ' 引用评论'; document.body.appendChild(selectionPopup); // Prevent mousedown on popup from clearing selection selectionPopup.addEventListener('mousedown', function(e) { e.preventDefault(); e.stopPropagation(); }); // Handle click on popup selectionPopup.addEventListener('click', function(e) { e.preventDefault(); e.stopPropagation(); let selection = window.getSelection().toString(); // Optimize: Remove extra whitespace and limit length if (selection) { // Collapse multiple empty lines into max 2 newlines, and trim each line selection = selection.split('\n') .map(line => line.trim()) .filter(line => line !== '') .join('\n'); if (selection.length > 500) { selection = selection.substring(0, 500) + '...'; } scrollToCommentsAndQuote(selection); hideSelectionPopup(); window.getSelection().removeAllRanges(); } }); // Global listeners for selection document.addEventListener('mouseup', handleSelectionChange); document.addEventListener('keyup', handleSelectionChange); } function hideSelectionPopup() { if (selectionPopup) { selectionPopup.style.display = 'none'; } } function handleSelectionChange(e) { // Debounce to avoid rapid firing clearTimeout(selectionTimeout); selectionTimeout = setTimeout(() => { const selection = window.getSelection(); const text = selection.toString().trim(); const content = document.querySelector('.post-content'); // Main article container if (!selectionPopup || !content) return; // Check if selection is empty if (!text || selection.rangeCount === 0) { hideSelectionPopup(); return; } const range = selection.getRangeAt(0); // Check if the common ancestor is inside .post-content // Optimization: Check container existence before heavy contains check if (!content.contains(range.commonAncestorContainer)) { hideSelectionPopup(); return; } // Optimization: Use requestAnimationFrame for UI updates requestAnimationFrame(() => { // Show first to ensure measurements if needed (though we set top/left directly) selectionPopup.style.display = 'flex'; // Position logic: Follow mouse if available (mouseup), else fallback to selection rect (keyup) if (e && e.type === 'mouseup') { // Optimization: Avoid getBoundingClientRect on mouseup // Position above the mouse cursor selectionPopup.style.top = `${e.clientY - 40}px`; selectionPopup.style.left = `${e.clientX}px`; } else { // Fallback for keyboard selection (needs rect) const rect = range.getBoundingClientRect(); // Check if visible and valid if (rect.width === 0 || rect.height === 0) { hideSelectionPopup(); return; } selectionPopup.style.top = `${rect.top}px`; selectionPopup.style.left = `${rect.left + rect.width / 2}px`; } }); }, 150); } function scrollToCommentsAndQuote(text) { const comments = document.getElementById('Comments'); if (comments) { comments.scrollIntoView({ behavior: 'smooth' }); // Wait for scroll to complete (approximate) setTimeout(() => { // Find Artalk textarea const textarea = document.querySelector('.atk-textarea'); if (textarea) { const quote = `> ${text}\n\n`; // Overwrite existing content as requested textarea.value = quote; textarea.focus(); // Trigger input event for Vue/React binding (Artalk uses Vue) textarea.dispatchEvent(new Event('input', { bubbles: true })); textarea.dispatchEvent(new Event('change', { bubbles: true })); if (window.Qmsg) { Qmsg.success('引用成功'); } } }, 500); } } /* Search Functionality */ function initSearchFunctionality() { const searchBtn = document.getElementById('search-btn'); const searchModal = document.getElementById('search-modal'); const closeSearch = document.querySelector('.close-search'); const searchInput = document.getElementById('search-input'); const searchResults = document.getElementById('search-results'); const searchSubmit = document.getElementById('search-submit'); // Global variables (window scope) to persist across re-inits if needed, // but better to keep them scoped if we re-fetch. // For simplicity, we keep index loading check. if (!window.searchIndex) window.searchIndex = []; if (!window.isIndexLoaded) window.isIndexLoaded = false; if (!searchBtn || !searchModal) return; // Open Modal // Use named function to avoid duplicate listeners if we didn't clean up (though init is usually called once per page load/pjax) function openModal() { searchModal.classList.add('show'); if (!window.isIndexLoaded) { loadSearchIndex(); } setTimeout(() => searchInput.focus(), 100); } searchBtn.removeEventListener('click', openModal); // Prevent duplicates searchBtn.addEventListener('click', openModal); // Close Modal function closeModal() { searchModal.classList.remove('show'); } if (closeSearch) { closeSearch.onclick = closeModal; } // Close when clicking outside window.onclick = function(e) { if (e.target === searchModal) { closeModal(); } }; // Close on Escape key document.onkeydown = function(e) { if (e.key === 'Escape' && searchModal.classList.contains('show')) { closeModal(); } }; // Load Index function loadSearchIndex() { fetch('/index.json') .then(response => response.json()) .then(data => { window.searchIndex = data; window.isIndexLoaded = true; }) .catch(error => { console.error('Error loading search index:', error); if(searchResults) searchResults.innerHTML = '

搜索索引加载失败

'; }); } // Perform Search function performSearch() { if(!searchInput) return; const query = searchInput.value.trim().toLowerCase(); if (query === '') { searchResults.innerHTML = '

输入关键词并回车进行搜索

'; return; } if (!window.isIndexLoaded) { searchResults.innerHTML = '

正在加载索引...

'; // Retry after a short delay if index is loading setTimeout(performSearch, 500); return; } const results = window.searchIndex.filter(item => { const title = item.title ? item.title.toLowerCase() : ''; const summary = item.summary ? item.summary.toLowerCase() : ''; return title.includes(query) || summary.includes(query); }); displayResults(results); } // Display Results function displayResults(results) { if (results.length === 0) { searchResults.innerHTML = '

未找到相关文章

'; return; } let html = ''; results.forEach(item => { html += ` ${item.title} ${item.date}
${item.summary}
`; }); searchResults.innerHTML = html; if (window.pjax) window.pjax.refresh(searchResults); // Add click listener to close modal on result click const resultLinks = searchResults.querySelectorAll('.search-result-item'); resultLinks.forEach(link => { link.addEventListener('click', function() { closeModal(); }); }); } // Event Listeners for Search if (searchSubmit) { searchSubmit.onclick = performSearch; } if (searchInput) { searchInput.onkeypress = function(e) { if (e.key === 'Enter') { performSearch(); } }; // Optional: Live search with debounce let debounceTimer; searchInput.oninput = function() { clearTimeout(debounceTimer); debounceTimer = setTimeout(performSearch, 300); }; } /* Inline Search Logic */ const essayHeader = document.getElementById('essay-header'); const searchContainer = document.getElementById('search-container'); const inlineSearchInput = document.getElementById('inline-search-input'); const inlineSearchResults = document.getElementById('inline-search-results'); if (essayHeader && searchContainer && inlineSearchInput) { window.toggleSearchMode = function(show) { if (show) { essayHeader.style.display = 'none'; searchContainer.style.display = 'block'; inlineSearchInput.focus(); if (!window.isIndexLoaded) { loadSearchIndex(); } } else { essayHeader.style.display = 'block'; searchContainer.style.display = 'none'; inlineSearchInput.value = ''; inlineSearchResults.style.display = 'none'; } }; // Close when clicking outside - delegated to document body to handle pjax updates document.body.onclick = function(e) { const isClickInside = essayHeader.contains(e.target) || searchContainer.contains(e.target) || inlineSearchResults.contains(e.target); if (!isClickInside && searchContainer.style.display === 'block') { window.toggleSearchMode(false); } // Also handle modal outside click here if needed, but window.onclick handles that if (e.target === searchModal) { closeModal(); } }; // Inline Search Function function performInlineSearch() { const query = inlineSearchInput.value.trim().toLowerCase(); if (query === '') { inlineSearchResults.style.display = 'none'; return; } if (!window.isIndexLoaded) { // Wait if loading setTimeout(performInlineSearch, 500); return; } const results = window.searchIndex.filter(item => { const title = item.title ? item.title.toLowerCase() : ''; const summary = item.summary ? item.summary.toLowerCase() : ''; return title.includes(query) || summary.includes(query); }); displayInlineResults(results); } function displayInlineResults(results) { if (results.length === 0) { inlineSearchResults.innerHTML = '
未找到相关文章
'; inlineSearchResults.style.display = 'block'; return; } let html = ''; results.slice(0, 5).forEach(item => { // Limit to 5 inline results html += ` ${item.title}
${item.summary}
`; }); inlineSearchResults.innerHTML = html; inlineSearchResults.style.display = 'block'; if (window.pjax) window.pjax.refresh(inlineSearchResults); } let inlineDebounce; inlineSearchInput.oninput = function() { clearTimeout(inlineDebounce); inlineDebounce = setTimeout(performInlineSearch, 300); }; // Handle Escape key inlineSearchInput.onkeydown = function(e) { if (e.key === 'Escape') { window.toggleSearchMode(false); } }; } } document.addEventListener('DOMContentLoaded', initSearchFunctionality); // Re-init on Pjax complete document.addEventListener('pjax:complete', initSearchFunctionality);