- /* eslint-disable no-multi-spaces */
- /* eslint-disable userscripts/no-invalid-headers */
- /* eslint-disable userscripts/no-invalid-grant */
-
- // ==UserScript==
- // @name SidePanel
- // @namespace Wenku8++
- // @version 0.1.5
- // @description 轻小说文库++的侧边工具栏
- // @author PY-DNG
- // @license GPL-v3
- // @regurl https?://www\.wenku8\.net/.*
- // @require https://gf.qytechs.cn/scripts/449412-basic-functions/code/Basic%20Functions.js?version=1085783
- // @grant none
- // ==/UserScript==
-
- (function __MAIN__() {
- 'use strict';
-
- const tippy = require('tippy');
-
- exports = sideFunctions();
-
- // Side functions area
- function sideFunctions() {
- const SPanel = new SidePanel();
- SPanel.create();
- SPanel.setPosition('bottom-right');
- 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;}');
-
- commonButtons();
- return SPanel;
-
- function commonButtons() {
- // Button show/hide-all-buttons
- const btnShowHide = SPanel.add({
- faicon: 'fa-solid fa-down-left-and-up-right-to-center',
- className: 'accept-pointer',
- tip: '隐藏面板',
- onclick: (function() {
- let hidden = false;
- return (e) => {
- hidden = !hidden;
- btnShowHide.faicon.className = 'fa-solid ' + (hidden ? 'fa-up-right-and-down-left-from-center' : 'fa-down-left-and-up-right-to-center');
- btnShowHide.classList[hidden ? 'add' : 'remove']('low-opacity');
- btnShowHide.setAttribute('aria-label', (hidden ? '显示面板' : '隐藏面板'));
- SPanel.elements.panel.style.pointerEvents = hidden ? 'none' : 'auto';
- for (const button of SPanel.elements.buttons) {
- if (button === btnShowHide) {continue;}
- //button.style.display = hidden ? 'none' : 'block';
- button.style.pointerEvents = hidden ? 'none' : 'auto';
- button.style.opacity = hidden ? '0' : '1';
- }
- };
- }) ()
- });
-
- // Button scroll-to-bottom
- const btnDown = SPanel.add({
- faicon: 'fa-solid fa-angle-down',
- tip: '转到底部',
- onclick: (e) => {
- const elms = [document.body.parentElement, $('#content'), $('#contentmain')];
-
- for (const elm of elms) {
- elm && elm.scrollTo && elm.scrollTo(elm.scrollLeft, elm.scrollHeight);
- }
- }
- });
-
- // Button scroll-to-top
- const btnUp = SPanel.add({
- faicon: 'fa-solid fa-angle-up',
- tip: '转到顶部',
- onclick: (e) => {
- const elms = [document.body.parentElement, $('#content'), $('#contentmain')];
-
- for (const elm of elms) {
- elm && elm.scrollTo && elm.scrollTo(elm.scrollLeft, 0);
- }
- }
- });
-
- // Darkmode
- /*
- const btnDarkmode = SPanel.add({
- faicon: 'fa-solid ' + (DMode.isActivated() ? 'fa-sun' : 'fa-moon'),
- tip: '明暗切换',
- onclick: (e) => {
- DMode.toggle();
- btnDarkmode.faicon.className = 'fa-solid ' + (DMode.isActivated() ? 'fa-sun' : 'fa-moon');
- }
- });
- */
-
- // Refresh page
- const btnRefresh = SPanel.add({
- faicon: 'fa-solid fa-rotate-right',
- tip: '刷新页面',
- onclick: (e) => {
- location.href = location.href;
- }
- });
- }
- }
-
- // Side-located control panel
- // Requirements: FontAwesome, tippy.js, addStyle
- // Use 'new' keyword
- function SidePanel() {
- // Public SP
- const SP = this;
- const elms = SP.elements = {};
-
- // Private _SP
- // keys start with '_' shouldn't be modified
- const _SP = {
- _id: {
- css: 'sidepanel-style',
- usercss: 'sidepanel-style-user',
- panel: 'sidepanel-panel'
- },
- _class: {
- button: 'sidepanel-button'
- },
- _directions: ['left', 'right', 'top', 'bottom']
- };
-
- 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');
-
- SP.create = function() {
- // Create panel
- const panel = elms.panel = document.createElement('div');
- panel.id = _SP._id.panel;
- SP.setPosition('bottom-right');
- document.body.appendChild(panel);
-
- // Prepare buttons
- elms.buttons = [];
- }
-
- // Insert a button to given index
- // details = {index, text, faicon, id, tip, className, onclick, listeners}, all optional
- // listeners = [..[..args]]. [..args] will be applied as button.addEventListener's args
- // faicon = 'fa-icon-name-classname fa-icon-style-classname', this arg stands for a FontAwesome icon to be inserted inside the botton
- // Returns the button(HTMLDivElement), including button.faicon(HTMLElement/HTMLSpanElement in firefox, <i>) if faicon is set
- SP.insert = function(details) {
- const index = details.index;
- const text = details.text;
- const faicon = details.faicon;
- const id = details.id;
- const tip = details.tip;
- const className = details.className;
- const onclick = details.onclick;
- const listeners = details.listeners || [];
-
- const button = document.createElement('div');
- text && (button.innerHTML = text);
- id && (button.id = id);
- tip && setTooltip(button, tip); //settip(button, tip);
- className && (button.className = className);
- onclick && (button.onclick = onclick);
- if (faicon) {
- const i = document.createElement('i');
- i.className = faicon;
- button.faicon = i;
- button.appendChild(i);
- }
- for (const listener of listeners) {
- button.addEventListener.apply(button, listener);
- }
- button.classList.add(_SP._class.button);
-
- elms.buttons = insertItem(elms.buttons, button, index);
- index < elms.buttons.length ? elms.panel.insertBefore(button, elms.panel.children[index]) : elms.panel.appendChild(button);
- return button;
- }
-
- // Append a button
- SP.add = function(details) {
- details.index = elms.buttons.length;
- return SP.insert(details);
- }
-
- // Remove a button
- SP.remove = function(arg) {
- let index, elm;
- if (arg instanceof HTMLElement) {
- elm = arg;
- index = elms.buttons.indexOf(elm);
- } else if (typeof(arg) === 'number') {
- index = arg;
- elm = elms.buttons[index];
- } else if (typeof(arg) === 'string') {
- elm = $(elms.panel, arg);
- index = elms.buttons.indexOf(elm);
- }
-
- elms.buttons = delItem(elms.buttons, index);
- elm.parentElement.removeChild(elm);
- }
-
- // Sets the display position by texts like 'right-bottom'
- SP.setPosition = function(pos) {
- const poses = _SP.direction = pos.split('-');
- const avails = _SP._directions;
-
- // Available check
- if (poses.length !== 2) {return false;}
- for (const p of poses) {
- if (!avails.includes(p)) {return false;}
- }
-
- // remove all others
- for (const p of avails) {
- elms.panel.classList.remove(p);
- }
-
- // add new pos
- for (const p of poses) {
- elms.panel.classList.add(p);
- }
-
- // Change tooltips' direction
- elms.buttons && elms.buttons.forEach(setTooltipDirection);
- }
-
- // Gets the current display position
- SP.getPosition = function() {
- return _SP.direction.join('-');
- }
-
- // Append a style text to document(<head>) with a <style> element
- // Replaces existing id-specificed <style>s
- function spAddStyle(css, id) {
- const style = document.createElement("style");
- id && (style.id = id);
- style.textContent = css;
- for (const elm of $All('#'+id)) {
- elm.parentElement && elm.parentElement.removeChild(elm);
- }
- document.head.appendChild(style);
- }
-
- // Set a tooltip to the element
- function setTooltip(elm, text, direction='auto') {
- elm.tooltip = tippy(elm, {
- content: text,
- arrow: true,
- hideOnClick: false
- });
-
- setTooltipDirection(elm, direction);
- }
-
- function setTooltipDirection(elm, direction='auto') {
- direction === 'auto' && (direction = _SP.direction.includes('left') ? 'right' : 'left');
- if (!_SP._directions.includes(direction)) {throw new Error('setTooltip: invalid direction');}
-
- // Tippy direction
- if (!elm.tooltip) {
- DoLog(LogLevel.Error, 'SidePanel.setTooltipDirection: Given elm has no tippy instance(elm.tooltip)');
- throw new Error('SidePanel.setTooltipDirection: Given elm has no tippy instance(elm.tooltip)');
- }
- elm.tooltip.setProps({
- placement: direction
- });
- }
-
- // Del an item from an array using its index. Returns the array but can NOT modify the original array directly!!
- function delItem(arr, index) {
- arr = arr.slice(0, index).concat(arr.slice(index+1));
- return arr;
- }
-
- // Insert an item into an array using given index. Returns the array but can NOT modify the original array directly!!
- function insertItem(arr, item, index) {
- arr = arr.slice(0, index).concat(item).concat(arr.slice(index));
- return arr;
- }
- }
- })();