var headingList = []; var menuList = []; var bottomNavBtn; var bottomNavBtnLabel; var scrollBehaviorAvaliable = (function() { // 检测Chrome var v = navigator.userAgent.match(/Chrome\/(?\S+)/); if (v && v.groups.version) { var chromeVersion = parseInt(v.groups.version); return chromeVersion >= 61; } // 检测Firefox v = navigator.userAgent.match(/Firefox\/(?\S+)/); if (v && v.groups.version) { var firefoxVersion = parseInt(v.groups.version); return firefoxVersion >= 36; } // 检测Safari v = navigator.userAgent.match(/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(`
    `); // 创建按钮 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(); } });