轻小说文库++的脚本设置界面
This script should not be not be installed directly. It is a library for other scripts to include with the meta directive // @require https://update.greasyfork.org/scripts/450210/1094690/wk-settings.js
/* eslint-disable no-multi-spaces */ /* eslint-disable no-implicit-globals */ /* eslint-disable userscripts/no-invalid-headers */ /* eslint-disable userscripts/no-invalid-grant */ // ==UserScript== // @name settings // @displayname 设置界面 // @namespace Wenku8++ // @version 0.3.12 // @description 轻小说文库++的脚本设置界面 // @author PY-DNG // @license GPL-v3 // @regurl https?://www\.wenku8\.net/.* // @require https://greasyfork.org/scripts/449412-basic-functions/code/Basic%20Functions.js?version=1085783 // @require https://greasyfork.org/scripts/449583-configmanager/code/ConfigManager.js?version=1085836 // @grant GM_getValue // @grant GM_setValue // @grant GM_deleteValue // @grant GM_listValues // @grant ML_listModules // @grant ML_getModule // @grant ML_disableModule // @grant ML_enableModule // @grant ML_uninstallModule // @grant ML_moduleLoaded // @protect // ==/UserScript== /* 计划任务: [ ] 模块列表排序显示 [o] 不可用按钮灰选 [x] 模块详细信息展示 [x] 模块运行状态展示 [?] 合并禁用/启用按钮 [x] 按钮点击反馈 [x] 更改设置图标位置 [x] 模块卸载后删除模块行 [ ] 删除无用旧版设置界面相关代码 [ ] 未安装模块列表与安装功能外壳 [ ] 模块更新功能外壳 */ (function __MAIN__() { const ASSETS = require('assets'); const alertify = require('alertify'); const tippy = require('tippy'); const SPanel = require('SidePanel'); const SettingPanel = require('SettingPanel'); const mousetip = require('mousetip'); const CONST = { Text: { Button: '设置', Title: '脚本设置', ModuleManage: '模块管理', OpenModuleDialog: '点此打开管理面板', ModuleSettings: '模块设置', Module: '模块', Operation: '操作', DisableModule: '禁用模块', EnableModule: '启用模块', NotDisablable: '不可禁用', UninstallModule: '卸载模块', NotUninstallable: '不可卸载', AlertTitle: '模块设置界面', NoLoadNoSettings: '模块并未在此页面上运行,无法获取设置', NoSettings: '该模块当前并没有提供设置选项', ModuleEnabled: '已启用</br>相关页面需要刷新后才能启动此模块', ModuleDisabled: '已禁用</br>相关页面需要刷新后才能停止此模块', ModuleUninstalled: '已卸载</br>相关页面需要刷新/关闭后才能彻底清除此模块', ModuleDisableFailed: '禁用失败</br>请检查该模块是否不可禁用', ModuleUninstallFailed: '卸载失败</br>请检查该模块是否不可卸载', RememberSaving: '修改设置/恢复设置后记得点击保存哦:)', ModuleMissing: '模块不见了?!</br>您似乎没有安装这个模块哦…是不是之前在别的标签页里把它卸载了?', ModuleDetail: '<span class="{CT}">名称:</span><span name="name"></span></br><span class="{CT}">描述:</span><span name="description"></span></br><span class="{CT}">版本:</span><span name="version"></span></br><span class="{CT}">作者:</span><span name="author"></span></br><span class="{CT}">版权:</span><span name="license"></span></br><span class="{CT}">来源:</span><span name="src"></span></br><span class="{CT}">是否已运行:</span><span name="loaded"></span></br><span class="{CT}">是否已启用:</span><span name="enabled"></span></br><span class="{CT}">是否可禁用:</span><span name="can_disable"></span></br><span class="{CT}">是否可卸载:</span><span name="can_uninstall"></span></br><span class="{CT}">是否为系统模块:</span><span name="system"></span>'.replaceAll('{CT}', ASSETS.ClassName.Text), Boolean: { 'true': '是', 'false': '否' }, }, Faicon: { Info: 'fa-solid fa-circle-info' }, Config_Ruleset: { 'version-key': 'config-version', 'ignores': ["LOCAL-CDN"], 'defaultValues': { //'config-key': {}, }, 'updaters': { /*'config-key': [ function() { // This function contains updater for config['config-key'] from v0 to v1 }, function() { // This function contains updater for config['config-key'] from v1 to v2 } ]*/ } } }; const UMManager = new UserModuleManager(); SPanel.insert({ index: 1, faicon: 'fa-solid fa-gear', tip: CONST.Text.Button, onclick: UMManager.show }); exports = { isSettingPage: isSettingPage, insertLines: insertLines, registerSettings: UMManager.registerModuleSettings }; function main() { // Get elements const content = $('#content'); // Insert settings const title = [ [{html: CONST.Text.Title, colSpan: 3, class: 'foot', key: 'settitle'}], [{html: CONST.Text.ModuleManage, colSpan: 1}, {html: CONST.Text.OpenModuleDialog, colSpan: 2, onclick: UMManager.show}], //[{html: CONST.Text.XXXX, colSpan: 1, key: 'xxxxxxxx'}, {html: CONST.Text.XXXX, colSpan: 2, key: 'xxxxxxxx'}], ] const elements = insertLines(title); // scrollIntoView if need getUrlArgv('tosettings') === 'true' && elements.settitle.scrollIntoView(); } // Module manager user interface function UserModuleManager() { const UMM = this; const moduleSettingFuncs = {}; UMM.show = show; UMM.registerModuleSettings = registerModuleSettings; UMM.showModuleSettings = showModuleSettings; function show() { //box.set('message', 'No implemented yet!').show(); const modules = ML_listModules(); // Make panel const SetPanel = new SettingPanel.SettingPanel({ header: CONST.Text.ModuleManage, tables: [] }); // Make table const table = new SetPanel.PanelTable({}); // Make header table.appendRow({ blocks: [{ isHeader: true, colSpan: 1, width: '60%', innerText: CONST.Text.Module, },{ isHeader: true, colSpan: 4, width: '40%', innerText: CONST.Text.Operation, }] }); // Make module rows for (const module of modules) { const id = module.identifier; const row = new SetPanel.PanelRow({ blocks: [{ // Module info colSpan: 1, rowSpan: 1, children: [ (() => { const icon = $CrE('i'); icon.className = CONST.Faicon.Info; icon.style.marginRight = '0.5em'; icon.classList.add(ASSETS.ClassName.Text); tippy(icon, { content: makeContent(), onTrigger: (instance, event) => { instance.setContent(makeContent()); } }); return icon; function makeContent() { const module = ML_getModule(id); if (!module) { alertify.alert(CONST.Text.AlertTitle, CONST.Text.ModuleMissing); row.remove(); return false; } const status = { loaded: ML_moduleLoaded(id), system: !!(module.flags & ASSETS.FLAG.SYSTEM), can_uninstall: !(module.flags & ASSETS.FLAG.NO_UNINSTALL), can_disable: !(module.flags & ASSETS.FLAG.NO_DISABLE), } const tip = $CrE('div'); tip.innerHTML = CONST.Text.ModuleDetail; tip.childNodes.forEach((elm) => { if (!elm instanceof HTMLElement) {return;} const name = elm.getAttribute('name'); if (name && module.hasOwnProperty(name) || status.hasOwnProperty(name)) { const info = module.hasOwnProperty(name) ? module : status; elm.innerText = ({ string: (s) => (s), boolean: (b) => (CONST.Text.Boolean[b.toString()]) })[typeof info[name]](info[name]); } }); return tip; } }) (), (() => { const span = $CrE('span'); span.innerText = module.displayname || module.name; return span; }) (), ], },{ // Module settings colSpan: 1, rowSpan: 1, children: [makeBtn({ text: CONST.Text.ModuleSettings, onclick: function() { if (!ML_getModule(id)) { alertify.alert(CONST.Text.AlertTitle, CONST.Text.ModuleMissing); row.remove(); return false; } return showModuleSettings(id) ? 0 : 1; }, alt: [CONST.Text.RememberSaving, null] })] },{ // Diable module colSpan: 1, rowSpan: 1, children: [makeBtn({ text: canDisable(module) ? CONST.Text.DisableModule : CONST.Text.NotDisablable, onclick: function() { if (!ML_getModule(id)) { alertify.alert(CONST.Text.AlertTitle, CONST.Text.ModuleMissing); row.remove(); return false; } return ML_disableModule(id) ? 0 : 1; }, disabled: !canDisable(module), alt: [CONST.Text.ModuleDisabled, CONST.Text.ModuleDisableFailed] })] },{ // Enable module colSpan: 1, rowSpan: 1, children: [makeBtn({ text: CONST.Text.EnableModule, onclick: function () { if (!ML_getModule(id)) { alertify.alert(CONST.Text.AlertTitle, CONST.Text.ModuleMissing); row.remove(); return false; } ML_enableModule(id); }, alt: CONST.Text.ModuleEnabled })] },{ // Uninstall module colSpan: 1, rowSpan: 1, children: [makeBtn({ text: canUninstall(module) ? CONST.Text.UninstallModule : CONST.Text.NotUninstallable, onclick: function() { if (!ML_getModule(id)) { alertify.alert(CONST.Text.AlertTitle, CONST.Text.ModuleMissing); row.remove(); return false; } const success = ML_uninstallModule(id); success && row.remove(); return success ? 0 : 1; }, disabled: !canUninstall(module), alt: [CONST.Text.ModuleUninstalled, CONST.Text.ModuleUninstallFailed] })] }] }); table.appendRow(row); } SetPanel.appendTable(table); function makeBtn(details) { // Get arguments let text, onclick, disabled, alt; text = details.text; onclick = details.onclick; disabled = details.disabled; alt = details.alt; const span = $CrE('span'); span.innerText = text; onclick && span.addEventListener('click', _onclick); span.classList.add(ASSETS.ClassName.Button); disabled && span.classList.add(ASSETS.ClassName.Disabled); return span; function _onclick() { const r###lt = !disabled && onclick ? onclick() : 0; const alt_content = alt && (Array.isArray(alt) ? alt[r###lt] : alt); !disabled && ![null, false].includes(r###lt) && alt_content && alertify.message(alt_content); } } function canUninstall(module) { return !(module.flags & ASSETS.FLAG.NO_UNINSTALL); } function canDisable(module) { return !(module.flags & ASSETS.FLAG.NO_DISABLE); } } function registerModuleSettings(id, func) { moduleSettingFuncs[id] = func; } function showModuleSettings(id) { const func = moduleSettingFuncs[id]; if (typeof func === 'function') { func(); return true; } else { if (!ML_moduleLoaded(id)) { alertify.alert(CONST.Text.AlertTitle, CONST.Text.NoLoadNoSettings); } else { alertify.alert(CONST.Text.AlertTitle, CONST.Text.NoSettings); } return false; } } } function insertLines(lines, tbody) { !tbody && (tbody = $(content, 'table>tbody')); const elements = {}; for (const line of lines) { const tr = $CrE('tr'); for (const item of line) { const td = $CrE('td'); item.html && (td.innerHTML = item.html); item.colSpan && (td.colSpan = item.colSpan); item.class && (td.className = item.class); item.id && (td.id = item.id); item.tiptitle && mousetip.settip(td, item.tiptitle); item.key && (elements[item.key] = td); if (item.onclick) { td.style.color = 'grey'; td.style.textAlign = 'center'; td.addEventListener('click', item.onclick); } td.style.padding = '3px'; tr.appendChild(td); } tbody.appendChild(tr); } return elements; } function isSettingPage(callback) { const page = getAPI()[0] === 'userdetail.php'; page && callback && callback(); return page; } function htmlEncode(text) { const span = $CrE('div'); span.innerText = text; return span.innerHTML; } // Change location.href without reloading using history.pushState/replaceState function setPageUrl() { let win, url, push; switch (arguments.length) { case 1: win = window; url = arguments[0]; push = false; break; case 2: win = arguments[0]; url = arguments[1]; push = false; break; case 3: win = arguments[0]; url = arguments[1]; push = arguments[2]; } return win.history[push ? 'pushState' : 'replaceState']({modified: true, ...history.state}, '', url); } })();