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.

373 lines
12 KiB
JavaScript

3 years ago
var headingList = [];
2 years ago
var scrollBehaviorAvaliable = (function() {
// 检测Chrome
2 years ago
var v = navigator.userAgent.match(/Chrome\/(?<version>\S+)/);
if (v && v.groups.version) {
2 years ago
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;
})();
var isMobileSafari = (function(){
v = navigator.userAgent.match(/Version\/(?<version>\S+) Mobile\/\S+/);
if (v && v.groups.version) { // Safari
return true;
}
return false;
2 years ago
})();
var menuHidden = false;
var menuItems = [
{
label: mw.msg('isekai-offcanvastoc-hide-menubutton'),
onClick: () => {
$('#iseai-offcanvas-btn').hide();
menuHidden = true;
mw.notify( mw.msg('isekai-offcanvastoc-menubutton-hide-success'));
},
}
];
3 years ago
function getScrollOffset() {
if (mw.config.get('skin') === 'timeless' && window.innerWidth > 850) {
return 60;
} else if (mw.config.get('skin') === 'minerva') {
3 years ago
return 60;
} else {
return 10;
3 years ago
}
}
function getAnchorOffset() {
if (mw.config.get('skin') === 'timeless' && window.innerWidth > 850) {
return 70;
} else if (mw.config.get('skin') === 'minerva') {
3 years ago
return 70;
} else {
return 20;
3 years ago
}
}
function scrollToAnchor(link) {
var el = document.getElementById(link.replace(/^#/, ''));
if (el) {
var target = $(el);
3 years ago
function doScroll() {
var position = target.offset().top - getScrollOffset();
2 years ago
if (scrollBehaviorAvaliable) {
window.scrollTo({
top: position,
behavior: 'smooth'
});
} else {
$('html, body').animate({
scrollTop: position,
}, 500);
}
3 years ago
}
2 years ago
if (mw.config.get('skin') === 'minerva') { // 手机端主题,需要检测折叠状态
3 years ago
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;
}
}
function updateActive() {
var scrollTop = $(window).scrollTop() + getAnchorOffset();
$('#isekai-offcanvas-toc ul .list-item').removeClass('active');
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) {
2 years ago
activedId = headingList[headingList.length - 1].attr('id');
3 years ago
}
$('#isekai-offcanvas-toc ul .list-item[data-id="' + activedId + '"]').addClass('active');
}
}
function openOffcanvas() {
$('#iseai-offcanvas-btn').show();
menuHidden = false;
3 years ago
let scrollbarWidth = getScrollbarWidth();
$('#isekai-offcanvas-cover').removeClass('hidden');
3 years ago
window.requestAnimationFrame(function() {
$('body').addClass(['toc-offcanvas-show', 'toc-offcanvas-open'])
.css('margin-right', scrollbarWidth);
$('#iseai-offcanvas-btn').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);
}
});
3 years ago
}
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');
$('#iseai-offcanvas-btn').css('margin-right', 0);
if (mw.config.get('skin') === 'timeless') {
$('#mw-header-container').css('padding-right', 0);
}
}, 260);
}
}
function openContextMenu(position) {
var menuContainer = document.createElement('div');
menuContainer.id = 'iseai-offcanvas-contextmenu';
menuContainer.className = 'oo-ui-toolGroup-tools oo-ui-popupToolGroup-tools oo-ui-listToolGroup-tools oo-ui-toolGroup-enabled-tools oo-ui-popupToolGroup-active-tools';
menuContainer.style.minWidth = 'unset';
// 设置弹出位置
menuContainer.style.position = 'fixed';
menuContainer.style.zIndex = 105;
menuContainer.style.right = position.x ? ($(window).width() - position.x) + 'px' : null;
menuContainer.style.bottom = position.y ? ($(window).height() - position.y) + 'px' : null;
function hideMenu() {
menuContainer.remove();
$('#isekai-offcanvas-cover').addClass('hidden');
}
menuItems.forEach((menuItem) => {
var menuElem = document.createElement('span');
menuElem.className = 'oo-ui-widget oo-ui-widget-enabled oo-ui-tool';
var menuLink = document.createElement('a');
menuLink.className = 'oo-ui-tool-link';
menuLink.tabIndex = 0;
menuLink.role = 'button';
menuLink.addEventListener('click', function(e) {
e.preventDefault();
menuItem.onClick();
hideMenu();
});
var menuLabel = document.createElement('span');
menuLabel.className = 'oo-ui-tool-title';
menuLabel.innerText = menuItem.label;
menuLink.appendChild(menuLabel);
menuElem.appendChild(menuLink);
menuContainer.appendChild(menuElem);
});
$('#isekai-offcanvas-cover').removeClass('hidden');
document.body.appendChild(menuContainer);
3 years ago
}
$(function() {
2 years ago
// 创建目录dom
$('body').append(`
<div id="isekai-offcanvas-toc" class="toc-offcanvas">
<ul></ul>
</div>
<a role="button" href="#" id="iseai-offcanvas-btn" class="toc-offcanvas-btn" aria-label="Open float table of contents menu"></a>
<div id="isekai-offcanvas-cover" class="toc-offcanvas-cover hidden"></div>
2 years ago
`);
var menuIcon = new OO.ui.IconWidget({ icon: 'menu' });
$('#iseai-offcanvas-btn').append(menuIcon.$element);
// 生成目录
3 years ago
var parserOutput = $('.mw-parser-output');
var headings = parserOutput.find('h1,h2,h3,h4,h5,h6');
var headNum = new Array(6).fill(0);
var menuList = [{
number: false,
2 years ago
text: mw.msg('isekai-offcanvastoc-description-item'),
3 years ago
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/, ''));
// 计算折叠
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();
});
$('#iseai-offcanvas-btn').on('click', function(e) {
e.preventDefault();
3 years ago
updateActive();
openOffcanvas();
});
var btnElem = $('#iseai-offcanvas-btn')[0];
btnElem.addEventListener('contextmenu', function(e) {
e.preventDefault();
if (e.clientX) {
openContextMenu({
x: e.clientX,
y: e.clientY
});
} else {
openContextMenu({
x: e.pageX - window.scrollX,
y: e.pageY - window.scrollY
});
}
});
if (isMobileSafari) {
// Safari下对长按的特殊处理
var longPressTimer;
btnElem.addEventListener('touchstart', function(e) {
longPressTimer = setTimeout(function() {
e.preventDefault();
openContextMenu({
x: e.pageX - window.scrollX,
y: e.pageY - window.scrollY
});
}, 200);
});
btnElem.addEventListener('touchend', function(e) {
if (longPressTimer) {
clearInterval(longPressTimer);
}
});
}
3 years ago
$('#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) { // 手机端,关闭抽屉
3 years ago
closeOffcanvas();
}
}
return false;
});
$('#content').on('dblclick', function(e) {
if (menuHidden || window._openOffcanvasTocViaDblclick) {
openOffcanvas();
}
});
3 years ago
});