From bff7e4c49ccb29ec06f645f4a31143b64c2d37a4 Mon Sep 17 00:00:00 2001 From: Lex Lim Date: Wed, 15 Mar 2023 17:33:45 +0800 Subject: [PATCH] =?UTF-8?q?=E5=A2=9E=E5=8A=A0=E9=9A=90=E8=97=8F=E7=9B=AE?= =?UTF-8?q?=E5=BD=95=E6=8C=89=E9=92=AE=E5=8A=9F=E8=83=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- extension.json | 7 +- i18n/en.json | 5 +- i18n/zh-hans.json | 5 +- modules/ext.isekai.offcanvas-toc.js | 205 ++++++++++++++++++++++---- modules/ext.isekai.offcanvas-toc.less | 15 +- 5 files changed, 195 insertions(+), 42 deletions(-) diff --git a/extension.json b/extension.json index b132cc7..489eea4 100644 --- a/extension.json +++ b/extension.json @@ -2,7 +2,7 @@ "name": "Isekai Offcanvas Toc", "namemsg": "isekai-offcanvastoc-name", "author": "Hyperzlib", - "version": "1.0.0", + "version": "1.0.1", "url": "https://www.isekai.cn", "descriptionmsg": "isekai-offcanvastoc-desc", "license-name": "MIT", @@ -36,6 +36,7 @@ "styles": ["ext.isekai.offcanvas-toc.less"], "dependencies": [ "oojs-ui-core", + "oojs-ui-toolbars", "oojs-ui.styles.icons-layout" ], "targets": [ @@ -43,7 +44,9 @@ "mobile" ], "messages": [ - "isekai-offcanvastoc-description-item" + "isekai-offcanvastoc-description-item", + "isekai-offcanvastoc-hide-menubutton", + "isekai-offcanvastoc-menubutton-hide-success" ] } }, diff --git a/i18n/en.json b/i18n/en.json index 532d38d..38fcab0 100644 --- a/i18n/en.json +++ b/i18n/en.json @@ -3,5 +3,8 @@ "isekai-offcanvastoc-desc": "Show Offcanvas TOC on wiki", "prefs-offcanvas-toc": "Offcanvas TOC", "offcanvas-toc-enabled": "Show offcanvas TOC", - "isekai-offcanvastoc-description-item": "Description" + "isekai-offcanvastoc-description-item": "Description", + + "isekai-offcanvastoc-hide-menubutton": "Hide offcanvas TOC Button", + "isekai-offcanvastoc-menubutton-hide-success": "Offcanvas TOC button has been hidden, double click on the content area to show it again" } \ No newline at end of file diff --git a/i18n/zh-hans.json b/i18n/zh-hans.json index ee0e9c7..562f1dc 100644 --- a/i18n/zh-hans.json +++ b/i18n/zh-hans.json @@ -3,5 +3,8 @@ "isekai-offcanvastoc-desc": "在页面上显示悬浮目录", "prefs-offcanvas-toc": "悬浮目录", "offcanvas-toc-enabled": "显示悬浮目录", - "isekai-offcanvastoc-description-item": "简介" + "isekai-offcanvastoc-description-item": "简介", + + "isekai-offcanvastoc-hide-menubutton": "隐藏目录按钮", + "isekai-offcanvastoc-menubutton-hide-success": "目录按钮已隐藏,双击内容区域可以再次显示" } \ No newline at end of file diff --git a/modules/ext.isekai.offcanvas-toc.js b/modules/ext.isekai.offcanvas-toc.js index 0e598aa..1277202 100644 --- a/modules/ext.isekai.offcanvas-toc.js +++ b/modules/ext.isekai.offcanvas-toc.js @@ -1,16 +1,52 @@ var headingList = []; var scrollBehaviorAvaliable = (function() { + // 检测Chrome var v = navigator.userAgent.match(/Chrome\/(?\S+)/); - if (v && v.groups.version) { // 检测chrome版本 + if (v && v.groups.version) { var chromeVersion = parseInt(v.groups.version); return chromeVersion >= 61; - } else { // 非chrome - return true; } + + // 检测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; +})(); +var isMobileSafari = (function(){ + v = navigator.userAgent.match(/Version\/(?\S+) Mobile\/\S+/); + if (v && v.groups.version) { // Safari + return true; + } + return false; })(); +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')); + }, + } +]; function getScrollOffset() { - if (mw.config.get('skin') === 'timeless' && $(window).width() > 850) { + if (mw.config.get('skin') === 'timeless' && window.innerWidth > 850) { + return 60; + } else if (mw.config.get('skin') === 'minerva') { return 60; } else { return 10; @@ -18,7 +54,9 @@ function getScrollOffset() { } function getAnchorOffset() { - if (mw.config.get('skin') === 'timeless' && $(window).width() > 850) { + if (mw.config.get('skin') === 'timeless' && window.innerWidth > 850) { + return 70; + } else if (mw.config.get('skin') === 'minerva') { return 70; } else { return 20; @@ -26,8 +64,10 @@ function getAnchorOffset() { } function scrollToAnchor(link) { - var target = $(link.replace(/\./g, '\\.')); - if (target.length > 0) { + var el = document.getElementById(link.replace(/^#/, '')); + if (el) { + var target = $(el); + function doScroll() { var position = target.offset().top - getScrollOffset(); @@ -103,31 +143,88 @@ function updateActive() { } function openOffcanvas() { + $('#iseai-offcanvas-btn').show(); + menuHidden = false; + let scrollbarWidth = getScrollbarWidth(); - $('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); - } + $('#isekai-offcanvas-cover').removeClass('hidden'); - // 滚动到当前项目 - 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); - } + 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); + } + }); } function closeOffcanvas() { - $('body').removeClass('toc-offcanvas-open'); - setTimeout(function() { - $('body').removeClass('toc-offcanvas-show').css('margin-right', 0); - $('#iseai-offcanvas-btn').css('margin-right', 0); - if (mw.config.get('skin') === 'timeless') { - $('#mw-header-container').css('padding-right', 0); - } - }, 260); + 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); } $(function() { @@ -136,8 +233,8 @@ $(function() {
    - -
    + + `); var menuIcon = new OO.ui.IconWidget({ icon: 'menu' }); $('#iseai-offcanvas-btn').append(menuIcon.$element); @@ -207,10 +304,50 @@ $(function() { closeOffcanvas(); }); - $('#iseai-offcanvas-btn').on('click', function() { + $('#iseai-offcanvas-btn').on('click', function(e) { + e.preventDefault(); + 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); + } + }); + } $('#isekai-offcanvas-toc ul .list-item').on('click', function(e) { // 点击链接 @@ -221,10 +358,16 @@ $(function() { $('#isekai-offcanvas-toc ul .list-item').removeClass('active'); $(this).addClass('active'); scrollToAnchor(target); - if ($(window).width() < 550) { // 手机端,关闭抽屉 + if (window.innerWidth < 550) { // 手机端,关闭抽屉 closeOffcanvas(); } } return false; }); + + $('#content').on('dblclick', function(e) { + if (menuHidden || window._openOffcanvasTocViaDblclick) { + openOffcanvas(); + } + }); }); \ No newline at end of file diff --git a/modules/ext.isekai.offcanvas-toc.less b/modules/ext.isekai.offcanvas-toc.less index cb8d3af..04a7fb8 100644 --- a/modules/ext.isekai.offcanvas-toc.less +++ b/modules/ext.isekai.offcanvas-toc.less @@ -74,8 +74,6 @@ .toc-offcanvas-cover { position: fixed; - visibility: hidden; - pointer-events: none; top: 0; left: 0; right: 0; @@ -85,6 +83,10 @@ z-index: 101; transition: opacity 250ms linear; will-change: opacity; + + &.hidden { + display: none; + } } .toc-offcanvas-btn { @@ -107,6 +109,9 @@ background-color: rgba(255, 255, 255, 0.95); color: #333; box-shadow: 0 4px 16px 0 rgba(0, 0, 0, 0.12); + user-select: none; + -webkit-touch-callout: none; + -webkit-user-select: none; &:hover { background-color: rgba(255, 255, 255, 0.9); @@ -120,6 +125,7 @@ @media screen and (min-width: 768px) { right: 24px; + bottom: 15vh; } @media screen and (min-width: 1340px) { @@ -138,11 +144,6 @@ body.toc-offcanvas-show { overflow-y: hidden; - .toc-offcanvas-cover { - visibility: visible; - pointer-events: auto; - } - .toc-offcanvas { visibility: visible; }