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 '未找到相关文章
'; } }), 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 = '