/** * AKSRT Search Panel — desktop panel, mobile overlay, history, suggestions */ (function () { "use strict"; function initSearchPanel() { var mobileOverlay = document.getElementById("tabbar-search-overlay"); var mobileInput = document.getElementById("tabbar-search-input"); var mobileClose = document.getElementById("tabbar-search-close"); var mobileForm = mobileOverlay ? mobileOverlay.querySelector("form") : null; var mobileTypeInput = document.getElementById("tabbar-search-type"); var mobileTypeButtons = document.querySelectorAll("#tabbar-search-types [data-search-type]"); var historyWrap = document.getElementById("tabbar-search-history"); var historySection = document.getElementById("tabbar-search-history-section"); var historyClear = document.getElementById("tabbar-search-history-clear"); var suggestionsWrap = document.getElementById("tabbar-search-suggestions"); var suggestionsSection = document.getElementById("tabbar-search-suggestions-section"); var searchForms = Array.prototype.slice.call(document.querySelectorAll("[data-aksrt-search-form]")); if (!searchForms.length && !mobileForm) return; var HISTORY_KEY = "aksrt-mobile-search-history"; var suggestTimer = null; var searchConfig = (window.aksrtData && aksrtData.search) || {}; var historyEnabled = searchConfig.historyEnabled !== false; var suggestionsEnabled = !!searchConfig.suggestionsEnabled; if (historySection && !historyEnabled) { historySection.hidden = true; } if (suggestionsSection && !suggestionsEnabled) { suggestionsSection.hidden = true; } function getDesktopSearchType() { if (!searchForms.length) return searchConfig.defaultType || "post"; var input = searchForms[0].querySelector('input[name="aksrt_search_type"]'); var value = input ? (input.value || "") : ""; return value || searchConfig.defaultType || "post"; } function getDesktopCategory() { if (!searchForms.length) return "0"; var input = searchForms[0].querySelector('input[name="aksrt_category"]'); return input ? (input.value || "0") : "0"; } function syncSearchForms(type, category) { searchForms.forEach(function (form) { var typeInput = form.querySelector('input[name="aksrt_search_type"]'); if (typeInput) { typeInput.value = type || searchConfig.defaultType || "post"; } var categoryInput = form.querySelector('input[name="aksrt_category"]'); if (categoryInput) { if ((type || searchConfig.defaultType || "post") === "post" && category && category !== "0") { categoryInput.value = category; } else { categoryInput.value = ""; } } form.querySelectorAll("[data-search-type]").forEach(function (button) { button.classList.toggle("is-active", button.getAttribute("data-search-type") === (type || searchConfig.defaultType || "post")); }); }); } function syncCategorySelectors(type, category) { document.querySelectorAll("[data-search-category-select]").forEach(function (select) { select.value = category && category !== "" ? category : "0"; select.disabled = (type || searchConfig.defaultType || "post") !== "post"; }); } function getSearchType() { if (mobileTypeInput) { return mobileTypeInput.value || getDesktopSearchType(); } return getDesktopSearchType(); } function setSearchType(type) { var nextType = type || searchConfig.defaultType || getDesktopSearchType(); if (mobileTypeInput) { mobileTypeInput.value = nextType; } mobileTypeButtons.forEach(function (button) { button.classList.toggle("is-active", button.getAttribute("data-search-type") === nextType); }); syncSearchForms(nextType, getDesktopCategory()); syncCategorySelectors(nextType, getDesktopCategory()); requestSuggestions(); } function readHistory() { if (!historyEnabled) return []; if (aksrtData.isLoggedIn) return []; try { var raw = localStorage.getItem(HISTORY_KEY); var list = raw ? JSON.parse(raw) : []; return Array.isArray(list) ? list : []; } catch (e) { return []; } } function writeHistory(items) { if (!historyEnabled) return; if (aksrtData.isLoggedIn) return; try { localStorage.setItem(HISTORY_KEY, JSON.stringify(items.slice(0, 8))); } catch (e) {} } function requestHistorySync(mode, items, callback) { if (!historyEnabled) { if (historySection) historySection.hidden = true; if (typeof callback === "function") callback([]); return; } if (!aksrtData.isLoggedIn) { if (typeof callback === "function") callback(mode === "save" ? (items || []) : readHistory()); return; } var xhr = new XMLHttpRequest(); xhr.open("POST", aksrtData.ajaxUrl, true); xhr.setRequestHeader("Content-Type", "application/x-www-form-urlencoded; charset=UTF-8"); xhr.onload = function () { if (typeof callback !== "function") return; if (xhr.status < 200 || xhr.status >= 300) { callback([]); return; } try { var json = JSON.parse(xhr.responseText); callback(json.success && json.data && Array.isArray(json.data.items) ? json.data.items : []); } catch (e) { callback([]); } }; var body = "action=aksrt_search_history&nonce=" + encodeURIComponent(aksrtData.nonce) + "&mode=" + encodeURIComponent(mode); if (mode === "save") { body += "&items=" + encodeURIComponent(JSON.stringify(items || [])); } xhr.send(body); } function pushHistory(keyword, type) { if (!historyEnabled) return; if (!keyword) return; if (aksrtData.isLoggedIn) { requestHistorySync("get", null, function (remoteItems) { var nextRemote = (remoteItems || []).filter(function (item) { return !(item && item.keyword === keyword && item.type === type); }); nextRemote.unshift({ keyword: keyword, type: type }); requestHistorySync("save", nextRemote.slice(0, 8), function (savedItems) { renderHistory(savedItems); }); }); return; } var next = readHistory().filter(function (item) { return !(item && item.keyword === keyword && item.type === type); }); next.unshift({ keyword: keyword, type: type }); writeHistory(next); renderHistory(); } function renderHistory(providedItems) { if (!historyWrap) return; if (!historyEnabled) { if (historySection) historySection.hidden = true; historyWrap.innerHTML = ""; return; } if (historySection) { historySection.hidden = false; } var items = Array.isArray(providedItems) ? providedItems : readHistory(); if (!items.length) { historyWrap.innerHTML = '
暂无搜索历史
'; if (historyClear) historyClear.style.display = "none"; return; } if (historyClear) historyClear.style.display = "inline-flex"; historyWrap.innerHTML = items.map(function (item) { return ''; }).join(""); } function renderSuggestions(items) { if (!suggestionsWrap || !suggestionsSection) return; if (!suggestionsEnabled) { suggestionsSection.hidden = true; suggestionsWrap.innerHTML = ""; return; } if (!items || !items.length) { suggestionsWrap.innerHTML = '
暂无匹配结果
'; suggestionsSection.hidden = false; return; } suggestionsWrap.innerHTML = items.map(function (item) { var meta = item.meta ? '' + item.meta + '' : ''; return '' + '' + item.title + '' + meta + ''; }).join(""); suggestionsSection.hidden = false; } function requestSuggestions() { if (!mobileInput || !suggestionsWrap || !suggestionsSection) return; if (!suggestionsEnabled) { suggestionsSection.hidden = true; return; } var keyword = (mobileInput.value || "").trim(); if (suggestTimer) clearTimeout(suggestTimer); if (keyword.length < 2) { suggestionsSection.hidden = true; return; } suggestTimer = setTimeout(function () { var xhr = new XMLHttpRequest(); xhr.open("POST", aksrtData.ajaxUrl, true); xhr.setRequestHeader("Content-Type", "application/x-www-form-urlencoded; charset=UTF-8"); xhr.onload = function () { if (xhr.status < 200 || xhr.status >= 300) return; try { var json = JSON.parse(xhr.responseText); if (json.success && json.data && json.data.items) { renderSuggestions(json.data.items); } } catch (e) {} }; xhr.send( "action=aksrt_search_suggestions&nonce=" + encodeURIComponent(aksrtData.nonce) + "&q=" + encodeURIComponent(keyword) + "&type=" + encodeURIComponent(getSearchType()) ); }, 180); } function setOpen(open) { if (!mobileOverlay) return; var toggles = document.querySelectorAll(".main-search-btn, .nav-search-toggle, [data-aksrt-open-search]"); if (open) { mobileOverlay.removeAttribute("hidden"); mobileOverlay.setAttribute("aria-hidden", "false"); mobileOverlay.classList.add("is-open", "show"); document.body.classList.add("tabbar-search-open"); if (mobileInput) { try { mobileInput.focus(); } catch (e) {} } if (historyEnabled) { if (aksrtData.isLoggedIn) { requestHistorySync("get", null, function (items) { renderHistory(items); }); } else { renderHistory(); } } if (suggestionsEnabled) requestSuggestions(); toggles.forEach(function (btn) { btn.setAttribute("aria-expanded", "true"); }); } else { mobileOverlay.setAttribute("hidden", ""); mobileOverlay.setAttribute("aria-hidden", "true"); mobileOverlay.classList.remove("is-open", "show"); document.body.classList.remove("tabbar-search-open"); if (suggestionsSection) suggestionsSection.hidden = true; toggles.forEach(function (btn) { btn.setAttribute("aria-expanded", "false"); }); } } if (mobileOverlay) { mobileOverlay.addEventListener("click", function (e) { if (e.target === mobileOverlay) setOpen(false); }); } if (mobileClose) { mobileClose.addEventListener("click", function () { setOpen(false); }); } document.querySelectorAll(".main-search-btn, .nav-search-toggle, [data-aksrt-open-search]").forEach(function (btn) { btn.addEventListener("click", function (e) { e.preventDefault(); setOpen(true); }); }); // Zibll-compatible data-toggle-class for .navbar-search document.addEventListener("click", function (e) { var toggle = e.target.closest('[data-toggle-class][data-target=".navbar-search"], [data-toggle-class][data-target=".main-search"]'); if (!toggle) return; e.preventDefault(); var isOpen = mobileOverlay && !mobileOverlay.hasAttribute("hidden"); setOpen(!isOpen); }); if (historyClear) { historyClear.addEventListener("click", function () { if (aksrtData.isLoggedIn) { requestHistorySync("save", [], function (savedItems) { renderHistory(savedItems); }); return; } writeHistory([]); renderHistory(); }); } if (historyWrap) { historyWrap.addEventListener("click", function (e) { var item = e.target.closest("[data-history-keyword]"); if (!item || !mobileInput) return; mobileInput.value = decodeURIComponent(item.getAttribute("data-history-keyword") || ""); setSearchType(decodeURIComponent(item.getAttribute("data-history-type") || "post")); requestSuggestions(); }); } document.querySelectorAll("[data-search-category-select]").forEach(function (select) { select.addEventListener("change", function () { var nextCategory = this.value || "0"; syncSearchForms(getSearchType(), nextCategory); syncCategorySelectors(getSearchType(), nextCategory); }); }); mobileTypeButtons.forEach(function (button) { button.addEventListener("click", function () { setSearchType(button.getAttribute("data-search-type") || "post"); }); }); // Panel type buttons should also sync desktop/drawer forms document.querySelectorAll("#tabbar-search-types [data-search-type], [data-aksrt-search-form] [data-search-type]").forEach(function (button) { button.addEventListener("click", function () { setSearchType(button.getAttribute("data-search-type") || "post"); }); }); if (mobileInput) { mobileInput.addEventListener("input", requestSuggestions); } if (mobileForm) { mobileForm.addEventListener("submit", function () { if (mobileInput) { pushHistory((mobileInput.value || "").trim(), getSearchType()); } }); } searchForms.forEach(function (form) { if (form === mobileForm) return; form.addEventListener("submit", function () { if (!historyEnabled) return; var input = form.querySelector('input[name="s"]'); var typeInput = form.querySelector('input[name="aksrt_search_type"]'); var keyword = input ? (input.value || "").trim() : ""; var type = typeInput ? (typeInput.value || searchConfig.defaultType || "post") : (searchConfig.defaultType || "post"); pushHistory(keyword, type); }); }); syncSearchForms(getSearchType(), getDesktopCategory()); syncCategorySelectors(getSearchType(), getDesktopCategory()); setSearchType(getSearchType()); if (historyEnabled && aksrtData.isLoggedIn) { requestHistorySync("get", null, function (items) { renderHistory(items); }); } document.addEventListener("keydown", function (e) { if (e.key === "Escape" && mobileOverlay && !mobileOverlay.hasAttribute("hidden")) { setOpen(false); } }); window.__aksrtOpenSearchPanel = function () { setOpen(true); }; window.__aksrtCloseSearchPanel = function () { setOpen(false); }; // Sidebar / widget search history (zibll widget_ui_search show_history). initWidgetSearchHistory({ historyEnabled: historyEnabled, readHistory: readHistory, writeHistory: writeHistory, requestHistorySync: requestHistorySync, pushHistory: pushHistory, getSearchType: getSearchType, searchConfig: searchConfig, }); } function initWidgetSearchHistory(ctx) { var lists = document.querySelectorAll("[data-aksrt-search-history-list]"); if (!lists.length) return; if (!ctx.historyEnabled) { document.querySelectorAll("[data-aksrt-search-history]").forEach(function (el) { el.hidden = true; }); return; } function escapeHtml(s) { return String(s == null ? "" : s) .replace(/&/g, "&") .replace(//g, ">") .replace(/"/g, """); } function paint(items) { var list = Array.isArray(items) ? items : []; lists.forEach(function (wrap) { var section = wrap.closest("[data-aksrt-search-history]") || wrap.parentElement; var clearBtn = section ? section.querySelector("[data-aksrt-search-history-clear]") : null; if (!list.length) { wrap.innerHTML = '' + escapeHtml("暂无搜索历史") + ""; if (clearBtn) clearBtn.hidden = true; return; } if (clearBtn) clearBtn.hidden = false; wrap.innerHTML = list .map(function (item) { var kw = item && item.keyword ? String(item.keyword) : ""; if (!kw) return ""; var type = (item && item.type) || "post"; var href = "?s=" + encodeURIComponent(kw); return ( '' + escapeHtml(kw) + "" ); }) .join(""); }); } function load() { if (window.aksrtData && aksrtData.isLoggedIn) { ctx.requestHistorySync("get", null, function (items) { paint(items); }); return; } paint(ctx.readHistory()); } document.querySelectorAll("[data-aksrt-search-history-clear]").forEach(function (btn) { btn.addEventListener("click", function (e) { e.preventDefault(); if (window.aksrtData && aksrtData.isLoggedIn) { ctx.requestHistorySync("save", [], function (items) { paint(items); }); return; } ctx.writeHistory([]); paint([]); }); }); // Also push history when submitting bare widget forms without data-aksrt-search-form. document.querySelectorAll(".aksrt-search-widget form").forEach(function (form) { if (form.hasAttribute("data-aksrt-search-form")) return; form.addEventListener("submit", function () { var input = form.querySelector('input[name="s"]'); var typeInput = form.querySelector('input[name="aksrt_search_type"]'); var keyword = input ? (input.value || "").trim() : ""; var type = typeInput ? typeInput.value || ctx.searchConfig.defaultType || "post" : ctx.searchConfig.defaultType || "post"; if (keyword) ctx.pushHistory(keyword, type); }); }); load(); } window.AksrtFrontend.register("aksrt-search-panel", function () { initSearchPanel(); }); })();