MediaWiki:Common.js
来自卡厄思梦境WIKI
注意:在发布之后,您可能需要清除浏览器缓存才能看到所作出的更改的影响。
- Firefox或Safari:按住Shift的同时单击刷新,或按Ctrl-F5或Ctrl-R(Mac为⌘-R)
- Google Chrome:按Ctrl-Shift-R(Mac为⌘-Shift-R)
- Edge:按住Ctrl的同时单击刷新,或按Ctrl-F5。
/* 这里的任何JavaScript将为所有用户在每次页面加载时加载。 */
/* 切换标签 */
(function() {
'use strict';
function initTabSwitcher() {
var tabContainers = document.querySelectorAll('.resp-tabs');
tabContainers.forEach(function(container) {
var tabButtons = container.querySelectorAll('.czn-list-style');
if (tabButtons.length === 0) return;
var tabContents = container.querySelectorAll('.resp-tab-content');
// 初始化
tabButtons.forEach(function(button, index) {
button.classList.toggle('active', index === 0);
});
if (tabContents.length > 0) {
tabContents.forEach(function(content, index) {
content.style.display = index === 0 ? 'block' : 'none';
});
}
// 点击事件
tabButtons.forEach(function(button, index) {
button.addEventListener('click', function(e) {
e.preventDefault();
// 更新标签状态
tabButtons.forEach(function(btn, i) {
btn.classList.toggle('active', i === index);
});
// 切换内容
if (tabContents.length > 0) {
tabContents.forEach(function(content, i) {
content.style.display = i === index ? 'block' : 'none';
});
}
});
});
});
}
// 初始化
if (document.readyState === 'loading') {
document.addEventListener('DOMContentLoaded', initTabSwitcher);
} else {
initTabSwitcher();
}
})();
/* 角色立绘切换 */
$(document).ready(function() {
// 使用事件委托来确保动态内容也能响应
$(document).on('click', '.character-switch-btn', function() {
// 如果点击的是已经激活的按钮,不执行任何操作
if ($(this).hasClass('active')) {
return;
}
var targetType = $(this).attr('data-target');
var container = $(this).closest('#character-container');
var imageWrapper = container.find('.character-image-wrapper');
var allImages = imageWrapper.find('.character-image');
// 先将所有图片淡出
allImages.each(function() {
$(this).css('opacity', '0');
});
// 延迟后切换显示状态
setTimeout(function() {
allImages.each(function() {
$(this).css('display', 'none');
});
// 显示目标图片
imageWrapper.find('[data-image-type="' + targetType + '"]').each(function() {
$(this).css('display', 'block');
// 强制重绘
$(this)[0].offsetHeight;
$(this).css('opacity', '1');
});
}, 300);
// 更新按钮样式
container.find('.character-switch-btn').each(function() {
$(this).removeClass('active');
$(this).css({
'background': 'rgba(0,0,0,0.5)',
'cursor': 'pointer'
});
});
// 设置当前按钮为激活状态
$(this).addClass('active');
$(this).css({
'background': 'rgba(70, 130, 255, 0.8)',
'cursor': 'default'
});
});
// 鼠标悬停效果(仅对非激活按钮有效)
$(document).on('mouseenter', '.character-switch-btn:not(.active)', function() {
$(this).css('background', 'rgba(70, 130, 255, 0.5)');
});
$(document).on('mouseleave', '.character-switch-btn:not(.active)', function() {
$(this).css('background', 'rgba(0,0,0,0.5)');
});
});
/* 悬浮目录 */
$(document).ready(function() {
// 只在有目录的页面上执行
if ($('.toc').length > 0) {
// 创建侧边目录
var $sidebar = $('<div id="toc-sidebar"><div id="toc-sidebar-trigger">展开目录</div><div id="toc-sidebar-content"></div></div>');
$('body').append($sidebar);
// 提取并修复目录内容
var tocUl = $('<ul></ul>');
// 避免重复的目录项
var processedItems = new Set();
// 从原始目录构建新目录
$('.toc ul li').each(function() {
var $link = $(this).find('a').first();
var href = $link.attr('href');
var $number = $link.find('.tocnumber').first();
var $text = $link.find('.toctext').first();
// 创建唯一标识符,避免重复添加
var itemId = $number.text() + '-' + $text.text();
if (!processedItems.has(itemId)) {
processedItems.add(itemId);
// 创建新的目录项
var $li = $('<li></li>');
var $newLink = $('').attr('href', href);
// 如果有编号,则添加编号
if ($number.length) {
$newLink.append($('<span class="tocnumber"></span>').text($number.text()));
$newLink.append(' ');
}
$newLink.append($('<span class="toctext"></span>').text($text.text()));
$li.append($newLink);
tocUl.append($li);
}
});
$('#toc-sidebar-content').append(tocUl);
// 点击展开/折叠事件处理
$('#toc-sidebar-trigger').click(function() {
$('#toc-sidebar').toggleClass('expanded');
// 根据展开/折叠状态更改按钮文字
if ($('#toc-sidebar').hasClass('expanded')) {
$('#toc-sidebar-trigger').text('隐藏目录');
} else {
$('#toc-sidebar-trigger').text('展开目录');
}
});
}
});
/* 卡牌 */
(function() {
// 防止重复初始化
if (window.cardSystemInitialized) return;
window.cardSystemInitialized = true;
// 缓存DOM元素
var overlayCache = null;
var containerCache = null;
// 保存当前卡牌信息,用于返回
var currentCardInfo = null;
// 添加历史栈来记录浏览历史
var viewHistory = [];
// API缓存
var apiCache = {};
// 预加载队列
var preloadQueue = new Set();
var preloadWorker = null;
// 添加请求队列控制并发
var requestQueue = [];
var activeRequests = 0;
var MAX_CONCURRENT_REQUESTS = 3; // 限制最大并发请求数
// 添加请求超时控制
var REQUEST_TIMEOUT = 10000; // 10秒超时
// 智能缓存管理器
var cacheManager = {
maxSize: 100,
accessTime: {},
set: function(key, value) {
apiCache[key] = value;
this.accessTime[key] = Date.now();
var keys = Object.keys(apiCache);
if (keys.length > this.maxSize) {
var oldestKey = keys[0];
var oldestTime = this.accessTime[oldestKey] || 0;
for (var i = 1; i < keys.length; i++) {
var time = this.accessTime[keys[i]] || 0;
if (time < oldestTime) {
oldestKey = keys[i];
oldestTime = time;
}
}
delete apiCache[oldestKey];
delete this.accessTime[oldestKey];
}
},
get: function(key) {
if (apiCache[key]) {
this.accessTime[key] = Date.now();
return apiCache[key];
}
return null;
}
};
// 添加Loading动画CSS
function addLoadingStyles() {
if (!document.getElementById('card-loading-styles')) {
var style = document.createElement('style');
style.id = 'card-loading-styles';
style.textContent = `
@keyframes spin {
0% { transform: rotate(0deg); }
100% { transform: rotate(360deg); }
}
@keyframes fadeIn {
from { opacity: 0; }
to { opacity: 1; }
}
.card-fade-in {
animation: fadeIn 0.3s ease-in;
}
`;
document.head.appendChild(style);
}
}
// 创建loading占位符
function createLoadingPlaceholder(text, scale) {
var placeholder = document.createElement('div');
placeholder.className = 'card-loading';
var baseStyle = 'width: 168px; height: 230px; background: rgba(255,255,255,0.1); border: 2px dashed rgba(255,255,255,0.3); border-radius: 8px; display: flex; align-items: center; justify-content: center; color: white;';
if (scale) {
baseStyle += ' transform: scale(' + scale + '); transform-origin: center center;';
}
placeholder.style.cssText = baseStyle;
placeholder.innerHTML = '<div style="text-align: center;">' +
'<div class="loading-spinner" style="border: 3px solid rgba(255,255,255,0.3); border-top: 3px solid white; border-radius: 50%; width: 30px; height: 30px; animation: spin 1s linear infinite; margin: 0 auto 10px;"></div>' +
'<div style="font-size: 12px;">' + (text || '加载中...') + '</div>' +
'</div>';
return placeholder;
}
// 错误占位符
function createErrorPlaceholder(text, scale) {
var placeholder = document.createElement('div');
placeholder.className = 'card-error';
var baseStyle = 'width: 168px; height: 230px; background: rgba(255,0,0,0.1); border: 2px dashed rgba(255,0,0,0.3); border-radius: 8px; display: flex; align-items: center; justify-content: center; color: #ff6b6b;';
if (scale) {
baseStyle += ' transform: scale(' + scale + '); transform-origin: center center;';
}
placeholder.style.cssText = baseStyle;
placeholder.innerHTML = '<div style="text-align: center; padding: 10px;">' +
'<div style="font-size: 30px; margin-bottom: 10px;">⚠</div>' +
'<div style="font-size: 12px;">' + (text || '加载失败') + '</div>' +
'</div>';
return placeholder;
}
// 请求队列处理器
function processRequestQueue() {
if (requestQueue.length === 0 || activeRequests >= MAX_CONCURRENT_REQUESTS) {
return;
}
var request = requestQueue.shift();
activeRequests++;
request.execute(function() {
activeRequests--;
processRequestQueue(); // 处理下一个请求
});
}
// 添加到请求队列
function queueRequest(executeFn) {
return new Promise(function(resolve, reject) {
requestQueue.push({
execute: function(done) {
executeFn()
.then(function(result) {
resolve(result);
done();
})
.catch(function(error) {
reject(error);
done();
});
}
});
processRequestQueue();
});
}
// 优化的API请求函数,添加超时和错误处理
function fetchCardHTML(character, cardName, deckType, callback) {
var cacheKey = character + '|' + cardName + '|' + (deckType || '');
// 检查缓存
var cached = cacheManager.get(cacheKey);
if (cached) {
callback(cached, null);
return;
}
// 使用请求队列
queueRequest(function() {
return new Promise(function(resolve, reject) {
var api = new mw.Api();
var wikitext = '{{#invoke:卡牌|main|' + character + '|' + cardName + '|' + (deckType || '') + '}}';
var timeoutId = setTimeout(function() {
reject(new Error('请求超时'));
}, REQUEST_TIMEOUT);
api.parse(wikitext)
.done(function(html) {
clearTimeout(timeoutId);
cacheManager.set(cacheKey, html);
resolve(html);
})
.fail(function(error) {
clearTimeout(timeoutId);
console.error('加载卡牌失败:', character, cardName, deckType, error);
reject(error);
});
});
})
.then(function(html) {
callback(html, null);
})
.catch(function(error) {
callback(null, error);
});
}
// 批量获取卡牌HTML - 优化版本
function fetchCardHTMLBatch(requests, callback) {
var results = {};
var errors = {};
var pending = [];
// 先检查缓存
requests.forEach(function(req) {
var cacheKey = req.character + '|' + req.cardName + '|' + (req.deckType || '');
var cached = cacheManager.get(cacheKey);
if (cached) {
results[cacheKey] = cached;
} else {
pending.push(req);
}
});
// 如果全部都有缓存,直接返回
if (pending.length === 0) {
callback(results, errors);
return;
}
// 限制批量请求数量,分批处理
var batchSize = 5;
var batches = [];
for (var i = 0; i < pending.length; i += batchSize) {
batches.push(pending.slice(i, i + batchSize));
}
var currentBatch = 0;
function processBatch() {
if (currentBatch >= batches.length) {
callback(results, errors);
return;
}
var batch = batches[currentBatch];
var completed = 0;
batch.forEach(function(req) {
fetchCardHTML(req.character, req.cardName, req.deckType, function(html, error) {
var cacheKey = req.character + '|' + req.cardName + '|' + (req.deckType || '');
if (error) {
errors[cacheKey] = error;
} else {
results[cacheKey] = html;
}
completed++;
if (completed === batch.length) {
currentBatch++;
// 延迟处理下一批,避免请求过快
setTimeout(processBatch, 200);
}
});
});
}
processBatch();
}
// 预加载管理器 - 降低优先级
function preloadCardData(character, cardName, deckType) {
var cacheKey = character + '|' + cardName + '|' + (deckType || '');
if (!cacheManager.get(cacheKey) && !preloadQueue.has(cacheKey)) {
preloadQueue.add(cacheKey);
if (!preloadWorker) {
preloadWorker = setTimeout(processPreloadQueue, 500); // 增加延迟
}
}
}
// 批量处理预加载队列 - 降低频率
function processPreloadQueue() {
preloadWorker = null;
if (preloadQueue.size === 0) return;
var batch = Array.from(preloadQueue).slice(0, 2); // 减少每批数量
batch.forEach(function(key) {
preloadQueue.delete(key);
});
batch.forEach(function(cacheKey) {
var parts = cacheKey.split('|');
fetchCardHTML(parts[0], parts[1], parts[2], function() {
// 预加载完成,数据已缓存
});
});
// 如果还有剩余,继续处理
if (preloadQueue.size > 0) {
preloadWorker = setTimeout(processPreloadQueue, 1000); // 增加间隔
}
}
// 创建遮罩层和容器
function createCardOverlay() {
if (overlayCache && containerCache) {
return { overlay: overlayCache, container: containerCache };
}
var overlay = document.createElement('div');
overlay.id = 'card-overlay';
overlay.style.cssText = 'display: none; position: fixed; top: 0; left: 0; width: 100%; height: 100%; background: rgba(0,0,0,0.8); z-index: 9999; overflow-y: auto;';
var container = document.createElement('div');
container.id = 'card-display-container';
container.style.cssText = 'position: relative; min-height: 100vh; padding: 40px 20px; display: flex; align-items: center; justify-content: center;';
overlay.appendChild(container);
document.body.appendChild(overlay);
overlay.addEventListener('click', function(e) {
if (e.target === overlay) {
closeCardDisplay();
}
});
overlayCache = overlay;
containerCache = container;
return { overlay: overlay, container: container };
}
// 创建关闭按钮
function createCloseButton() {
var closeBtn = document.createElement('div');
closeBtn.style.cssText = 'position: fixed; top: 20px; right: 20px; width: 40px; height: 40px; background: rgba(255,255,255,0.1); border: 2px solid white; border-radius: 50%; cursor: pointer; display: flex; align-items: center; justify-content: center; z-index: 10001; transition: all 0.3s;';
closeBtn.innerHTML = '<span style="color: white; font-size: 24px; font-weight: bold; line-height: 1;">×</span>';
closeBtn.onmouseover = function() {
this.style.background = 'rgba(255,255,255,0.2)';
this.style.transform = 'scale(1.1)';
};
closeBtn.onmouseout = function() {
this.style.background = 'rgba(255,255,255,0.1)';
this.style.transform = 'scale(1)';
};
closeBtn.onclick = function() {
closeCardDisplay();
};
return closeBtn;
}
// 关闭卡牌展示
function closeCardDisplay() {
var overlay = document.getElementById('card-overlay');
if (overlay) {
overlay.style.display = 'none';
var container = overlay.querySelector('#card-display-container');
if (container) {
container.innerHTML = '';
}
}
currentCardInfo = null;
viewHistory = [];
}
// 返回上一层
function goBack() {
if (viewHistory.length > 1) {
viewHistory.pop();
var previousView = viewHistory[viewHistory.length - 1];
if (previousView.type === 'enlarged') {
showEnlargedCard(previousView.data.element, -1);
} else if (previousView.type === 'derivedCards') {
showAllDerivedCards(previousView.data.character, previousView.data.derivedCardsList, -1);
} else if (previousView.type === 'variantCards') {
showVariantCards(previousView.data.character, previousView.data.cardName, previousView.data.variantType, -1);
}
} else if (viewHistory.length === 1) {
closeCardDisplay();
} else {
closeCardDisplay();
}
}
// 检查卡牌变体 - 优化版本,添加错误处理
function checkCardVariants(character, cardName, callback) {
var variants = { lingguang: false, shenguang: false };
var checkCount = 0;
var totalChecks = 2;
function checkComplete() {
checkCount++;
if (checkCount === totalChecks) {
callback(variants);
}
}
fetchCardHTML(character, cardName, '灵光一闪', function(html, error) {
if (!error && html && !html.includes('找不到卡组')) {
variants.lingguang = true;
}
checkComplete();
});
fetchCardHTML(character, cardName, '神之一闪', function(html, error) {
if (!error && html && !html.includes('找不到卡组')) {
variants.shenguang = true;
}
checkComplete();
});
}
// 放大显示卡牌 - 添加错误处理
function showEnlargedCard(cardElement, addToHistory) {
var elements = createCardOverlay();
var container = elements.container;
var cardName = cardElement.dataset.cardName;
var character = cardElement.dataset.character;
var deckType = cardElement.dataset.deckType;
var derivedCards = cardElement.dataset.derivedCards;
var hasMechanism = cardElement.dataset.hasMechanism === 'true';
addLoadingStyles();
currentCardInfo = {
element: cardElement,
cardName: cardName,
character: character,
deckType: deckType,
derivedCards: derivedCards
};
if (addToHistory === -1) {
if (viewHistory.length > 0) {
viewHistory[viewHistory.length - 1] = {
type: 'enlarged',
data: currentCardInfo
};
}
} else if (addToHistory !== false) {
viewHistory.push({
type: 'enlarged',
data: currentCardInfo
});
}
container.style.cssText = 'position: relative; min-height: 100vh; padding: 40px 20px; display: flex; align-items: center; justify-content: center;';
var contentWrapper = document.createElement('div');
contentWrapper.style.cssText = 'display: flex; align-items: flex-start; gap: 0px; position: relative;';
var centerContainer = document.createElement('div');
centerContainer.style.cssText = 'display: flex; flex-direction: column; align-items: center; gap: 0px;';
var mainCardContainer = document.createElement('div');
mainCardContainer.style.cssText = 'width: 336px; height: 460px; display: flex; align-items: center; justify-content: center; position: relative;';
var cardInner = document.createElement('div');
cardInner.style.cssText = 'transform: scale(2); position: relative;';
var enlargedCard = cardElement.cloneNode(true);
enlargedCard.style.width = '168px';
enlargedCard.style.height = '230px';
enlargedCard.style.cursor = 'default';
enlargedCard.style.position = 'relative';
enlargedCard.style.display = 'block';
enlargedCard.onclick = null;
enlargedCard.classList.add('card-fade-in');
cardInner.appendChild(enlargedCard);
mainCardContainer.appendChild(cardInner);
centerContainer.appendChild(mainCardContainer);
// 处理衍生卡牌 - 添加错误处理
if (derivedCards && derivedCards.trim() !== '') {
var leftContainer = document.createElement('div');
leftContainer.style.cssText = 'display: flex; flex-direction: column; gap: 0px; align-items: center;';
var derivedCardsList = derivedCards.split('、');
var derivedCardWrapper = document.createElement('div');
derivedCardWrapper.style.cssText = 'width: 252px; height: 345px; display: flex; align-items: center; justify-content: center;';
var placeholder = createLoadingPlaceholder('加载衍生卡牌...', 1.5);
derivedCardWrapper.appendChild(placeholder);
leftContainer.appendChild(derivedCardWrapper);
fetchCardHTML(character, derivedCardsList[0].trim(), '', function(html, error) {
if (error || !html) {
var errorPlaceholder = createErrorPlaceholder('加载失败', 1.5);
derivedCardWrapper.removeChild(placeholder);
derivedCardWrapper.appendChild(errorPlaceholder);
return;
}
var firstDerivedCard = document.createElement('div');
firstDerivedCard.id = 'derived-cards-display';
firstDerivedCard.style.cssText = 'transform: scale(1.5); transform-origin: center center; opacity: 0;';
firstDerivedCard.innerHTML = html;
var cards = firstDerivedCard.querySelectorAll('.game-card');
cards.forEach(function(card) {
card.style.cursor = 'default';
card.onclick = function(e) {
e.stopPropagation();
e.preventDefault();
};
});
derivedCardWrapper.removeChild(placeholder);
derivedCardWrapper.appendChild(firstDerivedCard);
setTimeout(function() {
firstDerivedCard.style.transition = 'opacity 0.3s';
firstDerivedCard.style.opacity = '1';
}, 10);
});
if (derivedCardsList.length > 1) {
var viewAllBtn = document.createElement('div');
viewAllBtn.style.cssText = 'padding: 10px 20px; background: linear-gradient(135deg, #4a90e2, #357abd); color: white; border-radius: 6px; cursor: pointer; white-space: nowrap; font-weight: bold; box-shadow: 0 2px 8px rgba(0,0,0,0.3); transition: transform 0.2s; margin-top: 0px;';
viewAllBtn.textContent = '查看所有衍生卡牌';
viewAllBtn.onmouseover = function() { this.style.transform = 'scale(1.05)'; };
viewAllBtn.onmouseout = function() { this.style.transform = 'scale(1)'; };
viewAllBtn.onclick = function() {
showAllDerivedCards(character, derivedCardsList, true);
};
leftContainer.appendChild(viewAllBtn);
}
contentWrapper.appendChild(leftContainer);
}
var buttonsContainer = document.createElement('div');
buttonsContainer.style.cssText = 'display: flex; gap: 15px; white-space: nowrap; min-height: 50px; align-items: center; justify-content: center;';
var checkingVariants = document.createElement('div');
checkingVariants.style.cssText = 'color: rgba(255,255,255,0.5); font-size: 12px;';
checkingVariants.textContent = '检查变体卡牌...';
buttonsContainer.appendChild(checkingVariants);
centerContainer.appendChild(buttonsContainer);
checkCardVariants(character, cardName, function(variants) {
if (buttonsContainer.contains(checkingVariants)) {
buttonsContainer.removeChild(checkingVariants);
}
if (variants.lingguang) {
var lingguangBtn = document.createElement('div');
lingguangBtn.style.cssText = 'padding: 10px 30px; background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); color: white; border-radius: 6px; cursor: pointer; font-weight: bold; box-shadow: 0 2px 8px rgba(0,0,0,0.3); transition: transform 0.2s; white-space: nowrap; min-width: 120px; text-align: center; opacity: 0;';
lingguangBtn.textContent = '灵光一闪';
lingguangBtn.onmouseover = function() { this.style.transform = 'scale(1.05)'; };
lingguangBtn.onmouseout = function() { this.style.transform = 'scale(1)'; };
lingguangBtn.onclick = function() {
showVariantCards(character, cardName, '灵光一闪', true);
};
buttonsContainer.appendChild(lingguangBtn);
setTimeout(function() {
lingguangBtn.style.transition = 'opacity 0.3s';
lingguangBtn.style.opacity = '1';
}, 10);
}
if (variants.shenguang) {
var shenguangBtn = document.createElement('div');
shenguangBtn.style.cssText = 'padding: 10px 30px; background: linear-gradient(135deg, #f093fb 0%, #f5576c 100%); color: white; border-radius: 6px; cursor: pointer; font-weight: bold; box-shadow: 0 2px 8px rgba(0,0,0,0.3); transition: transform 0.2s; white-space: nowrap; min-width: 120px; text-align: center; opacity: 0;';
shenguangBtn.textContent = '神之一闪';
shenguangBtn.onmouseover = function() { this.style.transform = 'scale(1.05)'; };
shenguangBtn.onmouseout = function() { this.style.transform = 'scale(1)'; };
shenguangBtn.onclick = function() {
showVariantCards(character, cardName, '神之一闪', true);
};
buttonsContainer.appendChild(shenguangBtn);
setTimeout(function() {
shenguangBtn.style.transition = 'opacity 0.3s';
shenguangBtn.style.opacity = '1';
}, 10);
}
if (!variants.lingguang && !variants.shenguang) {
centerContainer.removeChild(buttonsContainer);
}
});
contentWrapper.appendChild(centerContainer);
if (hasMechanism) {
var mechanismContainer = cardElement.nextElementSibling;
if (mechanismContainer && mechanismContainer.classList.contains('mechanism-container')) {
var mechanismDisplay = document.createElement('div');
mechanismDisplay.style.cssText = 'display: flex; flex-direction: column; gap: 0px; max-width: 320px; min-width: 280px;';
mechanismDisplay.innerHTML = mechanismContainer.innerHTML;
mechanismDisplay.style.display = 'flex';
contentWrapper.appendChild(mechanismDisplay);
}
}
container.innerHTML = '';
container.appendChild(contentWrapper);
if (viewHistory.length > 1) {
var backBtn = document.createElement('div');
backBtn.style.cssText = 'position: fixed; top: 20px; left: 20px; padding: 10px 20px; background: linear-gradient(135deg, #4a90e2, #357abd); color: white; border-radius: 6px; cursor: pointer; font-weight: bold; box-shadow: 0 2px 8px rgba(0,0,0,0.3); z-index: 10001; transition: transform 0.2s;';
backBtn.textContent = '← 返回';
backBtn.onmouseover = function() { this.style.transform = 'scale(1.05)'; };
backBtn.onmouseout = function() { this.style.transform = 'scale(1)'; };
backBtn.onclick = function() {
goBack();
};
container.appendChild(backBtn);
}
container.appendChild(createCloseButton());
elements.overlay.style.display = 'block';
}
// 显示所有衍生卡牌 - 添加错误处理
function showAllDerivedCards(character, derivedCardsList, addToHistory) {
var overlay = document.getElementById('card-overlay');
var container = document.getElementById('card-display-container');
if (addToHistory === -1) {
if (viewHistory.length > 0) {
viewHistory[viewHistory.length - 1] = {
type: 'derivedCards',
data: {
character: character,
derivedCardsList: derivedCardsList
}
};
}
} else if (addToHistory === true) {
viewHistory.push({
type: 'derivedCards',
data: {
character: character,
derivedCardsList: derivedCardsList
}
});
} else if (addToHistory === 0) {
if (viewHistory.length > 0) {
viewHistory.pop();
}
viewHistory.push({
type: 'derivedCards',
data: {
character: character,
derivedCardsList: derivedCardsList
}
});
}
container.innerHTML = '';
container.style.cssText = 'position: relative; padding: 40px; min-height: 100vh; display: flex; flex-direction: column; align-items: center; justify-content: center;';
var title = document.createElement('div');
title.style.cssText = 'color: white; font-size: 28px; font-weight: bold; margin-bottom: 30px; text-align: center;';
title.textContent = '衍生卡牌';
container.appendChild(title);
var cardsContainer = document.createElement('div');
cardsContainer.style.cssText = 'display: flex; flex-wrap: wrap; gap: 30px; justify-content: center; max-width: 1400px; transform: scale(1.5); transform-origin: center center; padding: 40px;';
var uniqueCards = [...new Set(derivedCardsList.map(function(name) { return name.trim(); }))];
var placeholders = [];
uniqueCards.forEach(function() {
var placeholder = createLoadingPlaceholder('加载中...', null);
placeholders.push(placeholder);
cardsContainer.appendChild(placeholder);
});
container.appendChild(cardsContainer);
var requests = uniqueCards.map(function(cardName) {
return { character: character, cardName: cardName, deckType: '' };
});
fetchCardHTMLBatch(requests, function(results, errors) {
uniqueCards.forEach(function(cardName, index) {
var cacheKey = character + '|' + cardName + '|';
if (errors[cacheKey]) {
var errorPlaceholder = createErrorPlaceholder('加载失败', null);
cardsContainer.replaceChild(errorPlaceholder, placeholders[index]);
return;
}
var html = results[cacheKey];
if (html) {
var cardWrapper = document.createElement('div');
cardWrapper.style.cssText = 'opacity: 0;';
cardWrapper.innerHTML = html;
var cards = cardWrapper.querySelectorAll('.game-card');
cards.forEach(function(card) {
card.style.cursor = 'default';
card.onclick = function(e) {
e.stopPropagation();
e.preventDefault();
};
var derivedCards = card.dataset.derivedCards;
if (derivedCards && derivedCards.trim() !== '') {
var buttonContainer = document.createElement('div');
buttonContainer.style.cssText = 'display: flex; flex-direction: column; align-items: center; gap: 10px;';
var cardContainer = document.createElement('div');
cardContainer.appendChild(card.cloneNode(true));
buttonContainer.appendChild(cardContainer);
var viewDerivedBtn = document.createElement('div');
viewDerivedBtn.style.cssText = 'padding: 8px 15px; background: linear-gradient(135deg, #4a90e2, #357abd); color: white; border-radius: 6px; cursor: pointer; white-space: nowrap; font-weight: bold; box-shadow: 0 2px 8px rgba(0,0,0,0.3); transition: transform 0.2s; font-size: 12px;';
viewDerivedBtn.textContent = '查看衍生卡牌';
viewDerivedBtn.onmouseover = function() { this.style.transform = 'scale(1.05)'; };
viewDerivedBtn.onmouseout = function() { this.style.transform = 'scale(1)'; };
viewDerivedBtn.onclick = function(e) {
e.stopPropagation();
showAllDerivedCards(character, derivedCards.split('、'), 0);
};
buttonContainer.appendChild(viewDerivedBtn);
cardsContainer.replaceChild(buttonContainer, placeholders[index]);
setTimeout(function() {
buttonContainer.style.transition = 'opacity 0.3s';
buttonContainer.style.opacity = '1';
}, 10);
} else {
cardsContainer.replaceChild(card, placeholders[index]);
setTimeout(function() {
card.style.transition = 'opacity 0.3s';
card.style.opacity = '1';
}, 10);
}
});
}
});
});
var backBtn = document.createElement('div');
backBtn.style.cssText = 'position: fixed; top: 20px; left: 20px; padding: 10px 20px; background: linear-gradient(135deg, #4a90e2, #357abd); color: white; border-radius: 6px; cursor: pointer; font-weight: bold; box-shadow: 0 2px 8px rgba(0,0,0,0.3); z-index: 10001; transition: transform 0.2s;';
backBtn.textContent = '← 返回';
backBtn.onmouseover = function() { this.style.transform = 'scale(1.05)'; };
backBtn.onmouseout = function() { this.style.transform = 'scale(1)'; };
backBtn.onclick = function() {
goBack();
};
container.appendChild(backBtn);
container.appendChild(createCloseButton());
}
// 显示变体卡牌 - 添加错误处理
function showVariantCards(character, cardName, variantType, addToHistory) {
var overlay = document.getElementById('card-overlay');
var container = document.getElementById('card-display-container');
if (addToHistory === -1) {
if (viewHistory.length > 0) {
viewHistory[viewHistory.length - 1] = {
type: 'variantCards',
data: {
character: character,
cardName: cardName,
variantType: variantType
}
};
}
} else if (addToHistory === true) {
viewHistory.push({
type: 'variantCards',
data: {
character: character,
cardName: cardName,
variantType: variantType
}
});
}
container.innerHTML = '';
container.style.cssText = 'position: relative; padding: 40px; min-height: 100vh; display: flex; flex-direction: column; align-items: center; justify-content: center;';
var title = document.createElement('div');
title.style.cssText = 'color: white; font-size: 28px; font-weight: bold; margin-bottom: 30px; text-align: center; background: linear-gradient(135deg, #667eea, #764ba2); -webkit-background-clip: text; -webkit-text-fill-color: transparent; background-clip: text;';
title.textContent = cardName + ' - ' + variantType;
container.appendChild(title);
var cardsContainer = document.createElement('div');
cardsContainer.style.cssText = 'display: flex; flex-wrap: nowrap; gap: 0px; justify-content: center; max-width: 1400px; overflow-x: auto; padding: 40px; transform: scale(1.5); transform-origin: center center;';
var loadingPlaceholder = createLoadingPlaceholder('加载变体卡牌...', null);
cardsContainer.appendChild(loadingPlaceholder);
container.appendChild(cardsContainer);
fetchCardHTML(character, cardName, variantType, function(html, error) {
if (error || !html) {
cardsContainer.removeChild(loadingPlaceholder);
var errorPlaceholder = createErrorPlaceholder('加载变体失败', null);
cardsContainer.appendChild(errorPlaceholder);
return;
}
var tempContainer = document.createElement('div');
tempContainer.innerHTML = html;
cardsContainer.removeChild(loadingPlaceholder);
var cards = tempContainer.querySelectorAll('.game-card');
cards.forEach(function(card, index) {
card.style.cursor = 'default';
card.onclick = null;
card.style.flexShrink = '0';
card.style.opacity = '0';
var derivedCards = card.getAttribute('data-derived-cards');
var hasDerivedCards = false;
if (derivedCards !== null && derivedCards !== undefined) {
var trimmedValue = String(derivedCards).trim();
if (trimmedValue.length > 0 &&
trimmedValue !== 'undefined' &&
trimmedValue !== 'null' &&
trimmedValue !== 'false' &&
trimmedValue !== '0') {
hasDerivedCards = true;
}
}
if (hasDerivedCards) {
var cardWrapper = document.createElement('div');
cardWrapper.style.cssText = 'display: flex; flex-direction: column; align-items: center; gap: 10px; flex-shrink: 0; opacity: 0;';
var cardClone = card.cloneNode(true);
cardClone.style.cursor = 'default';
cardClone.onclick = null;
cardClone.style.opacity = '1';
cardWrapper.appendChild(cardClone);
var viewDerivedBtn = document.createElement('div');
viewDerivedBtn.style.cssText = 'padding: 8px 15px; background: linear-gradient(135deg, #4a90e2, #357abd); color: white; border-radius: 6px; cursor: pointer; white-space: nowrap; font-weight: bold; box-shadow: 0 2px 8px rgba(0,0,0,0.3); transition: transform 0.2s; font-size: 12px;';
viewDerivedBtn.textContent = '查看衍生卡牌';
viewDerivedBtn.onmouseover = function() { this.style.transform = 'scale(1.05)'; };
viewDerivedBtn.onmouseout = function() { this.style.transform = 'scale(1)'; };
viewDerivedBtn.setAttribute('data-derived-list', derivedCards);
viewDerivedBtn.onclick = function(e) {
e.stopPropagation();
var derivedList = this.getAttribute('data-derived-list');
showAllDerivedCards(character, derivedList.split('、'), true);
};
cardWrapper.appendChild(viewDerivedBtn);
cardsContainer.appendChild(cardWrapper);
setTimeout(function() {
cardWrapper.style.transition = 'opacity 0.3s';
cardWrapper.style.opacity = '1';
}, 50 * index);
} else {
cardsContainer.appendChild(card);
setTimeout(function() {
card.style.transition = 'opacity 0.3s';
card.style.opacity = '1';
}, 50 * index);
}
});
});
var backBtn = document.createElement('div');
backBtn.style.cssText = 'position: fixed; top: 20px; left: 20px; padding: 10px 20px; background: linear-gradient(135deg, #4a90e2, #357abd); color: white; border-radius: 6px; cursor: pointer; font-weight: bold; box-shadow: 0 2px 8px rgba(0,0,0,0.3); z-index: 10001; transition: transform 0.2s;';
backBtn.textContent = '← 返回';
backBtn.onmouseover = function() { this.style.transform = 'scale(1.05)'; };
backBtn.onmouseout = function() { this.style.transform = 'scale(1)'; };
backBtn.onclick = function() {
goBack();
};
container.appendChild(backBtn);
container.appendChild(createCloseButton());
}
// 鼠标悬停时预加载 - 降低优先级,避免影响主流程
function initPreloadEvents() {
var preloadTimeout = null;
document.addEventListener('mouseover', function(e) {
var card = e.target.closest('.game-card');
if (card && !card.closest('#card-overlay')) {
clearTimeout(preloadTimeout);
preloadTimeout = setTimeout(function() {
var character = card.dataset.character;
var cardName = card.dataset.cardName;
var derivedCards = card.dataset.derivedCards;
if (!character || !cardName) return;
// 只在空闲时预加载
if (activeRequests < 2) {
preloadCardData(character, cardName, '灵光一闪');
preloadCardData(character, cardName, '神之一闪');
if (derivedCards && derivedCards.trim() !== '') {
var cardsList = derivedCards.split('、');
var preloadCount = Math.min(2, cardsList.length); // 减少预加载数量
for (var i = 0; i < preloadCount; i++) {
preloadCardData(character, cardsList[i].trim(), '');
}
}
}
}, 500); // 增加延迟
}
});
document.addEventListener('mouseout', function(e) {
var card = e.target.closest('.game-card');
if (card && !card.closest('#card-overlay')) {
clearTimeout(preloadTimeout);
}
});
}
// 初始化卡牌点击事件
function initCardClickEvents() {
document.addEventListener('click', function(e) {
var card = e.target.closest('.game-card');
if (card && !card.closest('#card-overlay')) {
e.preventDefault();
showEnlargedCard(card);
}
});
// 延迟初始化预加载,避免影响页面初始加载
setTimeout(function() {
initPreloadEvents();
}, 3000); // 页面加载3秒后才启用预加载
addLoadingStyles();
}
// 添加键盘快捷键支持
function initKeyboardShortcuts() {
document.addEventListener('keydown', function(e) {
var overlay = document.getElementById('card-overlay');
if (overlay && overlay.style.display !== 'none') {
if (e.key === 'Escape') {
e.preventDefault();
closeCardDisplay();
} else if (e.key === 'Backspace' || e.key === 'ArrowLeft') {
e.preventDefault();
goBack();
}
}
});
}
// 优化滚动性能
function optimizeScrollPerformance() {
var scrollTimeout;
var overlay = document.getElementById('card-overlay');
if (overlay) {
overlay.addEventListener('scroll', function() {
if (!overlay.classList.contains('is-scrolling')) {
overlay.classList.add('is-scrolling');
}
clearTimeout(scrollTimeout);
scrollTimeout = setTimeout(function() {
overlay.classList.remove('is-scrolling');
}, 150);
}, { passive: true }); // 添加 passive 标志提升性能
}
}
// 清理函数 - 更激进的清理策略
function cleanup() {
if (Object.keys(apiCache).length > cacheManager.maxSize * 1.2) {
var keys = Object.keys(apiCache);
var toDelete = keys.length - cacheManager.maxSize;
keys.sort(function(a, b) {
return (cacheManager.accessTime[a] || 0) - (cacheManager.accessTime[b] || 0);
});
for (var i = 0; i < toDelete; i++) {
delete apiCache[keys[i]];
delete cacheManager.accessTime[keys[i]];
}
console.log('缓存清理完成,删除了 ' + toDelete + ' 条记录');
}
// 清理预加载队列
if (preloadQueue.size > 10) {
preloadQueue.clear();
console.log('预加载队列已清空');
}
}
// 定期清理(每3分钟)
setInterval(cleanup, 3 * 60 * 1000);
// 页面卸载时清理
window.addEventListener('beforeunload', function() {
apiCache = {};
cacheManager.accessTime = {};
preloadQueue.clear();
});
// 监控内存使用(仅在支持的浏览器中)
function monitorMemory() {
if (window.performance && window.performance.memory) {
var memory = window.performance.memory;
var usedPercent = (memory.usedJSHeapSize / memory.jsHeapSizeLimit) * 100;
if (usedPercent > 80) {
console.warn('内存使用率过高: ' + usedPercent.toFixed(2) + '%');
cleanup(); // 强制清理
}
}
}
// 每分钟检查一次内存
setInterval(monitorMemory, 60 * 1000);
// 页面加载完成后初始化
function init() {
initCardClickEvents();
initKeyboardShortcuts();
// 延迟执行性能优化,避免阻塞主线程
setTimeout(function() {
optimizeScrollPerformance();
}, 1000);
console.log('卡牌系统初始化完成');
}
if (document.readyState === 'loading') {
document.addEventListener('DOMContentLoaded', init);
} else {
init();
}
})();
/* 卡牌文字滚动 */
(function() {
'use strict';
function initCardScroll() {
var cards = document.querySelectorAll('.game-card.card-description-scrollable');
cards.forEach(function(card) {
var scrollContainer = card.querySelector('.card-description-scroll');
var scrollInner = card.querySelector('.card-description-scroll-inner');
var scrollText = card.querySelector('.card-description-text');
if (!scrollContainer || !scrollInner || !scrollText) {
return;
}
// 使用 requestAnimationFrame 优化性能
requestAnimationFrame(function() {
scrollInner.style.display = 'none';
scrollInner.offsetHeight;
scrollInner.style.display = 'block';
});
});
}
// 延迟初始化,使用防抖优化
var initTimeout;
function delayedInit() {
clearTimeout(initTimeout);
initTimeout = setTimeout(function() {
initCardScroll();
}, 100);
}
if (document.readyState === 'loading') {
document.addEventListener('DOMContentLoaded', delayedInit);
} else {
delayedInit();
}
// 监听动态内容加载(例如 AJAX 加载)
var observer = new MutationObserver(function(mutations) {
var shouldInit = false;
mutations.forEach(function(mutation) {
if (mutation.addedNodes.length > 0) {
mutation.addedNodes.forEach(function(node) {
if (node.nodeType === 1 &&
(node.classList.contains('game-card') ||
node.querySelector('.game-card'))) {
shouldInit = true;
}
});
}
});
if (shouldInit) {
delayedInit();
}
});
// 只观察特定容器,减少性能开销
observer.observe(document.body, {
childList: true,
subtree: true
});
})();
/* 卡牌文字滚动 */
(function() {
'use strict';
function initCardScroll() {
var cards = document.querySelectorAll('.game-card.card-description-scrollable');
cards.forEach(function(card) {
var scrollContainer = card.querySelector('.card-description-scroll');
var scrollInner = card.querySelector('.card-description-scroll-inner');
var scrollText = card.querySelector('.card-description-text');
if (!scrollContainer || !scrollInner || !scrollText) {
return;
}
// 强制触发重绘
scrollInner.style.display = 'none';
scrollInner.offsetHeight; // 触发重排
scrollInner.style.display = 'block';
});
}
// 延迟初始化,确保DOM完全加载
function delayedInit() {
setTimeout(initCardScroll, 100);
setTimeout(initCardScroll, 500);
setTimeout(initCardScroll, 1000);
}
if (document.readyState === 'loading') {
document.addEventListener('DOMContentLoaded', delayedInit);
} else {
delayedInit();
}
})();
/* 卡牌Tooltip */
$(function() {
// 初始化所有卡牌悬浮
function initCardHoverElements() {
$('.card-hover-container').each(function() {
var $container = $(this);
// 只初始化尚未处理的元素
if ($container.data('initialized')) return;
$container.data('initialized', true);
// 获取卡牌数据
var character = $container.data('character');
var cardName = $container.data('card');
var deckType = $container.data('deck') || '';
var index = $container.data('index') || 0;
var $popup = $container.find('.card-popup');
// 预加载卡牌数据
$container.on('mouseenter', function() {
// 如果卡牌数据已经加载过,直接显示
if ($popup.data('loaded')) {
// 调整位置到右下方
positionCardPopup($container);
return;
}
// 加载卡牌数据
var params = {
action: 'parse',
text: '{{#invoke:卡牌|main|' + character + '|' + cardName + '|' + deckType + '|' + index + '}}',
prop: 'text',
disablelimitreport: true,
format: 'json'
};
$.ajax({
url: mw.util.wikiScript('api'),
data: params,
dataType: 'json',
success: function(data) {
if (data && data.parse && data.parse.text) {
var cardHtml = data.parse.text['*'];
$popup.html(cardHtml).data('loaded', true);
// 调整位置到右下方
positionCardPopup($container);
}
},
error: function() {
$popup.html('<div style="color: #721c24;">无法加载卡牌数据</div>').data('loaded', true);
}
});
});
});
}
// 调整卡牌弹出位置到右下方
function positionCardPopup($container) {
var $popupContainer = $container.find('.card-popup-container');
var containerWidth = $container.width();
var windowWidth = $(window).width();
var containerOffset = $container.offset();
var rightSpace = windowWidth - containerOffset.left - containerWidth;
// 重置位置
$popupContainer.css({
'left': 'auto',
'right': 'auto',
'top': '100%',
'margin-top': '5px'
});
// 如果右侧空间足够,放在右下方
if (rightSpace >= 168) { // 卡牌宽度大约168px
$popupContainer.css({
'left': '0',
});
}
// 如果右侧空间不够,但左侧空间足够,放在左下方
else if (containerOffset.left >= 168) {
$popupContainer.css({
'right': '0',
});
}
// 如果两侧都不够,尝试居中并确保完全可见
else {
var leftPosition = Math.max(0, Math.min(containerOffset.left - (168/2), windowWidth - 168));
$popupContainer.css({
'left': (leftPosition - containerOffset.left) + 'px'
});
}
}
// 初次加载页面时初始化
$(document).ready(function() {
initCardHoverElements();
});
// 使用 MutationObserver 监听 DOM 变化,处理动态加载的内容
if (window.MutationObserver) {
var observer = new MutationObserver(function(mutations) {
var shouldInit = false;
mutations.forEach(function(mutation) {
if (mutation.addedNodes && mutation.addedNodes.length) {
shouldInit = true;
}
});
if (shouldInit) {
initCardHoverElements();
}
});
observer.observe(document.body, {
childList: true,
subtree: true
});
}
});
/* 词典Tooltip */
(function() {
var tooltipContainer = null;
var currentTerm = null;
var hideTimeout = null;
// 初始化
$(function() {
// 创建全局 tooltip 容器
tooltipContainer = $('<div class="dictionary-tooltip-container"></div>');
$('body').append(tooltipContainer);
// 鼠标进入词条
$(document).on('mouseenter', '.dictionary-term', function(e) {
var $term = $(this);
currentTerm = $term;
// 清除隐藏定时器
if (hideTimeout) {
clearTimeout(hideTimeout);
hideTimeout = null;
}
// 获取 tooltip 内容
var content = $term.attr('data-tooltip-content');
if (!content) return;
// 设置内容
tooltipContainer.html(content);
// 计算位置
updateTooltipPosition($term);
// 显示 tooltip
tooltipContainer.addClass('active');
});
// 鼠标离开词条
$(document).on('mouseleave', '.dictionary-term', function() {
hideTimeout = setTimeout(function() {
tooltipContainer.removeClass('active');
currentTerm = null;
}, 100);
});
// 鼠标进入 tooltip(防止快速隐藏)
tooltipContainer.on('mouseenter', function() {
if (hideTimeout) {
clearTimeout(hideTimeout);
hideTimeout = null;
}
});
// 鼠标离开 tooltip
tooltipContainer.on('mouseleave', function() {
hideTimeout = setTimeout(function() {
tooltipContainer.removeClass('active');
currentTerm = null;
}, 100);
});
// 窗口滚动时更新位置
$(window).on('scroll resize', function() {
if (currentTerm && tooltipContainer.hasClass('active')) {
updateTooltipPosition(currentTerm);
}
});
});
// 更新 tooltip 位置
function updateTooltipPosition($term) {
var rect = $term[0].getBoundingClientRect();
var scrollTop = $(window).scrollTop();
var scrollLeft = $(window).scrollLeft();
// 基本位置:词条下方
var top = rect.bottom + scrollTop + 2;
var left = rect.left + scrollLeft;
// 获取 tooltip 尺寸
var tooltipWidth = 250; // 固定宽度
var tooltipHeight = tooltipContainer.outerHeight();
// 获取窗口尺寸
var windowWidth = $(window).width();
var windowHeight = $(window).height();
// 检查右边界
if (left + tooltipWidth > windowWidth + scrollLeft) {
left = Math.max(scrollLeft, rect.right + scrollLeft - tooltipWidth);
}
// 检查下边界
if (rect.bottom + tooltipHeight > windowHeight) {
// 如果下方空间不足,显示在上方
if (rect.top - tooltipHeight > 0) {
top = rect.top + scrollTop - tooltipHeight - 2;
}
}
// 设置位置
tooltipContainer.css({
top: top + 'px',
left: left + 'px'
});
}
})();