Files
web_huangguo_web/app/static/js/pages/header.js
T
2026-09-24 12:42:30 +07:00

439 lines
16 KiB
JavaScript

(function () {
var SEARCH_HISTORY_KEY = "searchHistory";
var SEARCH_HISTORY_LIMIT = 10;
function readSearchHistory() {
try {
var stored = window.localStorage.getItem(SEARCH_HISTORY_KEY);
var parsed = stored ? JSON.parse(stored) : [];
if (!Array.isArray(parsed)) return [];
return parsed
.filter(function (item) {
return typeof item === "string" && item.trim() !== "";
})
.map(function (item) {
return item.trim();
})
.slice(0, SEARCH_HISTORY_LIMIT);
} catch (_) {
return [];
}
}
function saveSearchHistory(keyword) {
keyword = String(keyword || "").trim();
if (!keyword) return readSearchHistory();
var compareKeyword = keyword.toLocaleLowerCase();
var history = readSearchHistory().filter(function (item) {
return item.toLocaleLowerCase() !== compareKeyword;
});
history.unshift(keyword);
history = history.slice(0, SEARCH_HISTORY_LIMIT);
try {
window.localStorage.setItem(SEARCH_HISTORY_KEY, JSON.stringify(history));
} catch (_) {
// 隐私模式或存储空间不可用时不阻断搜索跳转。
}
return history;
}
window.SearchHistory = {
key: SEARCH_HISTORY_KEY,
limit: SEARCH_HISTORY_LIMIT,
get: readSearchHistory,
add: saveSearchHistory,
clear: function () {
try {
window.localStorage.removeItem(SEARCH_HISTORY_KEY);
} catch (_) {
// localStorage 不可用时保持静默。
}
},
};
if (!document.querySelector('meta[name="viewport"]')) {
var viewport = document.createElement("meta");
viewport.name = "viewport";
viewport.content = "width=device-width, initial-scale=1.0";
document.head.appendChild(viewport);
}
var mobileMenu = document.getElementById("homeMobileMenu");
var mobileMenuButton = document.querySelector(".header-menu-button");
var mobileMenuCloseButtons = mobileMenu
? mobileMenu.querySelectorAll("[data-home-menu-close]")
: [];
function setMobileMenu(open) {
if (!mobileMenu || !mobileMenuButton) return;
mobileMenu.hidden = !open;
mobileMenuButton.setAttribute("aria-expanded", open ? "true" : "false");
document.body.classList.toggle("home-mobile-menu-open", open);
if (open) {
var closeButton = mobileMenu.querySelector(".home-mobile-menu-close");
if (closeButton) closeButton.focus({ preventScroll: true });
} else {
mobileMenuButton.focus({ preventScroll: true });
}
}
if (mobileMenu && mobileMenuButton) {
mobileMenuButton.addEventListener("click", function () {
setMobileMenu(true);
});
Array.prototype.forEach.call(mobileMenuCloseButtons, function (button) {
button.addEventListener("click", function () {
setMobileMenu(false);
});
});
document.addEventListener("keydown", function (event) {
if (event.key === "Escape" && !mobileMenu.hidden) setMobileMenu(false);
});
}
function refreshDecrypt(root) {
if (typeof window.initLazyImages === "function") {
window.initLazyImages(root || document);
}
}
document.addEventListener(
"click",
function (event) {
var el = event.target;
if (el && el.nodeType === 3) {
el = el.parentElement;
}
if (!el || !el.closest) return;
var tag = el.closest(".poster-card-tag[data-tag-href], .ranking-card-tag[data-tag-href]");
if (!tag) return;
var href = tag.getAttribute("data-tag-href");
if (!href) return;
event.preventDefault();
event.stopPropagation();
if (event.stopImmediatePropagation) event.stopImmediatePropagation();
window.location.href = href;
},
true
);
function initDesktopSearchPopover() {
var form = document.querySelector('form.header-search[action="/search/"]');
var input = form && form.querySelector('input[name="q"]');
var popover = document.querySelector("[data-header-search-popover]");
if (!form || !input || !popover) return;
var desktopMedia = window.matchMedia("(min-width: 768px)");
var historyList = popover.querySelector("[data-search-history-list]");
var hotList = popover.querySelector("[data-search-hot-list]");
var guessList = popover.querySelector("[data-search-guess-list]");
var historySection = popover.querySelector(".header-search-history");
var hotSection = popover.querySelector(".header-search-hot");
var guessSection = popover.querySelector(".header-search-guess");
var dividers = popover.querySelectorAll(".header-search-popover-divider");
var clearButton = popover.querySelector("[data-search-history-clear]");
var closeButton = popover.querySelector("[data-header-search-close]");
var changeButton = popover.querySelector("[data-search-guess-change]");
var hotLikeData = null;
var hotLikeRequest = null;
var guessOffset = 0;
var searchActive = false;
function syncPopoverVisibility() {
var hasHistory = historySection && !historySection.hidden;
var hasHot = hotSection && !hotSection.hidden;
var hasGuess = guessSection && !guessSection.hidden;
var hasContent = hasHistory || hasHot || hasGuess;
if (dividers[0]) dividers[0].hidden = !(hasHistory && (hasHot || hasGuess));
if (dividers[1]) dividers[1].hidden = !(hasHot && hasGuess);
popover.hidden = !(searchActive && hasContent);
form.classList.toggle("is-search-open", searchActive && hasContent);
input.setAttribute("aria-expanded", searchActive && hasContent ? "true" : "false");
if (!popover.hidden) positionPopover();
}
function searchKeyword(keyword) {
keyword = String(keyword || "").trim();
if (!keyword) return;
saveSearchHistory(keyword);
window.location.href = "/search/" + encodeURIComponent(keyword) + "/";
}
function renderHistory() {
if (!historyList) return;
historyList.textContent = "";
var history = readSearchHistory();
if (historySection) historySection.hidden = history.length === 0;
history.forEach(function (keyword) {
var button = document.createElement("button");
button.type = "button";
button.className = "header-search-history-tag";
button.textContent = keyword;
button.addEventListener("click", function () {
searchKeyword(keyword);
});
historyList.appendChild(button);
});
syncPopoverVisibility();
}
function normalizeHotItems(data) {
var hot = data && Array.isArray(data.hotSearch) ? data.hotSearch : [];
return hot
.map(function (item) {
return { keyword: String((item && item.keyword) || "").trim() };
})
.filter(function (item) {
return item.keyword !== "";
})
.slice(0, 6);
}
function renderHotSearch() {
if (!hotList) return;
hotList.textContent = "";
var items = normalizeHotItems(hotLikeData);
if (hotSection) hotSection.hidden = items.length === 0;
items.forEach(function (item, index) {
var button = document.createElement("button");
var rank = document.createElement("span");
var keyword = document.createElement("span");
button.type = "button";
button.className = "header-search-hot-item";
rank.className = "header-search-hot-rank";
keyword.className = "header-search-hot-keyword";
rank.textContent = String(index + 1);
keyword.textContent = item.keyword;
button.appendChild(rank);
button.appendChild(keyword);
button.addEventListener("click", function () {
searchKeyword(item.keyword);
});
hotList.appendChild(button);
});
syncPopoverVisibility();
}
function detailHref(item) {
var slug = String((item && item.slug) || "").trim();
var id = Number(item && item.id);
var key = slug || (id > 0 ? String(id) : "");
return key ? "/detail/" + encodeURIComponent(key) + "/" : "#";
}
function firstCardTag(item) {
var tags = item && Array.isArray(item.tagNames) ? item.tagNames : [];
var themes = item && Array.isArray(item.themeNames) ? item.themeNames : [];
return String(tags[0] || themes[0] || "热门").trim();
}
function setCardCover(img, rawURL, title) {
var cover = String(rawURL || "").trim();
var imageTitle = String(title || "精彩短剧").trim();
img.alt = imageTitle;
img.title = imageTitle;
img.addEventListener("error", function () {
img.classList.remove("lazy");
img.removeAttribute("data-src");
img.src = PLACEHOLDER;
}, { once: true });
if (cover && typeof window.shouldDecryptImage === "function" && window.shouldDecryptImage(cover)) {
img.className = "lazy";
img.src = PLACEHOLDER;
img.setAttribute("data-src", cover);
return;
}
img.src = cover || PLACEHOLDER;
}
function createGuessCard(item) {
var card = document.createElement("a");
var cover = document.createElement("div");
var image = document.createElement("img");
var episode = document.createElement("span");
var copy = document.createElement("div");
var title = document.createElement("div");
var desc = document.createElement("div");
var tagRow = document.createElement("div");
var tag = document.createElement("span");
var totalEpisode = Math.max(0, Number(item && item.totalEpisode) || 0);
card.className = "header-search-guess-card";
card.href = detailHref(item);
card.setAttribute("aria-label", String((item && item.title) || "查看短剧"));
cover.className = "header-search-guess-cover";
episode.className = "header-search-guess-episode";
copy.className = "header-search-guess-copy";
title.className = "header-search-guess-title";
desc.className = "header-search-guess-desc";
tagRow.className = "header-search-guess-tag-row";
tag.className = "header-search-guess-tag";
setCardCover(image, item && item.cover, item && item.title);
episode.textContent = totalEpisode ? totalEpisode + "集" : "更新中";
title.textContent = String((item && item.title) || "精彩短剧");
desc.textContent = String((item && item.desc) || "精彩内容等你来看");
tag.textContent = firstCardTag(item);
cover.appendChild(image);
cover.appendChild(episode);
tagRow.appendChild(tag);
copy.appendChild(title);
copy.appendChild(desc);
copy.appendChild(tagRow);
card.appendChild(cover);
card.appendChild(copy);
return card;
}
function currentGuessBatch(items) {
if (items.length <= 4) return items.slice(0, 4);
var batch = [];
for (var index = 0; index < 4; index += 1) {
batch.push(items[(guessOffset + index) % items.length]);
}
return batch;
}
function renderGuessLike() {
if (!guessList) return;
guessList.textContent = "";
var items = hotLikeData && Array.isArray(hotLikeData.guessLike)
? hotLikeData.guessLike.filter(function (item) { return item && (item.id || item.slug); })
: [];
if (guessSection) guessSection.hidden = items.length === 0;
currentGuessBatch(items).forEach(function (item) {
guessList.appendChild(createGuessCard(item));
});
if (items.length) refreshDecrypt(guessList);
syncPopoverVisibility();
}
function renderHotLike() {
renderHotSearch();
renderGuessLike();
}
function loadHotLike() {
if (hotLikeData) {
renderHotLike();
return Promise.resolve(hotLikeData);
}
if (hotLikeRequest) return hotLikeRequest;
if (hotSection) hotSection.hidden = true;
if (guessSection) guessSection.hidden = true;
syncPopoverVisibility();
if (!window.HttpRequest || typeof window.HttpRequest.get !== "function") {
hotLikeData = {};
renderHotLike();
return Promise.resolve(hotLikeData);
}
hotLikeRequest = window.HttpRequest.get("/api/playlet/hot-like")
.then(function (response) {
hotLikeData = response && response.data ? response.data : {};
renderHotLike();
return hotLikeData;
})
.catch(function () {
hotLikeData = {};
renderHotLike();
return hotLikeData;
});
return hotLikeRequest;
}
function positionPopover() {
if (popover.hidden || !desktopMedia.matches) return;
var rect = form.getBoundingClientRect();
var header = form.closest(".site-header");
var headerRect = header ? header.getBoundingClientRect() : rect;
var viewportPadding = 24;
var width = popover.offsetWidth;
var left = rect.right - width;
var top = Math.max(rect.bottom + 12, headerRect.bottom + 12);
left = Math.max(viewportPadding, Math.min(left, window.innerWidth - width - viewportPadding));
popover.style.left = Math.round(left) + "px";
popover.style.top = Math.round(top) + "px";
popover.style.maxHeight = Math.max(240, window.innerHeight - top - 16) + "px";
}
function setPopover(open, options) {
options = options || {};
searchActive = Boolean(open && desktopMedia.matches);
if (searchActive) {
renderHistory();
if (hotLikeData) renderHotLike();
else loadHotLike();
} else if (options.blur) {
input.blur();
}
syncPopoverVisibility();
}
input.addEventListener("focus", function () {
setPopover(true);
});
input.addEventListener("click", function () {
setPopover(true);
});
clearButton.addEventListener("click", function () {
window.SearchHistory.clear();
renderHistory();
input.focus({ preventScroll: true });
});
closeButton.addEventListener("click", function () {
setPopover(false, { blur: true });
});
changeButton.addEventListener("click", function () {
var items = hotLikeData && Array.isArray(hotLikeData.guessLike) ? hotLikeData.guessLike : [];
if (items.length > 4) guessOffset = (guessOffset + 4) % items.length;
renderGuessLike();
});
document.addEventListener("pointerdown", function (event) {
if (!searchActive || form.contains(event.target) || popover.contains(event.target)) return;
setPopover(false);
});
document.addEventListener("keydown", function (event) {
if (event.key === "Escape" && searchActive) {
setPopover(false, { blur: true });
}
});
window.addEventListener("resize", function () {
if (!desktopMedia.matches) {
setPopover(false);
return;
}
positionPopover();
});
window.addEventListener("scroll", positionPopover, { passive: true });
if (typeof desktopMedia.addEventListener === "function") {
desktopMedia.addEventListener("change", function (event) {
if (!event.matches) setPopover(false);
});
}
}
initDesktopSearchPopover();
document.addEventListener("submit", function (event) {
var form = event.target;
if (!form || !form.matches || !form.matches('form[action="/search/"]')) return;
var input = form.querySelector('input[name="q"]');
var keyword = String((input && input.value) || "").trim();
if (!keyword) return;
saveSearchHistory(keyword);
event.preventDefault();
window.location.href = "/search/" + encodeURIComponent(keyword) + "/";
});
if (document.body && document.body.classList.contains("search-layout")) {
var currentSearchInput = document.querySelector('.header-search input[name="q"]');
saveSearchHistory(currentSearchInput && currentSearchInput.value);
}
})();