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.

330 lines
11 KiB
JavaScript

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

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 ++) {
if (headNum[i] == 0) continue;
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();
}
});