GC - Universal Userscripts Settings

Library for adding a user interface to manage settings for grundos.cafe userscripts

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

  1. async function addTextInput(configuration) {
  2. let {
  3. categoryName,
  4. settingName,
  5. labelText,
  6. labelTooltip = undefined,
  7. currentSetting = undefined,
  8. defaultSetting = '',
  9. callbackFunction = undefined,
  10. } = configuration;
  11.  
  12. const header = await _addCategoryHeader(categoryName);
  13. if (currentSetting === undefined) {
  14. currentSetting = await GM.getValue(settingName, defaultSetting);
  15. }
  16.  
  17. const textInput = document.createElement('input');
  18. textInput.type = 'text';
  19. textInput.name = settingName;
  20. textInput.value = currentSetting;
  21.  
  22. const label = _createLabel(labelText);
  23. _formatLabel(label, labelTooltip);
  24. _addBelowHeader(header, label);
  25.  
  26. const inputElement = _createInput(textInput);
  27. label.insertAdjacentElement('afterend', inputElement);
  28.  
  29. if (callbackFunction) {
  30. _addCallback(() => callbackFunction(settingName, textInput.value));
  31. } else {
  32. _addCallback(async () => await GM.setValue(settingName, textInput.value));
  33. }
  34. }
  35.  
  36. async function addNumberInput(configuration) {
  37. let {
  38. categoryName,
  39. settingName,
  40. labelText,
  41. labelTooltip = undefined,
  42. min = 0,
  43. max = 100,
  44. step = 1,
  45. currentSetting = undefined,
  46. defaultSetting = 0,
  47. callbackFunction = undefined
  48. } = configuration;
  49.  
  50. const header = await _addCategoryHeader(categoryName);
  51. if (currentSetting === undefined) {
  52. currentSetting = await GM.getValue(settingName, defaultSetting);
  53. }
  54.  
  55. const numberInput = document.createElement('input');
  56. numberInput.type = 'number';
  57. numberInput.name = settingName;
  58. numberInput.value = currentSetting;
  59. numberInput.min = min;
  60. numberInput.max = max;
  61. numberInput.step = step;
  62.  
  63. const label = _createLabel(labelText);
  64. _formatLabel(label, labelTooltip);
  65. _addBelowHeader(header, label);
  66.  
  67. const inputElement = _createInput(numberInput);
  68. label.insertAdjacentElement('afterend', inputElement);
  69.  
  70. if (callbackFunction) {
  71. _addCallback(() => callbackFunction(settingName, numberInput.value));
  72. } else {
  73. _addCallback(async () => await GM.setValue(settingName, numberInput.value));
  74. }
  75. }
  76.  
  77. async function addCheckboxInput(configuration) {
  78. let {
  79. categoryName,
  80. settingName,
  81. labelText,
  82. labelTooltip = undefined,
  83. currentSetting = undefined,
  84. defaultSetting = false,
  85. callbackFunction = undefined
  86. } = configuration;
  87.  
  88. const header = await _addCategoryHeader(categoryName);
  89. if (currentSetting === undefined) {
  90. currentSetting = await GM.getValue(settingName, defaultSetting);
  91. }
  92.  
  93. const checkbox = document.createElement('input');
  94. checkbox.type = 'checkbox';
  95. checkbox.name = settingName;
  96. checkbox.checked = currentSetting;
  97.  
  98. const label = _createLabel(labelText);
  99. _formatLabel(label, labelTooltip);
  100. _addBelowHeader(header, label);
  101.  
  102. const inputElement = _createInput(checkbox);
  103. label.insertAdjacentElement('afterend', inputElement);
  104.  
  105. if (callbackFunction) {
  106. _addCallback(() => callbackFunction(settingName, checkbox.checked));
  107. } else {
  108. _addCallback(async () => await GM.setValue(settingName, checkbox.checked));
  109. }
  110. }
  111.  
  112. async function addDropdown(configuration) {
  113. let {
  114. categoryName,
  115. settingName,
  116. labelText,
  117. labelTooltip = undefined,
  118. options, // [{ value: 'value', text: 'text', ... }]
  119. currentSetting = undefined,
  120. defaultSetting = undefined,
  121. callbackFunction = undefined
  122. } = configuration;
  123.  
  124. const header = await _addCategoryHeader(categoryName);
  125. if (currentSetting === undefined) {
  126. currentSetting = await GM.getValue(settingName, defaultSetting);
  127. }
  128.  
  129. const select = document.createElement('select');
  130. select.name = settingName;
  131. select.classList.add('form-control');
  132.  
  133. options.forEach(option => {
  134. const optionElement = document.createElement('option');
  135. optionElement.value = option.value;
  136. optionElement.textContent = option.text;
  137. if (option.value === currentSetting) {
  138. optionElement.selected = true;
  139. }
  140. select.appendChild(optionElement);
  141. });
  142.  
  143. const label = _createLabel(labelText);
  144. _formatLabel(label, labelTooltip);
  145. _addBelowHeader(header, label);
  146.  
  147. const inputElement = _createInput(select);
  148. label.insertAdjacentElement('afterend', inputElement);
  149.  
  150. if (callbackFunction) {
  151. _addCallback(() => callbackFunction(settingName, select.value));
  152. } else {
  153. _addCallback(async () => await GM.setValue(settingName, select.value));
  154. }
  155. }
  156.  
  157. function _formatLabel(label, labelTooltip) {
  158. if (labelTooltip) {
  159. const info = document.createElement('sup');
  160. info.textContent = ' ⓘ';
  161. label.appendChild(info);
  162.  
  163. const showTooltip = () => {
  164. const tooltip = document.getElementById('universal-userscript-tooltip');
  165. const hideTooltip = () => {
  166. tooltip.style.display = 'None';
  167. };
  168.  
  169. const closeButton = `<a href="#" id="tooltip-close" style="display: flex; justify-content: center; color: var(--link_color); margin-top: 0.5rem;">Close Tooltip</a>`;
  170. tooltip.innerHTML = labelTooltip + closeButton;
  171. tooltip.style.display = 'block';
  172.  
  173. tooltip.querySelector('#tooltip-close').addEventListener('click', (event) => {
  174. event.preventDefault();
  175. hideTooltip();
  176. });
  177. };
  178.  
  179. label.addEventListener('click', (event) => {
  180. showTooltip(event);
  181. });
  182. }
  183. const colon = document.createTextNode('\u00A0:');
  184. label.appendChild(colon);
  185. }
  186.  
  187. function _addBelowHeader(header, label) {
  188. let nextHeader = header.nextElementSibling;
  189. while (nextHeader && !nextHeader.matches('.header')) {
  190. nextHeader = nextHeader.nextElementSibling;
  191. }
  192. if (!nextHeader) {
  193. nextHeader = document.querySelector('#universal-userscript-preferences>.market_grid.profile.prefs.margin-auto>.footer.small-gap');
  194. }
  195. nextHeader.insertAdjacentElement('beforebegin', label);
  196. }
  197.  
  198. function _addSettingsLink() {
  199. const existingNav = document.querySelector('nav.center.margin-1');
  200.  
  201. if (existingNav) {
  202. const newNav = document.createElement('nav');
  203. newNav.classList.add('center', 'margin-1');
  204. newNav.id = 'universal-userscript-navigation';
  205.  
  206. const newLink = document.createElement('a');
  207. newLink.href = '/help/userscripts/';
  208. newLink.textContent = 'Userscript Preferences';
  209.  
  210. newNav.appendChild(newLink);
  211.  
  212. existingNav.after(newNav);
  213. } else {
  214. console.error('Existing navigation element not found.');
  215. }
  216. }
  217.  
  218. function _createLabel(labelText) {
  219. const labelContainer = document.createElement('div');
  220. labelContainer.classList.add('data', 'left');
  221.  
  222. const label = document.createElement('span');
  223. label.textContent = labelText;
  224. labelContainer.appendChild(label);
  225. return labelContainer;
  226. }
  227.  
  228. function _createInput(input) {
  229. const inputContainer = document.createElement('div');
  230. inputContainer.classList.add('data', 'flex-column', 'right');
  231. inputContainer.appendChild(input);
  232. return inputContainer;
  233. }
  234.  
  235. function _addCallback(callbackFunction) {
  236. document.getElementById('universal-userscript-update').addEventListener('click', callbackFunction);
  237. }
  238.  
  239. async function _addCategoryHeader(categoryName) {
  240. if (!window.location.href.includes('/help/userscripts/')) throw Error('Attempted to add setting outside of settings page.');
  241. await _checkSettingsSetup();
  242. const settings = document.querySelector('.market_grid.profile.prefs.margin-auto');
  243. const footer = document.querySelector('#universal-userscript-preferences>.market_grid.profile.prefs.margin-auto>.footer.small-gap');
  244. if (!settings) {
  245. console.error('Settings not found.');
  246. return;
  247. }
  248.  
  249. const headers = Array.from(settings.querySelectorAll('.header'));
  250. let header = headers.find(header => header.textContent.trim() === categoryName);
  251.  
  252. if (!header) {
  253. header = document.createElement('div');
  254. header.classList.add('header');
  255. header.innerHTML = `<strong>${categoryName}</strong>`;
  256.  
  257. const insertionPoint = headers.find(existingHeader => existingHeader.textContent.trim().localeCompare(categoryName) > 0);
  258.  
  259. if (insertionPoint) {
  260. insertionPoint.insertAdjacentElement('beforebegin', header);
  261. } else {
  262. footer.insertAdjacentElement('beforebegin', header);
  263. }
  264. }
  265.  
  266. return header;
  267. }
  268.  
  269. function _replaceBannerImage() {
  270. const bannerImage = document.getElementById('top-header-image');
  271. if (bannerImage) {
  272. bannerImage.src = 'https://grundoscafe.b-cdn.net/misc/banners/userinfo.gif';
  273. } else {
  274. console.error('Banner image not found.');
  275. }
  276. }
  277.  
  278. function _setupUniversalSettings() {
  279. const helpPages = /\/help\/(?:profile|siteprefs|sidebar|randomevents)\/|\/discord\//;
  280. const settings = window.location.href.includes('/help/userscripts/');
  281. const help = helpPages.test(window.location.href);
  282. if (help) {
  283. _addSettingsLink();
  284. } else if (settings) {
  285. document.title = `Grundo's Cafe - Userscript Preferences`;
  286. _replaceBannerImage();
  287. const element = document.querySelector('main');
  288. if (element) {
  289. element.id = 'universal-userscript-preferences';
  290. element.innerHTML = `
  291. <h1>Userscript Preferences</h1>
  292. <nav class="center margin-1">
  293. <a href="/help/profile/">Edit Profile</a> |
  294. <a href="/help/siteprefs/">Site Preferences</a> |
  295. <a href="/help/sidebar/">Edit Sidebar</a> |
  296. <a href="/discord/">Discord</a> |
  297. <a href="/help/randomevents/">Random Event Log</a>
  298. </nav>
  299. <nav class="center margin-1" id="universal-userscript-navigation"><a href="/help/userscripts/">Userscript Preferences</a></nav>
  300. <p>Adjust settings for participating userscripts here.<br>Click on settings with an for more details.</p>
  301. <div id="universal-userscript-tooltip" style="display: none; background-color: var(--grid_odd); padding: 10px; border-radius: 5px; box-shadow: 0 2px 10px rgba(0, 0, 0, 0.1); z-index: 1000; margin-bottom: 1rem; border: 1px solid var(--grid_head);"></div>
  302. <div class="market_grid profile prefs margin-auto">
  303. <div class="footer small-gap">
  304. <input class="form-control half-width" type="submit" value="Update Preferences" id="universal-userscript-update">
  305. </div>
  306. </div>
  307. `;
  308. document.getElementById('universal-userscript-update').addEventListener('click', () => {
  309. const button = document.getElementById('universal-userscript-update');
  310.  
  311. button.disabled = true;
  312.  
  313. const originalText = button.value;
  314. button.value = 'Preferences Updated!';
  315.  
  316. setTimeout(() => {
  317. button.disabled = false;
  318. button.value = originalText;
  319. }, 2000);
  320. });
  321. }
  322. }
  323. }
  324.  
  325. async function _checkSettingsSetup() {
  326. return new Promise((resolve, reject) => {
  327. const interval = setInterval(() => {
  328. if (document.getElementById('universal-userscript-preferences') !== null) {
  329. clearInterval(interval);
  330. clearTimeout(timeout);
  331. resolve(true);
  332. }
  333. }, 50);
  334.  
  335. const timeout = setTimeout(() => {
  336. clearInterval(interval);
  337. reject(new Error('Timeout: universal-userscript-preferences element not found within 10 seconds'));
  338. }, 10000);
  339. });
  340. }
  341.  
  342. if (!document.getElementById('universal-userscript-navigation')) {
  343. _setupUniversalSettings();
  344. }

QingJ © 2025

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