settings

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

当前为 2022-09-16 提交的版本,查看 最新版本

此脚本不应直接安装。它是供其他脚本使用的外部库,要使用该库请加入元指令 // @require https://update.cn-greasyfork.org/scripts/450210/1094427/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.2.9
  11. // @description 轻小说文库++的脚本设置界面
  12. // @author PY-DNG
  13. // @license GPL-v3
  14. // @regurl https?://www\.wenku8\.net/.*
  15. // @require https://greasyfork.org/scripts/449412-basic-functions/code/Basic%20Functions.js?version=1085783
  16. // @require https://greasyfork.org/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_disableModule
  23. // @grant ML_enableModule
  24. // @grant ML_uninstallModule
  25. // @grant ML_moduleLoaded
  26. // @protect
  27. // ==/UserScript==
  28.  
  29. /*
  30. 计划任务:
  31. [ ] 模块列表排序显示
  32. [-] 不可用按钮灰选
  33. [ ] 模块详细信息展示
  34. [ ] 模块运行状态展示
  35. */
  36.  
  37. (function __MAIN__() {
  38. const ASSETS = require('assets');
  39. const alertify = require('alertify');
  40. const tippy = require('tippy');
  41. const SPanel = require('SidePanel');
  42. const SettingPanel = require('SettingPanel');
  43. const mousetip = require('mousetip');
  44. const CONST = {
  45. Text: {
  46. Button: '设置',
  47. Title: '脚本设置',
  48. ModuleManage: '模块管理',
  49. OpenModuleDialog: '点此打开管理面板',
  50. ModuleSettings: '模块设置',
  51. Module: '模块',
  52. Operation: '操作',
  53. DisableModule: '禁用模块',
  54. EnableModule: '启用模块',
  55. NotDisablable: '不可禁用',
  56. UninstallModule: '卸载模块',
  57. NotUninstallable: '不可卸载',
  58. AlertTitle: '模块设置界面',
  59. NoLoadNoSettings: '模块并未在此页面上运行,无法获取设置',
  60. NoSettings: '该模块当前并没有提供设置选项',
  61. 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><span class="{CT}">是否已启用:</span><span name="enabled"></span>'.replaceAll('{CT}', ASSETS.ClassName.Text),
  62. Boolean: {
  63. 'true': '是',
  64. 'false': '否'
  65. },
  66. },
  67. Faicon: {
  68. Info: 'fa-solid fa-circle-info'
  69. },
  70. Config_Ruleset: {
  71. 'version-key': 'config-version',
  72. 'ignores': ["LOCAL-CDN"],
  73. 'defaultValues': {
  74. //'config-key': {},
  75. },
  76. 'updaters': {
  77. /*'config-key': [
  78. function() {
  79. // This function contains updater for config['config-key'] from v0 to v1
  80. },
  81. function() {
  82. // This function contains updater for config['config-key'] from v1 to v2
  83. }
  84. ]*/
  85. }
  86. }
  87. };
  88.  
  89. const UMManager = new UserModuleManager();
  90. SPanel.add({
  91. faicon: 'fa-solid fa-gear',
  92. tip: CONST.Text.Button,
  93. onclick: UMManager.show
  94. });
  95.  
  96. exports = {
  97. isSettingPage: isSettingPage,
  98. insertLines: insertLines,
  99. registerSettings: UMManager.registerModuleSettings
  100. };
  101.  
  102. function main() {
  103. // Get elements
  104. const content = $('#content');
  105.  
  106. // Insert settings
  107. const title = [
  108. [{html: CONST.Text.Title, colSpan: 3, class: 'foot', key: 'settitle'}],
  109. [{html: CONST.Text.ModuleManage, colSpan: 1}, {html: CONST.Text.OpenModuleDialog, colSpan: 2, onclick: UMManager.show}],
  110. //[{html: CONST.Text.XXXX, colSpan: 1, key: 'xxxxxxxx'}, {html: CONST.Text.XXXX, colSpan: 2, key: 'xxxxxxxx'}],
  111. ]
  112. const elements = insertLines(title);
  113.  
  114. // scrollIntoView if need
  115. getUrlArgv('tosettings') === 'true' && elements.settitle.scrollIntoView();
  116. }
  117.  
  118. // Module manager user interface
  119. function UserModuleManager() {
  120. const UMM = this;
  121. const moduleSettingFuncs = {};
  122.  
  123. UMM.show = show;
  124.  
  125. UMM.registerModuleSettings = registerModuleSettings;
  126.  
  127. UMM.showModuleSettings = showModuleSettings;
  128.  
  129. function show() {
  130. //box.set('message', 'No implemented yet!').show();
  131. const modules = ML_listModules();
  132.  
  133. // Make panel
  134. const SetPanel = new SettingPanel.SettingPanel({
  135. header: CONST.Text.ModuleManage,
  136. tables: []
  137. });
  138.  
  139. // Make table
  140. const table = new SetPanel.PanelTable({});
  141.  
  142. // Make header
  143. table.appendRow({
  144. blocks: [{
  145. isHeader: true,
  146. colSpan: 1,
  147. width: '60%',
  148. innerText: CONST.Text.Module,
  149. },{
  150. isHeader: true,
  151. colSpan: 4,
  152. width: '40%',
  153. innerText: CONST.Text.Operation,
  154. }]
  155. });
  156.  
  157. // Make module rows
  158. for (const module of modules) {
  159. const row = new SetPanel.PanelRow({
  160. blocks: [{
  161. colSpan: 1,
  162. rowSpan: 1,
  163. children: [
  164. (() => {
  165. const icon = $CrE('i');
  166. icon.className = CONST.Faicon.Info;
  167. icon.style.marginRight = '0.5em';
  168. icon.classList.add(ASSETS.ClassName.Text);
  169.  
  170. const tip = $CrE('div');
  171. tip.innerHTML = CONST.Text.ModuleDetail;
  172. tip.childNodes.forEach((elm) => {
  173. if (!elm instanceof HTMLElement) {return;}
  174. const name = elm.getAttribute('name');
  175. if (name && module.hasOwnProperty(name)) {
  176. elm.innerText = ({
  177. string: (s) => (s),
  178. boolean: (b) => (CONST.Text.Boolean[b.toString()])
  179. })[typeof module[name]](module[name]);
  180. }
  181. })
  182. tippy(icon, {content: tip});
  183. return icon;
  184. }) (),
  185. (() => {
  186. const span = $CrE('span');
  187. span.innerText = module.displayname || module.name;
  188. return span;
  189. }) (),
  190. ],
  191. },{
  192. colSpan: 1,
  193. rowSpan: 1,
  194. children: [makeBtn(
  195. CONST.Text.ModuleSettings,
  196. showModuleSettings.bind(null, module.identifier)
  197. )]
  198. },{
  199. colSpan: 1,
  200. rowSpan: 1,
  201. children: [makeBtn(
  202. canDisable(module) ? CONST.Text.DisableModule : CONST.Text.NotDisablable,
  203. canDisable(module) ? ML_disableModule.bind(null, module.identifier) : null,
  204. !canDisable(module)
  205. )]
  206. },{
  207. colSpan: 1,
  208. rowSpan: 1,
  209. children: [makeBtn(
  210. CONST.Text.EnableModule,
  211. ML_enableModule.bind(null, module.identifier)
  212. )]
  213. },{
  214. colSpan: 1,
  215. rowSpan: 1,
  216. children: [makeBtn(
  217. canUninstall(module) ? CONST.Text.UninstallModule : CONST.Text.NotUninstallable,
  218. canUninstall(module) ? ML_uninstallModule.bind(null, module.identifier) : null,
  219. !canUninstall(module)
  220. )]
  221. }]
  222. });
  223. table.appendRow(row);
  224. }
  225. SetPanel.appendTable(table);
  226.  
  227. function makeBtn(text, onclick, disabled) {
  228. const span = $CrE('span');
  229. span.innerText = text;
  230. onclick && span.addEventListener('click', onclick);
  231. span.classList.add(ASSETS.ClassName.Button);
  232. disabled && span.classList.add(ASSETS.ClassName.Disabled);
  233. return span;
  234. }
  235.  
  236. function canUninstall(module) {
  237. return !(module.flags & ASSETS.FLAG.NO_UNINSTALL);
  238. }
  239.  
  240. function canDisable(module) {
  241. return !(module.flags & ASSETS.FLAG.NO_DISABLE);
  242. }
  243. }
  244.  
  245. function registerModuleSettings(id, func) {
  246. moduleSettingFuncs[id] = func;
  247. }
  248.  
  249. function showModuleSettings(id) {
  250. const func = moduleSettingFuncs[id];
  251. if (typeof func === 'function') {
  252. func();
  253. return true;
  254. } else {
  255. if (!ML_moduleLoaded(id)) {
  256. alertify.alert(CONST.Text.AlertTitle, CONST.Text.NoLoadNoSettings);
  257. } else {
  258. alertify.alert(CONST.Text.AlertTitle, CONST.Text.NoSettings);
  259. }
  260. return false;
  261. }
  262. }
  263. }
  264.  
  265. function insertLines(lines, tbody) {
  266. !tbody && (tbody = $(content, 'table>tbody'));
  267. const elements = {};
  268. for (const line of lines) {
  269. const tr = $CrE('tr');
  270. for (const item of line) {
  271. const td = $CrE('td');
  272. item.html && (td.innerHTML = item.html);
  273. item.colSpan && (td.colSpan = item.colSpan);
  274. item.class && (td.className = item.class);
  275. item.id && (td.id = item.id);
  276. item.tiptitle && mousetip.settip(td, item.tiptitle);
  277. item.key && (elements[item.key] = td);
  278. if (item.onclick) {
  279. td.style.color = 'grey';
  280. td.style.textAlign = 'center';
  281. td.addEventListener('click', item.onclick);
  282. }
  283. td.style.padding = '3px';
  284. tr.appendChild(td);
  285. }
  286. tbody.appendChild(tr);
  287. }
  288. return elements;
  289. }
  290.  
  291. function isSettingPage(callback) {
  292. const page = getAPI()[0] === 'userdetail.php';
  293. page && callback && callback();
  294. return page;
  295. }
  296.  
  297. function htmlEncode(text) {
  298. const span = $CrE('div');
  299. span.innerText = text;
  300. return span.innerHTML;
  301. }
  302.  
  303. // Change location.href without reloading using history.pushState/replaceState
  304. function setPageUrl() {
  305. let win, url, push;
  306. switch (arguments.length) {
  307. case 1:
  308. win = window;
  309. url = arguments[0];
  310. push = false;
  311. break;
  312. case 2:
  313. win = arguments[0];
  314. url = arguments[1];
  315. push = false;
  316. break;
  317. case 3:
  318. win = arguments[0];
  319. url = arguments[1];
  320. push = arguments[2];
  321. }
  322. return win.history[push ? 'pushState' : 'replaceState']({modified: true, ...history.state}, '', url);
  323. }
  324. })();