wk-settings

轻小说文库++的脚本设置界面

此脚本不应直接安装。它是供其他脚本使用的外部库,要使用该库请加入元指令 // @require https://update.gf.qytechs.cn/scripts/450210/1094690/wk-settings.js

  1. /* eslint-disable no-multi-spaces */
  2. /* eslint-disable no-implicit-globals */
  3. /* eslint-disable userscripts/no-invalid-headers */
  4. /* eslint-disable userscripts/no-invalid-grant */
  5.  
  6. // ==UserScript==
  7. // @name settings
  8. // @displayname 设置界面
  9. // @namespace Wenku8++
  10. // @version 0.3.12
  11. // @description 轻小说文库++的脚本设置界面
  12. // @author PY-DNG
  13. // @license GPL-v3
  14. // @regurl https?://www\.wenku8\.net/.*
  15. // @require https://gf.qytechs.cn/scripts/449412-basic-functions/code/Basic%20Functions.js?version=1085783
  16. // @require https://gf.qytechs.cn/scripts/449583-configmanager/code/ConfigManager.js?version=1085836
  17. // @grant GM_getValue
  18. // @grant GM_setValue
  19. // @grant GM_deleteValue
  20. // @grant GM_listValues
  21. // @grant ML_listModules
  22. // @grant ML_getModule
  23. // @grant ML_disableModule
  24. // @grant ML_enableModule
  25. // @grant ML_uninstallModule
  26. // @grant ML_moduleLoaded
  27. // @protect
  28. // ==/UserScript==
  29.  
  30. /*
  31. 计划任务:
  32. [ ] 模块列表排序显示
  33. [o] 不可用按钮灰选
  34. [x] 模块详细信息展示
  35. [x] 模块运行状态展示
  36. [?] 合并禁用/启用按钮
  37. [x] 按钮点击反馈
  38. [x] 更改设置图标位置
  39. [x] 模块卸载后删除模块行
  40. [ ] 删除无用旧版设置界面相关代码
  41. [ ] 未安装模块列表与安装功能外壳
  42. [ ] 模块更新功能外壳
  43. */
  44.  
  45. (function __MAIN__() {
  46. const ASSETS = require('assets');
  47. const alertify = require('alertify');
  48. const tippy = require('tippy');
  49. const SPanel = require('SidePanel');
  50. const SettingPanel = require('SettingPanel');
  51. const mousetip = require('mousetip');
  52. const CONST = {
  53. Text: {
  54. Button: '设置',
  55. Title: '脚本设置',
  56. ModuleManage: '模块管理',
  57. OpenModuleDialog: '点此打开管理面板',
  58. ModuleSettings: '模块设置',
  59. Module: '模块',
  60. Operation: '操作',
  61. DisableModule: '禁用模块',
  62. EnableModule: '启用模块',
  63. NotDisablable: '不可禁用',
  64. UninstallModule: '卸载模块',
  65. NotUninstallable: '不可卸载',
  66. AlertTitle: '模块设置界面',
  67. NoLoadNoSettings: '模块并未在此页面上运行,无法获取设置',
  68. NoSettings: '该模块当前并没有提供设置选项',
  69. ModuleEnabled: '已启用</br>相关页面需要刷新后才能启动此模块',
  70. ModuleDisabled: '已禁用</br>相关页面需要刷新后才能停止此模块',
  71. ModuleUninstalled: '已卸载</br>相关页面需要刷新/关闭后才能彻底清除此模块',
  72. ModuleDisableFailed: '禁用失败</br>请检查该模块是否不可禁用',
  73. ModuleUninstallFailed: '卸载失败</br>请检查该模块是否不可卸载',
  74. RememberSaving: '修改设置/恢复设置后记得点击保存哦:)',
  75. ModuleMissing: '模块不见了?!</br>您似乎没有安装这个模块哦…是不是之前在别的标签页里把它卸载了?',
  76. 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),
  77. Boolean: {
  78. 'true': '是',
  79. 'false': '否'
  80. },
  81. },
  82. Faicon: {
  83. Info: 'fa-solid fa-circle-info'
  84. },
  85. Config_Ruleset: {
  86. 'version-key': 'config-version',
  87. 'ignores': ["LOCAL-CDN"],
  88. 'defaultValues': {
  89. //'config-key': {},
  90. },
  91. 'updaters': {
  92. /*'config-key': [
  93. function() {
  94. // This function contains updater for config['config-key'] from v0 to v1
  95. },
  96. function() {
  97. // This function contains updater for config['config-key'] from v1 to v2
  98. }
  99. ]*/
  100. }
  101. }
  102. };
  103.  
  104. const UMManager = new UserModuleManager();
  105. SPanel.insert({
  106. index: 1,
  107. faicon: 'fa-solid fa-gear',
  108. tip: CONST.Text.Button,
  109. onclick: UMManager.show
  110. });
  111.  
  112. exports = {
  113. isSettingPage: isSettingPage,
  114. insertLines: insertLines,
  115. registerSettings: UMManager.registerModuleSettings
  116. };
  117.  
  118. function main() {
  119. // Get elements
  120. const content = $('#content');
  121.  
  122. // Insert settings
  123. const title = [
  124. [{html: CONST.Text.Title, colSpan: 3, class: 'foot', key: 'settitle'}],
  125. [{html: CONST.Text.ModuleManage, colSpan: 1}, {html: CONST.Text.OpenModuleDialog, colSpan: 2, onclick: UMManager.show}],
  126. //[{html: CONST.Text.XXXX, colSpan: 1, key: 'xxxxxxxx'}, {html: CONST.Text.XXXX, colSpan: 2, key: 'xxxxxxxx'}],
  127. ]
  128. const elements = insertLines(title);
  129.  
  130. // scrollIntoView if need
  131. getUrlArgv('tosettings') === 'true' && elements.settitle.scrollIntoView();
  132. }
  133.  
  134. // Module manager user interface
  135. function UserModuleManager() {
  136. const UMM = this;
  137. const moduleSettingFuncs = {};
  138.  
  139. UMM.show = show;
  140.  
  141. UMM.registerModuleSettings = registerModuleSettings;
  142.  
  143. UMM.showModuleSettings = showModuleSettings;
  144.  
  145. function show() {
  146. //box.set('message', 'No implemented yet!').show();
  147. const modules = ML_listModules();
  148.  
  149. // Make panel
  150. const SetPanel = new SettingPanel.SettingPanel({
  151. header: CONST.Text.ModuleManage,
  152. tables: []
  153. });
  154.  
  155. // Make table
  156. const table = new SetPanel.PanelTable({});
  157.  
  158. // Make header
  159. table.appendRow({
  160. blocks: [{
  161. isHeader: true,
  162. colSpan: 1,
  163. width: '60%',
  164. innerText: CONST.Text.Module,
  165. },{
  166. isHeader: true,
  167. colSpan: 4,
  168. width: '40%',
  169. innerText: CONST.Text.Operation,
  170. }]
  171. });
  172.  
  173. // Make module rows
  174. for (const module of modules) {
  175. const id = module.identifier;
  176. const row = new SetPanel.PanelRow({
  177. blocks: [{
  178. // Module info
  179. colSpan: 1,
  180. rowSpan: 1,
  181. children: [
  182. (() => {
  183. const icon = $CrE('i');
  184. icon.className = CONST.Faicon.Info;
  185. icon.style.marginRight = '0.5em';
  186. icon.classList.add(ASSETS.ClassName.Text);
  187.  
  188. tippy(icon, {
  189. content: makeContent(),
  190. onTrigger: (instance, event) => {
  191. instance.setContent(makeContent());
  192. }
  193. });
  194. return icon;
  195.  
  196. function makeContent() {
  197. const module = ML_getModule(id);
  198. if (!module) {
  199. alertify.alert(CONST.Text.AlertTitle, CONST.Text.ModuleMissing);
  200. row.remove();
  201. return false;
  202. }
  203. const status = {
  204. loaded: ML_moduleLoaded(id),
  205. system: !!(module.flags & ASSETS.FLAG.SYSTEM),
  206. can_uninstall: !(module.flags & ASSETS.FLAG.NO_UNINSTALL),
  207. can_disable: !(module.flags & ASSETS.FLAG.NO_DISABLE),
  208. }
  209. const tip = $CrE('div');
  210. tip.innerHTML = CONST.Text.ModuleDetail;
  211. tip.childNodes.forEach((elm) => {
  212. if (!elm instanceof HTMLElement) {return;}
  213. const name = elm.getAttribute('name');
  214. if (name && module.hasOwnProperty(name) || status.hasOwnProperty(name)) {
  215. const info = module.hasOwnProperty(name) ? module : status;
  216. elm.innerText = ({
  217. string: (s) => (s),
  218. boolean: (b) => (CONST.Text.Boolean[b.toString()])
  219. })[typeof info[name]](info[name]);
  220. }
  221. });
  222.  
  223. return tip;
  224. }
  225. }) (),
  226. (() => {
  227. const span = $CrE('span');
  228. span.innerText = module.displayname || module.name;
  229. return span;
  230. }) (),
  231. ],
  232. },{
  233. // Module settings
  234. colSpan: 1,
  235. rowSpan: 1,
  236. children: [makeBtn({
  237. text: CONST.Text.ModuleSettings,
  238. onclick: function() {
  239. if (!ML_getModule(id)) {
  240. alertify.alert(CONST.Text.AlertTitle, CONST.Text.ModuleMissing);
  241. row.remove();
  242. return false;
  243. }
  244. return showModuleSettings(id) ? 0 : 1;
  245. },
  246. alt: [CONST.Text.RememberSaving, null]
  247. })]
  248. },{
  249. // Diable module
  250. colSpan: 1,
  251. rowSpan: 1,
  252. children: [makeBtn({
  253. text: canDisable(module) ? CONST.Text.DisableModule : CONST.Text.NotDisablable,
  254. onclick: function() {
  255. if (!ML_getModule(id)) {
  256. alertify.alert(CONST.Text.AlertTitle, CONST.Text.ModuleMissing);
  257. row.remove();
  258. return false;
  259. }
  260. return ML_disableModule(id) ? 0 : 1;
  261. },
  262. disabled: !canDisable(module),
  263. alt: [CONST.Text.ModuleDisabled, CONST.Text.ModuleDisableFailed]
  264. })]
  265. },{
  266. // Enable module
  267. colSpan: 1,
  268. rowSpan: 1,
  269. children: [makeBtn({
  270. text: CONST.Text.EnableModule,
  271. onclick: function () {
  272. if (!ML_getModule(id)) {
  273. alertify.alert(CONST.Text.AlertTitle, CONST.Text.ModuleMissing);
  274. row.remove();
  275. return false;
  276. }
  277. ML_enableModule(id);
  278. },
  279. alt: CONST.Text.ModuleEnabled
  280. })]
  281. },{
  282. // Uninstall module
  283. colSpan: 1,
  284. rowSpan: 1,
  285. children: [makeBtn({
  286. text: canUninstall(module) ? CONST.Text.UninstallModule : CONST.Text.NotUninstallable,
  287. onclick: function() {
  288. if (!ML_getModule(id)) {
  289. alertify.alert(CONST.Text.AlertTitle, CONST.Text.ModuleMissing);
  290. row.remove();
  291. return false;
  292. }
  293. const success = ML_uninstallModule(id);
  294. success && row.remove();
  295. return success ? 0 : 1;
  296. },
  297. disabled: !canUninstall(module),
  298. alt: [CONST.Text.ModuleUninstalled, CONST.Text.ModuleUninstallFailed]
  299. })]
  300. }]
  301. });
  302. table.appendRow(row);
  303. }
  304. SetPanel.appendTable(table);
  305.  
  306. function makeBtn(details) {
  307. // Get arguments
  308. let text, onclick, disabled, alt;
  309. text = details.text;
  310. onclick = details.onclick;
  311. disabled = details.disabled;
  312. alt = details.alt;
  313.  
  314. const span = $CrE('span');
  315. span.innerText = text;
  316. onclick && span.addEventListener('click', _onclick);
  317. span.classList.add(ASSETS.ClassName.Button);
  318. disabled && span.classList.add(ASSETS.ClassName.Disabled);
  319.  
  320. return span;
  321.  
  322. function _onclick() {
  323. const result = !disabled && onclick ? onclick() : 0;
  324. const alt_content = alt && (Array.isArray(alt) ? alt[result] : alt);
  325. !disabled && ![null, false].includes(result) && alt_content && alertify.message(alt_content);
  326. }
  327. }
  328.  
  329. function canUninstall(module) {
  330. return !(module.flags & ASSETS.FLAG.NO_UNINSTALL);
  331. }
  332.  
  333. function canDisable(module) {
  334. return !(module.flags & ASSETS.FLAG.NO_DISABLE);
  335. }
  336. }
  337.  
  338. function registerModuleSettings(id, func) {
  339. moduleSettingFuncs[id] = func;
  340. }
  341.  
  342. function showModuleSettings(id) {
  343. const func = moduleSettingFuncs[id];
  344. if (typeof func === 'function') {
  345. func();
  346. return true;
  347. } else {
  348. if (!ML_moduleLoaded(id)) {
  349. alertify.alert(CONST.Text.AlertTitle, CONST.Text.NoLoadNoSettings);
  350. } else {
  351. alertify.alert(CONST.Text.AlertTitle, CONST.Text.NoSettings);
  352. }
  353. return false;
  354. }
  355. }
  356. }
  357.  
  358. function insertLines(lines, tbody) {
  359. !tbody && (tbody = $(content, 'table>tbody'));
  360. const elements = {};
  361. for (const line of lines) {
  362. const tr = $CrE('tr');
  363. for (const item of line) {
  364. const td = $CrE('td');
  365. item.html && (td.innerHTML = item.html);
  366. item.colSpan && (td.colSpan = item.colSpan);
  367. item.class && (td.className = item.class);
  368. item.id && (td.id = item.id);
  369. item.tiptitle && mousetip.settip(td, item.tiptitle);
  370. item.key && (elements[item.key] = td);
  371. if (item.onclick) {
  372. td.style.color = 'grey';
  373. td.style.textAlign = 'center';
  374. td.addEventListener('click', item.onclick);
  375. }
  376. td.style.padding = '3px';
  377. tr.appendChild(td);
  378. }
  379. tbody.appendChild(tr);
  380. }
  381. return elements;
  382. }
  383.  
  384. function isSettingPage(callback) {
  385. const page = getAPI()[0] === 'userdetail.php';
  386. page && callback && callback();
  387. return page;
  388. }
  389.  
  390. function htmlEncode(text) {
  391. const span = $CrE('div');
  392. span.innerText = text;
  393. return span.innerHTML;
  394. }
  395.  
  396. // Change location.href without reloading using history.pushState/replaceState
  397. function setPageUrl() {
  398. let win, url, push;
  399. switch (arguments.length) {
  400. case 1:
  401. win = window;
  402. url = arguments[0];
  403. push = false;
  404. break;
  405. case 2:
  406. win = arguments[0];
  407. url = arguments[1];
  408. push = false;
  409. break;
  410. case 3:
  411. win = arguments[0];
  412. url = arguments[1];
  413. push = arguments[2];
  414. }
  415. return win.history[push ? 'pushState' : 'replaceState']({modified: true, ...history.state}, '', url);
  416. }
  417. })();

QingJ © 2025

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