Files
2026-09-29 15:30:33 +07:00

462 lines
14 KiB
JavaScript

(function () {
var header = document.querySelector(".site-header");
var drawer = document.querySelector("[data-drawer]");
var openBtn = document.querySelector("[data-drawer-open]");
if (header) {
var threshold = 48;
var ticking = false;
function measure() {
document.documentElement.style.setProperty("--site-header-height", header.getBoundingClientRect().height + "px");
}
function update() {
ticking = false;
header.classList.toggle("is-scrolled", window.scrollY > threshold);
}
function onScroll() {
if (ticking) return;
ticking = true;
window.requestAnimationFrame(update);
}
measure();
update();
window.addEventListener("scroll", onScroll, { passive: true });
window.addEventListener("resize", measure);
if ("ResizeObserver" in window) new ResizeObserver(measure).observe(header);
}
// 左侧抽屉
if (drawer && openBtn) {
var closeEls = drawer.querySelectorAll("[data-drawer-close]");
function openDrawer() {
drawer.classList.add("is-open");
document.body.classList.add("drawer-open");
openBtn.setAttribute("aria-expanded", "true");
}
function closeDrawer() {
drawer.classList.remove("is-open");
document.body.classList.remove("drawer-open");
openBtn.setAttribute("aria-expanded", "false");
}
openBtn.setAttribute("aria-expanded", "false");
openBtn.setAttribute("aria-controls", "site-drawer");
openBtn.addEventListener("click", openDrawer);
closeEls.forEach(function (el) {
el.addEventListener("click", closeDrawer);
});
document.addEventListener("keydown", function (e) {
if (e.key === "Escape" && drawer.classList.contains("is-open")) {
closeDrawer();
}
});
}
// 搜索表单:走路径 /search/{q},不用 query;同时写入本地搜索历史
var SEARCH_HISTORY_KEY = "hgdj_search_history";
var SEARCH_HISTORY_MAX = 10;
function searchResultsPath(q) {
q = String(q || "").trim();
if (!q) return "/search";
return "/search/" + encodeURIComponent(q).replace(/%20/g, "%20");
}
function readSearchHistory() {
try {
var raw = window.localStorage.getItem(SEARCH_HISTORY_KEY);
var list = raw ? JSON.parse(raw) : [];
if (!Array.isArray(list)) return [];
return list
.map(function (item) {
return String(item || "").trim();
})
.filter(Boolean)
.slice(0, SEARCH_HISTORY_MAX);
} catch (_) {
return [];
}
}
function writeSearchHistory(list) {
try {
window.localStorage.setItem(
SEARCH_HISTORY_KEY,
JSON.stringify(list.slice(0, SEARCH_HISTORY_MAX))
);
} catch (_) {
/* ignore quota / private mode */
}
}
function pushSearchHistory(q) {
q = String(q || "").trim();
if (!q) return;
var list = readSearchHistory().filter(function (item) {
return item !== q;
});
list.unshift(q);
writeSearchHistory(list);
}
function clearSearchHistory() {
try {
window.localStorage.removeItem(SEARCH_HISTORY_KEY);
} catch (_) {
/* ignore */
}
}
document.querySelectorAll("form[data-path-search]").forEach(function (form) {
form.addEventListener("submit", function (event) {
event.preventDefault();
var input = form.querySelector('input[type="search"], input[name="q"]');
var q = input ? String(input.value || "").trim() : "";
if (!q) {
if (input) input.focus();
return;
}
pushSearchHistory(q);
window.location.href = searchResultsPath(q);
});
var input = form.querySelector("[data-header-search-input], input[type='search'], input[name='q']");
var clear = form.querySelector("[data-header-search-clear]");
if (input && clear) {
function syncClear() {
clear.hidden = !String(input.value || "").trim();
}
input.addEventListener("input", syncClear);
clear.addEventListener("click", function () {
input.value = "";
syncClear();
input.dispatchEvent(new Event("input", { bubbles: true }));
input.focus();
});
syncClear();
}
// PC header 搜索下拉:聚焦展开历史 + 热搜;输入时切换联想
var dropdown = form.querySelector("[data-header-search-dropdown]");
if (!dropdown || !input) return;
var historyBox = form.querySelector("[data-header-search-history]");
var historyChips = form.querySelector("[data-header-history-chips]");
var historyClear = form.querySelector("[data-header-history-clear]");
var idleBox = form.querySelector("[data-header-search-idle]");
var suggestBox = form.querySelector("[data-header-search-suggest]");
var closeTimer = null;
var suggestTimer = null;
var suggestSeq = 0;
var PLACEHOLDER_IMG = "/static/images/placeholder.png?v=2";
function escapeHtml(text) {
return String(text || "")
.replace(/&/g, "&")
.replace(/</g, "&lt;")
.replace(/>/g, "&gt;")
.replace(/"/g, "&quot;");
}
function highlightHTML(text, keyword) {
var raw = String(text || "");
var key = String(keyword || "").trim();
if (!raw) return "";
if (!key) return escapeHtml(raw);
var lowerRaw = raw.toLowerCase();
var lowerKey = key.toLowerCase();
var idx = lowerRaw.indexOf(lowerKey);
if (idx < 0) return escapeHtml(raw);
var before = raw.slice(0, idx);
var hit = raw.slice(idx, idx + key.length);
var after = raw.slice(idx + key.length);
return (
escapeHtml(before) +
'<span class="is-hit">' +
escapeHtml(hit) +
"</span>" +
escapeHtml(after)
);
}
function renderHistory() {
if (!historyBox || !historyChips) return;
var list = readSearchHistory();
historyChips.innerHTML = "";
if (!list.length) {
historyBox.hidden = true;
return;
}
historyBox.hidden = false;
list.forEach(function (term) {
var link = document.createElement("a");
link.className = "search-dropdown__chip";
link.href = searchResultsPath(term);
link.textContent = term;
historyChips.appendChild(link);
});
}
function showIdlePanel() {
if (idleBox) idleBox.hidden = false;
if (suggestBox) {
suggestBox.hidden = true;
suggestBox.innerHTML = "";
}
}
function showSuggestPanel() {
if (idleBox) idleBox.hidden = true;
if (suggestBox) suggestBox.hidden = false;
}
function renderSuggest(data, keyword) {
if (!suggestBox) return;
var html = "";
var feature = data && data.feature;
var items = (data && data.items) || [];
if (feature && feature.url) {
html +=
'<article class="search-dropdown__feature">' +
'<a class="search-dropdown__feature-cover" href="' +
escapeHtml(feature.url) +
'">' +
'<img decoding="async" loading="lazy" src="' +
PLACEHOLDER_IMG +
'" data-src="' +
escapeHtml(feature.cover || "") +
'" alt="' +
escapeHtml(feature.title || "") +
'">' +
"</a>" +
'<div class="search-dropdown__feature-copy">' +
'<h2 class="search-dropdown__feature-title">' +
highlightHTML(feature.title, keyword) +
"</h2>" +
(feature.meta
? '<p class="search-dropdown__feature-meta">' + escapeHtml(feature.meta) + "</p>"
: "") +
"</div>" +
'<a class="search-dropdown__feature-play" href="' +
escapeHtml(feature.url) +
'">' +
'<img decoding="async" loading="lazy" src="' +
PLACEHOLDER_IMG +
'" data-src="/static/images/play_hollow.svg" alt="播放">播放</a>' +
"</article>";
}
if (items.length) {
html += '<div class="search-dropdown__suggest-list">';
items.forEach(function (item) {
if (!item || !item.url) return;
html +=
'<a class="search-dropdown__suggest-item" href="' +
escapeHtml(item.url) +
'" data-suggest-query="' +
escapeHtml(item.query || item.title || keyword) +
'">' +
'<img decoding="async" loading="lazy" class="search-dropdown__suggest-icon" src="' +
PLACEHOLDER_IMG +
'" data-src="/static/images/search.svg" alt="">' +
'<span class="search-dropdown__suggest-text">' +
highlightHTML(item.title || item.query || "", keyword) +
"</span>" +
'<span class="search-dropdown__suggest-type">' +
escapeHtml(item.kind || "") +
"</span>" +
"</a>";
});
html += "</div>";
} else if (!feature) {
html +=
'<p class="search-dropdown__suggest-empty">暂无相关结果,回车搜索“' +
escapeHtml(keyword) +
'”</p>';
}
suggestBox.innerHTML = html;
showSuggestPanel();
if (typeof window.initLazyImages === "function") {
window.initLazyImages(suggestBox, { eager: true });
}
}
function fetchSuggest(keyword) {
var q = String(keyword || "").trim();
if (!q || !suggestBox) {
showIdlePanel();
openDropdown();
return;
}
var seq = ++suggestSeq;
showSuggestPanel();
suggestBox.innerHTML =
'<p class="search-dropdown__suggest-empty">搜索中…</p>';
openDropdown();
var req;
if (window.HttpRequest && typeof window.HttpRequest.get === "function") {
req = window.HttpRequest.get("/api/web/search/suggest", {
params: { keyword: q },
});
} else {
req = fetch(
"/api/web/search/suggest?keyword=" + encodeURIComponent(q),
{ credentials: "include", headers: { Accept: "application/json" } }
).then(function (res) {
return res.json();
});
}
Promise.resolve(req)
.then(function (data) {
if (seq !== suggestSeq) return;
renderSuggest(data || {}, q);
})
.catch(function () {
if (seq !== suggestSeq) return;
renderSuggest(
{
feature: null,
items: [
{
title: q,
kind: "搜索",
query: q,
url: searchResultsPath(q),
},
],
},
q
);
});
}
function scheduleSuggest() {
var q = String(input.value || "").trim();
if (suggestTimer) clearTimeout(suggestTimer);
if (!q) {
suggestSeq += 1;
showIdlePanel();
openDropdown();
return;
}
suggestTimer = setTimeout(function () {
suggestTimer = null;
fetchSuggest(q);
}, 220);
}
function openDropdown() {
if (closeTimer) {
clearTimeout(closeTimer);
closeTimer = null;
}
var q = String(input.value || "").trim();
if (q) {
if (suggestBox && suggestBox.hidden) {
scheduleSuggest();
}
dropdown.hidden = false;
form.classList.add("is-open");
input.setAttribute("aria-expanded", "true");
return;
}
showIdlePanel();
renderHistory();
var hasHistory = historyBox && !historyBox.hidden;
var hasHot = !!(idleBox && idleBox.querySelector(".search-dropdown__hot"));
if (!hasHistory && !hasHot) {
dropdown.hidden = true;
form.classList.remove("is-open");
input.setAttribute("aria-expanded", "false");
return;
}
dropdown.hidden = false;
form.classList.add("is-open");
input.setAttribute("aria-expanded", "true");
}
function closeDropdown() {
dropdown.hidden = true;
form.classList.remove("is-open");
input.setAttribute("aria-expanded", "false");
}
function scheduleClose() {
if (closeTimer) clearTimeout(closeTimer);
closeTimer = setTimeout(function () {
closeTimer = null;
if (!form.contains(document.activeElement)) {
closeDropdown();
}
}, 120);
}
input.addEventListener("focus", openDropdown);
input.addEventListener("click", openDropdown);
input.addEventListener("input", function () {
if (clear) clear.hidden = !String(input.value || "").trim();
scheduleSuggest();
});
input.addEventListener("blur", scheduleClose);
dropdown.addEventListener("mousedown", function (event) {
// 点击下拉内链接/按钮时先阻止 input blur 抢先关闭
event.preventDefault();
});
dropdown.addEventListener("click", function (event) {
var chip = event.target.closest
? event.target.closest(
".search-dropdown__chip, .search-dropdown__link, .search-dropdown__feature-play, .search-dropdown__feature-cover, .search-dropdown__suggest-item"
)
: null;
if (chip && chip.href) {
var q = "";
if (chip.classList.contains("search-dropdown__chip")) {
q = String(chip.textContent || "").trim();
} else if (chip.classList.contains("search-dropdown__link")) {
var name = chip.querySelector(".search-dropdown__name");
if (name) q = String(name.textContent || "").trim();
} else if (chip.getAttribute("data-suggest-query")) {
q = String(chip.getAttribute("data-suggest-query") || "").trim();
} else {
q = String(input.value || "").trim();
}
if (q) pushSearchHistory(q);
window.location.href = chip.href;
}
});
if (historyClear) {
historyClear.addEventListener("click", function (event) {
event.preventDefault();
event.stopPropagation();
clearSearchHistory();
renderHistory();
openDropdown();
input.focus();
});
}
document.addEventListener("keydown", function (event) {
if (event.key === "Escape" && !dropdown.hidden) {
closeDropdown();
input.blur();
}
});
document.addEventListener("mousedown", function (event) {
if (dropdown.hidden) return;
if (form.contains(event.target)) return;
closeDropdown();
});
});
})();