MediaWiki:Common.js:修订间差异
来自Mindustry中文wiki
无编辑摘要 |
无编辑摘要 标签:已被回退 |
||
| 第441行: | 第441行: | ||
} | } | ||
}); | }); | ||
})(); | |||
(function () { | |||
'use strict'; | |||
function onReady(fn) { | |||
if (document.readyState === 'loading') { | |||
document.addEventListener('DOMContentLoaded', fn); | |||
} else { | |||
fn(); | |||
} | |||
} | |||
function el(tag, cls) { | |||
var n = document.createElement(tag); | |||
if (cls) n.className = cls; | |||
return n; | |||
} | |||
function svgIcon(pathD) { | |||
var svg = document.createElementNS('http://www.w3.org/2000/svg', 'svg'); | |||
svg.setAttribute('viewBox', '0 0 24 24'); | |||
var p = document.createElementNS('http://www.w3.org/2000/svg', 'path'); | |||
p.setAttribute('d', pathD); | |||
svg.appendChild(p); | |||
return svg; | |||
} | |||
function findPortletUl(idCandidates) { | |||
for (var i = 0; i < idCandidates.length; i++) { | |||
var p = document.getElementById(idCandidates[i]); | |||
if (!p) continue; | |||
var ul = p.querySelector('.mw-portlet-body > ul') || p.querySelector('ul'); | |||
if (ul) return ul; | |||
} | |||
return null; | |||
} | |||
function clonePortlet(title, ul) { | |||
var wrap = el('div', 'mdtDockSection'); | |||
var h = el('div', 'mdtDockSectionTitle'); | |||
h.textContent = title; | |||
wrap.appendChild(h); | |||
var cloned = ul.cloneNode(true); | |||
// 清掉某些 id,避免重复 | |||
cloned.removeAttribute('id'); | |||
wrap.appendChild(cloned); | |||
return wrap; | |||
} | |||
function initDock() { | |||
// 可选:只在手机 / 小屏启用。你想全端都启用就删掉这段判断。 | |||
// if (window.matchMedia && window.matchMedia('(min-width: 980px)').matches) return; | |||
var body = document.body; | |||
if (!body || body.classList.contains('mdtDockEnabled')) return; | |||
body.classList.add('mdtDockEnabled'); | |||
var overlay = el('div', 'mdtDockOverlay'); | |||
overlay.addEventListener('click', function () { | |||
body.classList.remove('mdtDockDrawerOpen'); | |||
}); | |||
var drawer = el('aside', 'mdtDockDrawer'); | |||
var drawerInner = el('div', 'mdtDockDrawerInner'); | |||
drawer.appendChild(drawerInner); | |||
// 把原本的侧边栏内容“克隆”进抽屉 | |||
// Timeless: p-navigation, p-tb | |||
// Vector/others: p-navigation, p-tb 也常见 | |||
var nav = findPortletUl(['p-navigation', 'p-Navigation']); | |||
if (nav) drawerInner.appendChild(clonePortlet('导航', nav)); | |||
var tools = findPortletUl(['p-tb', 'p-Toolbox']); | |||
if (tools) drawerInner.appendChild(clonePortlet('wiki工具', tools)); | |||
// 折叠树:把 "-- " 前缀的条目变成可折叠子项 | |||
function buildNavTree(ulRoot) { | |||
if (!ulRoot) return; | |||
if (ulRoot.getAttribute('data-mdtNav') === '1') return; | |||
ulRoot.setAttribute('data-mdtNav', '1'); | |||
var KEY = 'mdtNavTreeOpen'; | |||
var store = {}; | |||
try { | |||
store = JSON.parse(localStorage.getItem(KEY) || '{}'); | |||
} catch (e) { | |||
store = {}; | |||
} | |||
function getKey(li) { | |||
if (li && li.id) return li.id; | |||
var a = li ? li.querySelector('a') : null; | |||
return a ? (a.getAttribute('href') || '') : ''; | |||
} | |||
function setStored(li, open) { | |||
var k = getKey(li); | |||
if (!k) return; | |||
store[k] = open; | |||
try { | |||
localStorage.setItem(KEY, JSON.stringify(store)); | |||
} catch (e) {} | |||
} | |||
function setOpen(li, open, persist) { | |||
li.classList.toggle('mdtNavOpen', open); | |||
if (persist) setStored(li, open); | |||
} | |||
function ensureGroup(li) { | |||
li.classList.add('mdtNavGroup'); | |||
var sub = li.querySelector('ul.mdtNavSublist'); | |||
if (!sub) { | |||
sub = document.createElement('ul'); | |||
sub.className = 'mdtNavSublist'; | |||
li.appendChild(sub); | |||
} | |||
var btn = li.querySelector('button.mdtNavToggle'); | |||
if (!btn) { | |||
btn = document.createElement('button'); | |||
btn.type = 'button'; | |||
btn.className = 'mdtNavToggle'; | |||
btn.setAttribute('aria-label', '展开/收起'); | |||
btn.setAttribute('aria-expanded', 'false'); | |||
li.insertBefore(btn, sub); | |||
btn.addEventListener('click', function (ev) { | |||
ev.preventDefault(); | |||
ev.stopPropagation(); | |||
var isOpen = li.classList.contains('mdtNavOpen'); | |||
setOpen(li, !isOpen, true); | |||
btn.setAttribute('aria-expanded', String(!isOpen)); | |||
}); | |||
} | |||
var k = getKey(li); | |||
if (store[k] === true) { | |||
li.classList.add('mdtNavOpen'); | |||
btn.setAttribute('aria-expanded', 'true'); | |||
} | |||
return sub; | |||
} | |||
var items = Array.prototype.slice.call(ulRoot.children); | |||
var lastAtDepth = []; | |||
for (var i = 0; i < items.length; i++) { | |||
var li = items[i]; | |||
if (!li || li.tagName !== 'LI') continue; | |||
var a = li.querySelector('a'); | |||
if (!a) continue; | |||
var raw = (a.textContent || '').trim(); | |||
var m = raw.match(/^(-{2,})\s*(.*)$/); | |||
var depth = 0; | |||
if (m) { | |||
depth = Math.min(Math.floor(m[1].length / 2), 3); | |||
a.textContent = m[2]; | |||
} | |||
if (depth > 0 && lastAtDepth[depth - 1]) { | |||
li.classList.add('mdtNavSubItem'); | |||
li.classList.add('mdtNavDepth' + String(depth)); | |||
var sublist = ensureGroup(lastAtDepth[depth - 1]); | |||
sublist.appendChild(li); | |||
} else { | |||
depth = 0; | |||
} | |||
lastAtDepth[depth] = li; | |||
lastAtDepth.length = depth + 1; | |||
} | |||
// 当前页面高亮 | |||
try { | |||
if (typeof mw !== 'undefined' && mw.config) { | |||
var current = (mw.config.get('wgPageName') || '').split(' ').join('_'); | |||
var links = ulRoot.querySelectorAll('a'); | |||
for (var j = 0; j < links.length; j++) { | |||
var href = links[j].getAttribute('href') || ''; | |||
if (!href) continue; | |||
if (href.indexOf('title=') !== -1 && typeof mw.util !== 'undefined') { | |||
var t = mw.util.getParamValue('title', href); | |||
if (t && t.split(' ').join('_') === current) { | |||
links[j].classList.add('mdtDockActiveLink'); | |||
break; | |||
} | |||
} | |||
} | |||
} | |||
} catch (e) {} | |||
} | |||
// 对抽屉中的导航/工具分别构建折叠树 | |||
var drawerSections = drawerInner.querySelectorAll('.mdtDockSection > ul'); | |||
for (var s = 0; s < drawerSections.length; s++) { | |||
buildNavTree(drawerSections[s]); | |||
} | |||
// 你也可以把其它 portlet 加进来,例如: | |||
// var lang = findPortletUl(['p-lang']); | |||
// if (lang) drawerInner.appendChild(clonePortlet('语言', lang)); | |||
// 左侧图标栏 | |||
var dock = el('nav', 'mdtDock'); | |||
// 图标:简单用 SVG path(不依赖字体/图片) | |||
// home | |||
var btnHome = el('a', 'mdtDockBtn'); | |||
btnHome.href = '/'; | |||
btnHome.title = '首页'; | |||
btnHome.appendChild(svgIcon('M10 20v-6h4v6h5v-8h3L12 3 2 12h3v8z')); | |||
dock.appendChild(btnHome); | |||
// search(点击聚焦搜索框) | |||
var btnSearch = el('button', 'mdtDockBtn'); | |||
btnSearch.type = 'button'; | |||
btnSearch.title = '搜索'; | |||
btnSearch.appendChild(svgIcon('M15.5 14h-.79l-.28-.27A6.471 6.471 0 0016 9.5 6.5 6.5 0 109.5 16c1.61 0 3.09-.59 4.23-1.57l.27.28v.79l5 4.99L20.49 19l-4.99-5zM9.5 14C7.01 14 5 11.99 5 9.5S7.01 5 9.5 5 14 7.01 14 9.5 11.99 14 9.5 14z')); | |||
btnSearch.addEventListener('click', function () { | |||
// Timeless 常见 #searchInput,Vector 常见 input[name=search] | |||
var input = document.getElementById('searchInput') || | |||
document.querySelector('input[name=search]') || | |||
document.querySelector('#searchform input[type=search]'); | |||
if (input) input.focus(); | |||
}); | |||
dock.appendChild(btnSearch); | |||
// menu(打开抽屉) | |||
var btnMenu = el('button', 'mdtDockBtn mdtDockBtnActive'); | |||
btnMenu.type = 'button'; | |||
btnMenu.title = '导航菜单'; | |||
btnMenu.appendChild(svgIcon('M3 6h18v2H3V6zm0 5h18v2H3v-2zm0 5h18v2H3v-2z')); | |||
btnMenu.addEventListener('click', function () { | |||
body.classList.toggle('mdtDockDrawerOpen'); | |||
}); | |||
dock.appendChild(btnMenu); | |||
dock.appendChild(el('div', 'mdtDockSpacer')); | |||
// settings(先做占位:你以后可做主题切换等) | |||
var btnSettings = el('button', 'mdtDockBtn'); | |||
btnSettings.type = 'button'; | |||
btnSettings.title = '设置'; | |||
btnSettings.appendChild(svgIcon('M19.14 12.94c.04-.31.06-.63.06-.94s-.02-.63-.06-.94l2.03-1.58c.18-.14.23-.41.12-.61l-1.92-3.32c-.11-.2-.36-.28-.57-.2l-2.39.96c-.5-.38-1.04-.69-1.64-.92l-.36-2.54A.488.488 0 0014.41 2h-3.82c-.24 0-.44.17-.48.41l-.36 2.54c-.6.23-1.14.54-1.64.92l-2.39-.96c-.21-.08-.46 0-.57.2L2.23 8.43c-.11.2-.06.46.12.61l2.03 1.58c-.04.31-.06.63-.06.94s.02.63.06.94l-2.03 1.58a.5.5 0 00-.12.61l1.92 3.32c.11.2.36.28.57.2l2.39-.96c.5.38 1.04.69 1.64.92l.36 2.54c.04.24.24.41.48.41h3.82c.24 0 .44-.17.48-.41l.36-2.54c.6-.23 1.14-.54 1.64-.92l2.39.96c.21.08.46 0 .57-.2l1.92-3.32a.5.5 0 00-.12-.61l-2.03-1.58zM12 15.5c-1.93 0-3.5-1.57-3.5-3.5S10.07 8.5 12 8.5s3.5 1.57 3.5 3.5-1.57 3.5-3.5 3.5z')); | |||
btnSettings.addEventListener('click', function () { | |||
alert('这里可以做:主题切换/字号等'); | |||
}); | |||
dock.appendChild(btnSettings); | |||
// 组装 | |||
document.body.appendChild(overlay); | |||
document.body.appendChild(drawer); | |||
document.body.appendChild(dock); | |||
// Esc 关闭抽屉 | |||
document.addEventListener('keydown', function (ev) { | |||
if (ev.key === 'Escape') { | |||
body.classList.remove('mdtDockDrawerOpen'); | |||
} | |||
}); | |||
// 注意:如果你已经在 Common.js 里有同名的折叠树脚本,也没关系, | |||
// 这里对 clone 的 ulRoot 设置了 data-mdtNav=1,避免重复处理。 | |||
} | |||
onReady(initDock); | |||
})(); | })(); | ||
2026年1月21日 (三) 20:32的版本
/* MediaWiki:Common.js */
(function () {
'use strict';
function fallbackCopy(text) {
if (!window.jQuery) return;
var $ = window.jQuery;
var $temp = $('<textarea>');
$('body').append($temp);
$temp.val(text).select();
document.execCommand('copy');
$temp.remove();
alert('复制成功!');
}
// 点击按钮复制内容:<button class="copy-button" data-text="...">复制</button>
if (window.jQuery) {
window.jQuery(function ($) {
$(document).on('click', '.copy-button', function () {
var text = $(this).attr('data-text') || '';
if (navigator.clipboard && navigator.clipboard.writeText) {
navigator.clipboard.writeText(text).then(function () {
alert('复制成功!');
}, function () {
fallbackCopy(text);
});
} else {
fallbackCopy(text);
}
});
});
}
// Timeless:把 Sidebar 里以 "-- " 开头的条目变成可折叠子项
mw.loader.using(['mediawiki.util'], function () {
if (mw.config.get('skin') !== 'timeless') return;
function initNavTree() {
var siteNav = document.getElementById('mw-site-navigation');
if (!siteNav) return;
var pNav = siteNav.querySelector('#p-navigation');
if (!pNav) return;
var root = pNav.querySelector('.mw-portlet-body > ul') ||
pNav.querySelector('ul');
if (!root) return;
if (root.getAttribute('data-mdtNav') === '1') return;
root.setAttribute('data-mdtNav', '1');
var KEY = 'mdtNavTreeOpen';
var store = {};
try {
store = JSON.parse(localStorage.getItem(KEY) || '{}');
} catch (e) {
store = {};
}
function getKey(li) {
if (li && li.id) return li.id;
var a = li ? li.querySelector('a') : null;
return a ? (a.getAttribute('href') || '') : '';
}
function setStored(li, open) {
var k = getKey(li);
if (!k) return;
store[k] = open;
try {
localStorage.setItem(KEY, JSON.stringify(store));
} catch (e) {}
}
function setOpen(li, open, persist) {
li.classList.toggle('mdtNavOpen', open);
if (persist) setStored(li, open);
}
function ensureGroup(li) {
li.classList.add('mdtNavGroup');
var sub = li.querySelector('ul.mdtNavSublist');
if (!sub) {
sub = document.createElement('ul');
sub.className = 'mdtNavSublist';
li.appendChild(sub);
}
var btn = li.querySelector('button.mdtNavToggle');
if (!btn) {
btn = document.createElement('button');
btn.type = 'button';
btn.className = 'mdtNavToggle';
btn.setAttribute('aria-label', '展开/收起');
btn.setAttribute('aria-expanded', 'false');
li.insertBefore(btn, sub);
btn.addEventListener('click', function (ev) {
ev.preventDefault();
ev.stopPropagation();
var isOpen = li.classList.contains('mdtNavOpen');
setOpen(li, !isOpen, true);
btn.setAttribute('aria-expanded', String(!isOpen));
});
}
var k = getKey(li);
if (store[k] === true) {
li.classList.add('mdtNavOpen');
btn.setAttribute('aria-expanded', 'true');
}
return sub;
}
var items = Array.prototype.slice.call(root.children);
var lastAtDepth = [];
for (var i = 0; i < items.length; i++) {
var li = items[i];
if (!li || li.tagName !== 'LI') continue;
var a = li.querySelector('a');
if (!a) continue;
var raw = (a.textContent || '').trim();
var m = raw.match(/^(-{2,})\s*(.*)$/);
var depth = 0;
if (m) {
depth = Math.min(Math.floor(m[1].length / 2), 3);
a.textContent = m[2];
}
li.classList.add('mdtNavItem');
if (depth > 0 && lastAtDepth[depth - 1]) {
li.classList.add('mdtNavSubItem');
li.classList.add('mdtNavDepth' + String(depth));
var sublist = ensureGroup(lastAtDepth[depth - 1]);
sublist.appendChild(li);
} else {
depth = 0;
}
lastAtDepth[depth] = li;
lastAtDepth.length = depth + 1;
}
// 当前页面高亮 + 自动展开父级
var current = (mw.config.get('wgPageName') || '').replace(/ /g, '_');
var links = pNav.querySelectorAll('a');
function titleFromHref(href) {
var t = mw.util.getParamValue('title', href);
if (t) return t.replace(/ /g, '_');
var mark = '/index.php/';
var pos = href.indexOf(mark);
if (pos !== -1) {
return decodeURIComponent(href.slice(pos + mark.length))
.replace(/ /g, '_');
}
return '';
}
for (var j = 0; j < links.length; j++) {
var href = links[j].getAttribute('href') || '';
var page = titleFromHref(href);
if (page && page === current) {
links[j].classList.add('mdtNavActive');
var n = links[j].parentNode;
while (n && n !== root) {
if (n.tagName === 'LI' && n.classList.contains('mdtNavGroup')) {
setOpen(n, true, false);
var btn = n.querySelector('button.mdtNavToggle');
if (btn) btn.setAttribute('aria-expanded', 'true');
}
n = n.parentNode;
}
break;
}
}
}
if (document.readyState === 'loading') {
document.addEventListener('DOMContentLoaded', initNavTree);
} else {
initNavTree();
}
});
// 可折叠标题:在标题旁边加一个展开/收起按钮
// 用法:把任意标题写成:== 战役区块 <span class="mdtFold"></span> ==
// 默认收起:== 战役区块 <span class="mdtFold mdtFoldClosed"></span> ==
mw.loader.using(['mediawiki.util'], function () {
var skin = String(mw.config.get('skin') || '').toLowerCase();
if (skin !== 'timeless') return;
var action = String(mw.config.get('wgAction') || 'view').toLowerCase();
if (action === 'edit' || action === 'submit') return;
function findHeading(node) {
while (node && node !== document.body) {
if (node.nodeType === 1 && /^H[1-6]$/.test(node.tagName)) return node;
node = node.parentNode;
}
return null;
}
var KEY = 'mdtFoldSections';
var store = {};
try {
store = JSON.parse(localStorage.getItem(KEY) || '{}');
} catch (e) {
store = {};
}
function saveStore() {
try {
localStorage.setItem(KEY, JSON.stringify(store));
} catch (e) {}
}
function getHeadingBlock(headingEl) {
if (!headingEl || !headingEl.parentElement) return headingEl;
// MediaWiki 新版可能会把 h2 包在 <div class="mw-heading"> 里。
// 如果不处理这个 wrapper,折叠只会影响 wrapper 内部(看起来“没效果”)。
var p = headingEl.parentElement;
if (p.classList && p.classList.contains('mw-heading')) return p;
return headingEl;
}
function getHeadingLevelFromNode(node) {
if (!node || node.nodeType !== 1) return null;
if (/^H[1-6]$/.test(node.tagName)) {
return parseInt(String(node.tagName).slice(1), 10) || 6;
}
if (node.classList && node.classList.contains('mw-heading')) {
var h = node.querySelector('h1,h2,h3,h4,h5,h6');
if (h && /^H[1-6]$/.test(h.tagName)) {
return parseInt(String(h.tagName).slice(1), 10) || 6;
}
}
return null;
}
function initFoldSections(root) {
var content = root || document.getElementById('mw-content-text') ||
document.querySelector('.mw-parser-output') ||
document.body;
if (!content) return;
var nodes = [];
var markers = content.querySelectorAll('.mdtFold');
var headingNodes = content.querySelectorAll('h1.mdtFoldHeading,h2.mdtFoldHeading,h3.mdtFoldHeading,h4.mdtFoldHeading,h5.mdtFoldHeading,h6.mdtFoldHeading');
for (var h = 0; h < headingNodes.length; h++) nodes.push(headingNodes[h]);
for (var m = 0; m < markers.length; m++) nodes.push(markers[m]);
if (!nodes.length) return;
for (var i = 0; i < nodes.length; i++) {
(function (nodeOrMarker) {
var marker = null;
var heading = null;
if (nodeOrMarker && nodeOrMarker.nodeType === 1 && /^H[1-6]$/.test(nodeOrMarker.tagName)) {
heading = nodeOrMarker;
} else {
marker = nodeOrMarker;
heading = findHeading(marker);
}
if (!heading) return;
// 兼容:如果是通过 h2.mdtFoldHeading 触发,但里面也有 span.mdtFold,
// 也要把 marker 当作触发点,这样可以读取默认收起并移除 marker。
if (!marker) {
marker = heading.querySelector('.mdtFold');
}
var headingBlock = getHeadingBlock(heading);
if (!headingBlock) return;
if (headingBlock.getAttribute('data-mdtFoldInit') === '1') return;
headingBlock.setAttribute('data-mdtFoldInit', '1');
var level = parseInt(String(heading.tagName).slice(1), 10) || 2;
var headline = heading.querySelector('.mw-headline');
// Body: 优先折叠“紧跟着”的图标网格(你这个场景就是 h2 + .mdtIconGrid)。
// 也支持手动标记:<div class="mdtFoldBody">...</div>
var body = null;
var nextEl = headingBlock.nextElementSibling;
if (nextEl && nextEl.classList) {
if (nextEl.classList.contains('mdtFoldBody') || nextEl.classList.contains('mdtIconGrid')) {
body = nextEl;
body.classList.add('mdtFoldBody');
}
}
// 自动模式:把标题后面直到下一个“同级或更高级标题”的所有节点包起来。
if (!body) {
body = document.createElement('div');
body.className = 'mdtFoldBody';
var node = headingBlock.nextSibling;
while (node) {
var next = node.nextSibling;
var nodeLevel = getHeadingLevelFromNode(node);
if (nodeLevel !== null && nodeLevel <= level) break;
body.appendChild(node);
node = next;
}
headingBlock.parentNode.insertBefore(body, node);
}
// Toggle button
var btn = document.createElement('button');
btn.type = 'button';
btn.className = 'mdtFoldToggle';
btn.setAttribute('aria-label', '展开/收起');
btn.setAttribute('aria-expanded', 'true');
btn.title = '展开/收起';
btn.textContent = '\u25BE';
// Inline style fallback: in case Timeless.css is not loaded yet.
btn.style.width = '1.6rem';
btn.style.height = '1.6rem';
btn.style.marginLeft = '0.25rem';
btn.style.border = '0';
btn.style.padding = '0';
btn.style.borderRadius = '10px';
btn.style.display = 'inline-flex';
btn.style.alignItems = 'center';
btn.style.justifyContent = 'center';
btn.style.background = 'rgba(0, 175, 137, 0.10)';
btn.style.color = 'var(--mdtBrand)';
btn.style.cursor = 'pointer';
btn.style.fontFamily = 'inherit';
btn.style.fontSize = '16px';
btn.style.fontWeight = '700';
btn.style.lineHeight = '1';
btn.style.boxShadow = 'none';
btn.style.outline = 'none';
btn.style.appearance = 'none';
btn.style.webkitAppearance = 'none';
if (headline) {
headline.classList.add('mdtFoldHeadline');
headline.appendChild(btn);
} else {
var wrap = document.createElement('span');
wrap.className = 'mdtFoldHeadline';
while (heading.firstChild) {
wrap.appendChild(heading.firstChild);
}
heading.appendChild(wrap);
wrap.appendChild(btn);
headline = wrap;
}
// Marker/heading default state
var defaultClosed = false;
if (marker && marker.classList && marker.classList.contains('mdtFoldClosed')) {
defaultClosed = true;
}
if (heading.classList && heading.classList.contains('mdtFoldClosed')) {
defaultClosed = true;
}
// Remove marker to avoid extra spacing
if (marker && marker.parentNode) marker.parentNode.removeChild(marker);
var page = (mw.config.get('wgPageName') || '').replace(/ /g, '_');
var id = '';
if (headline && headline.id) id = headline.id;
if (!id) id = (heading.textContent || '').trim();
var storageKey = page + '::' + id;
function setCollapsed(collapsed, persist) {
headingBlock.classList.toggle('mdtFoldCollapsed', collapsed);
btn.setAttribute('aria-expanded', String(!collapsed));
btn.textContent = collapsed ? '\u25B8' : '\u25BE';
if (body) {
body.classList.toggle('mdtFoldHidden', collapsed);
// Ensure it works even if CSS is missing/cached.
body.style.display = collapsed ? 'none' : '';
}
if (persist) {
store[storageKey] = collapsed;
saveStore();
}
}
var collapsed = false;
if (typeof store[storageKey] === 'boolean') {
collapsed = store[storageKey];
} else if (defaultClosed) {
collapsed = true;
}
setCollapsed(collapsed, false);
btn.addEventListener('click', function (ev) {
ev.preventDefault();
ev.stopPropagation();
var isCollapsed = headingBlock.classList.contains('mdtFoldCollapsed');
setCollapsed(!isCollapsed, true);
});
})(nodes[i]);
}
}
function runOnPage() {
initFoldSections(document.getElementById('mw-content-text') ||
document.querySelector('.mw-parser-output') ||
document.body);
}
// MediaWiki 推荐:内容渲染/替换后触发(兼容预览、部分皮肤/插件)。
if (mw.hook && window.jQuery) {
mw.hook('wikipage.content').add(function ($content) {
initFoldSections($content && $content[0] ? $content[0] : null);
});
}
if (document.readyState === 'loading') {
document.addEventListener('DOMContentLoaded', runOnPage);
} else {
runOnPage();
}
});
})();
(function () {
'use strict';
function onReady(fn) {
if (document.readyState === 'loading') {
document.addEventListener('DOMContentLoaded', fn);
} else {
fn();
}
}
function el(tag, cls) {
var n = document.createElement(tag);
if (cls) n.className = cls;
return n;
}
function svgIcon(pathD) {
var svg = document.createElementNS('http://www.w3.org/2000/svg', 'svg');
svg.setAttribute('viewBox', '0 0 24 24');
var p = document.createElementNS('http://www.w3.org/2000/svg', 'path');
p.setAttribute('d', pathD);
svg.appendChild(p);
return svg;
}
function findPortletUl(idCandidates) {
for (var i = 0; i < idCandidates.length; i++) {
var p = document.getElementById(idCandidates[i]);
if (!p) continue;
var ul = p.querySelector('.mw-portlet-body > ul') || p.querySelector('ul');
if (ul) return ul;
}
return null;
}
function clonePortlet(title, ul) {
var wrap = el('div', 'mdtDockSection');
var h = el('div', 'mdtDockSectionTitle');
h.textContent = title;
wrap.appendChild(h);
var cloned = ul.cloneNode(true);
// 清掉某些 id,避免重复
cloned.removeAttribute('id');
wrap.appendChild(cloned);
return wrap;
}
function initDock() {
// 可选:只在手机 / 小屏启用。你想全端都启用就删掉这段判断。
// if (window.matchMedia && window.matchMedia('(min-width: 980px)').matches) return;
var body = document.body;
if (!body || body.classList.contains('mdtDockEnabled')) return;
body.classList.add('mdtDockEnabled');
var overlay = el('div', 'mdtDockOverlay');
overlay.addEventListener('click', function () {
body.classList.remove('mdtDockDrawerOpen');
});
var drawer = el('aside', 'mdtDockDrawer');
var drawerInner = el('div', 'mdtDockDrawerInner');
drawer.appendChild(drawerInner);
// 把原本的侧边栏内容“克隆”进抽屉
// Timeless: p-navigation, p-tb
// Vector/others: p-navigation, p-tb 也常见
var nav = findPortletUl(['p-navigation', 'p-Navigation']);
if (nav) drawerInner.appendChild(clonePortlet('导航', nav));
var tools = findPortletUl(['p-tb', 'p-Toolbox']);
if (tools) drawerInner.appendChild(clonePortlet('wiki工具', tools));
// 折叠树:把 "-- " 前缀的条目变成可折叠子项
function buildNavTree(ulRoot) {
if (!ulRoot) return;
if (ulRoot.getAttribute('data-mdtNav') === '1') return;
ulRoot.setAttribute('data-mdtNav', '1');
var KEY = 'mdtNavTreeOpen';
var store = {};
try {
store = JSON.parse(localStorage.getItem(KEY) || '{}');
} catch (e) {
store = {};
}
function getKey(li) {
if (li && li.id) return li.id;
var a = li ? li.querySelector('a') : null;
return a ? (a.getAttribute('href') || '') : '';
}
function setStored(li, open) {
var k = getKey(li);
if (!k) return;
store[k] = open;
try {
localStorage.setItem(KEY, JSON.stringify(store));
} catch (e) {}
}
function setOpen(li, open, persist) {
li.classList.toggle('mdtNavOpen', open);
if (persist) setStored(li, open);
}
function ensureGroup(li) {
li.classList.add('mdtNavGroup');
var sub = li.querySelector('ul.mdtNavSublist');
if (!sub) {
sub = document.createElement('ul');
sub.className = 'mdtNavSublist';
li.appendChild(sub);
}
var btn = li.querySelector('button.mdtNavToggle');
if (!btn) {
btn = document.createElement('button');
btn.type = 'button';
btn.className = 'mdtNavToggle';
btn.setAttribute('aria-label', '展开/收起');
btn.setAttribute('aria-expanded', 'false');
li.insertBefore(btn, sub);
btn.addEventListener('click', function (ev) {
ev.preventDefault();
ev.stopPropagation();
var isOpen = li.classList.contains('mdtNavOpen');
setOpen(li, !isOpen, true);
btn.setAttribute('aria-expanded', String(!isOpen));
});
}
var k = getKey(li);
if (store[k] === true) {
li.classList.add('mdtNavOpen');
btn.setAttribute('aria-expanded', 'true');
}
return sub;
}
var items = Array.prototype.slice.call(ulRoot.children);
var lastAtDepth = [];
for (var i = 0; i < items.length; i++) {
var li = items[i];
if (!li || li.tagName !== 'LI') continue;
var a = li.querySelector('a');
if (!a) continue;
var raw = (a.textContent || '').trim();
var m = raw.match(/^(-{2,})\s*(.*)$/);
var depth = 0;
if (m) {
depth = Math.min(Math.floor(m[1].length / 2), 3);
a.textContent = m[2];
}
if (depth > 0 && lastAtDepth[depth - 1]) {
li.classList.add('mdtNavSubItem');
li.classList.add('mdtNavDepth' + String(depth));
var sublist = ensureGroup(lastAtDepth[depth - 1]);
sublist.appendChild(li);
} else {
depth = 0;
}
lastAtDepth[depth] = li;
lastAtDepth.length = depth + 1;
}
// 当前页面高亮
try {
if (typeof mw !== 'undefined' && mw.config) {
var current = (mw.config.get('wgPageName') || '').split(' ').join('_');
var links = ulRoot.querySelectorAll('a');
for (var j = 0; j < links.length; j++) {
var href = links[j].getAttribute('href') || '';
if (!href) continue;
if (href.indexOf('title=') !== -1 && typeof mw.util !== 'undefined') {
var t = mw.util.getParamValue('title', href);
if (t && t.split(' ').join('_') === current) {
links[j].classList.add('mdtDockActiveLink');
break;
}
}
}
}
} catch (e) {}
}
// 对抽屉中的导航/工具分别构建折叠树
var drawerSections = drawerInner.querySelectorAll('.mdtDockSection > ul');
for (var s = 0; s < drawerSections.length; s++) {
buildNavTree(drawerSections[s]);
}
// 你也可以把其它 portlet 加进来,例如:
// var lang = findPortletUl(['p-lang']);
// if (lang) drawerInner.appendChild(clonePortlet('语言', lang));
// 左侧图标栏
var dock = el('nav', 'mdtDock');
// 图标:简单用 SVG path(不依赖字体/图片)
// home
var btnHome = el('a', 'mdtDockBtn');
btnHome.href = '/';
btnHome.title = '首页';
btnHome.appendChild(svgIcon('M10 20v-6h4v6h5v-8h3L12 3 2 12h3v8z'));
dock.appendChild(btnHome);
// search(点击聚焦搜索框)
var btnSearch = el('button', 'mdtDockBtn');
btnSearch.type = 'button';
btnSearch.title = '搜索';
btnSearch.appendChild(svgIcon('M15.5 14h-.79l-.28-.27A6.471 6.471 0 0016 9.5 6.5 6.5 0 109.5 16c1.61 0 3.09-.59 4.23-1.57l.27.28v.79l5 4.99L20.49 19l-4.99-5zM9.5 14C7.01 14 5 11.99 5 9.5S7.01 5 9.5 5 14 7.01 14 9.5 11.99 14 9.5 14z'));
btnSearch.addEventListener('click', function () {
// Timeless 常见 #searchInput,Vector 常见 input[name=search]
var input = document.getElementById('searchInput') ||
document.querySelector('input[name=search]') ||
document.querySelector('#searchform input[type=search]');
if (input) input.focus();
});
dock.appendChild(btnSearch);
// menu(打开抽屉)
var btnMenu = el('button', 'mdtDockBtn mdtDockBtnActive');
btnMenu.type = 'button';
btnMenu.title = '导航菜单';
btnMenu.appendChild(svgIcon('M3 6h18v2H3V6zm0 5h18v2H3v-2zm0 5h18v2H3v-2z'));
btnMenu.addEventListener('click', function () {
body.classList.toggle('mdtDockDrawerOpen');
});
dock.appendChild(btnMenu);
dock.appendChild(el('div', 'mdtDockSpacer'));
// settings(先做占位:你以后可做主题切换等)
var btnSettings = el('button', 'mdtDockBtn');
btnSettings.type = 'button';
btnSettings.title = '设置';
btnSettings.appendChild(svgIcon('M19.14 12.94c.04-.31.06-.63.06-.94s-.02-.63-.06-.94l2.03-1.58c.18-.14.23-.41.12-.61l-1.92-3.32c-.11-.2-.36-.28-.57-.2l-2.39.96c-.5-.38-1.04-.69-1.64-.92l-.36-2.54A.488.488 0 0014.41 2h-3.82c-.24 0-.44.17-.48.41l-.36 2.54c-.6.23-1.14.54-1.64.92l-2.39-.96c-.21-.08-.46 0-.57.2L2.23 8.43c-.11.2-.06.46.12.61l2.03 1.58c-.04.31-.06.63-.06.94s.02.63.06.94l-2.03 1.58a.5.5 0 00-.12.61l1.92 3.32c.11.2.36.28.57.2l2.39-.96c.5.38 1.04.69 1.64.92l.36 2.54c.04.24.24.41.48.41h3.82c.24 0 .44-.17.48-.41l.36-2.54c.6-.23 1.14-.54 1.64-.92l2.39.96c.21.08.46 0 .57-.2l1.92-3.32a.5.5 0 00-.12-.61l-2.03-1.58zM12 15.5c-1.93 0-3.5-1.57-3.5-3.5S10.07 8.5 12 8.5s3.5 1.57 3.5 3.5-1.57 3.5-3.5 3.5z'));
btnSettings.addEventListener('click', function () {
alert('这里可以做:主题切换/字号等');
});
dock.appendChild(btnSettings);
// 组装
document.body.appendChild(overlay);
document.body.appendChild(drawer);
document.body.appendChild(dock);
// Esc 关闭抽屉
document.addEventListener('keydown', function (ev) {
if (ev.key === 'Escape') {
body.classList.remove('mdtDockDrawerOpen');
}
});
// 注意:如果你已经在 Common.js 里有同名的折叠树脚本,也没关系,
// 这里对 clone 的 ulRoot 设置了 data-mdtNav=1,避免重复处理。
}
onReady(initDock);
})();
