SidePanel

轻小说文库++的侧边工具栏

此脚本不应直接安装,它是一个供其他脚本使用的外部库。如果您需要使用该库,请在脚本元属性加入:// @require https://update.gf.qytechs.cn/scripts/449718/1085793/SidePanel.js

  1. /* eslint-disable no-multi-spaces */
  2. /* eslint-disable userscripts/no-invalid-headers */
  3. /* eslint-disable userscripts/no-invalid-grant */
  4.  
  5. // ==UserScript==
  6. // @name SidePanel
  7. // @namespace Wenku8++
  8. // @version 0.1.5
  9. // @description 轻小说文库++的侧边工具栏
  10. // @author PY-DNG
  11. // @license GPL-v3
  12. // @regurl https?://www\.wenku8\.net/.*
  13. // @require https://gf.qytechs.cn/scripts/449412-basic-functions/code/Basic%20Functions.js?version=1085783
  14. // @grant none
  15. // ==/UserScript==
  16.  
  17. (function __MAIN__() {
  18. 'use strict';
  19.  
  20. const tippy = require('tippy');
  21.  
  22. exports = sideFunctions();
  23.  
  24. // Side functions area
  25. function sideFunctions() {
  26. const SPanel = new SidePanel();
  27. SPanel.create();
  28. SPanel.setPosition('bottom-right');
  29. addStyle('#sidepanel-panel {background-color: #00000000;z-index: 4000;}.sidepanel-button {font-size: 1vmin;color: #1E64DC;background-color: #FDFDFD;}.sidepanel-button:hover, .sidepanel-button.low-opacity:hover {opacity: 1;color: #FDFDFD;background-color: #1E64DC;}.sidepanel-button.low-opacity{opacity: 0.4 }.sidepanel-button>i[class^="fa-"] {line-height: 3vmin;width: 3vmin;}.sidepanel-button[class*="tooltip"]:hover::after {font-size: 0.9rem;top: calc((5vmin - 25px) / 2);}.sidepanel-button[class*="tooltip"]:hover::before {top: calc((5vmin - 12px) / 2);}.sidepanel-button.accept-pointer{pointer-events:auto;}');
  30.  
  31. commonButtons();
  32. return SPanel;
  33.  
  34. function commonButtons() {
  35. // Button show/hide-all-buttons
  36. const btnShowHide = SPanel.add({
  37. faicon: 'fa-solid fa-down-left-and-up-right-to-center',
  38. className: 'accept-pointer',
  39. tip: '隐藏面板',
  40. onclick: (function() {
  41. let hidden = false;
  42. return (e) => {
  43. hidden = !hidden;
  44. btnShowHide.faicon.className = 'fa-solid ' + (hidden ? 'fa-up-right-and-down-left-from-center' : 'fa-down-left-and-up-right-to-center');
  45. btnShowHide.classList[hidden ? 'add' : 'remove']('low-opacity');
  46. btnShowHide.setAttribute('aria-label', (hidden ? '显示面板' : '隐藏面板'));
  47. SPanel.elements.panel.style.pointerEvents = hidden ? 'none' : 'auto';
  48. for (const button of SPanel.elements.buttons) {
  49. if (button === btnShowHide) {continue;}
  50. //button.style.display = hidden ? 'none' : 'block';
  51. button.style.pointerEvents = hidden ? 'none' : 'auto';
  52. button.style.opacity = hidden ? '0' : '1';
  53. }
  54. };
  55. }) ()
  56. });
  57.  
  58. // Button scroll-to-bottom
  59. const btnDown = SPanel.add({
  60. faicon: 'fa-solid fa-angle-down',
  61. tip: '转到底部',
  62. onclick: (e) => {
  63. const elms = [document.body.parentElement, $('#content'), $('#contentmain')];
  64.  
  65. for (const elm of elms) {
  66. elm && elm.scrollTo && elm.scrollTo(elm.scrollLeft, elm.scrollHeight);
  67. }
  68. }
  69. });
  70.  
  71. // Button scroll-to-top
  72. const btnUp = SPanel.add({
  73. faicon: 'fa-solid fa-angle-up',
  74. tip: '转到顶部',
  75. onclick: (e) => {
  76. const elms = [document.body.parentElement, $('#content'), $('#contentmain')];
  77.  
  78. for (const elm of elms) {
  79. elm && elm.scrollTo && elm.scrollTo(elm.scrollLeft, 0);
  80. }
  81. }
  82. });
  83.  
  84. // Darkmode
  85. /*
  86. const btnDarkmode = SPanel.add({
  87. faicon: 'fa-solid ' + (DMode.isActivated() ? 'fa-sun' : 'fa-moon'),
  88. tip: '明暗切换',
  89. onclick: (e) => {
  90. DMode.toggle();
  91. btnDarkmode.faicon.className = 'fa-solid ' + (DMode.isActivated() ? 'fa-sun' : 'fa-moon');
  92. }
  93. });
  94. */
  95.  
  96. // Refresh page
  97. const btnRefresh = SPanel.add({
  98. faicon: 'fa-solid fa-rotate-right',
  99. tip: '刷新页面',
  100. onclick: (e) => {
  101. location.href = location.href;
  102. }
  103. });
  104. }
  105. }
  106.  
  107. // Side-located control panel
  108. // Requirements: FontAwesome, tippy.js, addStyle
  109. // Use 'new' keyword
  110. function SidePanel() {
  111. // Public SP
  112. const SP = this;
  113. const elms = SP.elements = {};
  114.  
  115. // Private _SP
  116. // keys start with '_' shouldn't be modified
  117. const _SP = {
  118. _id: {
  119. css: 'sidepanel-style',
  120. usercss: 'sidepanel-style-user',
  121. panel: 'sidepanel-panel'
  122. },
  123. _class: {
  124. button: 'sidepanel-button'
  125. },
  126. _directions: ['left', 'right', 'top', 'bottom']
  127. };
  128.  
  129. addStyle('#sidepanel-panel {position: fixed; background-color: #00000000; padding: 0.5vmin; line-height: 3.5vmin; height: auto; display: flex; transition-duration: 0.3s; z-index: 9999999999;} #sidepanel-panel.right {right: 3vmin;} #sidepanel-panel.bottom {bottom: 3vmin; flex-direction: column-reverse;} #sidepanel-panel.left {left: 3vmin;} #sidepanel-panel.top {top: 3vmin; flex-direction: column;} .sidepanel-button {padding: 1vmin; margin: 0.5vmin; font-size: 3.5vmin; border-radius: 10%; text-align: center; color: #00000088; background-color: #FFFFFF88; box-shadow:3px 3px 2px #00000022; user-select: none; transition-duration: inherit;} .sidepanel-button:hover {color: #FFFFFFDD; background-color: #000000DD;}', 'sidepanel');
  130.  
  131. SP.create = function() {
  132. // Create panel
  133. const panel = elms.panel = document.createElement('div');
  134. panel.id = _SP._id.panel;
  135. SP.setPosition('bottom-right');
  136. document.body.appendChild(panel);
  137.  
  138. // Prepare buttons
  139. elms.buttons = [];
  140. }
  141.  
  142. // Insert a button to given index
  143. // details = {index, text, faicon, id, tip, className, onclick, listeners}, all optional
  144. // listeners = [..[..args]]. [..args] will be applied as button.addEventListener's args
  145. // faicon = 'fa-icon-name-classname fa-icon-style-classname', this arg stands for a FontAwesome icon to be inserted inside the botton
  146. // Returns the button(HTMLDivElement), including button.faicon(HTMLElement/HTMLSpanElement in firefox, <i>) if faicon is set
  147. SP.insert = function(details) {
  148. const index = details.index;
  149. const text = details.text;
  150. const faicon = details.faicon;
  151. const id = details.id;
  152. const tip = details.tip;
  153. const className = details.className;
  154. const onclick = details.onclick;
  155. const listeners = details.listeners || [];
  156.  
  157. const button = document.createElement('div');
  158. text && (button.innerHTML = text);
  159. id && (button.id = id);
  160. tip && setTooltip(button, tip); //settip(button, tip);
  161. className && (button.className = className);
  162. onclick && (button.onclick = onclick);
  163. if (faicon) {
  164. const i = document.createElement('i');
  165. i.className = faicon;
  166. button.faicon = i;
  167. button.appendChild(i);
  168. }
  169. for (const listener of listeners) {
  170. button.addEventListener.apply(button, listener);
  171. }
  172. button.classList.add(_SP._class.button);
  173.  
  174. elms.buttons = insertItem(elms.buttons, button, index);
  175. index < elms.buttons.length ? elms.panel.insertBefore(button, elms.panel.children[index]) : elms.panel.appendChild(button);
  176. return button;
  177. }
  178.  
  179. // Append a button
  180. SP.add = function(details) {
  181. details.index = elms.buttons.length;
  182. return SP.insert(details);
  183. }
  184.  
  185. // Remove a button
  186. SP.remove = function(arg) {
  187. let index, elm;
  188. if (arg instanceof HTMLElement) {
  189. elm = arg;
  190. index = elms.buttons.indexOf(elm);
  191. } else if (typeof(arg) === 'number') {
  192. index = arg;
  193. elm = elms.buttons[index];
  194. } else if (typeof(arg) === 'string') {
  195. elm = $(elms.panel, arg);
  196. index = elms.buttons.indexOf(elm);
  197. }
  198.  
  199. elms.buttons = delItem(elms.buttons, index);
  200. elm.parentElement.removeChild(elm);
  201. }
  202.  
  203. // Sets the display position by texts like 'right-bottom'
  204. SP.setPosition = function(pos) {
  205. const poses = _SP.direction = pos.split('-');
  206. const avails = _SP._directions;
  207.  
  208. // Available check
  209. if (poses.length !== 2) {return false;}
  210. for (const p of poses) {
  211. if (!avails.includes(p)) {return false;}
  212. }
  213.  
  214. // remove all others
  215. for (const p of avails) {
  216. elms.panel.classList.remove(p);
  217. }
  218.  
  219. // add new pos
  220. for (const p of poses) {
  221. elms.panel.classList.add(p);
  222. }
  223.  
  224. // Change tooltips' direction
  225. elms.buttons && elms.buttons.forEach(setTooltipDirection);
  226. }
  227.  
  228. // Gets the current display position
  229. SP.getPosition = function() {
  230. return _SP.direction.join('-');
  231. }
  232.  
  233. // Append a style text to document(<head>) with a <style> element
  234. // Replaces existing id-specificed <style>s
  235. function spAddStyle(css, id) {
  236. const style = document.createElement("style");
  237. id && (style.id = id);
  238. style.textContent = css;
  239. for (const elm of $All('#'+id)) {
  240. elm.parentElement && elm.parentElement.removeChild(elm);
  241. }
  242. document.head.appendChild(style);
  243. }
  244.  
  245. // Set a tooltip to the element
  246. function setTooltip(elm, text, direction='auto') {
  247. elm.tooltip = tippy(elm, {
  248. content: text,
  249. arrow: true,
  250. hideOnClick: false
  251. });
  252.  
  253. setTooltipDirection(elm, direction);
  254. }
  255.  
  256. function setTooltipDirection(elm, direction='auto') {
  257. direction === 'auto' && (direction = _SP.direction.includes('left') ? 'right' : 'left');
  258. if (!_SP._directions.includes(direction)) {throw new Error('setTooltip: invalid direction');}
  259.  
  260. // Tippy direction
  261. if (!elm.tooltip) {
  262. DoLog(LogLevel.Error, 'SidePanel.setTooltipDirection: Given elm has no tippy instance(elm.tooltip)');
  263. throw new Error('SidePanel.setTooltipDirection: Given elm has no tippy instance(elm.tooltip)');
  264. }
  265. elm.tooltip.setProps({
  266. placement: direction
  267. });
  268. }
  269.  
  270. // Del an item from an array using its index. Returns the array but can NOT modify the original array directly!!
  271. function delItem(arr, index) {
  272. arr = arr.slice(0, index).concat(arr.slice(index+1));
  273. return arr;
  274. }
  275.  
  276. // Insert an item into an array using given index. Returns the array but can NOT modify the original array directly!!
  277. function insertItem(arr, item, index) {
  278. arr = arr.slice(0, index).concat(item).concat(arr.slice(index));
  279. return arr;
  280. }
  281. }
  282. })();

QingJ © 2025

镜像随时可能失效,请加Q群300939539或关注我们的公众号极客氢云获取最新地址