|
|
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();
|
|
|
}
|
|
|
}); |