/* 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.2.12
// @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
// @require https://gf.qytechs.cn/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] 不可用按钮灰选
[o] 模块详细信息展示
[ ] 模块运行状态展示
*/
(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: '该模块当前并没有提供设置选项',
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="enabled"></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.add({
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 row = new SetPanel.PanelRow({
blocks: [{
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 m = ML_getModule(module.identifier);
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 && m.hasOwnProperty(name)) {
elm.innerText = ({
string: (s) => (s),
boolean: (b) => (CONST.Text.Boolean[b.toString()])
})[typeof m[name]](m[name]);
}
});
return tip;
}
}) (),
(() => {
const span = $CrE('span');
span.innerText = module.displayname || module.name;
return span;
}) (),
],
},{
colSpan: 1,
rowSpan: 1,
children: [makeBtn(
CONST.Text.ModuleSettings,
showModuleSettings.bind(null, module.identifier)
)]
},{
colSpan: 1,
rowSpan: 1,
children: [makeBtn(
canDisable(module) ? CONST.Text.DisableModule : CONST.Text.NotDisablable,
canDisable(module) ? ML_disableModule.bind(null, module.identifier) : null,
!canDisable(module)
)]
},{
colSpan: 1,
rowSpan: 1,
children: [makeBtn(
CONST.Text.EnableModule,
ML_enableModule.bind(null, module.identifier)
)]
},{
colSpan: 1,
rowSpan: 1,
children: [makeBtn(
canUninstall(module) ? CONST.Text.UninstallModule : CONST.Text.NotUninstallable,
canUninstall(module) ? ML_uninstallModule.bind(null, module.identifier) : null,
!canUninstall(module)
)]
}]
});
table.appendRow(row);
}
SetPanel.appendTable(table);
function makeBtn(text, onclick, disabled) {
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 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);
}
})();