Tampermonkey 配置

简易的 Tampermonkey 脚本配置库

目前为 2023-08-03 提交的版本。查看 最新版本

此脚本不应直接安装,它是一个供其他脚本使用的外部库。如果您需要使用该库,请在脚本元属性加入:// @require https://update.gf.qytechs.cn/scripts/470224/1229647/Tampermonkey%20Config.js

  1. // ==UserScript==
  2. // @name Tampermonkey Config
  3. // @name:zh-CN Tampermonkey 配置
  4. // @license gpl-3.0
  5. // @namespace http://tampermonkey.net/
  6. // @version 0.4.0
  7. // @description Simple Tampermonkey script config library
  8. // @description:zh-CN 简易的 Tampermonkey 脚本配置库
  9. // @author PRO
  10. // @match *
  11. // @grant GM_setValue
  12. // @grant GM_getValue
  13. // @grant GM_registerMenuCommand
  14. // @grant GM_unregisterMenuCommand
  15. // ==/UserScript==
  16.  
  17. // let debug = (...args) => console.debug("[Tampermonkey Config]", ...args); // Debug function
  18. let debug = () => {};
  19. let GM_config_event = `GM_config_${Math.random().toString(36).slice(2)}`;
  20. function _GM_config_get(config_desc, prop) {
  21. let value = GM_getValue(prop, undefined);
  22. if (value !== undefined) {
  23. return value;
  24. } else {
  25. return config_desc[prop].value;
  26. }
  27. }
  28.  
  29. let _GM_config_builtin_processors = {
  30. same: (v) => v,
  31. not: (v) => !v,
  32. int: (s) => {
  33. let value = parseInt(s);
  34. if (isNaN(value)) throw `Invalid value: ${s}, expected integer!`;
  35. },
  36. int_range: (s, min_s, max_s) => {
  37. let value = parseInt(s);
  38. if (isNaN(value)) throw `Invalid value: ${s}, expected integer!`;
  39. let min = (min_s === "") ? -Infinity : parseInt(min_s);
  40. let max = (max_s === "") ? +Infinity : parseInt(max_s);
  41. if (min !== NaN && value < min) throw `Invalid value: ${s}, expected integer >= ${min}!`;
  42. if (max !== NaN && value > max) throw `Invalid value: ${s}, expected integer <= ${max}!`;
  43. return value;
  44. },
  45. float: (s) => {
  46. let value = parseFloat(s);
  47. if (isNaN(value)) throw `Invalid value: ${s}, expected float!`;
  48. },
  49. float_range: (s, min_s, max_s) => {
  50. let value = parseFloat(s);
  51. if (isNaN(value)) throw `Invalid value: ${s}, expected float!`;
  52. let min = (min_s === "") ? -Infinity : parseFloat(min_s);
  53. let max = (max_s === "") ? +Infinity : parseFloat(max_s);
  54. if (min !== NaN && value < min) throw `Invalid value: ${s}, expected float >= ${min}!`;
  55. if (max !== NaN && value > max) throw `Invalid value: ${s}, expected float <= ${max}!`;
  56. return value;
  57. },
  58. };
  59. let _GM_config_builtin_formatters = {
  60. default: (name, value) => `${name}: ${value}`,
  61. boolean: (name, value) => `${name}: ${value ? "✔" : "✘"}`,
  62. };
  63. let _GM_config_wrapper = {
  64. get: function (target, prop) {
  65. // Return stored value, else default value
  66. let value = _GM_config_get(target, prop);
  67. // Dispatch get event
  68. let event = new CustomEvent(GM_config_event, {
  69. detail: {
  70. type: "get",
  71. prop: prop,
  72. before: value,
  73. after: value
  74. }
  75. });
  76. window.dispatchEvent(event);
  77. return value;
  78. }
  79. , set: function (desc, prop, value) {
  80. // Dispatch set event
  81. let event = new CustomEvent(GM_config_event, {
  82. detail: {
  83. type: "set",
  84. prop: prop,
  85. before: _GM_config_get(desc, prop),
  86. after: value
  87. }
  88. });
  89. // Store value
  90. GM_setValue(prop, value);
  91. window.dispatchEvent(event);
  92. return true;
  93. }
  94. };
  95.  
  96. let _GM_config_registered = []; // Items: [id, prop]
  97. // (Re-)register menu items on demand
  98. function _GM_config_register(desc, config, until=undefined) {
  99. // `until` is the first property to be re-registered
  100. // If `until` is undefined, all properties will be re-registered
  101. let _GM_config_builtin_inputs = {
  102. current: (prop, orig) => { return orig },
  103. prompt: (prop, orig) => {
  104. let s = prompt(`🤔 New value for ${desc[prop].name}:`, orig);
  105. if (s === null) return orig;
  106. return s;
  107. },
  108. };
  109. // Unregister old menu items
  110. let id, prop, pack;
  111. let flag = true;
  112. while (pack = _GM_config_registered.pop()) {
  113. [id, prop] = pack; // prop=null means the menu command is currently a placeholder ("Show configuration")
  114. GM_unregisterMenuCommand(id);
  115. debug(`- Unregistered menu command: prop="${prop}", id=${id}`);
  116. if (prop === until) { // Nobody in their right mind would use `null` as a property name
  117. flag = false;
  118. break;
  119. }
  120. }
  121. for (let prop in desc) {
  122. if (prop === until) {
  123. flag = true;
  124. }
  125. if (!flag) continue;
  126. let name = desc[prop].name;
  127. let orig = _GM_config_get(desc, prop);
  128. let input = desc[prop].input || "prompt";
  129. let input_func = typeof input === "function" ? input : _GM_config_builtin_inputs[input];
  130. let formatter = desc[prop].formatter || "default";
  131. let formatter_func = typeof formatter === "function" ? formatter : _GM_config_builtin_formatters[formatter];
  132. let id = GM_registerMenuCommand(formatter_func(name, orig), function () {
  133. let value;
  134. try {
  135. value = input_func(prop, orig);
  136. let processor = desc[prop].processor || "same";
  137. if (typeof processor === "function") { // Process user input
  138. value = processor(value);
  139. } else if (typeof processor === "string") {
  140. let parts = processor.split("-");
  141. let processor_func = _GM_config_builtin_processors[parts[0]];
  142. if (processor_func !== undefined) // Process user input
  143. value = processor_func(value, ...parts.slice(1));
  144. else // Unknown processor
  145. throw `Unknown processor: ${processor}`;
  146. } else {
  147. throw `Unknown processor format: ${typeof processor}`;
  148. }
  149. } catch (error) {
  150. alert("⚠️ "+error);
  151. return;
  152. }
  153. if (value !== orig) {
  154. config[prop] = value;
  155. }
  156. });
  157. debug(`+ Registered menu command: prop="${prop}", id=${id}`);
  158. _GM_config_registered.push([id, prop]);
  159. }
  160. };
  161.  
  162. function GM_config(desc, menu=true) { // Register menu items based on given config description
  163. // Get proxied config
  164. let config = new Proxy(desc, _GM_config_wrapper);
  165. // Register menu items
  166. if (menu) {
  167. _GM_config_register(desc, config);
  168. } else {
  169. // Register menu items after user clicks "Show configuration"
  170. let id = GM_registerMenuCommand("Show configuration", function () {
  171. _GM_config_register(desc, config);
  172. });
  173. debug(`+ Registered menu command: prop="Show configuration", id=${id}`);
  174. _GM_config_registered.push([id, null]);
  175. }
  176. window.addEventListener(GM_config_event, (e) => { // Auto update menu items
  177. if (e.detail.type === "set" && e.detail.before !== e.detail.after) {
  178. debug(`🔧 "${e.detail.prop}" changed from ${e.detail.before} to ${e.detail.after}`);
  179. _GM_config_register(desc, config, e.detail.prop);
  180. } else if (e.detail.type === "get") {
  181. debug(`🔍 "${e.detail.prop}" requested, value is ${e.detail.after}`);
  182. }
  183. });
  184. // Return proxied config
  185. return config;
  186. };

QingJ © 2025

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