MediaWiki:Common.js:修订间差异
添加虾仁AI浮窗助手组件 |
重写自定义侧边栏导航:6大分类,虾仁宇宙动态加载,折叠记忆,每层+号编辑入口 |
||
| 第1行: | 第1行: | ||
/* 虾仁维基 - 自定义侧边栏导航 | /* 虾仁维基 - 自定义侧边栏导航 */ | ||
/* | /* 作者: 虾仁维基团队 */ | ||
/* 最后更新: 2026-07- | /* 最后更新: 2026-07-26 */ | ||
(function ($, mw) { | (function($, mw) { | ||
'use strict'; | 'use strict'; | ||
| 第11行: | 第11行: | ||
function init() { | function init() { | ||
if (!mw.config.get('wgIsArticle') && mw.config.get('wgCanonicalSpecialPageName')) { | |||
return; | return; | ||
} | } | ||
waitForPanel(function($panel) { | |||
buildNav($panel); | |||
}); | }); | ||
} | } | ||
| 第32行: | 第23行: | ||
if ($panel.length) return cb($panel); | if ($panel.length) return cb($panel); | ||
var tries = 0; | var tries = 0; | ||
var timer = setInterval(function () { | var timer = setInterval(function() { | ||
$panel = $('#mw-panel'); | $panel = $('#mw-panel'); | ||
if ($panel.length || ++tries > 50) { | if ($panel.length || ++tries > 50) { | ||
| 第40行: | 第31行: | ||
}, 100); | }, 100); | ||
} | } | ||
/* ========== BUILD ========== */ | |||
function buildNav($panel) { | function buildNav($panel) { | ||
var html = ''; | var html = ''; | ||
html += section('Wiki', [ | html += section('Wiki', [ | ||
| 第60行: | 第48行: | ||
html += section('虾仁简介', [ | html += section('虾仁简介', [ | ||
['虾仁?', ' | ['虾仁?', '虾仁'], | ||
['虾仁共创收录规则', '虾仁共创收录规则'], | ['虾仁共创收录规则', '虾仁共创收录规则'], | ||
['虾仁宇宙结构介绍', '虾仁宇宙结构介绍'] | ['虾仁宇宙结构介绍', '虾仁宇宙结构介绍'] | ||
| 第67行: | 第55行: | ||
html += '<div class="xn-section">'; | html += '<div class="xn-section">'; | ||
html += '<div class="xn-header xn-expandable" data-sid="universe">虾仁宇宙</div>'; | html += '<div class="xn-header xn-expandable" data-sid="universe">虾仁宇宙</div>'; | ||
html += '<div class="xn-body" id="xn-universe"><div class="xn-loading"> | html += '<div class="xn-body" id="xn-universe"><div class="xn-loading">加载中...</div></div>'; | ||
html += '</div>'; | html += '</div>'; | ||
| 第77行: | 第65行: | ||
html += link('虾仁异志', '虾仁异志'); | html += link('虾仁异志', '虾仁异志'); | ||
html += link('虾仁诗作', '虾仁诗作'); | html += link('虾仁诗作', '虾仁诗作'); | ||
html += '<div class="xn-subsection">'; | html += '<div class="xn-subsection">'; | ||
html += '<div class="xn-subheader xn-expandable" data-sid="papers">虾仁论文 ' + plus('虾仁论文') + '</div>'; | html += '<div class="xn-subheader xn-expandable" data-sid="papers">虾仁论文 ' + plus('虾仁论文') + '</div>'; | ||
| 第99行: | 第86行: | ||
restoreCollapse(); | restoreCollapse(); | ||
bindEvents(); | bindEvents(); | ||
loadUniverse(); | loadUniverse(); | ||
} | } | ||
/* ========== HTML HELPERS ========== */ | |||
function section(title, items) { | function section(title, items) { | ||
| 第124行: | 第112行: | ||
return '<a class="xn-plus" href="' + url(page) + '?action=edit" title="编辑/新增">+</a>'; | return '<a class="xn-plus" href="' + url(page) + '?action=edit" title="编辑/新增">+</a>'; | ||
} | } | ||
/* ========== UNIVERSE DYNAMIC LOAD ========== */ | |||
async function loadUniverse() { | async function loadUniverse() { | ||
| 第146行: | 第136行: | ||
}); | }); | ||
subpages = result.query.allpages || []; | subpages = result.query.allpages || []; | ||
} catch (e) { | } catch(e) { | ||
console.error(' | console.error('API error for ' + work, e); | ||
} | } | ||
html += '<div class="xn-work">'; | html += '<div class="xn-work">'; | ||
html += '<div class="xn- | html += '<div class="xn-header xn-expandable" data-sid="' + workSid + '">' + work + '</div>'; | ||
html += '<div class="xn-body" id="xn-' + workSid + '">'; | html += '<div class="xn-body" id="xn-' + workSid + '">'; | ||
// Direct subpages under work | |||
var directSet = {}; | var directSet = {}; | ||
for (var j = 0; j < subpages.length; j++) { | for (var j = 0; j < subpages.length; j++) { | ||
| 第166行: | 第157行: | ||
} | } | ||
// 剧情层 | |||
var plotSegs = []; | var plotSegs = []; | ||
for (j = 0; j < subpages.length; j++) { | for (j = 0; j < subpages.length; j++) { | ||
| 第185行: | 第177行: | ||
var segPage = work + '/剧情层/' + seg; | var segPage = work + '/剧情层/' + seg; | ||
// Fetch 金集 for this segment | |||
var jinji = []; | var jinji = []; | ||
try { | try { | ||
| 第197行: | 第190行: | ||
jinji.push(segPages[m].title.substring(segPage.length + 1)); | jinji.push(segPages[m].title.substring(segPage.length + 1)); | ||
} | } | ||
} catch (e2) {} | } catch(e2) {} | ||
html += '<div class="xn-subsubsection">'; | html += '<div class="xn-subsubsection">'; | ||
| 第210行: | 第203行: | ||
} | } | ||
html += '</div></div>'; | html += '</div></div>'; // close 剧情层 | ||
html += '</div></div>'; | html += '</div></div>'; // close work | ||
} | } | ||
// Add new work link | |||
html += '<div class="xn-link xn-add-work"><a href="' + url('Special:Search') + '" title="搜索已有作品">+ 发现更多作品</a></div>'; | html += '<div class="xn-link xn-add-work"><a href="' + url('Special:Search') + '" title="搜索已有作品">+ 发现更多作品</a></div>'; | ||
| 第219行: | 第213行: | ||
restoreCollapse(); | restoreCollapse(); | ||
bindEvents(); | bindEvents(); | ||
} catch (e) { | } catch(e) { | ||
console.error(' | console.error('Failed to load universe nav', e); | ||
$body.html('<div class="xn-link">(加载失败,请刷新)</div>'); | $body.html('<div class="xn-link">(加载失败,请刷新)</div>'); | ||
} | } | ||
} | } | ||
/* ========== EVENTS & STATE ========== */ | |||
function bindEvents() { | function bindEvents() { | ||
$(document).off('click.xn').on('click.xn', '.xn-expandable', function (e) { | $(document).off('click.xn').on('click.xn', '.xn-expandable', function(e) { | ||
if ($(e.target).closest('.xn-plus').length) return; | if ($(e.target).closest('.xn-plus').length) return; // don't toggle when clicking [+] | ||
var sid = $(this).data('sid'); | var sid = $(this).data('sid'); | ||
var $body = $('#xn-' + sid); | var $body = $('#xn-' + sid); | ||
| 第280行: | 第246行: | ||
} | } | ||
Object.keys(state).forEach(function (sid) { | Object.keys(state).forEach(function(sid) { | ||
var $header = $('[data-sid="' + sid + '"]'); | var $header = $('[data-sid="' + sid + '"]'); | ||
var $body = $('#xn-' + sid); | var $body = $('#xn-' + sid); | ||
| 第298行: | 第264行: | ||
function loadState() { | function loadState() { | ||
try { return JSON.parse(localStorage.getItem(COLLAPSE_KEY) || '{}'); } | try { return JSON.parse(localStorage.getItem(COLLAPSE_KEY) || '{}'); } | ||
catch (e) { return {}; } | catch(e) { return {}; } | ||
} | } | ||
function saveState(s) { | function saveState(s) { | ||
try { localStorage.setItem(COLLAPSE_KEY, JSON.stringify(s)); } | try { localStorage.setItem(COLLAPSE_KEY, JSON.stringify(s)); } | ||
catch (e) {} | catch(e) {} | ||
} | } | ||
/* ========== START ========== */ | |||
mw.loader.using('mediawiki.api').then(function() { | |||
$(init); | |||
}); | }); | ||
})(jQuery, mediaWiki); | |||