(function () {
var section = document.querySelector("[data-dh-episodes]");
if (!section) return;
var ranges = section.querySelectorAll(".dh-range");
var episodes = section.querySelectorAll(".dh-ep");
if (!episodes.length) return;
if (!ranges.length) return;
function applyRange(start, end) {
episodes.forEach(function (el) {
var n = Number(el.getAttribute("data-ep"));
var inRange = !Number.isNaN(n) && n >= start && n <= end;
el.classList.toggle("is-range-hidden", !inRange);
});
}
function activate(btn) {
ranges.forEach(function (item) {
item.classList.toggle("is-active", item === btn);
});
applyRange(Number(btn.dataset.rangeStart), Number(btn.dataset.rangeEnd));
}
ranges.forEach(function (btn) {
btn.addEventListener("click", function () {
activate(btn);
});
});
var active = section.querySelector(".dh-range.is-active") || ranges[0];
activate(active);
})();
// 简介超出行数时显示「展开 / 收起」
(function () {
var intro = document.querySelector("[data-dh-intro]");
var toggle = document.querySelector("[data-dh-intro-toggle]");
if (!intro || !toggle) return;
function sync() {
if (intro.classList.contains("is-expanded")) return;
toggle.hidden = intro.scrollHeight <= intro.clientHeight + 1;
}
toggle.addEventListener("click", function () {
var expanded = intro.classList.toggle("is-expanded");
toggle.textContent = expanded ? "收起" : "展开";
});
window.addEventListener("resize", sync);
sync();
})();
(function () {
var shareBtns = document.querySelectorAll("[data-detail-share]");
if (!shareBtns.length) return;
function toast(message, type) {
if (typeof window.showToast === "function") {
window.showToast(message, type);
return;
}
window.alert(message);
}
function resolvePlayletId(btn) {
var id = Number(btn.getAttribute("data-playlet-id") || 0);
if (id > 0) return id;
var host =
btn.closest("[data-playlet-id]") ||
document.querySelector("[data-play-player][data-playlet-id]") ||
document.querySelector("[data-playlet-id]");
if (host) {
id = Number(host.getAttribute("data-playlet-id") || 0);
}
return id > 0 ? id : 0;
}
function reportForwardHeat(playletId) {
if (!playletId || !window.HttpRequest) return;
window.HttpRequest.post("/api/playlet/forward/heat", { id: playletId }).catch(
function () {
/* 加热失败不影响分享体验 */
}
);
}
function fallbackCopy(text) {
var area = document.createElement("textarea");
area.value = text;
area.setAttribute("readonly", "");
area.style.cssText = "position:fixed;left:-9999px;top:0;opacity:0;";
document.body.appendChild(area);
area.focus();
area.select();
area.setSelectionRange(0, area.value.length);
var ok = false;
try {
ok = document.execCommand("copy");
} catch (e) {
ok = false;
}
document.body.removeChild(area);
return ok;
}
function copyPageUrl() {
var url = window.location.href;
if (navigator.clipboard && navigator.clipboard.writeText && window.isSecureContext) {
return navigator.clipboard.writeText(url).then(
function () {
return true;
},
function () {
return fallbackCopy(url);
}
);
}
return Promise.resolve(fallbackCopy(url));
}
shareBtns.forEach(function (btn) {
btn.addEventListener("click", function () {
var playletId = resolvePlayletId(btn);
copyPageUrl()
.then(function (ok) {
if (ok) {
toast("链接已复制", "success");
reportForwardHeat(playletId);
} else {
toast("复制失败,请手动复制链接", "error");
}
})
.catch(function () {
toast("复制失败,请手动复制链接", "error");
});
});
});
})();
(function () {
// 付费剧集:不再站内解锁,统一引导下载 APP
var lockedEpisodes = document.querySelectorAll("[data-episode-locked]");
if (!lockedEpisodes.length) return;
lockedEpisodes.forEach(function (el) {
el.addEventListener("click", function (e) {
e.preventDefault();
if (typeof window.openDownloadApp === "function") {
window.openDownloadApp();
}
});
});
})();
(function () {
var grid = document.querySelector("[data-related-grid]");
var btn = document.querySelector("[data-related-refresh]");
if (!grid || !btn) return;
var section = document.querySelector("[data-related-section]");
var mobileMedia = window.matchMedia && window.matchMedia("(max-width: 767px)");
var isMobileDetail =
document.body.classList.contains("is-detail-page") &&
mobileMedia &&
mobileMedia.matches;
if (isMobileDetail) {
var PLACEHOLDER = "/static/images/placeholder.png?v=2";
var guessLikeData = [];
var guessOffset = 0;
var pageSize = 4;
grid.textContent = "";
btn.hidden = true;
if (section) section.hidden = true;
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(image, rawURL, title) {
var cover = String(rawURL || "").trim();
image.className = "poster-cover-img";
image.alt = title;
image.title = title;
image.addEventListener("error", function () {
image.classList.remove("lazy");
image.removeAttribute("data-src");
image.src = PLACEHOLDER;
}, { once: true });
if (
cover &&
typeof window.shouldDecryptImage === "function" &&
window.shouldDecryptImage(cover)
) {
image.classList.add("lazy");
image.src = PLACEHOLDER;
image.setAttribute("data-src", cover);
return;
}
image.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("div");
var episodeText = document.createElement("div");
var body = document.createElement("div");
var title = document.createElement("div");
var subtitle = document.createElement("div");
var meta = document.createElement("div");
var tag = document.createElement("span");
var titleText = String((item && item.title) || "精彩短剧");
var totalEpisode = Math.max(0, Number(item && item.totalEpisode) || 0);
card.className = "poster-card";
card.href = detailHref(item);
card.setAttribute("aria-label", titleText);
cover.className = "poster-cover";
episode.className = "episode-badge";
episodeText.className = "text-white-xs";
body.className = "poster-card-body";
title.className = "poster-card-title";
subtitle.className = "poster-card-subtitle";
meta.className = "poster-card-meta";
tag.className = "poster-card-tag";
setCardCover(image, (item && (item.cover || item.banner)) || "", titleText);
episodeText.textContent = totalEpisode ? totalEpisode + "集" : "更新中";
title.textContent = titleText;
subtitle.textContent = String((item && item.desc) || "精彩内容等你来看");
tag.textContent = firstCardTag(item);
episode.appendChild(episodeText);
cover.appendChild(image);
cover.appendChild(episode);
meta.appendChild(tag);
body.appendChild(title);
body.appendChild(subtitle);
body.appendChild(meta);
card.appendChild(cover);
card.appendChild(body);
return card;
}
function renderMobileGuessLike() {
var count = Math.min(pageSize, guessLikeData.length);
grid.textContent = "";
if (section) section.hidden = count === 0;
btn.hidden = guessLikeData.length <= pageSize;
for (var index = 0; index < count; index += 1) {
grid.appendChild(
createGuessCard(guessLikeData[(guessOffset + index) % guessLikeData.length])
);
}
if (count && typeof window.initLazyImages === "function") {
window.initLazyImages(grid);
}
}
btn.addEventListener("click", function () {
if (guessLikeData.length > pageSize) {
guessOffset = (guessOffset + pageSize) % guessLikeData.length;
}
renderMobileGuessLike();
});
function loadMobileGuessLike() {
if (!window.HttpRequest || typeof window.HttpRequest.get !== "function") {
renderMobileGuessLike();
return;
}
window.HttpRequest.get("/api/playlet/hot-like")
.then(function (response) {
var source = response && response.data && response.data.guessLike;
guessLikeData = Array.isArray(source)
? source.filter(function (item) {
return item && (item.id || item.slug);
})
: [];
guessOffset = 0;
renderMobileGuessLike();
})
.catch(function () {
guessLikeData = [];
renderMobileGuessLike();
});
}
if (document.readyState === "loading") {
document.addEventListener("DOMContentLoaded", loadMobileGuessLike, { once: true });
} else {
loadMobileGuessLike();
}
return;
}
var cards = Array.prototype.slice.call(grid.querySelectorAll(".poster-card"));
if (cards.length <= 6) {
btn.hidden = true;
return;
}
var pageSize = 6;
var page = 0;
function render() {
var start = (page * pageSize) % cards.length;
cards.forEach(function (card) {
card.style.display = "none";
});
for (var i = 0; i < pageSize; i++) {
var card = cards[(start + i) % cards.length];
card.style.display = "";
grid.appendChild(card);
}
}
render();
btn.addEventListener("click", function () {
page += 1;
render();
});
})();
(function () {
// 注意:本文件在页面底部同步加载,早于 header 中 defer 的 request.js。
// 不能在此用 `if (!HttpRequest) return`,否则评论初始化会整段跳过。
// 实际请求在 DOMContentLoaded(HttpRequest 已就绪)后再发起。
var DEFAULT_AVATAR = "/static/images/avatar.png";
var PLACEHOLDER = "/static/images/placeholder.png?v=2";
function toast(message, type) {
if (typeof window.showToast === "function") {
window.showToast(message, type);
return;
}
window.alert(message);
}
function escapeHtml(s) {
return String(s == null ? "" : s)
.replace(/&/g, "&")
.replace(//g, ">")
.replace(/"/g, """);
}
function formatCommentTime(raw) {
if (!raw) return "";
var d = new Date(raw);
if (Number.isNaN(d.getTime())) return String(raw);
return (
d.getFullYear() +
"/" +
(d.getMonth() + 1) +
"/" +
d.getDate() +
" " +
String(d.getHours()).padStart(2, "0") +
":" +
String(d.getMinutes()).padStart(2, "0")
);
}
function formatRelativeTime(raw) {
if (!raw) return "";
var d = new Date(raw);
if (Number.isNaN(d.getTime())) return String(raw);
var diff = Date.now() - d.getTime();
if (diff < 0) diff = 0;
var minutes = Math.floor(diff / 60000);
if (minutes < 1) return "刚刚";
if (minutes < 60) return minutes + "分钟前";
var hours = Math.floor(minutes / 60);
if (hours < 24) return hours + "小时前";
var days = Math.floor(hours / 24);
if (days < 30) return days + "天前";
return formatCommentTime(raw);
}
function avatarURL(url) {
url = String(url || "").trim();
return url || DEFAULT_AVATAR;
}
function replyToggleLabel(expanded, count) {
count = Number(count || 0);
if (expanded) return "收起回复";
return "查看全部 " + count + " 条回复 ›";
}
function renderDetailComment(item, playPcMode) {
var avatar = avatarURL(item.userAvatar);
var avatarHtml = item.userAvatar
? ''
: '
';
var textHtml = escapeHtml(item.text || "");
if (item.replyUserName) {
textHtml =
' " +
textHtml;
}
var children = Array.isArray(item.children) ? item.children : [];
var replyCount = Number(item.commentCount || children.length || 0);
var repliesHtml = "";
if (children.length) {
var collapsed = children.length > 1;
repliesHtml =
'