MediaWiki:Common.js
MediaWiki界面页面
更多操作
注意:在发布之后,您可能需要清除浏览器缓存才能看到所作出的更改的影响。
- Firefox或Safari:按住Shift的同时单击刷新,或按Ctrl-F5或Ctrl-R(Mac为⌘-R)
- Google Chrome:按Ctrl-Shift-R(Mac为⌘-Shift-R)
- Edge:按住Ctrl的同时单击刷新,或按Ctrl-F5。
/* 这里的任何JavaScript将为所有用户在每次页面加载时加载。 */
/* 引入卡片数据渲染模块 */
mw.loader.load( '/index.php?title=MediaWiki:CardData.js&action=raw&ctype=text/javascript' );
/* 卡面滚动 */
window.addEventListener("scroll", function() {
var bg = document.querySelector(".card_fullscreen-img");
if (!bg) return;
if (window.scrollY > 10) {
bg.classList.add("scrolled");
} else {
bg.classList.remove("scrolled");
}
});
/* 卡面切换 */
mw.hook("wikipage.content").add(function(){
document.querySelectorAll(".ron-card").forEach(function(card){
var bg = card.querySelector(".card_fullscreen-img--switchable");
var buttons = card.querySelectorAll(".card_face-switch-btn");
if (!bg || !buttons.length) return;
function activate(face) {
bg.setAttribute("data-active-face", face);
bg.querySelectorAll(".card_face").forEach(function(item){
item.classList.toggle("is-active", item.getAttribute("data-card-face") === face);
});
buttons.forEach(function(button){
button.classList.toggle("is-active", button.getAttribute("data-card-face") === face);
});
}
buttons.forEach(function(button){
button.addEventListener("click", function(){
activate(button.getAttribute("data-card-face"));
});
button.addEventListener("keydown", function(event){
if (event.key === "Enter" || event.key === " ") {
event.preventDefault();
activate(button.getAttribute("data-card-face"));
}
});
});
});
});
/* 卡面灯箱 */
(function(){
var lightbox;
var stage;
var lightboxImg;
var lastTrigger;
var state = { scale: 1, x: 0, y: 0 };
var drag = null;
var suppressStageClickUntil = 0;
function clamp(value, min, max) {
return Math.min(max, Math.max(min, value));
}
function pointInRect(rect, event) {
if (!rect || !event || event.clientX == null || event.clientY == null) return false;
return event.clientX >= rect.left
&& event.clientX <= rect.right
&& event.clientY >= rect.top
&& event.clientY <= rect.bottom;
}
function cardImageTriggerRect(card, img) {
if (!card || !img) return null;
if (card.classList.contains("ron-card--rarity-4")) {
var stageNode = card.querySelector(".card_fullscreen-img > a, .card_fullscreen-img > .mw-file-description, .card_fullscreen-img figure, .card_fullscreen-img .mw-halign-none");
if (stageNode) {
var stageRect = stageNode.getBoundingClientRect();
if (stageRect.width > 20 && stageRect.height > 20) return stageRect;
}
}
var imageRect = img.getBoundingClientRect();
if (imageRect.width > 20 && imageRect.height > 20) return imageRect;
return null;
}
function visibleCardImageRect(card, img) {
var rect = cardImageTriggerRect(card, img);
if (!rect) return null;
var content = card.querySelector(".card_content-container");
if (content) {
var contentRect = content.getBoundingClientRect();
if (contentRect.top > rect.top && contentRect.top < rect.bottom) {
rect = {
left: rect.left,
right: rect.right,
top: rect.top,
bottom: contentRect.top,
width: rect.width,
height: Math.max(0, contentRect.top - rect.top),
};
}
}
return rect;
}
function pointInCardImage(card, img, event) {
return pointInRect(visibleCardImageRect(card, img), event);
}
function cardLightboxPointInUi(selector, event) {
if (!event || event.clientX == null || event.clientY == null) return false;
var nodes = document.querySelectorAll(selector);
for (var i = 0; i < nodes.length; i++) {
var rect = nodes[i].getBoundingClientRect();
if (rect.width > 0 && rect.height > 0 && pointInRect(rect, event)) return true;
}
return false;
}
function cardLightboxSearchUiOpen(event) {
var details = document.getElementById("citizen-search-details");
if (details && details.open) return true;
if (document.querySelector(".citizen-dropdown-details[open]")) return true;
var active = document.activeElement;
if (active && active.closest && active.closest(".citizen-search, #citizen-search__card, #searchform, #searchInput, .cdx-typeahead-search, .cdx-search-input")) return true;
return cardLightboxPointInUi(".citizen-search, #citizen-search__card, #searchform, #searchInput, [role=\"search\"], .cdx-menu, .cdx-menu-item, .cdx-typeahead-search, .cdx-search-input, .suggestions, .suggestions-results, .suggestions-special, .mw-searchSuggest-link", event);
}
function cardLightboxBlockedTarget(event) {
if (cardLightboxSearchUiOpen(event)) return true;
var target = event && event.target;
if (!target || !target.closest) return false;
return !!target.closest(".card_lightbox, .card_face-switch, .card_lightbox-tool, .card_lightbox-close, .card_content-container, .citizen-header, .citizen-page-header, .citizen-search, #citizen-search-details, #citizen-search__card, #searchform, #searchInput, [role=\"search\"], .cdx-menu, .cdx-menu-item, .cdx-typeahead-search, .cdx-search-input, .suggestions, .suggestions-results, .suggestions-special, .mw-searchSuggest-link, #mw-navigation, #p-personal");
}
function cardLightboxHit(event) {
if (!event || event.clientX == null || event.clientY == null) return null;
var cards = document.querySelectorAll(".ron-card");
for (var i = cards.length - 1; i >= 0; i--) {
var card = cards[i];
var img = currentCardImage(card);
if (!pointInCardImage(card, img, event)) continue;
return {
card: card,
trigger: null,
};
}
return null;
}
function cardLightboxAllowedHeroTrigger(card, event) {
var target = event && event.target;
if (!card || !target || !target.closest) return false;
var hero = target.closest(".card_hero");
if (!hero || !card.contains(hero)) return false;
if (target.closest(".card_face-switch")) return false;
return true;
}
function bindDocumentCardLightbox() {
if (document.documentElement.getAttribute("data-card-lightbox-document-bound") === "1") return;
document.documentElement.setAttribute("data-card-lightbox-document-bound", "1");
document.addEventListener("mouseleave", function(){
document.documentElement.classList.remove("card_lightbox-hover");
});
window.addEventListener("blur", function(){
document.documentElement.classList.remove("card_lightbox-hover");
});
window.addEventListener("scroll", function(){
document.documentElement.classList.remove("card_lightbox-hover");
}, { passive: true });
}
function applyTransform(animate) {
if (!lightboxImg) return;
if (!animate) lightbox.classList.add("is-dragging");
lightboxImg.style.setProperty("--card-lightbox-scale", state.scale);
lightboxImg.style.setProperty("--card-lightbox-x", state.x + "px");
lightboxImg.style.setProperty("--card-lightbox-y", state.y + "px");
if (!animate) {
window.requestAnimationFrame(function(){
if (!drag) lightbox.classList.remove("is-dragging");
});
}
}
function resetTransform() {
state = { scale: 1, x: 0, y: 0 };
applyTransform(true);
}
function zoomBy(delta, clientX, clientY) {
var previous = state.scale;
var next = clamp(previous * delta, 1, 4);
if (next === previous) return;
if (clientX != null && clientY != null && stage) {
var rect = stage.getBoundingClientRect();
var cx = clientX - rect.left - rect.width / 2;
var cy = clientY - rect.top - rect.height / 2;
var ratio = next / previous;
state.x = (state.x - cx) * ratio + cx;
state.y = (state.y - cy) * ratio + cy;
}
state.scale = next;
if (state.scale === 1) {
state.x = 0;
state.y = 0;
}
applyTransform(true);
}
function ensureLightbox() {
if (lightbox) return lightbox;
lightbox = document.createElement("div");
lightbox.className = "card_lightbox";
lightbox.setAttribute("role", "dialog");
lightbox.setAttribute("aria-modal", "true");
stage = document.createElement("div");
stage.className = "card_lightbox-stage";
lightboxImg = document.createElement("img");
lightboxImg.className = "card_lightbox-img";
lightboxImg.alt = "";
lightboxImg.draggable = false;
var close = document.createElement("div");
close.className = "card_lightbox-close";
close.setAttribute("role", "button");
close.setAttribute("tabindex", "0");
close.setAttribute("aria-label", "关闭");
close.textContent = "×";
var tools = document.createElement("div");
tools.className = "card_lightbox-tools";
var zoomOut = document.createElement("div");
zoomOut.className = "card_lightbox-tool";
zoomOut.setAttribute("role", "button");
zoomOut.setAttribute("tabindex", "0");
zoomOut.setAttribute("aria-label", "缩小");
zoomOut.setAttribute("data-card-lightbox-action", "zoom-out");
zoomOut.textContent = "−";
var reset = document.createElement("div");
reset.className = "card_lightbox-tool";
reset.setAttribute("role", "button");
reset.setAttribute("tabindex", "0");
reset.setAttribute("aria-label", "重置");
reset.setAttribute("data-card-lightbox-action", "reset");
reset.textContent = "1:1";
var zoomIn = document.createElement("div");
zoomIn.className = "card_lightbox-tool";
zoomIn.setAttribute("role", "button");
zoomIn.setAttribute("tabindex", "0");
zoomIn.setAttribute("aria-label", "放大");
zoomIn.setAttribute("data-card-lightbox-action", "zoom-in");
zoomIn.textContent = "+";
stage.appendChild(lightboxImg);
tools.appendChild(zoomOut);
tools.appendChild(reset);
tools.appendChild(zoomIn);
lightbox.appendChild(stage);
lightbox.appendChild(close);
lightbox.appendChild(tools);
document.body.appendChild(lightbox);
function keyboardButton(event, fn) {
if (event.type === "keydown" && event.key !== "Enter" && event.key !== " ") return;
event.preventDefault();
fn(event);
}
close.addEventListener("click", function(event){ keyboardButton(event, closeLightbox); });
close.addEventListener("keydown", function(event){ keyboardButton(event, closeLightbox); });
zoomOut.addEventListener("click", function(event){ keyboardButton(event, function(){ zoomBy(1 / 1.22); }); });
zoomOut.addEventListener("keydown", function(event){ keyboardButton(event, function(){ zoomBy(1 / 1.22); }); });
zoomIn.addEventListener("click", function(event){ keyboardButton(event, function(){ zoomBy(1.22); }); });
zoomIn.addEventListener("keydown", function(event){ keyboardButton(event, function(){ zoomBy(1.22); }); });
reset.addEventListener("click", function(event){ keyboardButton(event, resetTransform); });
reset.addEventListener("keydown", function(event){ keyboardButton(event, resetTransform); });
lightbox.addEventListener("click", function(event){
if (Date.now() < suppressStageClickUntil) {
event.preventDefault();
event.stopPropagation();
return;
}
if (event.target === lightbox || event.target === stage) closeLightbox();
});
lightbox.addEventListener("wheel", function(event){
if (!lightbox.classList.contains("is-open")) return;
event.preventDefault();
zoomBy(event.deltaY < 0 ? 1.12 : 1 / 1.12, event.clientX, event.clientY);
}, { passive: false });
stage.addEventListener("dblclick", function(event){
event.preventDefault();
if (state.scale === 1) zoomBy(2, event.clientX, event.clientY);
else resetTransform();
});
stage.addEventListener("pointerdown", function(event){
if (event.button != null && event.button !== 0) return;
if (state.scale <= 1) return;
event.preventDefault();
drag = {
startX: event.clientX,
startY: event.clientY,
baseX: state.x,
baseY: state.y,
moved: false,
};
lightbox.classList.add("is-dragging");
stage.setPointerCapture(event.pointerId);
});
stage.addEventListener("pointermove", function(event){
if (!drag) return;
var dx = event.clientX - drag.startX;
var dy = event.clientY - drag.startY;
if (Math.abs(dx) + Math.abs(dy) > 4) drag.moved = true;
state.x = drag.baseX + dx;
state.y = drag.baseY + dy;
applyTransform(false);
});
stage.addEventListener("pointerup", function(event){
if (!drag) return;
if (drag.moved) suppressStageClickUntil = Date.now() + 280;
drag = null;
stage.releasePointerCapture(event.pointerId);
lightbox.classList.remove("is-dragging");
});
stage.addEventListener("pointercancel", function(){
drag = null;
lightbox.classList.remove("is-dragging");
});
document.addEventListener("keydown", function(event){
if (!lightbox.classList.contains("is-open")) return;
if (event.key === "Escape") closeLightbox();
if (event.key === "+" || event.key === "=") zoomBy(1.18);
if (event.key === "-") zoomBy(1 / 1.18);
if (event.key === "0") resetTransform();
});
return lightbox;
}
function currentCardImage(card) {
var activeFace = card.querySelector(".card_face.is-active img");
if (activeFace) return activeFace;
return card.querySelector(".card_fullscreen-img img");
}
function originalImageSrc(img) {
var src = img && (img.currentSrc || img.src || img.getAttribute("src"));
if (!src) return "";
try {
var url = new URL(src, window.location.href);
var thumbMarker = "/images/thumb/";
var index = url.pathname.indexOf(thumbMarker);
if (index === -1) return url.href;
var prefix = url.pathname.slice(0, index) + "/images/";
var rest = url.pathname.slice(index + thumbMarker.length);
var parts = rest.split("/");
if (parts.length < 4) return url.href;
parts.pop();
url.pathname = prefix + parts.join("/");
url.search = "";
url.hash = "";
return url.href;
} catch (error) {
return src;
}
}
function openImageLightbox(img, trigger) {
if (!img || !img.src) return;
ensureLightbox();
lastTrigger = trigger || null;
resetTransform();
lightboxImg.src = originalImageSrc(img) || img.currentSrc || img.src;
lightboxImg.alt = img.alt || "";
document.body.classList.add("card_lightbox-open");
window.requestAnimationFrame(function(){
lightbox.classList.add("is-open");
});
var close = lightbox.querySelector(".card_lightbox-close");
if (close) close.focus({ preventScroll: true });
}
function openLightbox(card, trigger) {
openImageLightbox(currentCardImage(card), trigger);
}
function closeLightbox() {
if (!lightbox) return;
lightbox.classList.remove("is-open");
document.body.classList.remove("card_lightbox-open");
drag = null;
window.setTimeout(resetTransform, 180);
if (lastTrigger && typeof lastTrigger.focus === "function") {
lastTrigger.focus({ preventScroll: true });
}
}
function bindInlineImageLightbox(container) {
container.querySelectorAll("[data-card-lightbox-image], .character_profile-portrait-layer--avatar, .character_profile-bg-art").forEach(function(trigger){
if (trigger.getAttribute("data-card-lightbox-bound") === "1") return;
var img = trigger.querySelector("img");
if (!img) return;
trigger.setAttribute("data-card-lightbox-bound", "1");
var lightboxLabel = trigger.classList && trigger.classList.contains("character_profile-bg-art") ? "查看角色立绘" : "查看角色头像";
if (!trigger.hasAttribute("role")) trigger.setAttribute("role", "button");
if (!trigger.hasAttribute("tabindex")) trigger.setAttribute("tabindex", "0");
if (!trigger.hasAttribute("title")) trigger.setAttribute("title", lightboxLabel);
if (!trigger.hasAttribute("aria-label")) trigger.setAttribute("aria-label", lightboxLabel);
function activate(event) {
if (event) {
event.preventDefault();
event.stopPropagation();
if (typeof event.stopImmediatePropagation === "function") event.stopImmediatePropagation();
}
openImageLightbox(img, trigger);
}
trigger.addEventListener("click", activate, true);
trigger.addEventListener("keydown", function(event){
if (event.key !== "Enter" && event.key !== " ") return;
activate(event);
});
});
}
mw.hook("wikipage.content").add(function($content){
bindDocumentCardLightbox();
var container = $content && $content[0] ? $content[0] : document;
bindInlineImageLightbox(container);
container.querySelectorAll(".ron-card").forEach(function(card){
var hero = card.querySelector(".card_hero");
if (!hero || hero.getAttribute("data-card-lightbox-bound") === "1") return;
hero.setAttribute("data-card-lightbox-bound", "1");
hero.setAttribute("role", "button");
hero.setAttribute("tabindex", "0");
hero.setAttribute("aria-label", "放大卡面");
hero.addEventListener("mousemove", function(event){
var img = currentCardImage(card);
var active = cardLightboxAllowedHeroTrigger(card, event) && pointInCardImage(card, img, event);
hero.style.cursor = active ? "zoom-in" : "default";
document.documentElement.classList.toggle("card_lightbox-hover", active);
}, true);
hero.addEventListener("mouseleave", function(){
hero.style.cursor = "default";
document.documentElement.classList.remove("card_lightbox-hover");
});
hero.addEventListener("click", function(event){
if (!cardLightboxAllowedHeroTrigger(card, event)) return;
var img = currentCardImage(card);
if (!pointInCardImage(card, img, event)) return;
event.preventDefault();
event.stopPropagation();
openLightbox(card, hero);
}, true);
hero.addEventListener("keydown", function(event){
if (event.key !== "Enter" && event.key !== " ") return;
if (cardLightboxBlockedTarget(event)) return;
event.preventDefault();
openLightbox(card, hero);
});
});
});
}());
/* 小传折叠 + 标题 sticky */
mw.hook("wikipage.content").add(function(){
document.querySelectorAll(".card_content_story").forEach(function(story){
var toggle = story.querySelector(".card_content_story-toggle");
var timeline = story.querySelector(".card_content_story-timeline");
var headerLeft = story.querySelector(".card_content_story-header-left");
if (!toggle || !timeline || !headerLeft) return;
var icon = toggle.querySelector(".story-toggle-icon");
var text = toggle.querySelector(".story-toggle-text");
toggle.addEventListener("click", function(){
var collapsed = toggle.getAttribute("data-collapsed") === "true";
if (collapsed) {
timeline.style.opacity = "1";
timeline.style.transform = "translateY(0)";
timeline.style.maxHeight = "none";
} else {
timeline.style.opacity = "0";
timeline.style.transform = "translateY(-6px)";
timeline.style.maxHeight = "0";
}
toggle.setAttribute("data-collapsed", collapsed ? "false" : "true");
icon.innerText = collapsed ? "-" : "+";
text.innerText = collapsed ? "收起" : "展开";
if (!collapsed) headerLeft.classList.remove("is-sticky");
});
function updateSticky() {
if (toggle.getAttribute("data-collapsed") === "true") {
headerLeft.classList.remove("is-sticky");
return;
}
var storyRect = story.getBoundingClientRect();
var timelineRect = timeline.getBoundingClientRect();
if (storyRect.top < 0 && timelineRect.bottom > window.innerHeight) {
headerLeft.classList.add("is-sticky");
} else {
headerLeft.classList.remove("is-sticky");
}
}
window.addEventListener("scroll", updateSticky, { passive: true });
updateSticky();
});
});
/* 邀约故事区 */
mw.hook("wikipage.content").add(function(){
var cards = document.querySelectorAll(".card_content_date-item");
var panel = document.getElementById("date-story-panel");
var contents = document.querySelectorAll(".card_date_story-content");
if (!cards.length || !panel) return;
var currentIndex = -1;
cards.forEach(function(card){
card.addEventListener("click", function(){
var index = parseInt(card.getAttribute("data-date-index"), 10);
if (currentIndex === index) {
panel.classList.remove("is-visible");
card.classList.remove("is-active");
currentIndex = -1;
return;
}
cards.forEach(function(c){ c.classList.remove("is-active"); });
card.classList.add("is-active");
currentIndex = index;
panel.classList.remove("is-visible");
void panel.offsetWidth;
panel.classList.add("is-visible");
contents.forEach(function(content){
var storyIndex = parseInt(content.getAttribute("data-story-index"), 10);
content.classList.toggle("is-active", storyIndex === index);
});
});
});
});
/* 道具页:列表切换 + 搜索过滤 */
mw.hook("wikipage.content").add(function(){
var tabs = document.querySelectorAll(".item-tab");
var details = document.querySelectorAll(".item-detail");
var search = document.getElementById("item-search");
if (!tabs.length) return;
function activate(id) {
details.forEach(function(el){ el.style.display = "none"; });
tabs.forEach(function(t){ t.setAttribute("data-active", "false"); });
var detail = document.getElementById("item-" + id);
if (detail) detail.style.display = "block";
var tab = document.querySelector(".item-tab[data-item='" + id + "']");
if (tab) tab.setAttribute("data-active", "true");
}
tabs.forEach(function(tab){
tab.addEventListener("click", function(){
activate(tab.getAttribute("data-item"));
});
});
if (search) {
search.addEventListener("input", function(){
var q = search.value.trim().toLowerCase();
tabs.forEach(function(tab){
var text = (tab.getAttribute("data-name") || tab.textContent || "").toLowerCase();
tab.style.display = text.indexOf(q) !== -1 ? "" : "none";
});
});
}
});
/* Character archive sandbox anchor controls. */
(function(){
function archiveEscapeId(id) {
if (window.CSS && CSS.escape) return CSS.escape(id);
return String(id).replace(/["\\#.;?+*~':!^$[\]()=>|/@]/g, "\\$&");
}
function setArchiveNavActive(control, active) {
control.setAttribute("data-archive-active", active ? "1" : "0");
}
function bindArchiveAnchorControls(root) {
var controls = Array.prototype.slice.call(root.querySelectorAll("[data-archive-snap-target]"));
controls.forEach(function(control){
if (control.getAttribute("data-archive-bound") === "1") return;
control.setAttribute("data-archive-bound", "1");
var activate = function(){
var id = control.getAttribute("data-archive-snap-target");
if (!id) return;
var target = root.querySelector("#" + archiveEscapeId(id));
if (!target) return;
if (typeof root.__archiveForceActive === "function") root.__archiveForceActive(id, 1200);
target.scrollIntoView({ behavior: "smooth", block: "start" });
};
control.addEventListener("click", activate);
control.addEventListener("keydown", function(event){
if (event.key === "Enter" || event.key === " ") {
event.preventDefault();
activate();
}
});
});
}
function bindArchiveScrollSpy(root) {
var nav = root.querySelector("[data-archive-sticky-nav]");
var controls = Array.prototype.slice.call(root.querySelectorAll("[data-archive-nav-item][data-archive-snap-target]"));
if (!controls.length) return;
var targets = [];
var seen = {};
controls.forEach(function(control){
var id = control.getAttribute("data-archive-snap-target");
if (!id || seen[id]) return;
var target = root.querySelector("#" + archiveEscapeId(id));
if (!target) return;
seen[id] = true;
targets.push({ id: id, element: target });
});
if (!targets.length) return;
function measureNav() {
if (!nav) return;
var half = Math.max(120, Math.round(nav.getBoundingClientRect().height / 2));
nav.style.setProperty("--archive-nav-half-height", half + "px");
}
var currentId = "";
var forcedId = "";
var forcedUntil = 0;
function applyActive(activeId) {
if (activeId === currentId) return;
currentId = activeId;
controls.forEach(function(control){
var targetId = control.getAttribute("data-archive-snap-target");
var parentScope = control.getAttribute("data-archive-nav-parent-scope");
var parentActive = parentScope && (activeId === "archive-dossier-top" || activeId.indexOf(parentScope + "-") === 0);
setArchiveNavActive(control, targetId === activeId || parentActive);
});
}
root.__archiveForceActive = function(id, duration) {
forcedId = id || "";
forcedUntil = Date.now() + (duration || 1000);
if (forcedId) applyActive(forcedId);
requestUpdate();
};
function updateActive() {
if (forcedId && Date.now() < forcedUntil) {
applyActive(forcedId);
return;
}
forcedId = "";
var anchor = window.innerHeight * 0.46;
var activeId = targets[0].id;
targets.forEach(function(item){
if (item.element.getBoundingClientRect().top <= anchor) activeId = item.id;
});
if (window.innerHeight + window.pageYOffset >= document.documentElement.scrollHeight - 3) {
activeId = targets[targets.length - 1].id;
}
applyActive(activeId);
}
var ticking = false;
function requestUpdate() {
if (ticking) return;
ticking = true;
window.requestAnimationFrame(function(){
ticking = false;
measureNav();
updateActive();
});
}
if (nav && nav.getAttribute("data-archive-spy-bound") !== "1") {
nav.setAttribute("data-archive-spy-bound", "1");
window.addEventListener("resize", requestUpdate, { passive: true });
}
window.addEventListener("scroll", requestUpdate, { passive: true });
requestUpdate();
window.setTimeout(requestUpdate, 240);
}
mw.hook("wikipage.content").add(function($content){
var container = $content && $content[0] ? $content[0] : document;
container.querySelectorAll(".character_archive-snap").forEach(function(root){
bindArchiveAnchorControls(root);
bindArchiveScrollSpy(root);
});
});
}());
/* Style attribute growth slider */
(function(){
var BREAK_LEVELS = [20, 40, 60, 80, 100];
function numberAttr(root, name, fallback) {
var value = Number(root.getAttribute(name));
return Number.isFinite(value) ? value : fallback;
}
function breakForLevel(level) {
if (level <= 20) return 0;
if (level <= 40) return 1;
if (level <= 60) return 2;
if (level <= 80) return 3;
return 4;
}
function clampLevel(value) {
value = Math.round(Number(value));
if (!Number.isFinite(value)) return 1;
return Math.min(100, Math.max(1, value));
}
function formatNumber(value) {
return Math.floor(value).toLocaleString("zh-CN");
}
function calc(data, level) {
var breakCount = breakForLevel(level);
return {
breakCount: breakCount,
hp: Math.floor(data.baseHp + (level - 1) * data.hpRate + breakCount * data.breakHp),
atk: Math.floor(data.baseAtk + (level - 1) * data.atkRate + breakCount * data.breakAtk),
def: Math.floor(data.baseDef + (level - 1) * data.defRate + breakCount * data.breakDef)
};
}
function styleAttrThumbColorForPct(pct) {
var t = Math.max(0, Math.min(1, pct / 100));
var start = { r: 255, g: 198, b: 198 };
var end = { r: 255, g: 124, b: 124 };
var r = Math.round(start.r + (end.r - start.r) * t);
var g = Math.round(start.g + (end.g - start.g) * t);
var b = Math.round(start.b + (end.b - start.b) * t);
return "rgba(" + r + ", " + g + ", " + b + ", 0.78)";
}
function setLevel(root, data, level) {
level = clampLevel(level);
var result = calc(data, level);
var pct = ((level - 1) / 99) * 100;
var slider = root.querySelector("[data-style-attr-slider]");
var fill = root.querySelector(".style_attr-slider-fill");
var thumb = root.querySelector(".style_attr-slider-thumb");
var levelNumber = root.querySelector("[data-style-attr-level-number]");
var levelLabel = root.querySelector("[data-style-attr-level-label]");
var breakLabel = root.querySelector("[data-style-attr-break-label]");
var hp = root.querySelector('[data-style-attr-stat="hp"]');
var atk = root.querySelector('[data-style-attr-stat="atk"]');
var def = root.querySelector('[data-style-attr-stat="def"]');
if (slider) {
slider.setAttribute("aria-valuenow", String(level));
slider.setAttribute("aria-valuetext", "Lv." + level);
}
if (fill) fill.style.width = pct + "%";
if (thumb) {
thumb.style.left = pct + "%";
thumb.style.setProperty("--style-attr-thumb-color", styleAttrThumbColorForPct(pct));
}
if (levelNumber) {
levelNumber.textContent = String(level);
} else if (levelLabel) {
levelLabel.textContent = "Lv." + level;
}
if (breakLabel) breakLabel.textContent = "晋升 " + result.breakCount;
if (hp) hp.textContent = formatNumber(result.hp);
if (atk) atk.textContent = formatNumber(result.atk);
if (def) def.textContent = formatNumber(result.def);
root.querySelectorAll("[data-style-attr-preset]").forEach(function(item){
item.classList.toggle("is-active", Number(item.getAttribute("data-style-attr-preset")) === level);
});
}
function levelFromPointer(slider, event) {
var rect = slider.getBoundingClientRect();
var ratio = rect.width ? (event.clientX - rect.left) / rect.width : 0;
ratio = Math.min(1, Math.max(0, ratio));
return 1 + Math.round(ratio * 99);
}
function bindGrowth(root) {
if (root.getAttribute("data-style-attr-bound") === "1") return;
root.setAttribute("data-style-attr-bound", "1");
var data = {
baseHp: numberAttr(root, "data-base-hp", 0),
baseAtk: numberAttr(root, "data-base-atk", 0),
baseDef: numberAttr(root, "data-base-def", 0),
hpRate: numberAttr(root, "data-rate-hp", 0),
atkRate: numberAttr(root, "data-rate-atk", 0),
defRate: numberAttr(root, "data-rate-def", 0),
breakHp: numberAttr(root, "data-break-hp", 0),
breakAtk: numberAttr(root, "data-break-atk", 0),
breakDef: numberAttr(root, "data-break-def", 0)
};
var slider = root.querySelector("[data-style-attr-slider]");
var defaultLevel = clampLevel(root.getAttribute("data-default-level") || 100);
if (slider) {
var dragging = false;
var applyPointer = function(event) {
setLevel(root, data, levelFromPointer(slider, event));
};
slider.addEventListener("pointerdown", function(event){
event.preventDefault();
dragging = true;
slider.setPointerCapture(event.pointerId);
applyPointer(event);
});
slider.addEventListener("pointermove", function(event){
if (!dragging) return;
applyPointer(event);
});
slider.addEventListener("pointerup", function(event){
dragging = false;
if (slider.hasPointerCapture(event.pointerId)) slider.releasePointerCapture(event.pointerId);
});
slider.addEventListener("pointercancel", function(event){
dragging = false;
if (slider.hasPointerCapture(event.pointerId)) slider.releasePointerCapture(event.pointerId);
});
slider.addEventListener("keydown", function(event){
var current = clampLevel(slider.getAttribute("aria-valuenow") || defaultLevel);
var next = current;
if (event.key === "ArrowLeft" || event.key === "ArrowDown") next = current - 1;
else if (event.key === "ArrowRight" || event.key === "ArrowUp") next = current + 1;
else if (event.key === "PageDown") next = current - 10;
else if (event.key === "PageUp") next = current + 10;
else if (event.key === "Home") next = 1;
else if (event.key === "End") next = 100;
else return;
event.preventDefault();
setLevel(root, data, next);
});
}
root.querySelectorAll("[data-style-attr-preset]").forEach(function(item){
var activate = function() {
setLevel(root, data, item.getAttribute("data-style-attr-preset"));
};
item.addEventListener("click", activate);
item.addEventListener("keydown", function(event){
if (event.key !== "Enter" && event.key !== " ") return;
event.preventDefault();
activate();
});
});
setLevel(root, data, defaultLevel);
}
mw.hook("wikipage.content").add(function($content){
var container = $content && $content[0] ? $content[0] : document;
container.querySelectorAll("[data-style-attr-growth]").forEach(bindGrowth);
});
}());
/* atlas filter sandbox sticky settle */
(function(){
function isAllOption(option) {
var key = option && option.getAttribute("data-atlas-filter");
return !!key && /-all$/.test(key);
}
function filterKey(option) {
var key = option && option.getAttribute("data-atlas-filter");
if (!key) return null;
var match = key.match(/^([^-]+)-(.+)$/);
if (!match) return null;
return { group: match[1], value: match[2], all: match[2] === "all" };
}
function normalize(text) {
return String(text || "").trim().toLowerCase();
}
function searchValue(node) {
if (!node) return "";
if (typeof node.value === "string") return node.value;
return node.textContent || "";
}
function pageUrlForTitle(title) {
if (window.mw && mw.util && mw.util.getUrl) return mw.util.getUrl(title);
return "/wiki/" + encodeURIComponent(title).replace(/%20/g, "_");
}
function openTitleInNewWindow(title) {
var url = pageUrlForTitle(title);
var opened = window.open(url, "_blank", "noopener");
if (opened) {
try { opened.opener = null; } catch (error) {}
}
}
function previewModeEnabled(root) {
return root && root.getAttribute("data-atlas-preview-mode") === "1";
}
function previewImageUrl(card) {
var img = card && card.querySelector(".atlas_card-proto-img img");
return img ? (img.currentSrc || img.src || img.getAttribute("src") || "") : "";
}
function fileRedirectUrl(fileName) {
if (!fileName) return "";
if (window.mw && mw.config && mw.config.get && mw.config.get("wgScript")) {
return mw.config.get("wgScript") + "?title=Special:Redirect/file/" + encodeURIComponent(fileName);
}
return "/wiki/Special:Redirect/file/" + encodeURIComponent(fileName);
}
function setPreviewIcon(root, selector, fileName) {
var node = root.querySelector(selector);
if (!node) return;
node.innerHTML = "";
if (!fileName || fileName === "—") return;
var img = document.createElement("img");
img.alt = "";
img.loading = "lazy";
img.decoding = "async";
img.src = fileRedirectUrl(fileName);
node.appendChild(img);
}
function setText(root, selector, value) {
root.querySelectorAll(selector).forEach(function(node){
node.textContent = value || "—";
});
}
function atlasDangerStars(value) {
var count = parseInt(value, 10);
if (!isFinite(count) || count < 1) return "—";
count = Math.max(1, Math.min(6, count));
return Array(count + 1).join("★");
}
function renderAtlasPreviewTags(root, card) {
var node = root.querySelector("[data-atlas-preview-tags]");
if (!node) return;
node.innerHTML = "";
var raw = card.getAttribute("data-atlas-tags") || "";
var tags = raw.split("|").map(function(tag){ return tag.trim(); }).filter(Boolean);
if (!tags.length) {
node.textContent = "—";
return;
}
tags.forEach(function(tag){
var item = document.createElement("div");
item.className = "card_content_style-tag card_content_skill-tag atlas_frame-preview-tag";
item.textContent = tag;
node.appendChild(item);
});
}
function cloneAtlasSkillDesc(root, card, slot) {
var target = root.querySelector("[data-atlas-preview-skill-" + slot + "-desc]");
if (!target) return;
target.innerHTML = "";
var source = card.querySelector('[data-atlas-skill-desc-source="' + slot + '"]');
if (!source) {
target.textContent = card.getAttribute("data-atlas-skill-" + slot + "-desc") || "—";
return;
}
Array.prototype.forEach.call(source.childNodes, function(child){
target.appendChild(child.cloneNode(true));
});
}
function renderAtlasPreviewCollectionTerms(root, card) {
var target = root.querySelector("[data-atlas-preview-collection-list]");
if (!target) return;
target.innerHTML = "";
if (!card) {
target.textContent = "—";
return;
}
var sources = card.querySelectorAll("[data-atlas-collection-term-source]");
if (!sources.length) {
target.textContent = "—";
return;
}
Array.prototype.forEach.call(sources, function(source){
target.appendChild(source.cloneNode(true));
});
}
function clearAtlasPreview(root) {
if (!root) return;
root.__atlasPreviewCard = null;
root.setAttribute("data-atlas-has-selection", "0");
root.querySelectorAll("[data-atlas-preview-face]").forEach(function(faceButton){
if (faceButton.getAttribute("data-atlas-preview-face-bound") === "1") return;
faceButton.setAttribute("data-atlas-preview-face-bound", "1");
function activateFace(event) {
if (event) event.preventDefault();
var card = root.__atlasPreviewCard;
if (!card) return;
setAtlasPreviewFace(root, card, faceButton.getAttribute("data-atlas-preview-face"));
}
faceButton.addEventListener("click", activateFace);
faceButton.addEventListener("keydown", function(event){
if (event.key !== "Enter" && event.key !== " ") return;
event.preventDefault();
activateFace(event);
});
});
var previewOpen = root.querySelector("[data-atlas-preview-open]");
if (previewOpen && previewOpen.getAttribute("data-atlas-preview-open-bound") !== "1") {
previewOpen.setAttribute("data-atlas-preview-open-bound", "1");
function openPreviewStyle(event) {
if (event) {
event.preventDefault();
event.stopPropagation();
}
if (previewOpen.getAttribute("aria-disabled") === "true") return;
var title = previewOpen.getAttribute("data-atlas-preview-open-title");
if (!title && root.__atlasPreviewCard) {
title = root.__atlasPreviewCard.getAttribute("data-atlas-page-title")
|| root.__atlasPreviewCard.getAttribute("data-atlas-style-name");
}
if (!title) return;
var opened = window.open(pageUrlForTitle(title), "_blank", "noopener");
if (opened) {
try { opened.opener = null; } catch (error) {}
}
}
previewOpen.addEventListener("click", openPreviewStyle);
previewOpen.addEventListener("keydown", function(event){
if (event.key !== "Enter" && event.key !== " ") return;
openPreviewStyle(event);
});
}
root.querySelectorAll("[data-atlas-card]").forEach(function(card){
card.classList.remove("is-active");
});
var panel = root.querySelector("[data-atlas-preview-panel]");
if (panel) panel.classList.remove("is-filled");
var openAction = root.querySelector("[data-atlas-preview-open]");
if (openAction) {
openAction.setAttribute("aria-disabled", "true");
openAction.removeAttribute("data-atlas-preview-open-title");
}
var art = root.querySelector("[data-atlas-preview-art]");
if (art) art.innerHTML = "";
var switcher = root.querySelector("[data-atlas-preview-face-switch]");
if (switcher) switcher.style.display = "none";
setText(root, "[data-atlas-preview-title]", "详情预览");
setText(root, "[data-atlas-preview-style]", "未选择");
setText(root, "[data-atlas-preview-sub]", "—");
setText(root, "[data-atlas-preview-copy]", "");
renderAtlasPreviewCollectionTerms(root, null);
var clearPreviewPanel = root.querySelector("[data-atlas-preview-panel]");
if (clearPreviewPanel) clearPreviewPanel.scrollTop = 0;
}
function updateAtlasPreview(root, card) {
if (!root || !card) return;
root.setAttribute("data-atlas-has-selection", "1");
root.querySelectorAll("[data-atlas-card]").forEach(function(other){
other.classList.toggle("is-active", other === card);
});
var panel = root.querySelector("[data-atlas-preview-panel]");
if (panel) panel.classList.add("is-filled");
var styleName = card.getAttribute("data-atlas-style-name") || card.getAttribute("data-atlas-page-title") || "—";
var openAction = root.querySelector("[data-atlas-preview-open]");
if (openAction) {
openAction.setAttribute("aria-disabled", "false");
openAction.setAttribute("data-atlas-preview-open-title", card.getAttribute("data-atlas-page-title") || styleName);
}
setText(root, "[data-atlas-preview-title]", "详情预览");
setText(root, "[data-atlas-preview-style]", styleName);
setText(root, "[data-atlas-preview-sub]", card.getAttribute("data-atlas-character-name") || "—");
setText(root, "[data-atlas-preview-character]", card.getAttribute("data-atlas-character-name"));
setText(root, "[data-atlas-preview-profession-text]", card.getAttribute("data-atlas-profession-name"));
setText(root, "[data-atlas-preview-desire-text]", card.getAttribute("data-atlas-desire-name"));
setText(root, "[data-atlas-preview-danger]", atlasDangerStars(card.getAttribute("data-atlas-danger")));
setText(root, "[data-atlas-preview-acquisition]", card.getAttribute("data-atlas-acquisition-label"));
renderAtlasPreviewTags(root, card);
setPreviewIcon(root, "[data-atlas-preview-profession-icon]", card.getAttribute("data-atlas-profession-icon"));
setPreviewIcon(root, "[data-atlas-preview-desire-icon]", card.getAttribute("data-atlas-desire-icon"));
setText(root, "[data-atlas-preview-attr-intel]", card.getAttribute("data-atlas-info-intel"));
setText(root, "[data-atlas-preview-attr-supply]", card.getAttribute("data-atlas-info-supply"));
setText(root, "[data-atlas-preview-attr-execute]", card.getAttribute("data-atlas-info-execute"));
setText(root, "[data-atlas-preview-attr-strategy]", card.getAttribute("data-atlas-info-strategy"));
["normal", "passive", "ultimate"].forEach(function(slot){
var name = card.getAttribute("data-atlas-skill-" + slot + "-name") || "—";
var meta = card.getAttribute("data-atlas-skill-" + slot + "-meta") || "";
var tags = card.getAttribute("data-atlas-skill-" + slot + "-tags") || "";
var type = card.getAttribute("data-atlas-skill-" + slot + "-type") || "—";
var metaParts = [];
if (tags) metaParts.push(tags);
if (meta && meta !== type) metaParts.push(meta);
setText(root, "[data-atlas-preview-skill-" + slot + "-name]", name);
setText(root, "[data-atlas-preview-skill-" + slot + "-type]", "「" + type + "」");
setText(root, "[data-atlas-preview-skill-" + slot + "-meta]", metaParts.length ? metaParts.join(" / ") : "—");
cloneAtlasSkillDesc(root, card, slot);
});
renderAtlasPreviewCollectionTerms(root, card);
setText(root, "[data-atlas-preview-copy]", "");
var updatePreviewPanel = root.querySelector("[data-atlas-preview-panel]");
if (updatePreviewPanel) updatePreviewPanel.scrollTop = 0;
root.__atlasPreviewCard = card;
var face = card.getAttribute("data-atlas-preview-face") || (card.getAttribute("data-atlas-danger") === "6" ? "2" : "1");
setAtlasPreviewFace(root, card, face);
}
function setAtlasPreviewFace(root, card, face) {
if (!root || !card) return;
var rarity = card.getAttribute("data-atlas-danger") || "";
var normalizedFace = rarity === "6" ? String(face || "2") : "1";
card.setAttribute("data-atlas-preview-face", normalizedFace);
var art = root.querySelector("[data-atlas-preview-art]");
if (art) {
art.innerHTML = "";
var source = card.querySelector('[data-atlas-preview-face-source="' + normalizedFace + '"]')
|| card.querySelector('[data-atlas-preview-face-source="1"]');
if (source) {
Array.prototype.forEach.call(source.childNodes, function(child){
art.appendChild(child.cloneNode(true));
});
}
art.querySelectorAll("img").forEach(function(img){
img.setAttribute("role", "button");
img.setAttribute("tabindex", "0");
img.setAttribute("aria-label", "放大卡面");
img.setAttribute("data-atlas-preview-lightbox-image", "1");
});
}
var switcher = root.querySelector("[data-atlas-preview-face-switch]");
if (switcher) {
switcher.style.display = rarity === "6" ? "flex" : "none";
switcher.querySelectorAll("[data-atlas-preview-face]").forEach(function(btn){
btn.classList.toggle("is-active", btn.getAttribute("data-atlas-preview-face") === normalizedFace);
});
}
}
function atlasPreviewOriginalImageSrc(img) {
var src = img && (img.currentSrc || img.src || img.getAttribute("src"));
if (!src) return "";
try {
var url = new URL(src, window.location.href);
var marker = "/images/thumb/";
var index = url.pathname.indexOf(marker);
if (index !== -1) {
var path = url.pathname.slice(0, index) + "/images/" + url.pathname.slice(index + marker.length);
var parts = path.split("/");
if (parts.length > 1 && /^\d+px-/.test(parts[parts.length - 1])) parts.pop();
url.pathname = parts.join("/");
url.search = "";
url.hash = "";
return url.toString();
}
} catch (error) {}
return src;
}
function ensureAtlasPreviewLightbox() {
var box = document.querySelector(".card_lightbox");
if (box) return box;
box = document.createElement("div");
box.className = "card_lightbox";
box.setAttribute("role", "dialog");
box.setAttribute("aria-modal", "true");
var image = document.createElement("img");
image.className = "card_lightbox-img";
image.alt = "";
box.appendChild(image);
var close = document.createElement("div");
close.className = "card_lightbox-close";
close.setAttribute("role", "button");
close.setAttribute("tabindex", "0");
close.setAttribute("aria-label", "关闭");
close.textContent = "×";
box.appendChild(close);
function closeLightbox(event) {
if (event && event.type === "keydown" && event.key !== "Enter" && event.key !== " ") return;
if (event) event.preventDefault();
box.classList.remove("is-open");
document.body.classList.remove("card_lightbox-open");
}
close.addEventListener("click", closeLightbox);
close.addEventListener("keydown", closeLightbox);
box.addEventListener("click", function(event){
if (event.target === box) closeLightbox(event);
});
document.addEventListener("keydown", function(event){
if (event.key === "Escape" && box.classList.contains("is-open")) closeLightbox(event);
});
document.body.appendChild(box);
return box;
}
function openAtlasPreviewLightbox(img) {
if (!img) return;
var box = ensureAtlasPreviewLightbox();
var image = box.querySelector(".card_lightbox-img");
if (!image) return;
image.src = atlasPreviewOriginalImageSrc(img);
image.alt = img.alt || "";
document.body.classList.add("card_lightbox-open");
window.requestAnimationFrame(function(){
box.classList.add("is-open");
});
var close = box.querySelector(".card_lightbox-close");
if (close && typeof close.focus === "function") close.focus({ preventScroll: true });
}
function setPreviewMode(root, enabled) {
if (root.__atlasPreviewModeTimer) {
window.clearTimeout(root.__atlasPreviewModeTimer);
root.__atlasPreviewModeTimer = null;
}
root.classList.remove("atlas_frame-preview-closing", "atlas_frame-mode-crossfade");
var current = root.getAttribute("data-atlas-preview-mode") === "1";
var initialized = root.hasAttribute("data-atlas-preview-mode");
var toggle = root.querySelector("[data-atlas-preview-toggle]");
if (toggle) {
toggle.classList.toggle("is-active", enabled);
toggle.setAttribute("aria-checked", enabled ? "true" : "false");
}
setText(root, "[data-atlas-preview-mode-label]", enabled ? "预览模式开启" : "点击卡片会在新窗口打开风格页");
if (!initialized || current === enabled) {
root.setAttribute("data-atlas-preview-mode", enabled ? "1" : "0");
clearAtlasPreview(root);
return;
}
root.classList.add("atlas_frame-mode-crossfade");
root.__atlasPreviewModeTimer = window.setTimeout(function(){
clearAtlasPreview(root);
root.setAttribute("data-atlas-preview-mode", enabled ? "1" : "0");
var layout = root.querySelector(".atlas_frame-layout");
if (layout) void layout.offsetWidth;
window.requestAnimationFrame(function(){
root.classList.remove("atlas_frame-mode-crossfade");
root.__atlasPreviewModeTimer = null;
});
}, 135);
}
function activeFilterValues(root, groupName) {
var group = root.querySelector('[data-atlas-filter-group="' + groupName + '"]');
if (!group) return [];
var options = Array.prototype.slice.call(group.querySelectorAll(".atlas_frame-filter-option.is-active"));
if (options.some(isAllOption)) return [];
return options.map(function(option){
var parsed = filterKey(option);
return parsed && !parsed.all ? parsed.value : null;
}).filter(Boolean);
}
function tagsFor(card) {
return String(card.getAttribute("data-atlas-tags") || "")
.split("|")
.map(function(item){ return item.trim(); })
.filter(Boolean);
}
function applyAtlasFilters(root) {
var cards = Array.prototype.slice.call(root.querySelectorAll("[data-atlas-card]"));
var activeStatus = root.querySelector(".atlas_frame-status-item.is-active");
var status = activeStatus ? activeStatus.getAttribute("data-atlas-status") : "all";
var danger = activeFilterValues(root, "danger");
var profession = activeFilterValues(root, "profession");
var desire = activeFilterValues(root, "desire");
var character = activeFilterValues(root, "character");
var acquisition = activeFilterValues(root, "acquisition");
var selectedTags = Array.prototype.slice.call(root.querySelectorAll(".atlas_frame-tag-option.is-active"))
.map(function(item){ return item.getAttribute("data-atlas-tag"); })
.filter(Boolean);
var searchNode = root.querySelector("[data-atlas-search]");
var query = normalize(searchValue(searchNode));
var visibleCount = 0;
cards.forEach(function(card){
var visible = true;
if (status && status !== "all") {
visible = visible && card.getAttribute("data-atlas-status") === status;
}
if (danger.length) {
visible = visible && danger.indexOf(card.getAttribute("data-atlas-danger")) !== -1;
}
if (profession.length) {
visible = visible && profession.indexOf(card.getAttribute("data-atlas-profession")) !== -1;
}
if (desire.length) {
visible = visible && desire.indexOf(card.getAttribute("data-atlas-desire")) !== -1;
}
if (character.length) {
visible = visible && character.indexOf(card.getAttribute("data-atlas-character")) !== -1;
}
if (acquisition.length) {
visible = visible && acquisition.indexOf(card.getAttribute("data-atlas-acquisition")) !== -1;
}
if (selectedTags.length) {
var cardTags = tagsFor(card);
visible = visible && selectedTags.every(function(tag){ return cardTags.indexOf(tag) !== -1; });
}
if (query) {
visible = visible && normalize(card.getAttribute("data-atlas-search-text")).indexOf(query) !== -1;
}
card.hidden = !visible;
card.classList.toggle("is-filtered-out", !visible);
if (visible) visibleCount += 1;
});
var empty = root.querySelector("[data-atlas-empty]");
if (empty) empty.hidden = visibleCount !== 0;
root.setAttribute("data-atlas-visible-count", String(visibleCount));
}
function activateOption(option) {
if (!option || option.classList.contains("is-disabled")) return;
var group = option.closest("[data-atlas-filter-group]");
if (group) {
var options = Array.prototype.slice.call(group.querySelectorAll(".atlas_frame-filter-option"));
var allOption = options.find(isAllOption);
if (isAllOption(option)) {
options.forEach(function(item){
item.classList.toggle("is-active", item === option);
});
return;
}
option.classList.toggle("is-active");
if (allOption) allOption.classList.remove("is-active");
var activeSpecific = options.some(function(item){
return item !== allOption && item.classList.contains("is-active");
});
if (!activeSpecific && allOption) allOption.classList.add("is-active");
return;
}
if (option.classList.contains("atlas_frame-tag-option")) {
option.classList.toggle("is-active");
}
}
function bindAtlasFrame(root) {
if (!root || root.getAttribute("data-atlas-frame-bound") === "1") return;
root.setAttribute("data-atlas-frame-bound", "1");
var ticking = false;
var pinned = false;
function settle() {
root.classList.remove("atlas_frame-pin-kick");
void root.offsetWidth;
root.classList.add("atlas_frame-pin-kick");
}
function updatePinned() {
ticking = false;
var rect = root.getBoundingClientRect();
var next = rect.top < -96;
if (next === pinned) return;
pinned = next;
root.classList.toggle("atlas_frame-is-pinned", pinned);
if (pinned) settle();
}
function requestUpdate() {
if (ticking) return;
ticking = true;
window.requestAnimationFrame(updatePinned);
}
window.addEventListener("scroll", requestUpdate, { passive: true });
window.addEventListener("resize", requestUpdate, { passive: true });
requestUpdate();
root.querySelectorAll("[data-atlas-tag-toggle]").forEach(function(toggle){
if (toggle.getAttribute("data-atlas-toggle-bound") === "1") return;
toggle.setAttribute("data-atlas-toggle-bound", "1");
function activate() {
var expanded = root.classList.toggle("atlas_frame-tags-expanded");
toggle.setAttribute("aria-expanded", expanded ? "true" : "false");
}
toggle.addEventListener("click", activate);
toggle.addEventListener("keydown", function(event){
if (event.key !== "Enter" && event.key !== " ") return;
event.preventDefault();
activate();
});
});
root.querySelectorAll(".atlas_frame-filter-option, .atlas_frame-tag-option").forEach(function(option){
if (option.getAttribute("data-atlas-option-bound") === "1") return;
option.setAttribute("data-atlas-option-bound", "1");
option.addEventListener("click", function(){
activateOption(option);
applyAtlasFilters(root);
});
option.addEventListener("keydown", function(event){
if (event.key !== "Enter" && event.key !== " ") return;
event.preventDefault();
activateOption(option);
applyAtlasFilters(root);
});
});
root.querySelectorAll(".atlas_frame-status-item").forEach(function(item){
if (item.getAttribute("data-atlas-status-bound") === "1") return;
item.setAttribute("data-atlas-status-bound", "1");
function activateStatus() {
root.querySelectorAll(".atlas_frame-status-item").forEach(function(other){
other.classList.toggle("is-active", other === item);
});
applyAtlasFilters(root);
}
item.addEventListener("click", activateStatus);
item.addEventListener("keydown", function(event){
if (event.key !== "Enter" && event.key !== " ") return;
event.preventDefault();
activateStatus();
});
});
var search = root.querySelector("[data-atlas-search]");
if (search && search.getAttribute("data-atlas-search-bound") !== "1") {
search.setAttribute("data-atlas-search-bound", "1");
if (!search.getAttribute("contenteditable")) {
search.setAttribute("contenteditable", "true");
}
search.setAttribute("spellcheck", "false");
search.addEventListener("input", function(){ applyAtlasFilters(root); });
search.addEventListener("search", function(){ applyAtlasFilters(root); });
search.addEventListener("keydown", function(event){
if (event.key === "Enter") event.preventDefault();
});
}
var previewArt = root.querySelector("[data-atlas-preview-art]");
if (previewArt && previewArt.getAttribute("data-atlas-preview-lightbox-bound") !== "1") {
previewArt.setAttribute("data-atlas-preview-lightbox-bound", "1");
previewArt.addEventListener("click", function(event){
var img = event.target && event.target.closest ? event.target.closest("[data-atlas-preview-lightbox-image]") : null;
if (!img || !previewArt.contains(img)) return;
event.preventDefault();
event.stopPropagation();
openAtlasPreviewLightbox(img);
});
previewArt.addEventListener("keydown", function(event){
if (event.key !== "Enter" && event.key !== " ") return;
var img = event.target && event.target.closest ? event.target.closest("[data-atlas-preview-lightbox-image]") : null;
if (!img || !previewArt.contains(img)) return;
event.preventDefault();
openAtlasPreviewLightbox(img);
});
}
var previewToggle = root.querySelector("[data-atlas-preview-toggle]");
if (previewToggle && previewToggle.getAttribute("data-atlas-preview-toggle-bound") !== "1") {
previewToggle.setAttribute("data-atlas-preview-toggle-bound", "1");
function togglePreviewMode() {
setPreviewMode(root, !previewModeEnabled(root));
}
previewToggle.addEventListener("click", togglePreviewMode);
previewToggle.addEventListener("keydown", function(event){
if (event.key !== "Enter" && event.key !== " ") return;
event.preventDefault();
togglePreviewMode();
});
setPreviewMode(root, false);
}
root.querySelectorAll("[data-atlas-card]").forEach(function(card){
if (card.getAttribute("data-atlas-card-bound") === "1") return;
card.setAttribute("data-atlas-card-bound", "1");
function activateCard(event) {
if (event) {
event.preventDefault();
event.stopPropagation();
}
if (previewModeEnabled(root)) {
if (root.__atlasPreviewCard === card) {
clearAtlasPreview(root);
} else {
updateAtlasPreview(root, card);
}
return;
}
var title = card.getAttribute("data-atlas-page-title") || card.getAttribute("data-atlas-style-name");
if (title) openTitleInNewWindow(title);
}
card.addEventListener("click", activateCard);
card.addEventListener("keydown", function(event){
if (event.key !== "Enter" && event.key !== " ") return;
activateCard(event);
});
});
applyAtlasFilters(root);
}
mw.hook("wikipage.content").add(function($content){
var container = $content && $content[0] ? $content[0] : document;
container.querySelectorAll("[data-atlas-frame-sandbox]").forEach(bindAtlasFrame);
});
}());
/* /atlas filter sandbox sticky settle */
/* Homepage today clock. */
(function(){
function pad(value) {
return String(value).padStart(2, "0");
}
function greeting(date) {
var hour = date.getHours();
if (hour < 5) return "夜深了";
if (hour < 11) return "早上好";
if (hour < 14) return "中午好";
if (hour < 18) return "下午好";
return "晚上好";
}
function updateToday(root) {
var now = new Date();
var greetingNode = root.querySelector("[data-wiki-home-greeting]");
var month = root.querySelector("[data-wiki-home-month]");
var day = root.querySelector("[data-wiki-home-day]");
var dateNode = root.querySelector("[data-wiki-home-date]");
var year = root.querySelector("[data-wiki-home-year]");
var time = root.querySelector("[data-wiki-home-time]");
var monthValue = now.getMonth() + 1;
var dayValue = now.getDate();
if (greetingNode) greetingNode.textContent = greeting(now);
if (month) month.textContent = monthValue + "月";
if (day) day.textContent = pad(dayValue);
if (dateNode) dateNode.textContent = monthValue + "月" + pad(dayValue) + "日";
if (year) year.textContent = String(now.getFullYear());
if (time) time.textContent = pad(now.getHours()) + ":" + pad(now.getMinutes());
}
function bindToday(root) {
if (!root || root.getAttribute("data-wiki-home-today-bound") === "1") return;
root.setAttribute("data-wiki-home-today-bound", "1");
updateToday(root);
window.setInterval(function(){ updateToday(root); }, 1000);
}
mw.hook("wikipage.content").add(function($content){
var container = $content && $content[0] ? $content[0] : document;
container.querySelectorAll("[data-wiki-home-today]").forEach(bindToday);
});
}());
/* /Homepage today clock. */
/* Card weapon attack popovers */
(function(){
function closeAll(exceptPanel) {
document.querySelectorAll(".card_weapon-popover.is-open").forEach(function(panel){
if (exceptPanel && panel === exceptPanel) return;
panel.classList.remove("is-open");
panel.setAttribute("aria-hidden", "true");
var id = panel.getAttribute("data-card-weapon-popover-panel");
if (!id) return;
document.querySelectorAll('[data-card-weapon-popover="' + CSS.escape(id) + '"]').forEach(function(trigger){
trigger.setAttribute("aria-expanded", "false");
var root = trigger.closest(".card_weapon-popover-root");
if (root) root.classList.remove("is-open");
});
});
}
function bindCardWeaponPopovers(container) {
container.querySelectorAll("[data-card-weapon-popover]").forEach(function(trigger){
if (trigger.getAttribute("data-card-weapon-bound") === "1") return;
trigger.setAttribute("data-card-weapon-bound", "1");
var activate = function(event){
if (event) {
event.preventDefault();
event.stopPropagation();
}
var id = trigger.getAttribute("data-card-weapon-popover");
if (!id) return;
var root = trigger.closest(".card_weapon-popover-root") || document;
var panel = root.querySelector('[data-card-weapon-popover-panel="' + CSS.escape(id) + '"]');
if (!panel) return;
var willOpen = !panel.classList.contains("is-open");
closeAll(panel);
panel.classList.toggle("is-open", willOpen);
panel.setAttribute("aria-hidden", willOpen ? "false" : "true");
root.classList.toggle("is-open", willOpen);
root.querySelectorAll('[data-card-weapon-popover="' + CSS.escape(id) + '"]').forEach(function(item){
item.setAttribute("aria-expanded", willOpen ? "true" : "false");
});
};
trigger.addEventListener("click", activate);
trigger.addEventListener("keydown", function(event){
if (event.key === "Enter" || event.key === " ") activate(event);
if (event.key === "Escape") closeAll();
});
});
}
document.addEventListener("click", function(event){
if (event.target.closest && event.target.closest(".card_weapon-popover, [data-card-weapon-popover]")) return;
closeAll();
});
document.addEventListener("keydown", function(event){
if (event.key === "Escape") closeAll();
});
mw.hook("wikipage.content").add(function($content){
bindCardWeaponPopovers($content && $content[0] ? $content[0] : document);
});
}());
/* Homepage style entry target. */
(function(){
function bindHomepageStyleTarget(container) {
var root = container.querySelector ? container : document;
root.querySelectorAll(".wiki_home-style-card a").forEach(function(link){
if (link.getAttribute("data-wiki-home-style-target-bound") === "1") return;
link.setAttribute("data-wiki-home-style-target-bound", "1");
link.setAttribute("target", "_blank");
link.setAttribute("rel", "noopener noreferrer");
});
}
mw.hook("wikipage.content").add(function($content){
bindHomepageStyleTarget($content && $content[0] ? $content[0] : document);
});
}());
/* /Homepage style entry target. */
/* Character profile avatar/full-body art toggle. */
(function(){
function setMode(root, mode) {
root.setAttribute("data-character-profile-art-mode", mode);
root.querySelectorAll("[data-character-profile-art-toggle]").forEach(function(toggle){
toggle.setAttribute("aria-pressed", mode === "figure" ? "true" : "false");
toggle.setAttribute("title", mode === "figure" ? "返回头像档案" : "切换为立绘欣赏");
toggle.setAttribute("aria-label", mode === "figure" ? "返回头像档案" : "切换为立绘欣赏");
});
}
function bindCharacterProfileArtToggle(container) {
var rootNode = container && container.querySelector ? container : document;
rootNode.querySelectorAll("[data-character-profile-art-toggle]").forEach(function(toggle){
if (toggle.getAttribute("data-character-profile-art-toggle-bound") === "1") return;
toggle.setAttribute("data-character-profile-art-toggle-bound", "1");
var activate = function(event){
if (event) {
event.preventDefault();
event.stopPropagation();
}
var root = toggle.closest(".character_profile-root");
if (!root) return;
var current = root.getAttribute("data-character-profile-art-mode") || "avatar";
setMode(root, current === "figure" ? "avatar" : "figure");
};
toggle.addEventListener("click", activate);
toggle.addEventListener("keydown", function(event){
if (event.key === "Enter" || event.key === " ") activate(event);
});
});
}
mw.hook("wikipage.content").add(function($content){
bindCharacterProfileArtToggle($content && $content[0] ? $content[0] : document);
});
}());
/* /Character profile avatar/full-body art toggle. */
/* character atlas wheel handoff */
(function () {
function onReady(fn) {
if (document.readyState === "loading") {
document.addEventListener("DOMContentLoaded", fn, { once: true });
} else {
fn();
}
}
function normalizedDeltaY(event) {
var delta = event.deltaY || 0;
if (event.deltaMode === 1) return delta * 16;
if (event.deltaMode === 2) return delta * window.innerHeight;
return delta;
}
function canScrollGrid(grid, deltaY) {
if (!grid || !deltaY) return false;
if (deltaY > 0) return grid.scrollTop + grid.clientHeight < grid.scrollHeight - 1;
return grid.scrollTop > 0;
}
function atlasSurfaceIsSticky(surface) {
if (!surface) return false;
var rect = surface.getBoundingClientRect();
var top = parseFloat(window.getComputedStyle(surface).top) || 0;
return rect.top <= top + 2 && rect.bottom > top + 120;
}
function visibleAtlasRoot() {
var roots = document.querySelectorAll(".character_atlas-root");
for (var i = 0; i < roots.length; i += 1) {
var rect = roots[i].getBoundingClientRect();
if (rect.top < window.innerHeight && rect.bottom > 0) return roots[i];
}
return null;
}
onReady(function () {
if (document.documentElement.dataset.characterAtlasWheelHandoff === "1") return;
if (!document.querySelector(".character_atlas-root")) return;
document.documentElement.dataset.characterAtlasWheelHandoff = "1";
document.addEventListener("wheel", function (event) {
if (event.defaultPrevented || event.ctrlKey || event.metaKey) return;
if (event.target && event.target.closest && event.target.closest(".rn-card-lightbox.is-open")) return;
var root = visibleAtlasRoot();
if (!root) return;
var surface = root.querySelector(".atlas_frame-surface");
var grid = root.querySelector(".character_atlas-grid");
if (!atlasSurfaceIsSticky(surface)) return;
var deltaY = normalizedDeltaY(event);
if (!canScrollGrid(grid, deltaY)) return;
event.preventDefault();
grid.scrollTop += deltaY;
}, { passive: false });
});
})();
/* /character atlas wheel handoff */
(function(){
function setExpanded(root, expanded){
var toggle = root.querySelector("[data-style-collection-toggle]");
if (!toggle) return;
root.setAttribute("data-style-collection-expanded", expanded ? "true" : "false");
toggle.setAttribute("aria-expanded", expanded ? "true" : "false");
var text = toggle.querySelector(".style_collection-toggle-text");
var icon = toggle.querySelector(".style_collection-toggle-icon");
if (text) text.textContent = expanded ? "收起映像详情" : "展开映像详情";
if (icon) icon.textContent = expanded ? "-" : "+";
}
document.addEventListener("click", function(event){
var toggle = event.target.closest("[data-style-collection-toggle]");
if (!toggle) return;
var root = toggle.closest("[data-style-collection]");
if (!root) return;
setExpanded(root, root.getAttribute("data-style-collection-expanded") !== "true");
});
document.addEventListener("keydown", function(event){
if (event.key !== "Enter" && event.key !== " ") return;
var toggle = event.target.closest("[data-style-collection-toggle]");
if (!toggle) return;
event.preventDefault();
var root = toggle.closest("[data-style-collection]");
if (!root) return;
setExpanded(root, root.getAttribute("data-style-collection-expanded") !== "true");
});
})();
/* Style page left column hover scroll. */
(function(){
function normalizedDeltaY(event) {
var delta = event.deltaY || 0;
if (event.deltaMode === 1) return delta * 16;
if (event.deltaMode === 2) return delta * window.innerHeight;
return delta;
}
function desktopStyleLayout() {
if (!window.matchMedia) return window.innerWidth >= 1400;
return window.matchMedia("(min-width: 1400px)").matches;
}
function canScroll(node, deltaY) {
if (!node || !deltaY) return false;
if (node.scrollHeight <= node.clientHeight + 1) return false;
if (deltaY < 0) return node.scrollTop > 0;
if (deltaY > 0) return node.scrollTop + node.clientHeight < node.scrollHeight - 1;
return false;
}
function bindStyleLeftScroll(container) {
container.querySelectorAll(".ron-card .card_content_left").forEach(function(left){
if (left.getAttribute("data-style-left-scroll-bound") === "1") return;
left.setAttribute("data-style-left-scroll-bound", "1");
left.addEventListener("wheel", function(event){
if (!desktopStyleLayout()) return;
var deltaY = normalizedDeltaY(event);
if (!canScroll(left, deltaY)) return;
event.preventDefault();
left.scrollTop += deltaY;
}, { passive: false });
});
}
mw.hook("wikipage.content").add(function($content){
bindStyleLeftScroll($content && $content[0] ? $content[0] : document);
});
}());
/* BEGIN ATLAS_PREVIEW_INTERNAL_SCROLL */
(function(){
function normalizedDeltaY(event) {
var delta = event.deltaY || 0;
if (event.deltaMode === 1) return delta * 16;
if (event.deltaMode === 2) return delta * window.innerHeight;
return delta;
}
function canScroll(node, deltaY) {
if (!node || !deltaY) return false;
if (node.scrollHeight <= node.clientHeight + 1) return false;
if (deltaY < 0) return node.scrollTop > 0;
if (deltaY > 0) return node.scrollTop + node.clientHeight < node.scrollHeight - 1;
return false;
}
function bindAtlasPreviewScroll(container) {
container.querySelectorAll(".atlas_frame-root .atlas_frame-preview").forEach(function(preview){
if (preview.getAttribute("data-atlas-preview-scroll-bound") === "1") return;
preview.setAttribute("data-atlas-preview-scroll-bound", "1");
preview.addEventListener("wheel", function(event){
if (event.ctrlKey || event.metaKey) return;
var deltaY = normalizedDeltaY(event);
if (!canScroll(preview, deltaY)) return;
event.preventDefault();
preview.scrollTop += deltaY;
}, { passive: false });
});
}
mw.hook("wikipage.content").add(function($content){
bindAtlasPreviewScroll($content && $content[0] ? $content[0] : document);
});
}());
/* END ATLAS_PREVIEW_INTERNAL_SCROLL */