You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

329 lines
11 KiB
JavaScript

var headingList = [];
var menuList = [];
var bottomNavBtn;
var bottomNavBtnLabel;
var scrollBehaviorAvaliable = (function() {
// 检测Chrome
var v = navigator.userAgent.match(/Chrome\/(?<version>\S+)/);
if (v && v.groups.version) {
var chromeVersion = parseInt(v.groups.version);
return chromeVersion >= 61;
}
// 检测Firefox
v = navigator.userAgent.match(/Firefox\/(?<version>\S+)/);
if (v && v.groups.version) {
var firefoxVersion = parseInt(v.groups.version);
return firefoxVersion >= 36;
}
// 检测Safari
v = navigator.userAgent.match(/Version\/(?<version>\S+)/);
if (v && v.groups.version) { // Safari
var safariVersion = parseFloat(v.groups.version);
return safariVersion >= 14;
}
return false;
})();
function getScrollOffset() {
if (mw.config.get('skin') === 'timeless' && window.innerWidth > 850) {
return 60;
} else if (mw.config.get('skin') === 'minerva') {
return 60;
} else {
return 10;
}
}
function getAnchorOffset() {
if (mw.config.get('skin') === 'timeless' && window.innerWidth > 850) {
return 70;
} else if (mw.config.get('skin') === 'minerva') {
return 70;
} else {
return 20;
}
}
function scrollToAnchor(link) {
var el = document.getElementById(link.replace(/^#/, ''));
if (el) {
var target = $(el);
function doScroll() {
var position = target.offset().top - getScrollOffset();
if (scrollBehaviorAvaliable) {
window.scrollTo({
top: position,
behavior: 'smooth'
});
} else {
$('html, body').animate({
scrollTop: position,
}, 500);
}
}
if (mw.config.get('skin') === 'minerva') { // 手机端主题,需要检测折叠状态
var collapseBlock = target.parents('.collapsible-block');
if (collapseBlock.length > 0 && !collapseBlock.hasClass('open-block')) {
var h1Elem = collapseBlock.prev('.collapsible-heading');
if (h1Elem.length > 0) {
// 展开目录
h1Elem.click();
var tid = setInterval(function() {
// 检测是否已经展开
if (collapseBlock.hasClass('open-block')) {
doScroll();
clearInterval(tid);
}
}, 100);
return false;
}
}
doScroll();
} else {
doScroll();
}
return false;
} else {
return true;
}
}
function getScrollbarWidth() {
if (window.innerWidth && document.body.clientWidth) {
return window.innerWidth - document.body.clientWidth;
} else {
return 0;
}
}
var lastActiveId = null;
function updateActive() {
var scrollTop = $(window).scrollTop() + getAnchorOffset();
if (headingList.length > 0) {
var activedId;
for (var i = 0; i < headingList.length; i ++) {
var headItem = headingList[i];
var headPos = headItem.offset().top;
if (i === 0 && scrollTop < headPos) { // 比第一个head位置靠上则是简介
activedId = 'bodyContent';
break;
} else if (scrollTop < headPos) { // 如果当前滚动条高度低于目前head则是上一个
activedId = headingList[i - 1].attr('id');
break;
}
}
if (!activedId) {
activedId = headingList[headingList.length - 1].attr('id');
}
if (activedId !== lastActiveId) {
$('#isekai-offcanvas-toc ul .list-item').removeClass('active');
$('#isekai-offcanvas-toc ul .list-item[data-id="' + activedId + '"]').addClass('active');
var menuItem = menuList.find((item) => {
return item.id === activedId;
});
if (menuItem) {
if (menuItem.number) {
bottomNavBtnLabel.text(menuItem.number + '. ' + menuItem.text);
} else {
bottomNavBtnLabel.text(menuItem.text);
}
}
lastActiveId = activedId;
}
}
}
function openOffcanvas() {
$('#iseai-offcanvas-btn').show();
menuHidden = false;
let scrollbarWidth = getScrollbarWidth();
$('#isekai-offcanvas-cover').removeClass('hidden');
window.requestAnimationFrame(function() {
$('body').addClass(['toc-offcanvas-show', 'toc-offcanvas-open'])
.css('margin-right', scrollbarWidth);
$('#isekai-fab-container').css('margin-right', scrollbarWidth);
if (mw.config.get('skin') === 'timeless') {
$('#mw-header-container').css('padding-right', scrollbarWidth);
}
// 滚动到当前项目
let activedItem = $('#isekai-offcanvas-toc ul .list-item.active');
if (activedItem.length > 0) {
let targetY = Math.max(activedItem.eq(0).prop('offsetTop') - 50, 0);
$('#isekai-offcanvas-toc').scrollTop(targetY);
}
});
}
function closeOffcanvas() {
if ($('#iseai-offcanvas-contextmenu').length > 0) {
$('#iseai-offcanvas-contextmenu').remove();
$('#isekai-offcanvas-cover').addClass('hidden');
} else {
$('body').removeClass('toc-offcanvas-open');
setTimeout(function() {
$('body').removeClass('toc-offcanvas-show').css('margin-right', 0);
$('#isekai-offcanvas-cover').addClass('hidden');
$('#isekai-fab-container').css('margin-right', 0);
if (mw.config.get('skin') === 'timeless') {
$('#mw-header-container').css('padding-right', 0);
}
}, 260);
}
}
function throttle(fn, delay) {
var timer = null;
return function() {
if (!timer) {
timer = setTimeout(function() {
fn();
timer = null;
}, delay);
}
};
}
$(function() {
if (mw.config.get('wgIsArticle')) {
// 创建目录dom
$('body').append(`
<div id="isekai-offcanvas-toc" class="toc-offcanvas">
<ul></ul>
</div>
<div id="isekai-offcanvas-cover" class="toc-offcanvas-cover hidden"></div>
`);
// 创建按钮
var menuIcon = new OO.ui.IconWidget({ icon: 'menu' });
isekai.fab.addButton({
id: 'offcanvas-toc',
label: mw.msg('isekai-offcanvastoc-menubutton'),
icon: menuIcon.$element[0],
priority: 0,
onClick: function() {
openOffcanvas();
}
});
var bottomMenuIcon = new OO.ui.IconWidget({ icon: 'menu' });
bottomNavBtn = isekai.bottomNav.addButton({
id: 'offcanvas-toc',
label: mw.msg('isekai-offcanvastoc-menubutton'),
icon: bottomMenuIcon.$element[0],
priority: 0,
expand: true,
onClick: function() {
openOffcanvas();
}
});
bottomNavBtnLabel = $(bottomNavBtn).find('.isekai-bottom-nav-btn-label');
bottomNavBtnLabel.text('');
// 生成目录
var parserOutput = $('.mw-parser-output');
var headings = parserOutput.find('h1,h2,h3,h4,h5,h6,heading-6');
var headNum = new Array(6).fill(0);
menuList = [{
number: false,
text: mw.msg('isekai-offcanvastoc-description-item'),
id: 'bodyContent'
}];
headings.each(function() {
var headline = $(this).find('.mw-headline');
if (headline.length > 0) {
headingList.push(headline);
var text = headline.text();
var headId = headline.prop('id');
var indentNum = parseInt(this.tagName.replace(/^H/, ''));
this.classList.forEach((className) => {
if (className.indexOf('heading-') === 0) {
indentNum = parseInt(className.replace(/^heading-/, ''));
}
})
// 计算折叠
var menuNumStringBuilder = [];
headNum[indentNum - 1] ++;
for (var i = 0; i < indentNum; i ++) {
menuNumStringBuilder.push(headNum[i]);
}
for (var i = indentNum; i < headNum.length; i ++) {
headNum[i] = 0;
}
var menuNum = menuNumStringBuilder.join('.');
menuList.push({
number: menuNum,
text: text,
id: headId
});
}
});
// 生成dom
var tocContainer = $('#isekai-offcanvas-toc ul');
menuList.forEach(function(menuInfo) {
var listItem = document.createElement('a');
listItem.href = '#' + menuInfo.id;
listItem.dataset.id = menuInfo.id;
listItem.classList.add('list-item');
var titleItem = document.createElement('span');
titleItem.classList.add('title');
titleItem.innerText = menuInfo.text;
if (menuInfo.number) {
var numberItem = document.createElement('span');
numberItem.classList.add('number');
numberItem.innerText = menuInfo.number;
listItem.appendChild(numberItem);
}
listItem.appendChild(titleItem);
tocContainer[0].appendChild(listItem);
});
// 事件
$('#isekai-offcanvas-cover').on('click', function() {
closeOffcanvas();
});
$('#isekai-offcanvas-toc ul .list-item').on('click', function(e) {
// 点击链接
e.preventDefault();
var target = $(this).data('id');
if (target && target != '') {
target = '#' + target;
$('#isekai-offcanvas-toc ul .list-item').removeClass('active');
$(this).addClass('active');
scrollToAnchor(target);
if (window.innerWidth < 550) { // 手机端,关闭抽屉
closeOffcanvas();
}
}
return false;
});
$('#content').on('dblclick', function(e) {
if (window._openOffcanvasTocViaDblclick) {
openOffcanvas();
}
});
window.addEventListener('scroll', throttle(updateActive, 500), { passive: true });
updateActive();
}
});