setupCommands

Library that creates regular, toggle, and radio menu commands for userscript managers

Script này sẽ không được không được cài đặt trực tiếp. Nó là một thư viện cho các script khác để bao gồm các chỉ thị meta // @require https://update.greatest.deepsurf.us/scripts/498119/1399005/setupCommands.js

  1. // ==UserScript==
  2. // @name setupCommands
  3. // @license MIT
  4. // @namespace rtonne
  5. // @match *://*/*
  6. // @version 2.1
  7. // @author Rtonne
  8. // @description Library that creates regular, toggle, and radio menu commands for userscript managers
  9. // @grant GM.registerMenuCommand
  10. // @grant GM.unregisterMenuCommand
  11. // @grant GM.getValue
  12. // @grant GM.setValue
  13. // ==/UserScript==
  14.  
  15. /**
  16. * Can be the only function of this library used externally.
  17. * @param {_Command[]} command_list
  18. */
  19. async function setupCommands(command_list) {
  20. for (const command of command_list) {
  21. await _runCommandCheckFunctions(command);
  22. }
  23. for (const command of command_list) {
  24. await _registerCommand(command_list, command);
  25. }
  26. }
  27.  
  28. /**
  29. * @typedef {_ButtonCommand | _ToggleCommand | _RadioCommandGroup} _Command
  30. */
  31.  
  32. /**
  33. * @typedef _ButtonCommand
  34. * @type {Object}
  35. * @property {"button"} type A string declaring what type of menu command this is.
  36. * @property {string} text The text displayed.
  37. * @property {() => void} clickFunction A function to be run when clicking the command.
  38. * @property {string} id The id of the command. Required so that if in place replacement is not supported it can be removed.
  39. * @property {string} [tooltip] The tooltip shown while the cursor hovers the command.
  40. * @property {boolean} [auto_close] If the userscript manager popup closes when the command is clicked. Its "false" by default.
  41. * @property {string} [access_key] A key shortcut for the command.
  42. */
  43.  
  44. /**
  45. * @typedef _ToggleCommand
  46. * @type {Object}
  47. * @property {"toggle"} type A string declaring what type of menu command this is.
  48. * @property {string} id The id of the toggle and the key for the value.
  49. * @property {string} text The text displayed.
  50. * @property {boolean} [default_value] The default value and toggle state. Its "false" and off by default.
  51. * @property {string} [tooltip] The tooltip shown while the cursor hovers the command.
  52. * @property {boolean} [auto_close] If the userscript manager popup closes when the toggle is clicked. Its "false" by default.
  53. * @property {string} [access_key] A key shortcut for the command.
  54. * @property {() => void} [uncheckedFunction] A function to be run when this command is unchecked. This will run once on startup if command is unchecked.
  55. * @property {() => void} [checkedFunction] A function to be run when this command is checked. This will run once on startup if command is checked
  56. */
  57.  
  58. /**
  59. * @typedef _RadioCommandGroup
  60. * @type {Object}
  61. * @property {"radio"} type
  62. * @property {string} id The key for the value.
  63. * @property {*} [default_value] The default value and which radio is checked by default. If not set or value does not correspond to a radio, no radio will be checked.
  64. * @property {_RadioCommand[]} radios
  65. *
  66. * @typedef _RadioCommand
  67. * @type {Object}
  68. * @property {string} text The text displayed.
  69. * @property {*} value The value that is set to the group's id when clicked.
  70. * @property {string} id The id of the command. Required so that if in place replacement is not supported it can be removed.
  71. * @property {string} [tooltip] The tooltip shown while the cursor hovers the command.
  72. * @property {boolean} [auto_close] If the userscript manager popup closes when the command is clicked. Its "false" by default.
  73. * @property {string} [access_key] A key shortcut for the command.
  74. * @property {() => void} [uncheckedFunction] A function to be run when another command in the group is checked. This will run once on startup if command is unchecked.
  75. * @property {() => void} [checkedFunction] A function to be run when this command is checked. This will run once on startup if command is checked
  76. */
  77.  
  78. // To check if in place command replacement is supported
  79. // https://violentmonkey.github.io/api/gm/#gm_registermenucommand
  80. const _can_replace_in_place =
  81. "test" === GM.registerMenuCommand("test", () => {}, { id: "test" });
  82. GM.unregisterMenuCommand("test");
  83.  
  84. /**
  85. * @param {_Command[]} command_list The list of all commands (may be used to replace old commands).
  86. * @param {_Command} command
  87. */
  88. async function _registerCommand(command_list, command) {
  89. if (command.type === "radio") {
  90. const checked_radio_value = await GM.getValue(
  91. command.id,
  92. command.default_value
  93. );
  94. for (const radio of command.radios) {
  95. if (radio.value === checked_radio_value) {
  96. const text_prefix = "🞊 ";
  97. GM.registerMenuCommand(text_prefix + radio.text, () => {}, {
  98. id: radio.id,
  99. title: radio.tooltip,
  100. accessKey: radio.access_key,
  101. autoClose: radio.auto_close !== undefined && radio.auto_close,
  102. });
  103. } else {
  104. const text_prefix = "🞅 ";
  105. GM.registerMenuCommand(
  106. text_prefix + radio.text,
  107. () => _radioCommand(command_list, command, radio.value),
  108. {
  109. id: radio.id,
  110. title: radio.tooltip,
  111. accessKey: radio.access_key,
  112. autoClose: radio.auto_close !== undefined && radio.auto_close,
  113. }
  114. );
  115. }
  116. }
  117. } else if (command.type === "toggle") {
  118. let text_prefix;
  119. if (await GM.getValue(command.id, command.default_value)) {
  120. text_prefix = "🞕 ";
  121. } else {
  122. text_prefix = "🞎 ";
  123. }
  124. GM.registerMenuCommand(
  125. text_prefix + command.text,
  126. () => _toggleCommand(command_list, command),
  127. {
  128. id: command.id,
  129. title: command.tooltip,
  130. accessKey: command.access_key,
  131. autoClose: command.auto_close !== undefined && command.auto_close,
  132. }
  133. );
  134. } else if (command.type === "button") {
  135. GM.registerMenuCommand(command.text, command.clickFunction, {
  136. id: command.id,
  137. title: command.tooltip,
  138. accessKey: command.access_key,
  139. autoClose: command.auto_close !== undefined && command.auto_close,
  140. });
  141. }
  142. }
  143.  
  144. /**
  145. * The callback to be added to the GM.registerCommand of RadioCommand.
  146. * @param {_Command[]} command_list The list of all commands (may be used to replace old commands).
  147. * @param {_RadioCommandGroup} command The group of the command being checked.
  148. * @param {string} value The value of the RadioCommand being checked.
  149. */
  150. async function _radioCommand(command_list, command, value) {
  151. await GM.setValue(command.id, value);
  152. _runCommandCheckFunctions(command);
  153. if (_can_replace_in_place) {
  154. await _registerCommand(command_list, command);
  155. } else {
  156. // If we can't replace commands, we need to remove them all, then re-add them
  157. _unregisterCommands(command_list);
  158. for (const command of command_list) {
  159. await _registerCommand(command_list, command);
  160. }
  161. }
  162. }
  163.  
  164. /**
  165. * The callback to be added to the GM.registerCommand of ToggleCommand
  166. * @param {_Command[]} command_list The list of all commands (may be used to replace old commands).
  167. * @param {_ToggleCommand} command The command being toggled.
  168. */
  169. async function _toggleCommand(command_list, command) {
  170. await GM.setValue(
  171. command.id,
  172. !(await GM.getValue(command.id, command.default_value))
  173. );
  174. _runCommandCheckFunctions(command);
  175. if (_can_replace_in_place) {
  176. await _registerCommand(command_list, command);
  177. } else {
  178. // If we can't replace commands, we need to remove them all, then re-add them
  179. _unregisterCommands(command_list);
  180. for (const command of command_list) {
  181. await _registerCommand(command_list, command);
  182. }
  183. }
  184. }
  185.  
  186. /**
  187. * @param {_Command[]} command_list
  188. */
  189. function _unregisterCommands(command_list) {
  190. for (const command of command_list) {
  191. if (command.type === "radio") {
  192. for (const radio of command.radios) {
  193. GM.unregisterMenuCommand(radio.id);
  194. }
  195. continue;
  196. }
  197. GM.unregisterMenuCommand(command.id);
  198. }
  199. }
  200.  
  201. /**
  202. * Runs the required uncheckedFunction() or checkedFunction() of the command.
  203. * @param {_Command} command
  204. */
  205. async function _runCommandCheckFunctions(command) {
  206. if (command.type === "toggle") {
  207. if (await GM.getValue(command.id, command.default_value)) {
  208. if (command.checkedFunction) {
  209. command.checkedFunction();
  210. }
  211. } else {
  212. if (command.uncheckedFunction) {
  213. command.uncheckedFunction();
  214. }
  215. }
  216. } else if (command.type === "radio") {
  217. const value = await GM.getValue(command.id, command.default_value);
  218. for (const radio of command.radios) {
  219. if (value === radio.value) {
  220. if (radio.checkedFunction) {
  221. radio.checkedFunction();
  222. }
  223. } else {
  224. if (radio.uncheckedFunction) {
  225. radio.uncheckedFunction();
  226. }
  227. }
  228. }
  229. }
  230. }