window.initSearch = function() { initSearchModal(); initInlineSearch(); }; var searchLoadingPromise = null; function debounce(fn, delay) { var timer; return function() { var context = this; var args = arguments; clearTimeout(timer); timer = setTimeout(function() { fn.apply(context, args); }, delay); }; } function loadSearchIndex() { if (window.searchIndex) { return Promise.resolve(window.searchIndex); } if (searchLoadingPromise) { return searchLoadingPromise; } window.isSearchingLoading = true; searchLoadingPromise = fetch('/index.json') .then(response => { if (!response.ok) throw new Error(response.statusText); return response.json(); }) .then(data => { window.searchIndex = data; window.isSearchingLoading = false; searchLoadingPromise = null; return data; }) .catch(err => { console.error('Failed to load search index:', err); window.isSearchingLoading = false; searchLoadingPromise = null; return null; // Return null on error to handle gracefully }); return searchLoadingPromise; } function initSearchModal() { var searchBtn = document.getElementById('search-btn'); var searchModal = document.getElementById('search-modal'); if (searchBtn && searchModal) { // Clone to remove old listeners var newSearchBtn = searchBtn.cloneNode(true); searchBtn.parentNode.replaceChild(newSearchBtn, searchBtn); searchBtn = newSearchBtn; searchBtn.addEventListener('click', function() { searchModal.classList.add('show'); var input = document.getElementById('search-input'); if (input) setTimeout(function() { input.focus(); }, 100); loadSearchIndex(); }); // Close logic var closeBtn = searchModal.querySelector('.close-search'); if (closeBtn) { closeBtn.onclick = function() { searchModal.classList.remove('show'); }; } // Click outside to close searchModal.onclick = function(e) { if (e.target === searchModal) { searchModal.classList.remove('show'); } }; // Search Input Logic var searchInput = document.getElementById('search-input'); var searchResults = document.getElementById('search-results'); if (searchInput && searchResults) { // Clone input to remove old listeners var newInput = searchInput.cloneNode(true); searchInput.parentNode.replaceChild(newInput, searchInput); searchInput = newInput; // Clone results to remove old listeners var newResults = searchResults.cloneNode(true); searchResults.parentNode.replaceChild(newResults, searchResults); searchResults = newResults; // Result click — use data-href to avoid pjax anchor conflicts searchResults.addEventListener('click', function(e) { var item = e.target.closest('.search-result-item'); if (!item) return; var href = item.getAttribute('data-href'); if (!href) return; e.preventDefault(); e.stopPropagation(); searchModal.classList.remove('show'); searchModal.style.transition = 'none'; requestAnimationFrame(function() { requestAnimationFrame(function() { searchModal.style.transition = ''; }); }); if (window.pjax) { window.pjax.loadUrl(href, { triggerElement: item }); } else { window.location.href = href; } }); searchInput.addEventListener('input', debounce(function() { var query = this.value.trim().toLowerCase(); if (query.length === 0) { searchResults.innerHTML = '

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

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

索引加载中...

'; loadSearchIndex().then(function(data) { if (data && searchInput.value.trim().length > 0) { searchInput.dispatchEvent(new Event('input')); } else if (!data) { searchResults.innerHTML = '

索引加载失败

'; } }); return; } var results = window.searchIndex.filter(function(item) { return (item.title && item.title.toLowerCase().includes(query)) || (item.summary && item.summary.toLowerCase().includes(query)); }); if (results.length > 0) { var html = results.map(function(item) { var path = item.permalink.replace(/^https?:\/\/[^\/]+/, ''); return '
' + '' + item.title + '' + '' + item.date + '' + '
' + (item.summary || '') + '
' + '
'; }).join(''); searchResults.innerHTML = html; } else { searchResults.innerHTML = '

未找到相关文章

'; } }), 200); } } } function initInlineSearch() { var header = document.getElementById('essay-header'); var searchContainer = document.getElementById('search-container'); var input = document.getElementById('inline-search-input'); var resultsContainer = document.getElementById('inline-search-results'); window.toggleSearchMode = function(show) { if (!header || !searchContainer || !input || !resultsContainer) { console.warn('Search elements missing, cannot toggle mode.'); return; } var searchHint = document.querySelector('.search-hint-wrapper'); if (show) { if (searchHint) searchHint.style.display = 'none'; // Hide hint completely to avoid layout shifts header.style.display = 'none'; searchContainer.style.display = 'block'; input.focus(); loadSearchIndex().then(() => { console.log('Search index loaded via toggle'); }).catch(e => console.error('Search index load failed', e)); // Add Esc key listener document.addEventListener('keydown', escListener); } else { if (searchHint) searchHint.style.display = ''; // Restore hint header.style.display = 'inline-flex'; searchContainer.style.display = 'none'; input.value = ''; // Remove .has-value class searchContainer.querySelector('.search-input-group').classList.remove('has-value'); resultsContainer.classList.remove('show'); resultsContainer.innerHTML = ''; // Remove Esc key listener document.removeEventListener('keydown', escListener); } }; var escListener = function(e) { if (e.key === 'Escape') { window.toggleSearchMode(false); } }; if (!header || !searchContainer || !input || !resultsContainer) return; // Input Event var newInput = input.cloneNode(true); input.parentNode.replaceChild(newInput, input); input = newInput; // Handle Search Icon Click (Focus input) var searchIcon = searchContainer.querySelector('.search-icon'); if (searchIcon) { searchIcon.addEventListener('click', function() { input.focus(); }); } // Handle Close Icon Logic var closeIcon = searchContainer.querySelector('.close-icon'); if (closeIcon) { // Remove inline onclick handler if present closeIcon.onclick = null; closeIcon.removeAttribute('onclick'); // Add new custom handler closeIcon.addEventListener('click', function(e) { e.preventDefault(); e.stopPropagation(); if (input.value.trim().length > 0) { // Clear input input.value = ''; input.focus(); // Trigger input event to update UI input.dispatchEvent(new Event('input')); } else { // Close search window.toggleSearchMode(false); } }); } input.addEventListener('input', debounce(function() { var query = this.value.trim().toLowerCase(); var inputGroup = this.closest('.search-input-group'); // Toggle has-value class if (this.value.length > 0) { inputGroup.classList.add('has-value'); } else { inputGroup.classList.remove('has-value'); } if (query.length === 0) { resultsContainer.classList.remove('show'); resultsContainer.innerHTML = ''; return; } if (!window.searchIndex) { resultsContainer.innerHTML = '
索引加载中...
'; resultsContainer.classList.add('show'); loadSearchIndex().then(function(data) { // If data loaded successfully and user hasn't cleared input if (data && input.value.trim().length > 0) { input.dispatchEvent(new Event('input')); } else if (!data) { resultsContainer.innerHTML = '
索引加载失败
'; } }); return; } var results = window.searchIndex.filter(function(item) { return (item.title && item.title.toLowerCase().includes(query)) || (item.summary && item.summary.toLowerCase().includes(query)); }); if (results.length > 0) { var html = results.slice(0, 5).map(function(item) { var path = item.permalink.replace(/^https?:\/\/[^\/]+/, ''); return '
' + '' + item.title + '' + '
' + '' + item.date + '' + '' + (item.summary || '') + '' + '
' + '
'; }).join(''); resultsContainer.innerHTML = html; resultsContainer.classList.add('show'); } else { resultsContainer.innerHTML = '
暂无相关内容
'; resultsContainer.classList.add('show'); } }), 200); // Close on click outside if (!window.inlineSearchClickOutside) { window.inlineSearchClickOutside = function(e) { var searchContainer = document.getElementById('search-container'); if (!searchContainer || searchContainer.style.display !== 'block') return; var header = document.getElementById('essay-header'); var resultsContainer = document.getElementById('inline-search-results'); if (header && resultsContainer && !searchContainer.contains(e.target) && !header.contains(e.target) && !resultsContainer.contains(e.target)) { window.toggleSearchMode(false); } }; document.addEventListener('click', window.inlineSearchClickOutside); } // Click delegation for results resultsContainer.addEventListener('click', function(e) { var item = e.target.closest('.search-result-item'); if (!item) return; var href = item.getAttribute('data-href'); if (!href) return; e.preventDefault(); e.stopPropagation(); window.toggleSearchMode(false); if (window.pjax) { window.pjax.loadUrl(href, { triggerElement: item }); } else { window.location.href = href; } }); // Search Hint Typewriter Logic var header = document.getElementById('essay-header'); var searchHintWrapper = header ? header.querySelector('.search-hint-wrapper') : null; var searchHintText = searchHintWrapper ? searchHintWrapper.querySelector('.search-hint') : null; if (searchHintWrapper && searchHintText) { // --- Smart Greeting Logic --- var text = window.searchHintText || "点击随笔 开始搜索"; var now = new Date(); var month = now.getMonth() + 1; var day = now.getDate(); var dateStr = (month < 10 ? '0' : '') + month + '-' + (day < 10 ? '0' : '') + day; var hour = now.getHours(); // 1. Check Holidays if (window.searchHolidays && window.searchHolidays[dateStr]) { text = window.searchHolidays[dateStr]; } // 2. Check Time of Day (if greetings configured) else if (window.searchGreetings) { function pickGreeting(key) { var val = window.searchGreetings[key]; if (!val) return null; if (Array.isArray(val)) return val[Math.floor(Math.random() * val.length)]; return val; } var picked = null; if (hour >= 6 && hour < 11) picked = pickGreeting('morning'); else if (hour >= 11 && hour < 14) picked = pickGreeting('noon'); else if (hour >= 14 && hour < 18) picked = pickGreeting('afternoon'); else if (hour >= 18 && hour < 22) picked = pickGreeting('evening'); else if (hour >= 22 || hour < 6) picked = pickGreeting('night'); if (picked) text = picked; } var typeInterval; var hideTimeout; function runTypewriter(str, callback) { if (typeInterval) clearInterval(typeInterval); if (hideTimeout) clearTimeout(hideTimeout); // Ensure visible searchHintWrapper.classList.add('visible'); var i = 1; // Pre-fill first char if (str.length > 0) { searchHintText.textContent = str.charAt(0); } typeInterval = setInterval(function() { if (i >= str.length) { clearInterval(typeInterval); if (callback) callback(); return; } searchHintText.textContent = str.substring(0, i + 1); i++; }, 100); } // Check session storage for Jiaotailang tip if (!sessionStorage.getItem('jiaotailang_shown')) { // Initial Delay setTimeout(function() { // Step 1: Show Greeting runTypewriter(text, function() { // Animation Finished // Step 2: Check Mobile Logic if (window.isMobile && window.isMobile()) { // Wait 2s then show secret hint hideTimeout = setTimeout(function() { var secretHint = "悄咪咪告诉你,点击随笔即可搜索"; runTypewriter(secretHint, function() { // Finished secret hint sessionStorage.setItem('jiaotailang_shown', 'true'); // Hide after 5s hideTimeout = setTimeout(function() { searchHintWrapper.classList.remove('visible'); }, 5000); }); }, 2000); } else { // Desktop: Just hide after 8s sessionStorage.setItem('jiaotailang_shown', 'true'); hideTimeout = setTimeout(function() { searchHintWrapper.classList.remove('visible'); }, 8000); } }); }, 500); } else { // If already shown, set full text but don't auto-show searchHintText.textContent = text; } // --- Hover Logic (Desktop) --- if (!window.isMobile || !window.isMobile()) { header.addEventListener('mouseenter', function() { // Stop any running animations if (typeInterval) clearInterval(typeInterval); if (hideTimeout) clearTimeout(hideTimeout); searchHintWrapper.classList.add('visible'); searchHintText.textContent = "点击随笔,开始搜索"; }); header.addEventListener('mouseleave', function() { // Simply hide when leaving searchHintWrapper.classList.remove('visible'); // Optional: Reset text to greeting in case it's shown again by some other logic? // But mostly it will be triggered by hover again. }); } } }