无编辑摘要 |
Guard legacy custom navigation script so it does not run on NovaSkin pages |
||
| (未显示2个用户的37个中间版本) | |||
| 第2行: | 第2行: | ||
// ============================================= | // ============================================= | ||
// | // 动态导航菜单系统 | ||
// ============================================= | // ============================================= | ||
// | // 导航菜单数据 | ||
var | var menuJsonData = [ | ||
{ | |||
"name": "首页", | |||
"link": "首页", | |||
"children": [] | |||
}, | |||
{ | |||
"name": "旅人图鉴", | |||
"link": "旅人图鉴", | |||
"children": [] | |||
}, | |||
{ | |||
"name": "秘纹图鉴", | |||
"link": "秘纹图鉴", | |||
"children": [] | |||
}, | |||
{ | |||
"name": "特殊页面", | |||
"link": "特殊:特殊页面", | |||
"children": [] | |||
} | |||
]; | |||
// 导航菜单管理器 | |||
var NavigationMenu = { | |||
// 初始化所有导航菜单 | |||
initAll: function() { | |||
// 确保容器存在 | |||
NavigationMenu.ensureContainerExists(); | |||
// 初始化容器 | |||
$('.custom-navigation-container').each(function() { | |||
NavigationMenu.initContainer($(this)); | |||
}); | |||
}, | |||
// 确保容器存在,如果不存在则在.header-nav-inner头部插入 | |||
var | ensureContainerExists: function() { | ||
// 如果容器已存在,直接返回 | |||
if ($('.custom-navigation-container').length > 0) { | |||
return; | |||
} | |||
// 查找.header-nav-inner元素 | |||
var $headerNavInner = $('.header-nav-inner'); | |||
if ($headerNavInner.length === 0) { | |||
console.warn('找不到.header-nav-inner元素,无法插入导航菜单'); | |||
return; | |||
} | |||
// 创建导航容器 | |||
var $container = $('<div>', { | |||
class: 'custom-navigation-container', | |||
id: 'custom-navigation-menu' | |||
}); | |||
if ( | // 插入到.header-nav-inner的头部 | ||
return | $headerNavInner.prepend($container); | ||
}, | |||
// 初始化单个容器 | |||
initContainer: function($container) { | |||
// 防止重复初始化 | |||
if ($container.data('initialized')) { | |||
return; | |||
} | } | ||
$container.data('initialized', true); | |||
// 加载导航数据 | |||
NavigationMenu.loadNavData() | |||
.then(function(navData) { | |||
// 构建导航菜单 | |||
NavigationMenu.buildNavigation($container, navData); | |||
}) | |||
.catch(function(error) { | |||
console.error('加载导航数据失败:', error); | |||
NavigationMenu.showError($container, error); | |||
}); | |||
}, | |||
// 加载导航数据 | |||
loadNavData: function() { | |||
return new Promise(function(resolve, reject) { | return new Promise(function(resolve, reject) { | ||
var | resolve(menuJsonData); | ||
if ( | }); | ||
}, | |||
} | // 构建导航菜单 | ||
buildNavigation: function($container, navData) { | |||
// 创建导航菜单HTML | |||
var navHTML = NavigationMenu.createNavHTML(navData); | |||
$container.html(navHTML); | |||
// 绑定事件 | |||
NavigationMenu.bindNavEvents($container); | |||
// 添加响应式菜单按钮(移动端) | |||
NavigationMenu.addMobileMenuButton($container); | |||
// 初始化当前页面高亮 | |||
NavigationMenu.highlightCurrentPage($container); | |||
}, | |||
// 创建导航HTML(递归) | |||
createNavHTML: function(menuItems, level) { | |||
level = level || 1; | |||
if (!menuItems || menuItems.length === 0) { | |||
return ''; | |||
} | |||
var ulClass = level === 1 ? 'nav-menu-primary' : 'nav-menu-sub nav-menu-level-' + level; | |||
var html = '<ul class="' + ulClass + '">'; | |||
menuItems.forEach(function(item, index) { | |||
var hasChildren = item.children && item.children.length > 0; | |||
var liClass = 'nav-item'; | |||
var aClass = 'nav-link'; | |||
var submenuHTML = ''; | |||
if (hasChildren) { | |||
liClass += ' has-children'; | |||
aClass += ' has-children-link'; | |||
submenuHTML = NavigationMenu.createNavHTML(item.children, level + 1); | |||
} | |||
// 构建链接 | |||
var href = item.link ? mw.util.getUrl(item.link) : '#'; | |||
var iconHTML = item.icon ? '<span class="nav-icon">' + item.icon + '</span>' : ''; | |||
var nameHTML = '<span class="nav-text">' + item.name + '</span>'; | |||
var arrowHTML = hasChildren ? '<span class="nav-arrow">▶</span>' : ''; | |||
html += '<li class="' + liClass + '" data-link="' + (item.link || '') + '">'; | |||
html += '<a href="' + href + '" class="' + aClass + '" title="' + (item.name || '') + '">'; | |||
html += iconHTML + nameHTML + arrowHTML; | |||
html += '</a>'; | |||
html += submenuHTML; | |||
html += '</li>'; | |||
// 在一级菜单项之间添加分隔符(除了最后一个) | |||
if (level === 1 && index < menuItems.length - 1) { | |||
html += '<span class="navigation-separator"></span>'; | |||
} | } | ||
}); | }); | ||
html += '</ul>'; | |||
return html; | |||
}, | }, | ||
// 绑定导航事件 | |||
var | bindNavEvents: function($container) { | ||
// 用于存储菜单的计时器 | |||
var menuTimers = {}; | |||
// 子菜单切换(桌面端悬停,移动端点击) | |||
if (window.innerWidth >= 768) { | |||
// 桌面端:悬停显示子菜单 | |||
$container.on('mouseenter', '.nav-item.has-children', function(e) { | |||
var $this = $(this); | |||
}, | var menuId = $this.data('link') || $this.index(); | ||
// 清除可能存在的关闭计时器 | |||
if (menuTimers[menuId]) { | |||
clearTimeout(menuTimers[menuId]); | |||
delete menuTimers[menuId]; | |||
} | |||
// 关闭其他打开的菜单 | |||
$container.find('.nav-item.hover').not($this).removeClass('hover'); | |||
$container.find('.nav-menu-sub.active').not($this.find('.nav-menu-sub')).removeClass('active'); | |||
// 显示当前菜单 | |||
$this.addClass('hover'); | |||
$this.find('.nav-menu-sub').first().addClass('active'); | |||
}); | |||
$container.on('mouseleave', '.nav-item.has-children', function(e) { | |||
var $this = $(this); | |||
var menuId = $this.data('link') || $this.index(); | |||
var $submenu = $this.find('.nav-menu-sub').first(); | |||
// 设置延迟关闭计时器 | |||
menuTimers[menuId] = setTimeout(function() { | |||
$this.removeClass('hover'); | |||
$submenu.removeClass('active'); | |||
delete menuTimers[menuId]; | |||
}, 300); // 300ms延迟,避免鼠标短暂离开时立即关闭 | |||
}); | |||
// 当鼠标进入子菜单时,清除关闭计时器 | |||
$container.on('mouseenter', '.nav-menu-sub', function(e) { | |||
var $submenu = $(this); | |||
var $parent = $submenu.closest('.nav-item'); | |||
var menuId = $parent.data('link') || $parent.index(); | |||
// 清除父菜单的关闭计时器 | |||
if (menuTimers[menuId]) { | |||
clearTimeout(menuTimers[menuId]); | |||
delete menuTimers[menuId]; | |||
} | } | ||
} | }); | ||
// 当鼠标离开子菜单时,设置关闭计时器 | |||
$container.on('mouseleave', '.nav-menu-sub', function(e) { | |||
var $submenu = $(this); | |||
var $parent = $submenu.closest('.nav-item'); | |||
var menuId = $parent.data('link') || $parent.index(); | |||
// 设置延迟关闭计时器 | |||
}, | menuTimers[menuId] = setTimeout(function() { | ||
$parent.removeClass('hover'); | |||
$submenu.removeClass('active'); | |||
delete menuTimers[menuId]; | |||
}, 300); | |||
}); | |||
} else { | |||
// 移动端:点击切换子菜单 | |||
$container.on('click', '.nav-link.has-children-link', function(e) { | |||
e.preventDefault(); | |||
e.stopPropagation(); | |||
var $link = $(this); | |||
var $parent = $link.closest('.nav-item'); | |||
var $submenu = $parent.find('.nav-menu-sub').first(); | |||
if ($submenu.hasClass('active')) { | |||
$submenu.removeClass('active'); | |||
$parent.removeClass('expanded'); | |||
} else { | |||
// 关闭其他打开的菜单 | |||
$container.find('.nav-menu-sub.active').removeClass('active'); | |||
$container.find('.nav-item.expanded').removeClass('expanded'); | |||
$submenu.addClass('active'); | |||
$parent.addClass('expanded'); | |||
} | } | ||
}, | }); | ||
} | |||
// 点击外部关闭菜单 | |||
$(document).on('click touchstart', function(e) { | |||
if (!$(e.target).closest('.custom-navigation-container').length) { | |||
$('.custom-navigation-container').find('.nav-menu-sub.active').removeClass('active'); | |||
$('.custom-navigation-container').find('.nav-item.expanded').removeClass('expanded'); | |||
$('.custom-navigation-container').find('.nav-item.hover').removeClass('hover'); | |||
// 清除所有计时器 | |||
for (var timerId in menuTimers) { | |||
if (menuTimers.hasOwnProperty(timerId)) { | |||
clearTimeout(menuTimers[timerId]); | |||
} | |||
} | } | ||
menuTimers = {}; | |||
} | |||
}); | |||
}, | |||
// 添加移动端菜单按钮 | |||
addMobileMenuButton: function($container) { | |||
var $button = $('<button>', { | |||
class: 'nav-mobile-toggle', | |||
html: '<span class="nav-toggle-icon">☰</span>', | |||
click: function() { | |||
$container.toggleClass('mobile-open'); | |||
$(this).toggleClass('active'); | |||
} | } | ||
}; | }); | ||
$container.prepend($button); | |||
}, | |||
// 高亮当前页面 | |||
highlightCurrentPage: function($container) { | |||
var currentPage = mw.config.get('wgPageName'); | |||
if (!currentPage) return; | |||
// 解码页面名称 | |||
currentPage = decodeURIComponent(currentPage); | |||
// 查找匹配的导航项 | |||
$container.find('.nav-item[data-link]').each(function() { | |||
var link = $(this).data('link'); | |||
if (link && link === currentPage) { | |||
$(this).addClass('current-page'); | |||
$(this).find('.nav-link').first().addClass('current'); | |||
// 展开父级菜单 | |||
$(this).parents('.nav-item.has-children').addClass('expanded'); | |||
$(this).parents('.nav-menu-sub').addClass('active'); | |||
} | |||
}); | |||
}, | }, | ||
// 显示错误信息 | |||
showError: function($container, error) { | |||
$container.html( | |||
'<div class="nav-error">' + | |||
' <div class="error-icon">⚠️</div>' + | |||
' <div class="error-title">导航菜单加载失败</div>' + | |||
' <div class="error-message">' + error + '</div>' + | |||
' <button class="error-retry" onclick="window.location.reload()">重试</button>' + | |||
'</div>' | |||
); | |||
} | } | ||
}; | }; | ||
// | // 添加导航菜单样式 | ||
function addNavigationStyles() { | |||
if ($('#navigation-styles').length) { | |||
return; | |||
} | |||
var styles = ` | |||
<style id="navigation-styles"> | |||
/* 导航容器 */ | |||
.custom-navigation-container { | |||
flex: 1; | |||
position: relative; | |||
background: #4b5082; | |||
border-radius: 8px; | |||
font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif; | |||
z-index: 1000; | |||
} | |||
/* 分隔符样式 */ | |||
.navigation-separator { | |||
display: inline-block; | |||
width: 1px; | |||
height: 20px; | |||
background-color: rgba(255, 255, 255, 0.2); | |||
margin: 0 5px; | |||
vertical-align: middle; | |||
} | |||
/* 错误状态 */ | |||
.nav-error { | |||
text-align: center; | |||
padding: 30px; | |||
color: #ecf0f1; | |||
} | |||
.error-icon { | |||
font-size: 40px; | |||
margin-bottom: 15px; | |||
} | |||
.error-title { | |||
font-size: 18px; | |||
font-weight: 600; | |||
margin-bottom: 10px; | |||
color: #e74c3c; | |||
} | |||
.error-message { | |||
font-size: 14px; | |||
margin-bottom: 20px; | |||
opacity: 0.8; | |||
} | |||
.error-retry { | |||
padding: 8px 20px; | |||
background: #3498db; | |||
color: white; | |||
border: none; | |||
border-radius: 4px; | |||
cursor: pointer; | |||
font-size: 14px; | |||
transition: background-color 0.3s; | |||
} | |||
.error-retry:hover { | |||
background: #2980b9; | |||
} | |||
/* 移动端菜单按钮 */ | |||
.nav-mobile-toggle { | |||
display: none; | |||
position: absolute; | |||
top: 10px; | |||
right: 10px; | |||
background: transparent; | |||
border: none; | |||
color: white; | |||
font-size: 24px; | |||
cursor: pointer; | |||
z-index: 1001; | |||
padding: 5px 10px; | |||
border-radius: 4px; | |||
} | |||
/* 主菜单 */ | |||
.nav-menu-primary { | |||
display: flex; | |||
margin: 0; | |||
padding: 0; | |||
list-style: none; | |||
align-items: center; | |||
} | |||
/* 导航项 */ | |||
.nav-item { | |||
position: relative; | |||
margin: 0; | |||
padding: 0; | |||
} | |||
.nav-item > .nav-link { | |||
display: flex; | |||
align-items: center; | |||
gap: 8px; | |||
padding: 5px 20px; | |||
color: #ecf0f1; | |||
text-decoration: none !important; | |||
font-size: 15px; | |||
font-weight: 500; | |||
transition: all 0.3s ease; | |||
white-space: nowrap; | |||
} | |||
.nav-link:hover { | |||
background: rgba(255, 255, 255, 0.1); | |||
color: #3498db; | |||
} | |||
.nav-link.current { | |||
color: #3498db; | |||
font-weight: 600; | |||
} | |||
.nav-item.current-page > .nav-link { | |||
color: #3498db; | |||
font-weight: 600; | |||
} | |||
.nav-icon { | |||
font-size: 16px; | |||
line-height: 1; | |||
} | |||
.nav-text { | |||
flex: 1; | |||
} | |||
.nav-arrow { | |||
font-size: 10px; | |||
margin-left: 5px; | |||
transition: transform 0.3s; | |||
} | |||
.nav-item.expanded > .nav-link > .nav-arrow { | |||
transform: rotate(90deg); | |||
} | |||
/* 子菜单 */ | |||
.nav-menu-sub { | |||
display: none; | |||
position: absolute; | |||
top: 100%; | |||
left: 0; | |||
min-width: 220px; | |||
background: white; | |||
border-radius: 0 0 8px 8px; | |||
box-shadow: 0 6px 20px rgba(0, 0, 0, 0.2); | |||
list-style: none; | |||
margin: 0; | |||
padding: 8px 0; | |||
z-index: 1000; | |||
} | |||
.nav-menu-sub.active { | |||
display: block; | |||
} | |||
.nav-menu-level-2 { | |||
background: #505587; | |||
} | |||
.nav-menu-level-3 { | |||
background: #2c3e50; | |||
} | |||
/* 子菜单项 */ | |||
.nav-menu-sub .nav-item { | |||
border-bottom: 1px solid rgba(255, 255, 255, 0.05); | |||
} | |||
.nav-menu-sub .nav-item:last-child { | |||
border-bottom: none; | |||
} | |||
.nav-menu-sub .nav-link { | |||
padding: 8px 20px; | |||
color: #ecf0f1; | |||
font-size: 14px; | |||
} | |||
.nav-menu-sub .nav-link:hover { | |||
background: rgba(255, 255, 255, 0.1); | |||
color: #3498db; | |||
} | |||
/* 三级菜单位置 */ | |||
.nav-menu-level-2 .nav-item.has-children > .nav-menu-sub { | |||
top: 0; | |||
left: 100%; | |||
border-radius: 0 8px 8px 8px; | |||
} | |||
/* 响应式设计 */ | |||
@media (max-width: 767px) { | |||
.custom-navigation-container { | |||
border-radius: 0; | |||
box-shadow: none; | |||
} | |||
.navigation-separator { | |||
display: none; | |||
} | |||
.nav-mobile-toggle { | |||
display: block; | |||
} | |||
.nav-menu-primary { | |||
display: none; | |||
flex-direction: column; | |||
padding: 10px 0; | |||
} | |||
.custom-navigation-container.mobile-open .nav-menu-primary { | |||
display: flex; | |||
} | |||
.nav-menu-sub { | |||
position: static; | |||
display: none; | |||
background: rgba(0, 0, 0, 0.2); | |||
box-shadow: none; | |||
padding-left: 20px; | |||
margin: 0; | |||
border-radius: 0; | |||
} | |||
.nav-menu-sub.active { | |||
display: block; | |||
} | |||
.nav-menu-level-2 .nav-item.has-children > .nav-menu-sub { | |||
position: static; | |||
left: auto; | |||
border-radius: 0; | |||
} | |||
.nav-link { | |||
padding: 12px 20px; | |||
} | |||
.nav-item.has-children > .nav-link { | |||
padding-right: 40px; | |||
} | |||
} | |||
@media (min-width: 768px) { | |||
.nav-item:hover > .nav-menu-sub { | |||
display: block; | |||
} | |||
.nav-menu-sub { | |||
animation: fadeInUp 0.3s ease; | |||
} | |||
@keyframes fadeInUp { | |||
from { | |||
opacity: 0; | |||
transform: translateY(10px); | |||
} | |||
to { | |||
opacity: 1; | |||
transform: translateY(0); | |||
} | |||
} | |||
} | |||
</style> | |||
`; | |||
$('head').append(styles); | |||
} | |||
// 页面加载完成后初始化 | |||
$(document).ready(function() { | $(document).ready(function() { | ||
// | if (!$('.header-nav-inner').length && !$('.custom-navigation-container').length) { | ||
return; // NovaSkin 已有新导航,旧导航脚本仅在旧页面结构存在时启用 | |||
} | |||
// 添加样式 | |||
addNavigationStyles(); | |||
// 初始化导航菜单 | |||
NavigationMenu.initAll(); | |||
// 监听页面内容动态加载 | |||
mw.hook('wikipage.content').add(function($content) { | |||
// 重新确保容器存在并初始化 | |||
NavigationMenu.ensureContainerExists(); | |||
NavigationMenu.initAll(); | |||
}); | |||
// 窗口大小变化时重新绑定事件 | |||
$(window).on('resize', function() { | |||
// 移除所有事件,重新绑定 | |||
$('.custom-navigation-container').off(); | |||
NavigationMenu.initAll(); | |||
}); | |||
}); | |||
// 全局函数:重新加载导航菜单(可用于调试) | |||
window.reloadNavigation = function() { | |||
// 确保容器存在 | |||
NavigationMenu.ensureContainerExists(); | |||
// | $('.custom-navigation-container').each(function() { | ||
$(this).removeData('initialized').empty(); | |||
NavigationMenu.initContainer($(this)); | |||
var | }); | ||
}; | |||
// 全局函数:手动插入导航菜单到指定位置 | |||
window.insertNavigationTo = function(selector) { | |||
// 移除现有的导航容器 | |||
$('.custom-navigation-container').remove(); | |||
// 在指定位置插入新容器 | |||
var $target = $(selector); | |||
if ($target.length === 0) { | |||
console.error('找不到目标元素: ' + selector); | |||
return false; | |||
} | |||
var $container = $('<div>', { | |||
class: 'custom-navigation-container', | |||
id: 'custom-navigation-menu' | |||
}); | |||
$target.prepend($container); | |||
// 重新初始化导航菜单 | |||
reloadNavigation(); | |||
return true; | |||
}; | |||
// ============================================= | |||
// 角色属性查看器 - 动态创建版本 | |||
// ============================================= | |||
// 角色数据管理器 | |||
var CharacterDataManager = { | |||
cache: {}, | |||
getCharacterData: function(characterId) { | |||
var self = this; | |||
// 检查缓存 | |||
if (self.cache[characterId]) { | |||
return Promise.resolve(self.cache[characterId]); | |||
} | |||
return new Promise(function(resolve, reject) { | |||
// 构建数据页面路径 | |||
var dataSource = 'Data:Character/' + characterId + '.json'; | |||
$.get("http://trekkers-wiki.jvav.net.cn/index.php", { | |||
action: 'raw', | |||
title: dataSource | |||
}).done(function(data) { | }).done(function(data) { | ||
try { | try { | ||
var characterData = JSON.parse(data); | |||
// 缓存数据 | |||
self.cache[characterId] = characterData; | |||
resolve(characterData); | |||
} catch (e) { | } catch (e) { | ||
reject('解析JSON数据失败: ' + e.message); | |||
} | } | ||
}).fail(function(error) { | |||
reject('API请求失败: ' + error); | |||
}); | }); | ||
}); | |||
}, | |||
// | clearCache: function() { | ||
$('.character-stats-container').each(function() { | this.cache = {}; | ||
} | |||
}; | |||
// 主初始化函数 | |||
$(document).ready(function() { | |||
var $initialContainers = $('.character-stats-container'); | |||
if ($initialContainers.length) { | |||
addCharacterStatsStyles(); | |||
$initialContainers.each(function() { | |||
createCharacterStatsWidget($(this)); | |||
}); | |||
} | |||
mw.hook('wikipage.content').add(function($content) { | mw.hook('wikipage.content').add(function($content) { | ||
$content.find('.character-stats-container').each(function() { | var $containers = $content.find('.character-stats-container'); | ||
if ($content.is('.character-stats-container')) { | |||
$containers = $containers.add($content); | |||
} | |||
if (!$containers.length) { | |||
return; | |||
} | |||
addCharacterStatsStyles(); | |||
$containers.each(function() { | |||
if (!$(this).find('.character-stats-widget').length) { | if (!$(this).find('.character-stats-widget').length) { | ||
createCharacterStatsWidget($(this)); | createCharacterStatsWidget($(this)); | ||
| 第149行: | 第745行: | ||
if ($('#character-stats-styles').length) { | if ($('#character-stats-styles').length) { | ||
return; // 样式已存在 | return; // 样式已存在 | ||
} | |||
if (!$('.character-stats-container').length && !$('.character-stats-widget').length) { | |||
return; // 只在旧角色属性查看器实际存在时注入样式 | |||
} | } | ||
| 第633行: | 第1,233行: | ||
return num.toString().replace(/\B(?=(\d{3})+(?!\d))/g, ","); | return num.toString().replace(/\B(?=(\d{3})+(?!\d))/g, ","); | ||
} | } | ||
// ============================================= | |||
// 赛季BOSS展示页面功能模块 | |||
// ============================================= | |||
// 赛季BOSS页面初始化器 | |||
var SeasonBossPage = { | |||
// 初始化所有赛季BOSS页面 | |||
initAll: function() { | |||
$('.season-boss-page').each(function() { | |||
SeasonBossPage.initPage($(this)); | |||
}); | |||
}, | |||
// 初始化单个页面 | |||
initPage: function($page) { | |||
// 防止重复初始化 | |||
if ($page.data('page-initialized') === 'true') { | |||
return; | |||
} | |||
$page.data('page-initialized', 'true'); | |||
// 绑定标签切换事件 | |||
SeasonBossPage.bindTabEvents($page); | |||
// 初始化第一个可见的赛季内容 | |||
SeasonBossPage.activateFirstTab($page); | |||
}, | |||
// 绑定标签切换事件 | |||
bindTabEvents: function($page) { | |||
// 使用事件委托处理标签点击 | |||
$page.on('click', '.season-tab', function(e) { | |||
e.preventDefault(); | |||
var $tab = $(this); | |||
var seasonId = $tab.data('season'); | |||
// 如果已经是激活状态,则不处理 | |||
if ($tab.hasClass('active')) { | |||
return; | |||
} | |||
// 切换标签和内容 | |||
SeasonBossPage.switchSeason($page, seasonId); | |||
}); | |||
// 添加键盘支持 | |||
$page.on('keydown', '.season-tab', function(e) { | |||
if (e.key === 'Enter' || e.key === ' ') { | |||
e.preventDefault(); | |||
$(this).trigger('click'); | |||
} | |||
}); | |||
}, | |||
// 切换赛季显示 | |||
switchSeason: function($page, seasonId) { | |||
// 移除所有激活状态 | |||
$page.find('.season-tab').removeClass('active'); | |||
$page.find('.season-content').removeClass('active'); | |||
// 添加新的激活状态 | |||
$page.find('.season-tab[data-season="' + seasonId + '"]').addClass('active'); | |||
$page.find('#' + seasonId).addClass('active'); | |||
// 触发自定义事件,以便其他模块可以监听赛季切换 | |||
$(document).trigger('seasonboss:seasonchanged', { | |||
seasonId: seasonId, | |||
page: $page | |||
}); | |||
// 记录切换日志(仅开发时使用) | |||
if (console && console.log) { | |||
console.log('赛季切换至: ' + seasonId); | |||
} | |||
}, | |||
// 激活第一个标签(如果当前没有激活的标签) | |||
activateFirstTab: function($page) { | |||
var $activeTab = $page.find('.season-tab.active'); | |||
// 如果没有激活的标签,激活第一个 | |||
if ($activeTab.length === 0) { | |||
var $firstTab = $page.find('.season-tab').first(); | |||
var seasonId = $firstTab.data('season'); | |||
$firstTab.addClass('active'); | |||
$page.find('#' + seasonId).addClass('active'); | |||
} | |||
}, | |||
// 添加新赛季(动态添加功能) | |||
addSeason: function($page, seasonData) { | |||
// 参数检查 | |||
if (!seasonData || !seasonData.id || !seasonData.name) { | |||
console.error('添加赛季失败:缺少必要的赛季数据'); | |||
return false; | |||
} | |||
var seasonId = seasonData.id; | |||
var seasonName = seasonData.name; | |||
var seasonIcon = seasonData.icon || '🏅'; | |||
// 检查赛季是否已存在 | |||
if ($page.find('.season-tab[data-season="' + seasonId + '"]').length > 0) { | |||
console.warn('赛季已存在:' + seasonId); | |||
return false; | |||
} | |||
// 创建新标签 | |||
var $newTab = $('<button>', { | |||
'class': 'season-tab', | |||
'data-season': seasonId, | |||
'html': '<span class="season-icon">' + seasonIcon + '</span>' + | |||
'<span class="season-name">' + seasonName + '</span>' | |||
}); | |||
// 添加到标签容器 | |||
$page.find('.season-tabs').append($newTab); | |||
// 创建新内容区域 | |||
var $newContent = $('<div>', { | |||
'class': 'season-content', | |||
'id': seasonId | |||
}); | |||
// 如果提供了内容,添加到内容区域 | |||
if (seasonData.content) { | |||
$newContent.html(seasonData.content); | |||
} | |||
// 添加到内容区域 | |||
$page.find('.season-content').last().after($newContent); | |||
// 重新绑定事件(新添加的元素需要绑定) | |||
SeasonBossPage.bindTabEvents($page); | |||
return true; | |||
}, | |||
// 获取当前激活的赛季ID | |||
getActiveSeason: function($page) { | |||
var $activeTab = $page.find('.season-tab.active'); | |||
return $activeTab.length > 0 ? $activeTab.data('season') : null; | |||
}, | |||
// 跳转到指定赛季 | |||
goToSeason: function($page, seasonId) { | |||
var $targetTab = $page.find('.season-tab[data-season="' + seasonId + '"]'); | |||
if ($targetTab.length === 0) { | |||
console.error('赛季不存在:' + seasonId); | |||
return false; | |||
} | |||
SeasonBossPage.switchSeason($page, seasonId); | |||
return true; | |||
} | |||
}; | |||
// 页面加载完成后初始化赛季BOSS页面 | |||
$(document).ready(function() { | |||
SeasonBossPage.initAll(); | |||
// 监听内容动态加载(例如通过Widgets或AJAX) | |||
$(document).on('seasonboss:contentloaded', function() { | |||
SeasonBossPage.initAll(); | |||
}); | |||
}); | |||
// 当有新内容添加到页面时也初始化 | |||
mw.hook('wikipage.content').add(function($content) { | |||
$content.find('.season-boss-page').each(function() { | |||
SeasonBossPage.initPage($(this)); | |||
}); | |||
}); | |||
// ============================================= | |||
// 全局可用的函数 | |||
// ============================================= | |||
// 全局函数:跳转到指定赛季 | |||
window.goToSeason = function(seasonId) { | |||
var $page = $('.season-boss-page').first(); | |||
if ($page.length > 0) { | |||
return SeasonBossPage.goToSeason($page, seasonId); | |||
} | |||
return false; | |||
}; | |||
// 全局函数:获取当前赛季 | |||
window.getCurrentSeason = function() { | |||
var $page = $('.season-boss-page').first(); | |||
if ($page.length > 0) { | |||
return SeasonBossPage.getActiveSeason($page); | |||
} | |||
return null; | |||
}; | |||
// 全局函数:添加新赛季 | |||
window.addNewSeason = function(seasonData) { | |||
var $page = $('.season-boss-page').first(); | |||
if ($page.length > 0) { | |||
return SeasonBossPage.addSeason($page, seasonData); | |||
} | |||
return false; | |||
}; | |||
// ============================================= | |||
// 筛选系统 | |||
// ============================================= | |||
// 筛选器管理器 | |||
var FilterManager = { | |||
instances: {}, | |||
defaults: { | |||
autoApply: false | |||
}, | |||
// 初始化所有筛选器 | |||
initAll: function() { | |||
// 查找所有筛选器容器 | |||
$('.filter-container').each(function() { | |||
var filterId = $(this).attr('id'); | |||
if (filterId) { | |||
FilterManager.init(filterId); | |||
} | |||
}); | |||
// 绑定全局事件 | |||
FilterManager.bindGlobalEvents(); | |||
}, | |||
// 初始化单个筛选器 | |||
init: function(filterId) { | |||
var $container = $('#' + filterId); | |||
if (!$container.length) return; | |||
// 获取配置 | |||
var $config = $container.siblings('.filter-config'); | |||
var target = $config.data('filter-target'); | |||
var auto = $config.data('filter-auto') === 'true'; | |||
if (!target) { | |||
console.warn('筛选器缺少目标选择器:', filterId); | |||
return; | |||
} | |||
// 创建实例 | |||
var instance = { | |||
id: filterId, | |||
$container: $container, | |||
$target: $(target), | |||
config: { | |||
autoApply: auto | |||
}, | |||
state: { | |||
activeFilters: {}, | |||
allOptions: {} | |||
} | |||
}; | |||
// 收集筛选组数据 | |||
instance.$container.find('.filter-group').each(function() { | |||
var $group = $(this); | |||
var groupName = $group.data('filter-group'); | |||
// 收集选项数据 | |||
$group.find('.filter-option').each(function() { | |||
var $option = $(this); | |||
var value = $option.data('filter-value'); | |||
var display = $option.data('filter-display'); | |||
if (!instance.state.allOptions[groupName]) { | |||
instance.state.allOptions[groupName] = {}; | |||
} | |||
instance.state.allOptions[groupName][value] = { | |||
$option: $option, | |||
value: value, | |||
display: display, | |||
isSelected: $option.hasClass('filter-selected') | |||
}; | |||
// 初始化选中状态 | |||
if ($option.hasClass('filter-selected')) { | |||
if (!instance.state.activeFilters[groupName] || value === 'all') { | |||
instance.state.activeFilters[groupName] = []; | |||
} | |||
else if (instance.state.activeFilters[groupName].indexOf(value) === -1) { | |||
instance.state.activeFilters[groupName].push(value); | |||
} | |||
} | |||
}); | |||
}); | |||
// 隐藏统计和活动筛选(初始状态) | |||
instance.$container.find('.filter-stats').hide(); | |||
instance.$container.find('.active-filters').hide(); | |||
// 存储实例 | |||
FilterManager.instances[filterId] = instance; | |||
// // 初始筛选 | |||
// setTimeout(function() { | |||
// FilterManager.applyFilters(instance); | |||
// }, 100); | |||
}, | |||
// 绑定全局事件(事件委托) | |||
bindGlobalEvents: function() { | |||
// 筛选项点击 | |||
$(document).on('click', '.filter-option', function(e) { | |||
e.preventDefault(); | |||
var $option = $(this); | |||
FilterManager.handleOptionClick($option); | |||
}); | |||
// 筛选项键盘事件 | |||
$(document).on('keydown', '.filter-option', function(e) { | |||
if (e.key === 'Enter' || e.key === ' ') { | |||
e.preventDefault(); | |||
var $option = $(this); | |||
FilterManager.handleOptionClick($option); | |||
} | |||
}); | |||
// 应用按钮 | |||
$(document).on('click', '.filter-apply', function(e) { | |||
e.preventDefault(); | |||
var $container = $(this).closest('.filter-container'); | |||
var filterId = $container.attr('id'); | |||
FilterManager.applyFilters(FilterManager.instances[filterId]); | |||
}); | |||
// 重置按钮 | |||
$(document).on('click', '.filter-reset', function(e) { | |||
e.preventDefault(); | |||
var $container = $(this).closest('.filter-container'); | |||
var filterId = $container.attr('id'); | |||
FilterManager.resetFilters(FilterManager.instances[filterId]); | |||
}); | |||
// 清除组按钮 | |||
$(document).on('click', '.filter-group-clear', function(e) { | |||
e.preventDefault(); | |||
e.stopPropagation(); | |||
var $btn = $(this); | |||
var group = $btn.data('filter-clear'); | |||
var $container = $btn.closest('.filter-container'); | |||
var filterId = $container.attr('id'); | |||
FilterManager.clearGroup(FilterManager.instances[filterId], group); | |||
}); | |||
// 移除活动筛选标签 | |||
$(document).on('click', '.active-filter-remove', function(e) { | |||
e.preventDefault(); | |||
e.stopPropagation(); | |||
var $removeBtn = $(this); | |||
var $tag = $removeBtn.closest('.active-filter-tag'); | |||
var group = $tag.data('filter-group'); | |||
var value = $tag.data('filter-value'); | |||
var $container = $tag.closest('.filter-container'); | |||
var filterId = $container.attr('id'); | |||
FilterManager.removeFilter(FilterManager.instances[filterId], group, value); | |||
}); | |||
}, | |||
// 处理选项点击 | |||
handleOptionClick: function($option) { | |||
var group = $option.data('filter-group'); | |||
var value = $option.data('filter-value'); | |||
var $container = $option.closest('.filter-container'); | |||
var filterId = $container.attr('id'); | |||
var instance = FilterManager.instances[filterId]; | |||
if (!instance) return; | |||
// 初始化组状态 | |||
if (!instance.state.activeFilters[group]) { | |||
instance.state.activeFilters[group] = []; | |||
} | |||
var filterValues = instance.state.activeFilters[group]; | |||
var index = filterValues.indexOf(value); | |||
var isSelected = $option.hasClass('filter-selected'); | |||
// 处理"全部"选项 | |||
if (value === 'all') { | |||
if (!isSelected) { | |||
// 清除同组其他选项 | |||
instance.$container.find('.filter-option[data-filter-group="' + group + '"]') | |||
.removeClass('filter-selected'); | |||
$option.addClass('filter-selected'); | |||
instance.state.activeFilters[group] = []; | |||
// 自动应用 | |||
if (instance.config.autoApply) { | |||
FilterManager.applyFilters(instance); | |||
} | |||
} | |||
return; | |||
} | |||
// 移除"全部"选项的选中 | |||
instance.$container.find('.filter-option[data-filter-group="' + group + '"][data-filter-value="all"]') | |||
.removeClass('filter-selected'); | |||
if (!isSelected) { | |||
// 添加选中 | |||
filterValues.push(value); | |||
$option.addClass('filter-selected'); | |||
} else { | |||
// 移除选中 | |||
filterValues.splice(index, 1); | |||
$option.removeClass('filter-selected'); | |||
// 如果清空了,选中"全部" | |||
if (filterValues.length === 0) { | |||
instance.$container.find('.filter-option[data-filter-group="' + group + '"][data-filter-value="all"]') | |||
.addClass('filter-selected'); | |||
} | |||
} | |||
// 自动应用 | |||
if (instance.config.autoApply) { | |||
FilterManager.applyFilters(instance); | |||
} | |||
}, | |||
// 应用筛选 | |||
applyFilters: function(instance) { | |||
if (!instance) return; | |||
var matchedCount = 0; | |||
var totalCount = instance.$target.length; | |||
// 遍历所有目标元素 | |||
instance.$target.each(function() { | |||
var $item = $(this); | |||
var show = true; | |||
// 检查每个筛选条件 | |||
for (var group in instance.state.activeFilters) { | |||
var filterValues = instance.state.activeFilters[group]; | |||
if (filterValues.length === 0) continue; | |||
var itemValue = $item.data(group); | |||
if (itemValue === undefined) continue; | |||
// 检查是否匹配 | |||
var isMatched = false; | |||
for (var i = 0; i < filterValues.length; i++) { | |||
if (String(itemValue) === String(filterValues[i])) { | |||
isMatched = true; | |||
break; | |||
} | |||
} | |||
if (!isMatched) { | |||
show = false; | |||
break; | |||
} | |||
} | |||
// 更新元素状态 | |||
if (show) { | |||
$item.removeClass('filtered-out').addClass('filtered-in'); | |||
$item.show(); | |||
matchedCount++; | |||
} else { | |||
$item.removeClass('filtered-in').addClass('filtered-out'); | |||
$item.hide(); | |||
} | |||
}); | |||
// 更新UI | |||
FilterManager.updateActiveFilters(instance); | |||
FilterManager.updateStats(instance, matchedCount, totalCount); | |||
}, | |||
// 重置筛选 | |||
resetFilters: function(instance) { | |||
if (!instance) return; | |||
// 清除所有选中状态 | |||
instance.state.activeFilters = {}; | |||
// 重置UI | |||
instance.$container.find('.filter-option').removeClass('filter-selected'); | |||
instance.$container.find('.filter-option-all').addClass('filter-selected'); | |||
// 重新初始化状态 | |||
for (var group in instance.state.allOptions) { | |||
instance.state.activeFilters[group] = []; | |||
} | |||
// 隐藏活动筛选标签 | |||
instance.$container.find('.active-filters').hide() | |||
.find('.active-filters-list').empty(); | |||
// 隐藏统计 | |||
instance.$container.find('.filter-stats').hide(); | |||
// 应用筛选(显示所有) | |||
FilterManager.applyFilters(instance); | |||
}, | |||
// 清除单个组 | |||
clearGroup: function(instance, group) { | |||
if (!instance) return; | |||
// 清除该组所有选中 | |||
instance.$container.find('.filter-option[data-filter-group="' + group + '"]') | |||
.removeClass('filter-selected'); | |||
// 选中"全部"选项 | |||
instance.$container.find('.filter-option[data-filter-group="' + group + '"][data-filter-value="all"]') | |||
.addClass('filter-selected'); | |||
// 更新状态 | |||
instance.state.activeFilters[group] = []; | |||
// 应用筛选 | |||
FilterManager.applyFilters(instance); | |||
}, | |||
// 移除单个筛选 | |||
removeFilter: function(instance, group, value) { | |||
if (!instance) return; | |||
var filterValues = instance.state.activeFilters[group]; | |||
if (filterValues) { | |||
var index = filterValues.indexOf(value); | |||
if (index !== -1) { | |||
filterValues.splice(index, 1); | |||
// 更新UI | |||
instance.$container.find('.filter-option[data-filter-group="' + group + '"][data-filter-value="' + value + '"]') | |||
.removeClass('filter-selected'); | |||
// 如果清空了,选中"全部" | |||
if (filterValues.length === 0) { | |||
instance.$container.find('.filter-option[data-filter-group="' + group + '"][data-filter-value="all"]') | |||
.addClass('filter-selected'); | |||
} | |||
// 应用筛选 | |||
FilterManager.applyFilters(instance); | |||
} | |||
} | |||
}, | |||
// 更新活动筛选标签 | |||
updateActiveFilters: function(instance) { | |||
if (!instance) return; | |||
var $activeFilters = instance.$container.find('.active-filters'); | |||
var $activeFiltersList = instance.$container.find('.active-filters-list'); | |||
$activeFiltersList.empty(); | |||
var hasActiveFilters = false; | |||
for (var group in instance.state.activeFilters) { | |||
var filterValues = instance.state.activeFilters[group]; | |||
filterValues.forEach(function(value) { | |||
if (value === 'all') return; | |||
var $option = instance.$container.find('.filter-option[data-filter-group="' + group + '"][data-filter-value="' + value + '"]'); | |||
if (!$option.length) return; | |||
var displayText = $option.data('filter-display') || value; | |||
var $groupHeader = instance.$container.find('.filter-group[data-filter-group="' + group + '"] h4'); | |||
var groupName = $groupHeader.length ? $groupHeader.text().replace(' 清除', '') : group; | |||
var $tag = $('<span class="active-filter-tag"></span>') | |||
.attr('data-filter-group', group) | |||
.attr('data-filter-value', value) | |||
.html( | |||
'<span class="active-filter-text">' + groupName + ': ' + displayText + '</span>' + | |||
'<span class="active-filter-remove" tabindex="0" role="button">×</span>' | |||
); | |||
$activeFiltersList.append($tag); | |||
hasActiveFilters = true; | |||
}); | |||
} | |||
if (hasActiveFilters) { | |||
$activeFilters.show(); | |||
} else { | |||
$activeFilters.hide(); | |||
} | |||
}, | |||
// 更新统计信息 | |||
updateStats: function(instance, matched, total) { | |||
if (!instance) return; | |||
var $stats = instance.$container.find('.filter-stats'); | |||
if ($stats.length) { | |||
$stats.find('.filter-total').text(total); | |||
$stats.find('.filter-showing').text(matched); | |||
if (matched !== total) { | |||
$stats.show(); | |||
} else { | |||
$stats.hide(); | |||
} | |||
} | |||
} | |||
}; | |||
// 页面加载完成后初始化 | |||
$(document).ready(function() { | |||
// FilterManager.initAll(); | |||
// 监听页面内容动态加载 | |||
mw.hook('wikipage.content').add(function($content) { | |||
FilterManager.initAll(); | |||
}); | |||
}); | |||
// // 公开API(可选) | |||
// window.MediaWikiFilter = { | |||
// getInstance: function(filterId) { | |||
// return FilterManager.instances[filterId]; | |||
// }, | |||
// apply: function(filterId) { | |||
// var instance = FilterManager.instances[filterId]; | |||
// if (instance) { | |||
// FilterManager.applyFilters(instance); | |||
// } | |||
// }, | |||
// reset: function(filterId) { | |||
// var instance = FilterManager.instances[filterId]; | |||
// if (instance) { | |||
// FilterManager.resetFilters(instance); | |||
// } | |||
// } | |||
// }; | |||
2026年6月4日 (四) 10:20的最新版本
/* 这里的任何JavaScript将为所有用户在每次页面加载时加载。 */
// =============================================
// 动态导航菜单系统
// =============================================
// 导航菜单数据
var menuJsonData = [
{
"name": "首页",
"link": "首页",
"children": []
},
{
"name": "旅人图鉴",
"link": "旅人图鉴",
"children": []
},
{
"name": "秘纹图鉴",
"link": "秘纹图鉴",
"children": []
},
{
"name": "特殊页面",
"link": "特殊:特殊页面",
"children": []
}
];
// 导航菜单管理器
var NavigationMenu = {
// 初始化所有导航菜单
initAll: function() {
// 确保容器存在
NavigationMenu.ensureContainerExists();
// 初始化容器
$('.custom-navigation-container').each(function() {
NavigationMenu.initContainer($(this));
});
},
// 确保容器存在,如果不存在则在.header-nav-inner头部插入
ensureContainerExists: function() {
// 如果容器已存在,直接返回
if ($('.custom-navigation-container').length > 0) {
return;
}
// 查找.header-nav-inner元素
var $headerNavInner = $('.header-nav-inner');
if ($headerNavInner.length === 0) {
console.warn('找不到.header-nav-inner元素,无法插入导航菜单');
return;
}
// 创建导航容器
var $container = $('<div>', {
class: 'custom-navigation-container',
id: 'custom-navigation-menu'
});
// 插入到.header-nav-inner的头部
$headerNavInner.prepend($container);
},
// 初始化单个容器
initContainer: function($container) {
// 防止重复初始化
if ($container.data('initialized')) {
return;
}
$container.data('initialized', true);
// 加载导航数据
NavigationMenu.loadNavData()
.then(function(navData) {
// 构建导航菜单
NavigationMenu.buildNavigation($container, navData);
})
.catch(function(error) {
console.error('加载导航数据失败:', error);
NavigationMenu.showError($container, error);
});
},
// 加载导航数据
loadNavData: function() {
return new Promise(function(resolve, reject) {
resolve(menuJsonData);
});
},
// 构建导航菜单
buildNavigation: function($container, navData) {
// 创建导航菜单HTML
var navHTML = NavigationMenu.createNavHTML(navData);
$container.html(navHTML);
// 绑定事件
NavigationMenu.bindNavEvents($container);
// 添加响应式菜单按钮(移动端)
NavigationMenu.addMobileMenuButton($container);
// 初始化当前页面高亮
NavigationMenu.highlightCurrentPage($container);
},
// 创建导航HTML(递归)
createNavHTML: function(menuItems, level) {
level = level || 1;
if (!menuItems || menuItems.length === 0) {
return '';
}
var ulClass = level === 1 ? 'nav-menu-primary' : 'nav-menu-sub nav-menu-level-' + level;
var html = '<ul class="' + ulClass + '">';
menuItems.forEach(function(item, index) {
var hasChildren = item.children && item.children.length > 0;
var liClass = 'nav-item';
var aClass = 'nav-link';
var submenuHTML = '';
if (hasChildren) {
liClass += ' has-children';
aClass += ' has-children-link';
submenuHTML = NavigationMenu.createNavHTML(item.children, level + 1);
}
// 构建链接
var href = item.link ? mw.util.getUrl(item.link) : '#';
var iconHTML = item.icon ? '<span class="nav-icon">' + item.icon + '</span>' : '';
var nameHTML = '<span class="nav-text">' + item.name + '</span>';
var arrowHTML = hasChildren ? '<span class="nav-arrow">▶</span>' : '';
html += '<li class="' + liClass + '" data-link="' + (item.link || '') + '">';
html += '<a href="' + href + '" class="' + aClass + '" title="' + (item.name || '') + '">';
html += iconHTML + nameHTML + arrowHTML;
html += '</a>';
html += submenuHTML;
html += '</li>';
// 在一级菜单项之间添加分隔符(除了最后一个)
if (level === 1 && index < menuItems.length - 1) {
html += '<span class="navigation-separator"></span>';
}
});
html += '</ul>';
return html;
},
// 绑定导航事件
bindNavEvents: function($container) {
// 用于存储菜单的计时器
var menuTimers = {};
// 子菜单切换(桌面端悬停,移动端点击)
if (window.innerWidth >= 768) {
// 桌面端:悬停显示子菜单
$container.on('mouseenter', '.nav-item.has-children', function(e) {
var $this = $(this);
var menuId = $this.data('link') || $this.index();
// 清除可能存在的关闭计时器
if (menuTimers[menuId]) {
clearTimeout(menuTimers[menuId]);
delete menuTimers[menuId];
}
// 关闭其他打开的菜单
$container.find('.nav-item.hover').not($this).removeClass('hover');
$container.find('.nav-menu-sub.active').not($this.find('.nav-menu-sub')).removeClass('active');
// 显示当前菜单
$this.addClass('hover');
$this.find('.nav-menu-sub').first().addClass('active');
});
$container.on('mouseleave', '.nav-item.has-children', function(e) {
var $this = $(this);
var menuId = $this.data('link') || $this.index();
var $submenu = $this.find('.nav-menu-sub').first();
// 设置延迟关闭计时器
menuTimers[menuId] = setTimeout(function() {
$this.removeClass('hover');
$submenu.removeClass('active');
delete menuTimers[menuId];
}, 300); // 300ms延迟,避免鼠标短暂离开时立即关闭
});
// 当鼠标进入子菜单时,清除关闭计时器
$container.on('mouseenter', '.nav-menu-sub', function(e) {
var $submenu = $(this);
var $parent = $submenu.closest('.nav-item');
var menuId = $parent.data('link') || $parent.index();
// 清除父菜单的关闭计时器
if (menuTimers[menuId]) {
clearTimeout(menuTimers[menuId]);
delete menuTimers[menuId];
}
});
// 当鼠标离开子菜单时,设置关闭计时器
$container.on('mouseleave', '.nav-menu-sub', function(e) {
var $submenu = $(this);
var $parent = $submenu.closest('.nav-item');
var menuId = $parent.data('link') || $parent.index();
// 设置延迟关闭计时器
menuTimers[menuId] = setTimeout(function() {
$parent.removeClass('hover');
$submenu.removeClass('active');
delete menuTimers[menuId];
}, 300);
});
} else {
// 移动端:点击切换子菜单
$container.on('click', '.nav-link.has-children-link', function(e) {
e.preventDefault();
e.stopPropagation();
var $link = $(this);
var $parent = $link.closest('.nav-item');
var $submenu = $parent.find('.nav-menu-sub').first();
if ($submenu.hasClass('active')) {
$submenu.removeClass('active');
$parent.removeClass('expanded');
} else {
// 关闭其他打开的菜单
$container.find('.nav-menu-sub.active').removeClass('active');
$container.find('.nav-item.expanded').removeClass('expanded');
$submenu.addClass('active');
$parent.addClass('expanded');
}
});
}
// 点击外部关闭菜单
$(document).on('click touchstart', function(e) {
if (!$(e.target).closest('.custom-navigation-container').length) {
$('.custom-navigation-container').find('.nav-menu-sub.active').removeClass('active');
$('.custom-navigation-container').find('.nav-item.expanded').removeClass('expanded');
$('.custom-navigation-container').find('.nav-item.hover').removeClass('hover');
// 清除所有计时器
for (var timerId in menuTimers) {
if (menuTimers.hasOwnProperty(timerId)) {
clearTimeout(menuTimers[timerId]);
}
}
menuTimers = {};
}
});
},
// 添加移动端菜单按钮
addMobileMenuButton: function($container) {
var $button = $('<button>', {
class: 'nav-mobile-toggle',
html: '<span class="nav-toggle-icon">☰</span>',
click: function() {
$container.toggleClass('mobile-open');
$(this).toggleClass('active');
}
});
$container.prepend($button);
},
// 高亮当前页面
highlightCurrentPage: function($container) {
var currentPage = mw.config.get('wgPageName');
if (!currentPage) return;
// 解码页面名称
currentPage = decodeURIComponent(currentPage);
// 查找匹配的导航项
$container.find('.nav-item[data-link]').each(function() {
var link = $(this).data('link');
if (link && link === currentPage) {
$(this).addClass('current-page');
$(this).find('.nav-link').first().addClass('current');
// 展开父级菜单
$(this).parents('.nav-item.has-children').addClass('expanded');
$(this).parents('.nav-menu-sub').addClass('active');
}
});
},
// 显示错误信息
showError: function($container, error) {
$container.html(
'<div class="nav-error">' +
' <div class="error-icon">⚠️</div>' +
' <div class="error-title">导航菜单加载失败</div>' +
' <div class="error-message">' + error + '</div>' +
' <button class="error-retry" onclick="window.location.reload()">重试</button>' +
'</div>'
);
}
};
// 添加导航菜单样式
function addNavigationStyles() {
if ($('#navigation-styles').length) {
return;
}
var styles = `
<style id="navigation-styles">
/* 导航容器 */
.custom-navigation-container {
flex: 1;
position: relative;
background: #4b5082;
border-radius: 8px;
font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
z-index: 1000;
}
/* 分隔符样式 */
.navigation-separator {
display: inline-block;
width: 1px;
height: 20px;
background-color: rgba(255, 255, 255, 0.2);
margin: 0 5px;
vertical-align: middle;
}
/* 错误状态 */
.nav-error {
text-align: center;
padding: 30px;
color: #ecf0f1;
}
.error-icon {
font-size: 40px;
margin-bottom: 15px;
}
.error-title {
font-size: 18px;
font-weight: 600;
margin-bottom: 10px;
color: #e74c3c;
}
.error-message {
font-size: 14px;
margin-bottom: 20px;
opacity: 0.8;
}
.error-retry {
padding: 8px 20px;
background: #3498db;
color: white;
border: none;
border-radius: 4px;
cursor: pointer;
font-size: 14px;
transition: background-color 0.3s;
}
.error-retry:hover {
background: #2980b9;
}
/* 移动端菜单按钮 */
.nav-mobile-toggle {
display: none;
position: absolute;
top: 10px;
right: 10px;
background: transparent;
border: none;
color: white;
font-size: 24px;
cursor: pointer;
z-index: 1001;
padding: 5px 10px;
border-radius: 4px;
}
/* 主菜单 */
.nav-menu-primary {
display: flex;
margin: 0;
padding: 0;
list-style: none;
align-items: center;
}
/* 导航项 */
.nav-item {
position: relative;
margin: 0;
padding: 0;
}
.nav-item > .nav-link {
display: flex;
align-items: center;
gap: 8px;
padding: 5px 20px;
color: #ecf0f1;
text-decoration: none !important;
font-size: 15px;
font-weight: 500;
transition: all 0.3s ease;
white-space: nowrap;
}
.nav-link:hover {
background: rgba(255, 255, 255, 0.1);
color: #3498db;
}
.nav-link.current {
color: #3498db;
font-weight: 600;
}
.nav-item.current-page > .nav-link {
color: #3498db;
font-weight: 600;
}
.nav-icon {
font-size: 16px;
line-height: 1;
}
.nav-text {
flex: 1;
}
.nav-arrow {
font-size: 10px;
margin-left: 5px;
transition: transform 0.3s;
}
.nav-item.expanded > .nav-link > .nav-arrow {
transform: rotate(90deg);
}
/* 子菜单 */
.nav-menu-sub {
display: none;
position: absolute;
top: 100%;
left: 0;
min-width: 220px;
background: white;
border-radius: 0 0 8px 8px;
box-shadow: 0 6px 20px rgba(0, 0, 0, 0.2);
list-style: none;
margin: 0;
padding: 8px 0;
z-index: 1000;
}
.nav-menu-sub.active {
display: block;
}
.nav-menu-level-2 {
background: #505587;
}
.nav-menu-level-3 {
background: #2c3e50;
}
/* 子菜单项 */
.nav-menu-sub .nav-item {
border-bottom: 1px solid rgba(255, 255, 255, 0.05);
}
.nav-menu-sub .nav-item:last-child {
border-bottom: none;
}
.nav-menu-sub .nav-link {
padding: 8px 20px;
color: #ecf0f1;
font-size: 14px;
}
.nav-menu-sub .nav-link:hover {
background: rgba(255, 255, 255, 0.1);
color: #3498db;
}
/* 三级菜单位置 */
.nav-menu-level-2 .nav-item.has-children > .nav-menu-sub {
top: 0;
left: 100%;
border-radius: 0 8px 8px 8px;
}
/* 响应式设计 */
@media (max-width: 767px) {
.custom-navigation-container {
border-radius: 0;
box-shadow: none;
}
.navigation-separator {
display: none;
}
.nav-mobile-toggle {
display: block;
}
.nav-menu-primary {
display: none;
flex-direction: column;
padding: 10px 0;
}
.custom-navigation-container.mobile-open .nav-menu-primary {
display: flex;
}
.nav-menu-sub {
position: static;
display: none;
background: rgba(0, 0, 0, 0.2);
box-shadow: none;
padding-left: 20px;
margin: 0;
border-radius: 0;
}
.nav-menu-sub.active {
display: block;
}
.nav-menu-level-2 .nav-item.has-children > .nav-menu-sub {
position: static;
left: auto;
border-radius: 0;
}
.nav-link {
padding: 12px 20px;
}
.nav-item.has-children > .nav-link {
padding-right: 40px;
}
}
@media (min-width: 768px) {
.nav-item:hover > .nav-menu-sub {
display: block;
}
.nav-menu-sub {
animation: fadeInUp 0.3s ease;
}
@keyframes fadeInUp {
from {
opacity: 0;
transform: translateY(10px);
}
to {
opacity: 1;
transform: translateY(0);
}
}
}
</style>
`;
$('head').append(styles);
}
// 页面加载完成后初始化
$(document).ready(function() {
if (!$('.header-nav-inner').length && !$('.custom-navigation-container').length) {
return; // NovaSkin 已有新导航,旧导航脚本仅在旧页面结构存在时启用
}
// 添加样式
addNavigationStyles();
// 初始化导航菜单
NavigationMenu.initAll();
// 监听页面内容动态加载
mw.hook('wikipage.content').add(function($content) {
// 重新确保容器存在并初始化
NavigationMenu.ensureContainerExists();
NavigationMenu.initAll();
});
// 窗口大小变化时重新绑定事件
$(window).on('resize', function() {
// 移除所有事件,重新绑定
$('.custom-navigation-container').off();
NavigationMenu.initAll();
});
});
// 全局函数:重新加载导航菜单(可用于调试)
window.reloadNavigation = function() {
// 确保容器存在
NavigationMenu.ensureContainerExists();
$('.custom-navigation-container').each(function() {
$(this).removeData('initialized').empty();
NavigationMenu.initContainer($(this));
});
};
// 全局函数:手动插入导航菜单到指定位置
window.insertNavigationTo = function(selector) {
// 移除现有的导航容器
$('.custom-navigation-container').remove();
// 在指定位置插入新容器
var $target = $(selector);
if ($target.length === 0) {
console.error('找不到目标元素: ' + selector);
return false;
}
var $container = $('<div>', {
class: 'custom-navigation-container',
id: 'custom-navigation-menu'
});
$target.prepend($container);
// 重新初始化导航菜单
reloadNavigation();
return true;
};
// =============================================
// 角色属性查看器 - 动态创建版本
// =============================================
// 角色数据管理器
var CharacterDataManager = {
cache: {},
getCharacterData: function(characterId) {
var self = this;
// 检查缓存
if (self.cache[characterId]) {
return Promise.resolve(self.cache[characterId]);
}
return new Promise(function(resolve, reject) {
// 构建数据页面路径
var dataSource = 'Data:Character/' + characterId + '.json';
$.get("http://trekkers-wiki.jvav.net.cn/index.php", {
action: 'raw',
title: dataSource
}).done(function(data) {
try {
var characterData = JSON.parse(data);
// 缓存数据
self.cache[characterId] = characterData;
resolve(characterData);
} catch (e) {
reject('解析JSON数据失败: ' + e.message);
}
}).fail(function(error) {
reject('API请求失败: ' + error);
});
});
},
clearCache: function() {
this.cache = {};
}
};
// 主初始化函数
$(document).ready(function() {
var $initialContainers = $('.character-stats-container');
if ($initialContainers.length) {
addCharacterStatsStyles();
$initialContainers.each(function() {
createCharacterStatsWidget($(this));
});
}
mw.hook('wikipage.content').add(function($content) {
var $containers = $content.find('.character-stats-container');
if ($content.is('.character-stats-container')) {
$containers = $containers.add($content);
}
if (!$containers.length) {
return;
}
addCharacterStatsStyles();
$containers.each(function() {
if (!$(this).find('.character-stats-widget').length) {
createCharacterStatsWidget($(this));
}
});
});
});
// 添加样式到页面
function addCharacterStatsStyles() {
if ($('#character-stats-styles').length) {
return; // 样式已存在
}
if (!$('.character-stats-container').length && !$('.character-stats-widget').length) {
return; // 只在旧角色属性查看器实际存在时注入样式
}
var styles = `
<style id="character-stats-styles">
.character-stats-widget {
max-width: 800px;
margin: 0 auto;
background-color: #ffffff;
border-radius: 8px;
box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1);
padding: 30px;
font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
}
.character-header {
text-align: center;
margin-bottom: 20px;
}
.character-name {
color: #4a6fa5;
margin-bottom: 10px;
font-size: 28px;
}
.character-description {
color: #666;
font-style: italic;
font-size: 16px;
}
.loading-message, .error-message {
text-align: center;
padding: 20px;
color: #666;
font-size: 16px;
}
.error-message {
color: #d32f2f;
background-color: #ffebee;
border-radius: 4px;
border: 1px solid #f5c6cb;
}
.level-selector {
margin-bottom: 30px;
}
.level-display {
text-align: center;
font-size: 24px;
font-weight: bold;
margin-bottom: 15px;
color: #4a6fa5;
}
.slider-container {
position: relative;
margin-bottom: 10px;
}
.slider-container .level-slider {
width: 100%;
height: 10px;
-webkit-appearance: none;
appearance: none;
background: linear-gradient(to right, #e0e0e0, #4a6fa5);
outline: none;
border-radius: 5px;
cursor: pointer;
}
.slider-container .level-slider::-webkit-slider-thumb {
-webkit-appearance: none;
appearance: none;
width: 24px;
height: 24px;
border-radius: 50%;
background: #4a6fa5;
cursor: pointer;
border: 3px solid white;
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.2);
transition: all 0.2s;
}
.slider-container .level-slider::-webkit-slider-thumb:hover {
background: #3a5a80;
transform: scale(1.1);
}
.slider-container .level-slider::-moz-range-thumb {
width: 24px;
height: 24px;
border-radius: 50%;
background: #4a6fa5;
cursor: pointer;
border: 3px solid white;
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.2);
}
.quick-buttons {
display: flex;
justify-content: space-between;
flex-wrap: wrap;
gap: 5px;
margin-top: 10px;
}
.level-btn {
flex: 1;
min-width: 60px;
padding: 10px 5px;
background-color: #e9ecef;
border: none;
border-radius: 6px;
cursor: pointer;
font-weight: bold;
transition: all 0.2s;
font-size: 14px;
}
.level-btn:hover {
background-color: #d0d8e2;
transform: translateY(-2px);
}
.level-btn.active {
background-color: #4a6fa5;
color: white;
box-shadow: 0 2px 4px rgba(74, 111, 165, 0.3);
}
.stats-container {
display: grid;
grid-template-columns: 1fr 1fr;
gap: 20px;
margin-top: 20px;
}
.stat-card {
background-color: white;
border-radius: 8px;
padding: 20px;
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.05);
border-left: 4px solid #4a6fa5;
transition: transform 0.2s;
}
.stat-card:hover {
transform: translateY(-2px);
box-shadow: 0 4px 8px rgba(0, 0, 0, 0.1);
}
.stat-card.attack {
border-left-color: #ff6b6b;
}
.stat-card.health {
border-left-color: #4caf50;
}
.stat-title {
font-size: 18px;
margin-bottom: 10px;
display: flex;
align-items: center;
gap: 8px;
font-weight: 600;
}
.stat-card .stat-value {
font-size: 32px;
font-weight: bold;
margin-bottom: 15px;
color: #333;
}
.stat-card .attack .stat-value {
color: #ff6b6b;
}
.stat-card .health .stat-value {
color: #4caf50;
}
.stat-progress {
height: 12px;
background-color: #e9ecef;
border-radius: 6px;
overflow: hidden;
margin-bottom: 10px;
}
.stat-bar {
height: 100%;
border-radius: 6px;
width: 0%;
transition: width 0.5s ease;
}
.attack .stat-bar {
background: linear-gradient(90deg, #ff6b6b, #ff8e8e);
}
.health .stat-bar {
background: linear-gradient(90deg, #4caf50, #66bb6a);
}
.stat-growth {
margin-top: 10px;
font-size: 14px;
color: #666;
font-style: italic;
}
@media (max-width: 768px) {
.character-stats-widget {
padding: 20px;
margin: 10px;
}
.stats-container {
grid-template-columns: 1fr;
gap: 15px;
}
.quick-buttons {
gap: 3px;
}
.level-btn {
min-width: 50px;
padding: 8px 3px;
font-size: 12px;
}
.character-name {
font-size: 24px;
}
.stat-card .stat-value {
font-size: 28px;
}
}
@media (max-width: 480px) {
.character-stats-widget {
padding: 15px;
}
.level-btn {
min-width: 40px;
padding: 6px 2px;
font-size: 11px;
}
.quick-buttons {
gap: 2px;
}
}
</style>
`;
$('head').append(styles);
}
// 创建角色属性查看器
function createCharacterStatsWidget($container) {
var characterId = $container.data('character');
if (!characterId) {
$container.html('<div class="error-message">错误:未指定角色ID</div>');
return;
}
// 创建 Widget HTML 结构
var widgetHTML = `
<div class="character-stats-widget">
<div class="character-header">
<h2 class="character-name">加载中...</h2>
<p class="character-description"></p>
</div>
<div class="level-selector">
<div class="level-display">当前等级: <span class="current-level">1</span></div>
<div class="slider-container">
<input type="range" min="1" max="90" value="1" class="level-slider">
</div>
<div class="quick-buttons">
<button class="level-btn" data-level="1">1级</button>
<button class="level-btn" data-level="10">10级</button>
<button class="level-btn" data-level="20">20级</button>
<button class="level-btn" data-level="30">30级</button>
<button class="level-btn" data-level="40">40级</button>
<button class="level-btn" data-level="50">50级</button>
<button class="level-btn" data-level="60">60级</button>
<button class="level-btn" data-level="70">70级</button>
<button class="level-btn" data-level="80">80级</button>
<button class="level-btn" data-level="90">90级</button>
</div>
</div>
<div class="stats-container">
<div class="stat-card attack">
<div class="stat-title">
<span>⚔️ 攻击力</span>
</div>
<div class="stat-value attack-value">-</div>
<div class="stat-progress">
<div class="stat-bar attack-bar"></div>
</div>
<div class="stat-growth">每级成长: <span class="attack-growth">-</span></div>
</div>
<div class="stat-card health">
<div class="stat-title">
<span>❤️ 生命值</span>
</div>
<div class="stat-value health-value">-</div>
<div class="stat-progress">
<div class="stat-bar health-bar"></div>
</div>
<div class="stat-growth">每级成长: <span class="health-growth">-</span></div>
</div>
</div>
<div class="loading-message">正在加载角色数据...</div>
<div class="error-message" style="display: none;"></div>
</div>
`;
// 插入 HTML
$container.html(widgetHTML);
// 显示加载状态
var $widget = $container.find('.character-stats-widget');
$widget.find('.level-selector, .stats-container').hide();
$widget.find('.loading-message').show();
// 加载角色数据并初始化
CharacterDataManager.getCharacterData(characterId)
.then(function(characterData) {
initCharacterStats($widget, characterData);
$widget.find('.loading-message').hide();
$widget.find('.level-selector, .stats-container').show();
})
.catch(function(error) {
console.error('加载角色数据失败:', error);
$widget.find('.loading-message').hide();
$widget.find('.error-message').text('加载角色数据失败: ' + error).show();
});
}
// 初始化角色属性显示
function initCharacterStats($widget, characterData) {
// 防止重复初始化
if ($widget.data('initialized')) {
return;
}
$widget.data('initialized', true);
// 更新角色信息
$widget.find('.character-name').text(characterData.name);
$widget.find('.character-description').text(characterData.description);
// 生成完整的等级数据
var fullLevelData = generateFullLevelData(characterData);
// 获取UI元素
var $levelSlider = $widget.find('.level-slider');
var $currentLevel = $widget.find('.current-level');
var $attackValue = $widget.find('.attack-value');
var $healthValue = $widget.find('.health-value');
var $attackBar = $widget.find('.attack-bar');
var $healthBar = $widget.find('.health-bar');
var $attackGrowth = $widget.find('.attack-growth');
var $healthGrowth = $widget.find('.health-growth');
var $quickButtons = $widget.find('.level-btn');
// 更新显示函数
function updateDisplay(level) {
var stats = fullLevelData[level];
$currentLevel.text(level);
$attackValue.text(formatNumber(stats.attack));
$healthValue.text(formatNumber(stats.health));
// 显示成长值
if (characterData.stats) {
$attackGrowth.text('+' + (characterData.stats.attack_growth || '?'));
$healthGrowth.text('+' + (characterData.stats.health_growth || '?'));
}
// 更新进度条宽度 (基于最大值的百分比)
var maxAttack = fullLevelData[90].attack;
var maxHealth = fullLevelData[90].health;
$attackBar.css('width', (stats.attack / maxAttack) * 100 + '%');
$healthBar.css('width', (stats.health / maxHealth) * 100 + '%');
// 更新快速按钮激活状态
$quickButtons.removeClass('active');
$quickButtons.filter('[data-level="' + level + '"]').addClass('active');
}
// 事件绑定
$levelSlider.on('input', function() {
updateDisplay(parseInt(this.value));
});
$quickButtons.on('click', function() {
var level = parseInt($(this).data('level'));
$levelSlider.val(level);
updateDisplay(level);
});
// 添加键盘支持
$levelSlider.on('keydown', function(e) {
var currentValue = parseInt(this.value);
switch(e.key) {
case 'ArrowLeft':
case 'ArrowDown':
e.preventDefault();
this.value = Math.max(1, currentValue - 1);
updateDisplay(this.value);
break;
case 'ArrowRight':
case 'ArrowUp':
e.preventDefault();
this.value = Math.min(90, currentValue + 1);
updateDisplay(this.value);
break;
case 'Home':
e.preventDefault();
this.value = 1;
updateDisplay(this.value);
break;
case 'End':
e.preventDefault();
this.value = 90;
updateDisplay(this.value);
break;
}
});
// 初始显示
updateDisplay(1);
}
// 生成完整等级数据
function generateFullLevelData(characterData) {
var fullData = {};
var baseAttack = (characterData.stats && characterData.stats.base_attack) || 100;
var baseHealth = (characterData.stats && characterData.stats.base_health) || 1000;
var attackGrowth = (characterData.stats && characterData.stats.attack_growth) || 25;
var healthGrowth = (characterData.stats && characterData.stats.health_growth) || 300;
// 生成1-90级的完整数据
for (var level = 1; level <= 90; level++) {
// 如果有预定义的数据,使用预定义数据,否则计算
if (characterData.levels && characterData.levels[level]) {
fullData[level] = characterData.levels[level];
} else {
fullData[level] = {
attack: baseAttack + attackGrowth * (level - 1),
health: baseHealth + healthGrowth * (level - 1)
};
}
}
return fullData;
}
// 数字格式化函数
function formatNumber(num) {
if (num >= 1000000) {
return (num / 1000000).toFixed(1) + 'M';
} else if (num >= 1000) {
return (num / 1000).toFixed(1) + 'K';
}
return num.toString().replace(/\B(?=(\d{3})+(?!\d))/g, ",");
}
// =============================================
// 赛季BOSS展示页面功能模块
// =============================================
// 赛季BOSS页面初始化器
var SeasonBossPage = {
// 初始化所有赛季BOSS页面
initAll: function() {
$('.season-boss-page').each(function() {
SeasonBossPage.initPage($(this));
});
},
// 初始化单个页面
initPage: function($page) {
// 防止重复初始化
if ($page.data('page-initialized') === 'true') {
return;
}
$page.data('page-initialized', 'true');
// 绑定标签切换事件
SeasonBossPage.bindTabEvents($page);
// 初始化第一个可见的赛季内容
SeasonBossPage.activateFirstTab($page);
},
// 绑定标签切换事件
bindTabEvents: function($page) {
// 使用事件委托处理标签点击
$page.on('click', '.season-tab', function(e) {
e.preventDefault();
var $tab = $(this);
var seasonId = $tab.data('season');
// 如果已经是激活状态,则不处理
if ($tab.hasClass('active')) {
return;
}
// 切换标签和内容
SeasonBossPage.switchSeason($page, seasonId);
});
// 添加键盘支持
$page.on('keydown', '.season-tab', function(e) {
if (e.key === 'Enter' || e.key === ' ') {
e.preventDefault();
$(this).trigger('click');
}
});
},
// 切换赛季显示
switchSeason: function($page, seasonId) {
// 移除所有激活状态
$page.find('.season-tab').removeClass('active');
$page.find('.season-content').removeClass('active');
// 添加新的激活状态
$page.find('.season-tab[data-season="' + seasonId + '"]').addClass('active');
$page.find('#' + seasonId).addClass('active');
// 触发自定义事件,以便其他模块可以监听赛季切换
$(document).trigger('seasonboss:seasonchanged', {
seasonId: seasonId,
page: $page
});
// 记录切换日志(仅开发时使用)
if (console && console.log) {
console.log('赛季切换至: ' + seasonId);
}
},
// 激活第一个标签(如果当前没有激活的标签)
activateFirstTab: function($page) {
var $activeTab = $page.find('.season-tab.active');
// 如果没有激活的标签,激活第一个
if ($activeTab.length === 0) {
var $firstTab = $page.find('.season-tab').first();
var seasonId = $firstTab.data('season');
$firstTab.addClass('active');
$page.find('#' + seasonId).addClass('active');
}
},
// 添加新赛季(动态添加功能)
addSeason: function($page, seasonData) {
// 参数检查
if (!seasonData || !seasonData.id || !seasonData.name) {
console.error('添加赛季失败:缺少必要的赛季数据');
return false;
}
var seasonId = seasonData.id;
var seasonName = seasonData.name;
var seasonIcon = seasonData.icon || '🏅';
// 检查赛季是否已存在
if ($page.find('.season-tab[data-season="' + seasonId + '"]').length > 0) {
console.warn('赛季已存在:' + seasonId);
return false;
}
// 创建新标签
var $newTab = $('<button>', {
'class': 'season-tab',
'data-season': seasonId,
'html': '<span class="season-icon">' + seasonIcon + '</span>' +
'<span class="season-name">' + seasonName + '</span>'
});
// 添加到标签容器
$page.find('.season-tabs').append($newTab);
// 创建新内容区域
var $newContent = $('<div>', {
'class': 'season-content',
'id': seasonId
});
// 如果提供了内容,添加到内容区域
if (seasonData.content) {
$newContent.html(seasonData.content);
}
// 添加到内容区域
$page.find('.season-content').last().after($newContent);
// 重新绑定事件(新添加的元素需要绑定)
SeasonBossPage.bindTabEvents($page);
return true;
},
// 获取当前激活的赛季ID
getActiveSeason: function($page) {
var $activeTab = $page.find('.season-tab.active');
return $activeTab.length > 0 ? $activeTab.data('season') : null;
},
// 跳转到指定赛季
goToSeason: function($page, seasonId) {
var $targetTab = $page.find('.season-tab[data-season="' + seasonId + '"]');
if ($targetTab.length === 0) {
console.error('赛季不存在:' + seasonId);
return false;
}
SeasonBossPage.switchSeason($page, seasonId);
return true;
}
};
// 页面加载完成后初始化赛季BOSS页面
$(document).ready(function() {
SeasonBossPage.initAll();
// 监听内容动态加载(例如通过Widgets或AJAX)
$(document).on('seasonboss:contentloaded', function() {
SeasonBossPage.initAll();
});
});
// 当有新内容添加到页面时也初始化
mw.hook('wikipage.content').add(function($content) {
$content.find('.season-boss-page').each(function() {
SeasonBossPage.initPage($(this));
});
});
// =============================================
// 全局可用的函数
// =============================================
// 全局函数:跳转到指定赛季
window.goToSeason = function(seasonId) {
var $page = $('.season-boss-page').first();
if ($page.length > 0) {
return SeasonBossPage.goToSeason($page, seasonId);
}
return false;
};
// 全局函数:获取当前赛季
window.getCurrentSeason = function() {
var $page = $('.season-boss-page').first();
if ($page.length > 0) {
return SeasonBossPage.getActiveSeason($page);
}
return null;
};
// 全局函数:添加新赛季
window.addNewSeason = function(seasonData) {
var $page = $('.season-boss-page').first();
if ($page.length > 0) {
return SeasonBossPage.addSeason($page, seasonData);
}
return false;
};
// =============================================
// 筛选系统
// =============================================
// 筛选器管理器
var FilterManager = {
instances: {},
defaults: {
autoApply: false
},
// 初始化所有筛选器
initAll: function() {
// 查找所有筛选器容器
$('.filter-container').each(function() {
var filterId = $(this).attr('id');
if (filterId) {
FilterManager.init(filterId);
}
});
// 绑定全局事件
FilterManager.bindGlobalEvents();
},
// 初始化单个筛选器
init: function(filterId) {
var $container = $('#' + filterId);
if (!$container.length) return;
// 获取配置
var $config = $container.siblings('.filter-config');
var target = $config.data('filter-target');
var auto = $config.data('filter-auto') === 'true';
if (!target) {
console.warn('筛选器缺少目标选择器:', filterId);
return;
}
// 创建实例
var instance = {
id: filterId,
$container: $container,
$target: $(target),
config: {
autoApply: auto
},
state: {
activeFilters: {},
allOptions: {}
}
};
// 收集筛选组数据
instance.$container.find('.filter-group').each(function() {
var $group = $(this);
var groupName = $group.data('filter-group');
// 收集选项数据
$group.find('.filter-option').each(function() {
var $option = $(this);
var value = $option.data('filter-value');
var display = $option.data('filter-display');
if (!instance.state.allOptions[groupName]) {
instance.state.allOptions[groupName] = {};
}
instance.state.allOptions[groupName][value] = {
$option: $option,
value: value,
display: display,
isSelected: $option.hasClass('filter-selected')
};
// 初始化选中状态
if ($option.hasClass('filter-selected')) {
if (!instance.state.activeFilters[groupName] || value === 'all') {
instance.state.activeFilters[groupName] = [];
}
else if (instance.state.activeFilters[groupName].indexOf(value) === -1) {
instance.state.activeFilters[groupName].push(value);
}
}
});
});
// 隐藏统计和活动筛选(初始状态)
instance.$container.find('.filter-stats').hide();
instance.$container.find('.active-filters').hide();
// 存储实例
FilterManager.instances[filterId] = instance;
// // 初始筛选
// setTimeout(function() {
// FilterManager.applyFilters(instance);
// }, 100);
},
// 绑定全局事件(事件委托)
bindGlobalEvents: function() {
// 筛选项点击
$(document).on('click', '.filter-option', function(e) {
e.preventDefault();
var $option = $(this);
FilterManager.handleOptionClick($option);
});
// 筛选项键盘事件
$(document).on('keydown', '.filter-option', function(e) {
if (e.key === 'Enter' || e.key === ' ') {
e.preventDefault();
var $option = $(this);
FilterManager.handleOptionClick($option);
}
});
// 应用按钮
$(document).on('click', '.filter-apply', function(e) {
e.preventDefault();
var $container = $(this).closest('.filter-container');
var filterId = $container.attr('id');
FilterManager.applyFilters(FilterManager.instances[filterId]);
});
// 重置按钮
$(document).on('click', '.filter-reset', function(e) {
e.preventDefault();
var $container = $(this).closest('.filter-container');
var filterId = $container.attr('id');
FilterManager.resetFilters(FilterManager.instances[filterId]);
});
// 清除组按钮
$(document).on('click', '.filter-group-clear', function(e) {
e.preventDefault();
e.stopPropagation();
var $btn = $(this);
var group = $btn.data('filter-clear');
var $container = $btn.closest('.filter-container');
var filterId = $container.attr('id');
FilterManager.clearGroup(FilterManager.instances[filterId], group);
});
// 移除活动筛选标签
$(document).on('click', '.active-filter-remove', function(e) {
e.preventDefault();
e.stopPropagation();
var $removeBtn = $(this);
var $tag = $removeBtn.closest('.active-filter-tag');
var group = $tag.data('filter-group');
var value = $tag.data('filter-value');
var $container = $tag.closest('.filter-container');
var filterId = $container.attr('id');
FilterManager.removeFilter(FilterManager.instances[filterId], group, value);
});
},
// 处理选项点击
handleOptionClick: function($option) {
var group = $option.data('filter-group');
var value = $option.data('filter-value');
var $container = $option.closest('.filter-container');
var filterId = $container.attr('id');
var instance = FilterManager.instances[filterId];
if (!instance) return;
// 初始化组状态
if (!instance.state.activeFilters[group]) {
instance.state.activeFilters[group] = [];
}
var filterValues = instance.state.activeFilters[group];
var index = filterValues.indexOf(value);
var isSelected = $option.hasClass('filter-selected');
// 处理"全部"选项
if (value === 'all') {
if (!isSelected) {
// 清除同组其他选项
instance.$container.find('.filter-option[data-filter-group="' + group + '"]')
.removeClass('filter-selected');
$option.addClass('filter-selected');
instance.state.activeFilters[group] = [];
// 自动应用
if (instance.config.autoApply) {
FilterManager.applyFilters(instance);
}
}
return;
}
// 移除"全部"选项的选中
instance.$container.find('.filter-option[data-filter-group="' + group + '"][data-filter-value="all"]')
.removeClass('filter-selected');
if (!isSelected) {
// 添加选中
filterValues.push(value);
$option.addClass('filter-selected');
} else {
// 移除选中
filterValues.splice(index, 1);
$option.removeClass('filter-selected');
// 如果清空了,选中"全部"
if (filterValues.length === 0) {
instance.$container.find('.filter-option[data-filter-group="' + group + '"][data-filter-value="all"]')
.addClass('filter-selected');
}
}
// 自动应用
if (instance.config.autoApply) {
FilterManager.applyFilters(instance);
}
},
// 应用筛选
applyFilters: function(instance) {
if (!instance) return;
var matchedCount = 0;
var totalCount = instance.$target.length;
// 遍历所有目标元素
instance.$target.each(function() {
var $item = $(this);
var show = true;
// 检查每个筛选条件
for (var group in instance.state.activeFilters) {
var filterValues = instance.state.activeFilters[group];
if (filterValues.length === 0) continue;
var itemValue = $item.data(group);
if (itemValue === undefined) continue;
// 检查是否匹配
var isMatched = false;
for (var i = 0; i < filterValues.length; i++) {
if (String(itemValue) === String(filterValues[i])) {
isMatched = true;
break;
}
}
if (!isMatched) {
show = false;
break;
}
}
// 更新元素状态
if (show) {
$item.removeClass('filtered-out').addClass('filtered-in');
$item.show();
matchedCount++;
} else {
$item.removeClass('filtered-in').addClass('filtered-out');
$item.hide();
}
});
// 更新UI
FilterManager.updateActiveFilters(instance);
FilterManager.updateStats(instance, matchedCount, totalCount);
},
// 重置筛选
resetFilters: function(instance) {
if (!instance) return;
// 清除所有选中状态
instance.state.activeFilters = {};
// 重置UI
instance.$container.find('.filter-option').removeClass('filter-selected');
instance.$container.find('.filter-option-all').addClass('filter-selected');
// 重新初始化状态
for (var group in instance.state.allOptions) {
instance.state.activeFilters[group] = [];
}
// 隐藏活动筛选标签
instance.$container.find('.active-filters').hide()
.find('.active-filters-list').empty();
// 隐藏统计
instance.$container.find('.filter-stats').hide();
// 应用筛选(显示所有)
FilterManager.applyFilters(instance);
},
// 清除单个组
clearGroup: function(instance, group) {
if (!instance) return;
// 清除该组所有选中
instance.$container.find('.filter-option[data-filter-group="' + group + '"]')
.removeClass('filter-selected');
// 选中"全部"选项
instance.$container.find('.filter-option[data-filter-group="' + group + '"][data-filter-value="all"]')
.addClass('filter-selected');
// 更新状态
instance.state.activeFilters[group] = [];
// 应用筛选
FilterManager.applyFilters(instance);
},
// 移除单个筛选
removeFilter: function(instance, group, value) {
if (!instance) return;
var filterValues = instance.state.activeFilters[group];
if (filterValues) {
var index = filterValues.indexOf(value);
if (index !== -1) {
filterValues.splice(index, 1);
// 更新UI
instance.$container.find('.filter-option[data-filter-group="' + group + '"][data-filter-value="' + value + '"]')
.removeClass('filter-selected');
// 如果清空了,选中"全部"
if (filterValues.length === 0) {
instance.$container.find('.filter-option[data-filter-group="' + group + '"][data-filter-value="all"]')
.addClass('filter-selected');
}
// 应用筛选
FilterManager.applyFilters(instance);
}
}
},
// 更新活动筛选标签
updateActiveFilters: function(instance) {
if (!instance) return;
var $activeFilters = instance.$container.find('.active-filters');
var $activeFiltersList = instance.$container.find('.active-filters-list');
$activeFiltersList.empty();
var hasActiveFilters = false;
for (var group in instance.state.activeFilters) {
var filterValues = instance.state.activeFilters[group];
filterValues.forEach(function(value) {
if (value === 'all') return;
var $option = instance.$container.find('.filter-option[data-filter-group="' + group + '"][data-filter-value="' + value + '"]');
if (!$option.length) return;
var displayText = $option.data('filter-display') || value;
var $groupHeader = instance.$container.find('.filter-group[data-filter-group="' + group + '"] h4');
var groupName = $groupHeader.length ? $groupHeader.text().replace(' 清除', '') : group;
var $tag = $('<span class="active-filter-tag"></span>')
.attr('data-filter-group', group)
.attr('data-filter-value', value)
.html(
'<span class="active-filter-text">' + groupName + ': ' + displayText + '</span>' +
'<span class="active-filter-remove" tabindex="0" role="button">×</span>'
);
$activeFiltersList.append($tag);
hasActiveFilters = true;
});
}
if (hasActiveFilters) {
$activeFilters.show();
} else {
$activeFilters.hide();
}
},
// 更新统计信息
updateStats: function(instance, matched, total) {
if (!instance) return;
var $stats = instance.$container.find('.filter-stats');
if ($stats.length) {
$stats.find('.filter-total').text(total);
$stats.find('.filter-showing').text(matched);
if (matched !== total) {
$stats.show();
} else {
$stats.hide();
}
}
}
};
// 页面加载完成后初始化
$(document).ready(function() {
// FilterManager.initAll();
// 监听页面内容动态加载
mw.hook('wikipage.content').add(function($content) {
FilterManager.initAll();
});
});
// // 公开API(可选)
// window.MediaWikiFilter = {
// getInstance: function(filterId) {
// return FilterManager.instances[filterId];
// },
// apply: function(filterId) {
// var instance = FilterManager.instances[filterId];
// if (instance) {
// FilterManager.applyFilters(instance);
// }
// },
// reset: function(filterId) {
// var instance = FilterManager.instances[filterId];
// if (instance) {
// FilterManager.resetFilters(instance);
// }
// }
// };