MonkeyConfig Mod

Enhanced Configuration Dialog Builder with column layout, custom styling, additional input types, and scrollable labels

当前为 2025-06-01 提交的版本,查看 最新版本

此脚本不应直接安装。它是供其他脚本使用的外部库,要使用该库请加入元指令 // @require https://update.gf.qytechs.cn/scripts/528923/1599357/MonkeyConfig%20Mod.js

  1. // ==UserScript==
  2. // @name MonkeyConfig Mod
  3. // @noframes
  4. // @version 2.5
  5. // @namespace http://odyniec.net/
  6. // @contributionURL https://saweria.co/Bloggerpemula
  7. // @description Enhanced Configuration Dialog Builder with column layout, custom styling, additional input types, and scrollable labels
  8. // @require https://cdnjs.cloudflare.com/ajax/libs/dompurify/3.2.5/purify.min.js#sha384-qSFej5dZNviyoPgYJ5+Xk4bEbX8AYddxAHPuzs1aSgRiXxJ3qmyWNaPsRkpv/+x5
  9. // ==/UserScript==
  10. /* eslint-disable no-undef */
  11. /*
  12. * MonkeyConfig Enhanced
  13. * Based on version 0.1.4 by Michal Wojciechowski (odyniec.net)
  14. * v0.1.4 - January 2020 - David Hosier (https://github.com/david-hosier/MonkeyConfig)
  15. * Enhanced by Bloggerpemula - March 2025
  16. * Additions: Column layout, font size/color customization, new input types (textarea, range, radio, file, button, group)
  17. * Modified: Checkbox, number, and text inputs aligned inline with labels - March 2025
  18. * Fixed: Improved Shadow DOM and Optimized Iframe for consistent styling across sites - March 2025
  19. * Enhanced: Scrollable labels, customizable checkbox/number sizes, new column options (left&top, right&top, left&bottom, right&bottom) - April 2025
  20. * Security: Added Trusted Types support, DOMPurify sanitization, and robust error handling - May 2025
  21. */
  22. function MonkeyConfig(data) {
  23. let cfg = this,
  24. params = data.parameters || data.params,
  25. values = {},
  26. storageKey,
  27. displayed,
  28. openLayer,
  29. shadowRoot,
  30. container,
  31. iframeFallback;
  32. function log(message, level = 'info') {
  33. try {console[level](`[MonkeyConfig v2.4] ${message}`);} catch (e) {
  34. console.error(`[MonkeyConfig v2.4] Logging failed: ${e.message}`);}}
  35. let trustedPolicy;
  36. try {trustedPolicy = window.trustedTypes?.createPolicy('monkeyConfigPolicy', {
  37. createHTML: (input) => {
  38. if (typeof DOMPurify === 'undefined') {
  39. log('DOMPurify not available, falling back to basic sanitization', 'warn');
  40. return input.replace(/<script\b[^<]*(?:(?!<\/script>)<[^<]*)*<\/script>/gi, '').replace(/on\w+\s*=\s*".*?"/gi, '');}
  41. return DOMPurify.sanitize(input, {RETURN_TRUSTED_TYPE: true});}}) || {createHTML: (input) => input};} catch (e) {
  42. log(`Failed to create Trusted Types policy: ${e.message}`, 'error');
  43. trustedPolicy = {
  44. createHTML: (input) => input.replace(/<script\b[^<]*(?:(?!<\/script>)<[^<]*)*<\/script>/gi, '')
  45. .replace(/on\w+\s*=\s*".*?"/gi, '')};}
  46. function createTrustedHTML(htmlString) {
  47. try {return trustedPolicy.createHTML(htmlString);} catch (e) {
  48. log(`Failed to create TrustedHTML: ${e.message}`, 'error');
  49. return '';}}
  50. function init() {
  51. try {data.buttons = data.buttons || ['save', 'reset', 'close', 'reload', 'homepage'];
  52. storageKey = '_MonkeyConfig_' + (data.title || 'Configuration').replace(/[^a-zA-Z0-9]/g, '_') + '_cfg';
  53. let storedValues;
  54. try {storedValues = GM_getValue(storageKey) ? JSON.parse(GM_getValue(storageKey)) : {};} catch (e) {
  55. log(`Failed to parse stored values: ${e.message}`, 'error');
  56. storedValues = {};}
  57. cfg.shadowWidth = data.shadowWidth || storedValues.shadowWidth || "600px";
  58. cfg.shadowHeight = data.shadowHeight || storedValues.shadowHeight || "400px";
  59. cfg.iframeWidth = data.iframeWidth || storedValues.iframeWidth || "600px";
  60. cfg.iframeHeight = data.iframeHeight || storedValues.iframeHeight || "400px";
  61. cfg.shadowFontSize = data.shadowFontSize || storedValues.shadowFontSize || "14px";
  62. cfg.shadowFontColor = data.shadowFontColor || storedValues.shadowFontColor || "#000000";
  63. cfg.iframeFontSize = data.iframeFontSize || storedValues.iframeFontSize || "14px";
  64. cfg.iframeFontColor = data.iframeFontColor || storedValues.iframeFontColor || "#000000";
  65. cfg.title = data.title || (typeof GM_getMetadata === 'function' ? GM_getMetadata('name') + ' Configuration' : 'Configuration');
  66. for (let key in params) {const param = params[key];values[key] = storedValues[key] ?? param.default ?? '';}
  67. if (data.menuCommand) {try {
  68. GM_registerMenuCommand(data.menuCommand === true ? cfg.title : data.menuCommand, () => cfg.open());} catch (e) {
  69. log(`Failed to register menu command: ${e.message}`, 'error');}}
  70. cfg.open = open;
  71. cfg.close = close;
  72. cfg.get = name => values[name];
  73. cfg.set = (name, value) => {
  74. try {values[name] = value;
  75. update();} catch (e) {
  76. log(`Failed to set value for ${name}: ${e.message}`, 'error');}};} catch (e) {
  77. log(`Initialization failed: ${e.message}`, 'error');}}
  78. function setDefaults() {
  79. try {for (let key in params) {
  80. if (params[key].default !== undefined) {values[key] = params[key].default;}}
  81. update();} catch (e) {
  82. log(`Failed to set defaults: ${e.message}`, 'error');}}
  83. function render() {
  84. try {let html = `<div class="__MonkeyConfig_container">
  85. <div style="position: absolute; top: 5px; right: 5px;">
  86. <button type="button" id="__MonkeyConfig_button_close" style="background: none !important; border: none !important; padding: 5px !important; display: flex !important; align-items: center !important; justify-content: center !important;">
  87. <img src="data:image/png;base64,${MonkeyConfig.res.icons.close}" alt="Close" style="width: 16px !important; height: 16px !important;"/>
  88. </button>
  89. </div>
  90. <h1>${MonkeyConfig.esc(cfg.title)}</h1><div class="__MonkeyConfig_content"><div class="__MonkeyConfig_top">`;
  91. for (let key in params) {
  92. if (params[key].column === 'top') {html += MonkeyConfig.formatters.tr(key, params[key]);}}
  93. html += `<div class="__MonkeyConfig_top_columns"><div class="__MonkeyConfig_left_top">`;
  94. for (let key in params) {
  95. if (params[key].column === 'left&top') {html += MonkeyConfig.formatters.tr(key, params[key]);}}
  96. html += `</div><div class="__MonkeyConfig_right_top">`;
  97. for (let key in params) {
  98. if (params[key].column === 'right&top') {html += MonkeyConfig.formatters.tr(key, params[key]);}}
  99. html += `</div></div>`;
  100. html += `</div><div class="__MonkeyConfig_columns"><div class="__MonkeyConfig_left_column">`;
  101. for (let key in params) {
  102. if (params[key].column === 'left') {html += MonkeyConfig.formatters.tr(key, params[key]);}}
  103. html += `</div><div class="__MonkeyConfig_right_column">`;
  104. for (let key in params) {
  105. if (params[key].column === 'right') {html += MonkeyConfig.formatters.tr(key, params[key]);}}
  106. html += `</div></div><table class="__MonkeyConfig_default">`;
  107. for (let key in params) {
  108. if (!params[key].column) {html += MonkeyConfig.formatters.tr(key, params[key]);}}
  109. html += `</table><div class="__MonkeyConfig_bottom">`;
  110. for (let key in params) {
  111. if (params[key].column === 'bottom') {html += MonkeyConfig.formatters.tr(key, params[key]);}}
  112. html += `<div class="__MonkeyConfig_bottom_columns"><div class="__MonkeyConfig_left_bottom">`;
  113. for (let key in params) {
  114. if (params[key].column === 'left&bottom') {html += MonkeyConfig.formatters.tr(key, params[key]);}}
  115. html += `</div><div class="__MonkeyConfig_right_bottom">`;
  116. for (let key in params) {
  117. if (params[key].column === 'right&bottom') {html += MonkeyConfig.formatters.tr(key, params[key]);}}
  118. html += `</div></div>`;
  119. html += `</div></div><div class="__MonkeyConfig_buttons_container"><table><tr>`;
  120. data.buttons.forEach(btn => {if (btn === 'close') return;
  121. html += '<td>';
  122. if (btn === 'save') {
  123. html += `<button type="button" id="__MonkeyConfig_button_save" style="display: flex !important; align-items: center !important; justify-content: center !important; padding: 4px 8px !important; border-radius: 0.5em !important; border: 1px solid #999 !important; background: #ccc linear-gradient(180deg, #ddd 0, #ccc 45%, #bbb 50%, #aaa 100%) !important; line-height: 1.2 !important;"><img src="data:image/png;base64,${MonkeyConfig.res.icons.save}" alt="Save Without Reload" style="width: 16px !important; height: 16px !important; margin-right: 8px !important;" onerror="this.style.display='none'"/> Save Without Reload</button>`;
  124. } else if (btn === 'reset') {
  125. html += `<button type="button" id="__MonkeyConfig_button_reset" style="display: flex !important; align-items: center !important; justify-content: center !important; padding: 4px 8px !important; border-radius: 0.5em !important; border: 1px solid #999 !important; background: #ccc linear-gradient(180deg, #ddd 0, #ccc 45%, #bbb 50%, #aaa 100%) !important; line-height: 1.2 !important;"><img src="data:image/png;base64,${MonkeyConfig.res.icons.reset}" alt="Reset" style="width: 16px !important; height: 16px !important; margin-right: 8px !important;" onerror="this.style.display='none'"/> Reset</button>`;
  126. } else if (btn === 'reload') {
  127. html += `<button type="button" id="__MonkeyConfig_button_reload" style="display: flex !important; align-items: center !important; justify-content: center !important; padding: 4px 8px !important; border-radius: 0.5em !important; border: 1px solid #999 !important; background: #ccc linear-gradient(180deg, #ddd 0, #ccc 45%, #bbb 50%, #aaa 100%) !important; line-height: 1.2 !important;"><img src="data:image/png;base64,${MonkeyConfig.res.icons.reload}" alt="Save With Reload" style="width: 16px !important; height: 16px !important; margin-right: 8px !important;" onerror="this.style.display='none'"/> Save With Reload</button>`;
  128. } else if (btn === 'homepage') {
  129. html += `<button type="button" id="__MonkeyConfig_button_homepage" style="display: flex !important; align-items: center !important; justify-content: center !important; padding: 4px 8px !important; border-radius: 0.5em !important; border: 1px solid #999 !important; background: #ccc linear-gradient(180deg, #ddd 0, #ccc 45%, #bbb 50%, #aaa 100%) !important; line-height: 1.2 !important;"><img src="data:image/png;base64,${MonkeyConfig.res.icons.home}" alt="Homepage" style="width: 16px !important; height: 16px !important; margin-right: 8px !important;" onerror="this.style.display='none'"/> Homepage</button>`;}
  130. html += '</td>';});
  131. html += '</tr></table></div></div>';
  132. return createTrustedHTML(html);} catch (e) {
  133. log(`Failed to render HTML: ${e.message}`, 'error');
  134. return createTrustedHTML('<div>Error rendering configuration dialog</div>');}}
  135. function update() {
  136. try {if (!displayed) return;
  137. const root = shadowRoot || (iframeFallback && iframeFallback.contentDocument);
  138. if (!root) {
  139. log('Root element not found for update', 'error');
  140. return;}
  141. for (let key in params) {
  142. const elem = root.querySelector(`[name="${MonkeyConfig.esc(key)}"]`),
  143. param = params[key];
  144. if (!elem) {log(`Element for ${key} not found`, 'warn');continue;}
  145. if (param.type === 'checkbox') {
  146. elem.checked = !!values[key];
  147. elem.style.width = param.checkboxWidth || '11px';
  148. elem.style.height = param.checkboxHeight || '11px';
  149. } else if (param.type === 'number') {
  150. elem.value = values[key] || param.default;
  151. elem.style.width = param.inputWidth || '50px';
  152. elem.style.height = param.inputHeight || '15px';
  153. } else if (param.type === 'text') {
  154. elem.value = values[key] || param.default;
  155. elem.style.width = param.inputWidth || '100px';
  156. elem.style.height = param.inputHeight || '15px';
  157. } else if (param.type === 'custom' && param.set) {
  158. try {
  159. param.set(values[key], root.querySelector(`#__MonkeyConfig_parent_${MonkeyConfig.esc(key)}`));} catch (e) {
  160. log(`Failed to set custom param ${key}: ${e.message}`, 'error');}
  161. } else if (['text', 'color', 'textarea', 'range'].includes(param.type)) {
  162. elem.value = values[key] || param.default;
  163. } else if (param.type === 'radio') {
  164. const radio = root.querySelector(`[name="${MonkeyConfig.esc(key)}"][value="${MonkeyConfig.esc(values[key])}"]`);
  165. if (radio) radio.checked = true;
  166. } else if (param.type === 'file') {
  167. elem.value = '';
  168. } else if (param.type === 'select') {
  169. const currentValue = values[key];
  170. if (elem.type === 'checkbox') {
  171. const checkboxes = root.querySelectorAll(`input[name="${MonkeyConfig.esc(key)}"]`);
  172. checkboxes.forEach(cb => {cb.checked = currentValue.includes(cb.value);});
  173. } else if (elem.multiple) {
  174. const options = root.querySelectorAll(`select[name="${MonkeyConfig.esc(key)}"] option`);
  175. options.forEach(opt => {opt.selected = currentValue.includes(opt.value);});} else {
  176. elem.value = currentValue;}}
  177. const fontSize = shadowRoot ? cfg.shadowFontSize : cfg.iframeFontSize;
  178. const defaultFontColor = shadowRoot ? cfg.shadowFontColor : cfg.iframeFontColor;
  179. const labelFontColor = param.fontColor || defaultFontColor;
  180. elem.style.fontSize = fontSize;elem.style.color = labelFontColor;
  181. if (param.type === 'checkbox' || param.type === 'textarea') {
  182. elem.style.backgroundColor = 'inherit';
  183. elem.style.color = labelFontColor;}
  184. const label = root.querySelector(`label[for="__MonkeyConfig_field_${MonkeyConfig.esc(key)}"]`);
  185. if (label) {label.style.fontSize = fontSize;
  186. label.style.color = labelFontColor;
  187. label.style.cssText += param.type === 'textarea' ? 'text-align:center;display:block;width:100%' : 'text-align:left;display:inline-block;width:auto';}}
  188. } catch (e) {
  189. log(`Failed to update UI: ${e.message}`, 'error');}}
  190. function saveClick() {
  191. try {const root = shadowRoot || (iframeFallback && iframeFallback.contentDocument);
  192. if (!root) {
  193. log('Root element not found for save', 'error');
  194. return;}
  195. for (let key in params) {
  196. const elem = root.querySelector(`[name="${MonkeyConfig.esc(key)}"]`),
  197. param = params[key];
  198. if (!elem) {
  199. log(`Element for ${key} not found during save`, 'warn');
  200. continue;}
  201. if (param.type === 'checkbox') {
  202. values[key] = elem.checked;
  203. } else if (param.type === 'custom' && param.get) {
  204. try {
  205. values[key] = param.get(root.querySelector(`#__MonkeyConfig_parent_${MonkeyConfig.esc(key)}`));} catch (e) {
  206. log(`Failed to get custom param ${key}: ${e.message}`, 'error');}
  207. } else if (['number', 'text', 'color', 'textarea', 'range'].includes(param.type)) {
  208. values[key] = elem.value;
  209. } else if (param.type === 'radio') {
  210. values[key] = root.querySelector(`[name="${MonkeyConfig.esc(key)}"]:checked`)?.value || '';
  211. } else if (param.type === 'file') {
  212. values[key] = elem.dataset.value || values[key];
  213. } else if (param.type === 'select') {
  214. if (elem.type === 'checkbox') {
  215. values[key] = Array.from(root.querySelectorAll(`input[name="${MonkeyConfig.esc(key)}"]:checked`)).map(input => input.value);
  216. } else if (elem.multiple) {
  217. values[key] = Array.from(root.querySelectorAll(`select[name="${MonkeyConfig.esc(key)}"] option:selected`)).map(opt => opt.value);
  218. } else {
  219. values[key] = elem.value;}}}
  220. const allValues = {
  221. ...values,
  222. shadowWidth: cfg.shadowWidth,
  223. shadowHeight: cfg.shadowHeight,
  224. iframeWidth: cfg.iframeWidth,
  225. iframeHeight: cfg.iframeHeight,
  226. shadowFontSize: cfg.shadowFontSize,
  227. shadowFontColor: cfg.shadowFontColor,
  228. iframeFontSize: cfg.iframeFontSize,
  229. iframeFontColor: cfg.iframeFontColor};
  230.  
  231. try {
  232. GM_setValue(storageKey, JSON.stringify(allValues));} catch (e) {
  233. log(`Failed to save values: ${e.message}`, 'error');}
  234. close();
  235. if (data.onSave) {try {data.onSave(values);} catch (e) {
  236. log(`onSave callback failed: ${e.message}`, 'error');}}
  237. } catch (e) {
  238. log(`Save operation failed: ${e.message}`, 'error');}}
  239. function open() {if (window.self !== window.top) {
  240. log('Cannot open dialog in iframe', 'warn');
  241. return;}
  242. function openDone(root) {
  243. try {
  244. const saveBtn = root.querySelector('#__MonkeyConfig_button_save');
  245. if (saveBtn) saveBtn.addEventListener('click', saveClick, false);
  246. const resetBtn = root.querySelector('#__MonkeyConfig_button_reset');
  247. if (resetBtn) resetBtn.addEventListener('click', setDefaults, false);
  248. const closeBtn = root.querySelector('#__MonkeyConfig_button_close');
  249. if (closeBtn) closeBtn.addEventListener('click', close, false);
  250. const reloadBtn = root.querySelector('#__MonkeyConfig_button_reload');
  251. if (reloadBtn) reloadBtn.addEventListener('click', () => {saveClick();location.reload();}, false);
  252. const homepageBtn = root.querySelector('#__MonkeyConfig_button_homepage');
  253. if (homepageBtn) homepageBtn.addEventListener('click', () => window.open('https://bloggerpemula.pythonanywhere.com/', '_blank'), false);
  254. displayed = true;
  255. const checkboxes = root.querySelectorAll('input[type="checkbox"]');
  256. checkboxes.forEach(cb => {
  257. cb.style.width = cb.style.width || '11px';
  258. cb.style.height = cb.style.height || '11px';});
  259. const numbers = root.querySelectorAll('input[type="number"]');
  260. numbers.forEach(num => {
  261. num.style.width = num.style.width || '40px';
  262. num.style.height = num.style.height || '20px';});
  263. update();} catch (e) {
  264. log(`Failed to initialize dialog: ${e.message}`, 'error');}}
  265. const body = document.querySelector('body') || document.documentElement;
  266. if (!body) {log('Body not found, cannot open dialog', 'error');
  267. return;}
  268. openLayer = document.createElement('div');
  269. openLayer.className = '__MonkeyConfig_layer';
  270. try {shadowRoot = openLayer.attachShadow({
  271. mode: 'open'});} catch (e) {
  272. log(`Failed to attach Shadow DOM: ${e.message}`, 'error');
  273. shadowRoot = null;}
  274. const shadowWidth = cfg.shadowWidth || "600px";
  275. const shadowHeight = cfg.shadowHeight || "300px";
  276. log(`Preparing Shadow DOM with title: ${MonkeyConfig.esc(cfg.title)}, dimensions - Width: ${shadowWidth}, Height: ${shadowHeight}`);
  277. const heightStyle = shadowHeight === 'auto' ? 'auto' : shadowHeight;
  278. if (shadowRoot) {try {shadowRoot.innerHTML = createTrustedHTML(`
  279. <style>
  280. :host { all: initial; display: block !important; font-family: Arial, sans-serif !important; isolation: isolate; z-index: 2147483647 !important; font-size: ${MonkeyConfig.esc(cfg.shadowFontSize)} !important; color: ${MonkeyConfig.esc(cfg.shadowFontColor)} !important; }
  281. h1 { font-size: 120% !important; font-weight: normal !important; margin: 0 !important; padding: 0 !important; }
  282. ${MonkeyConfig.res.stylesheets.main.replace(/__FONT_SIZE__/g, MonkeyConfig.esc(cfg.shadowFontSize)).replace(/__FONT_COLOR__/g, MonkeyConfig.esc(cfg.shadowFontColor))}
  283. .__MonkeyConfig_overlay { position: fixed !important; top: 0 !important; left: 0 !important; width: 100vw !important; height: 100vh !important; background-color: rgba(0, 0, 0, 0.6) !important; z-index: 2147483646 !important; }
  284. .__MonkeyConfig_container { position: fixed !important; top: 50% !important; left: 50% !important; transform: translate(-50%, -50%) !important; z-index: 2147483647 !important; width: ${MonkeyConfig.esc(shadowWidth)} !important; height: ${MonkeyConfig.esc(heightStyle)} !important; max-width: 90vw !important; max-height: 80vh !important; overflow-y: auto !important; box-sizing: border-box !important; }
  285. </style>
  286. <div class="__MonkeyConfig_overlay"></div>
  287. ${render()}`);
  288. container = shadowRoot.querySelector('.__MonkeyConfig_container');
  289. openLayer.style.cssText = 'position: fixed !important; top: 0 !important; left: 0 !important; width: 100% !important; height: 100% !important; z-index: 2147483647 !important;';
  290. body.appendChild(openLayer);
  291. log('Dialog appended to body via Shadow DOM');
  292. const appliedWidth = container?.offsetWidth || 'unknown';
  293. const appliedHeight = container?.offsetHeight || 'unknown';
  294. log(`Actual applied dimensions - Width: ${appliedWidth}px, Height: ${appliedHeight}px`);
  295. if (!container || shadowRoot.querySelector('.__MonkeyConfig_overlay')?.offsetHeight === 0) {
  296. throw new Error('Shadow DOM rendering failed');}
  297. openDone(shadowRoot);} catch (e) {
  298. log(`Shadow DOM failed: ${e.message}, switching to iframe fallback`, 'warn');
  299. body.removeChild(openLayer);
  300. shadowRoot = null;}}
  301. if (!shadowRoot) {
  302. iframeFallback = document.createElement('iframe');
  303. const iframeWidth = cfg.iframeWidth || "600px";
  304. const iframeHeight = cfg.iframeHeight || "300px";
  305. log(`Switching to iframe with dimensions - Width: ${iframeWidth}, Height: ${iframeHeight}`);
  306. iframeFallback.style.cssText = `position: fixed !important; top: 50% !important; left: 50% !important; transform: translate(-50%, -50%) !important; width: ${MonkeyConfig.esc(iframeWidth)} !important; height: ${MonkeyConfig.esc(iframeHeight)} !important; max-width: 90vw !important; max-height: 80vh !important; z-index: 2147483647 !important; border: none !important; background: #eee !important; box-shadow: 2px 2px 16px #000 !important; border-radius: 0.5em !important;`;
  307. body.appendChild(iframeFallback);
  308. const iframeDoc = iframeFallback.contentDocument;
  309. try {iframeDoc.open();
  310. const iframeHTML = createTrustedHTML(`<!DOCTYPE html><html><head><style>
  311. html, body, * { all: initial !important; margin: 0 !important; padding: 0 !important; font-family: Arial, sans-serif !important; font-size: ${MonkeyConfig.esc(cfg.iframeFontSize)} !important; color: ${MonkeyConfig.esc(cfg.iframeFontColor)} !important; height: 100% !important; width: 100% !important; box-sizing: border-box !important; }
  312. html, body { background: #eee !important; display: block !important; isolation: isolate !important; }
  313. input, textarea, button, label, table, td, div, span { all: unset !important; }
  314. ${MonkeyConfig.res.stylesheets.main.replace(/__FONT_SIZE__/g, MonkeyConfig.esc(cfg.iframeFontSize)).replace(/__FONT_COLOR__/g, MonkeyConfig.esc(cfg.iframeFontColor))}
  315. .__MonkeyConfig_overlay { position: fixed !important; top: 0 !important; left: 0 !important; width: 100vw !important; height: 100vh !important; background-color: rgba(0, 0, 0, 0.6) !important; z-index: 2147483646 !important; }
  316. .__MonkeyConfig_container { position: relative !important; width: 100% !important; height: 100% !important; padding: 1em !important; box-sizing: border-box !important; overflow-y: auto !important; border-radius: 0.5em !important; font-size: ${MonkeyConfig.esc(cfg.iframeFontSize)} !important; isolation: isolate !important; background: #eee linear-gradient(180deg, #f8f8f8 0, #ddd 100%) !important; }
  317. .__MonkeyConfig_container h1 { font-size: 120% !important; font-weight: normal !important; margin: 0 !important; padding: 0 !important; display: block !important; }
  318. .__MonkeyConfig_container td.__MonkeyConfig_inline input[type="checkbox"] { width: 11px !important; height: 11px !important; margin: 0 0.5em 0 0 !important; vertical-align: middle !important; accent-color: #007bff !important; display: inline-block !important; }
  319. .__MonkeyConfig_container td.__MonkeyConfig_inline input[type="number"] { width: 40px !important; height: 20px !important; margin: 0 0.5em 0 0 !important; vertical-align: middle !important; display: inline-block !important; }
  320. .__MonkeyConfig_container textarea { width: 100% !important; padding: 1.2em !important; border: 1px solid #ccc !important; border-radius: 0.3em !important; box-sizing: border-box !important; font-size: 20px !important; color: ${MonkeyConfig.esc(cfg.iframeFontColor)} !important; resize: vertical !important; min-height: 140px !important; white-space: pre-wrap !important; display: block !important; }
  321. .__MonkeyConfig_container button { background: #ccc linear-gradient(180deg, #ddd 0, #ccc 45%, #bbb 50%, #aaa 100%) !important; border: 1px solid #999 !important; border-radius: 0.5em !important; box-shadow: 0 0 1px #000 !important; padding: 12px 16px 12px 48px !important; white-space: nowrap !important; font-size: 20px !important; color: ${MonkeyConfig.esc(cfg.iframeFontColor)} !important; cursor: pointer !important; display: inline-block !important; }
  322. .__MonkeyConfig_container button:hover { background: #d2d2d2 linear-gradient(180deg, #e2e2e2 0, #d2d2d2 45%, #c2c2c2 50%, #b2b2b2 100%) !important; }
  323. .__MonkeyConfig_container label { display: inline-block !important; line-height: 120% !important; vertical-align: middle !important; }
  324. .__MonkeyConfig_container table { border-spacing: 0 !important; margin: 0 !important; width: 100% !important; display: table !important; }
  325. .__MonkeyConfig_container td { border: none !important; line-height: 100% !important; padding: 0.3em !important; text-align: left !important; vertical-align: middle !important; white-space: normal !important; display: table-cell !important; }
  326. </style></head><body><div class="__MonkeyConfig_overlay"></div>${render()}</body></html>`);
  327. iframeDoc.write(iframeHTML);
  328. iframeDoc.close();
  329. openLayer = iframeFallback;
  330. openDone(iframeDoc);
  331. const iframeAppliedWidth = iframeFallback.offsetWidth || 'unknown';
  332. const iframeAppliedHeight = iframeFallback.offsetHeight || 'unknown';
  333. log(`Iframe actual applied dimensions - Width: ${iframeAppliedWidth}px, Height: ${iframeAppliedHeight}px`);} catch (e) {
  334. log(`Iframe rendering failed: ${e.message}`, 'error');
  335. body.removeChild(iframeFallback);
  336. iframeFallback = null;}}}
  337. function close() {
  338. try {if (openLayer && openLayer.parentNode) {openLayer.parentNode.removeChild(openLayer);}
  339. openLayer = shadowRoot = iframeFallback = undefined;
  340. displayed = false;} catch (e) {log(`Failed to close dialog: ${e.message}`, 'error');}}
  341. init();}
  342. MonkeyConfig.esc = string => {
  343. try {
  344. return String(string).replace(/&/g, '&amp;').replace(/</g, '&lt;').replace(/>/g, '&gt;').replace(/"/g, '&quot;').replace(/'/g, '&#39;');} catch (e) {
  345. log(`Failed to escape string: ${e.message}`, 'error');
  346. return '';}};
  347. MonkeyConfig.HTML = {
  348. _field: (name, opt) => {
  349. try {
  350. return opt.type && MonkeyConfig.HTML[opt.type] ? (opt.html ? opt.html.replace(/\[FIELD\]/, MonkeyConfig.HTML[opt.type](name, opt)) :
  351. MonkeyConfig.HTML[opt.type](name, opt)) :
  352. '';} catch (e) {
  353. log(`Failed to render field ${name}: ${e.message}`, 'error');
  354. return '';}},
  355. _label: (name, opt) => {try {
  356. return `<label for="__MonkeyConfig_field_${MonkeyConfig.esc(name)}"${
  357. opt.labelAlign || opt.fontSize || opt.fontColor? ` style="${[
  358. opt.labelAlign && `text-align:${MonkeyConfig.esc(opt.labelAlign)}`,
  359. opt.fontSize && `font-size:${MonkeyConfig.esc(opt.fontSize)}`,
  360. opt.fontColor && `color:${MonkeyConfig.esc(opt.fontColor)}`
  361. ].filter(Boolean).join(';')};"`
  362. : ''
  363. }>${MonkeyConfig.esc(opt.label || name.charAt(0).toUpperCase() + name.slice(1).replace(/_/g, ' '))}</label>`;} catch (e) {
  364. log(`Failed to render label for ${name}: ${e.message}`, 'error');
  365. return '';}},
  366. checkbox: name => `<input id="__MonkeyConfig_field_${MonkeyConfig.esc(name)}" type="checkbox" name="${MonkeyConfig.esc(name)}" />`,
  367. custom: (name, opt) => opt.html || '',
  368. number: (name, opt) => `<input id="__MonkeyConfig_field_${MonkeyConfig.esc(name)}" type="number" class="__MonkeyConfig_field_number" name="${MonkeyConfig.esc(name)}" min="${MonkeyConfig.esc(opt.min || '')}" max="${MonkeyConfig.esc(opt.max || '')}" step="${MonkeyConfig.esc(opt.step || '1')}" />`,
  369. text: name => `<input id="__MonkeyConfig_field_${MonkeyConfig.esc(name)}" type="text" class="__MonkeyConfig_field_text" name="${MonkeyConfig.esc(name)}" />`,
  370. color: name => `<input id="__MonkeyConfig_field_${MonkeyConfig.esc(name)}" type="color" class="__MonkeyConfig_field_text" name="${MonkeyConfig.esc(name)}" />`,
  371. textarea: (name, opt) => `<textarea id="__MonkeyConfig_field_${MonkeyConfig.esc(name)}" class="__MonkeyConfig_field_text" name="${MonkeyConfig.esc(name)}" rows="${MonkeyConfig.esc(opt.rows || 4)}" cols="${MonkeyConfig.esc(opt.cols || 20)}"></textarea>`,
  372. range: (name, opt) => `<input id="__MonkeyConfig_field_${MonkeyConfig.esc(name)}" type="range" name="${MonkeyConfig.esc(name)}" min="${MonkeyConfig.esc(opt.min || 0)}" max="${MonkeyConfig.esc(opt.max || 100)}" step="${MonkeyConfig.esc(opt.step || 1)}" />`,
  373. radio: (name, opt) => {
  374. try {
  375. return Object.entries(opt.choices).map(([val, text]) =>
  376. `<label><input type="radio" name="${MonkeyConfig.esc(name)}" value="${MonkeyConfig.esc(val)}" /> ${MonkeyConfig.esc(text)}</label><br/>`
  377. ).join('');} catch (e) {
  378. log(`Failed to render radio for ${name}: ${e.message}`, 'error');
  379. return '';}},
  380. file: (name, opt) => `<input id="__MonkeyConfig_field_${MonkeyConfig.esc(name)}" type="file" name="${MonkeyConfig.esc(name)}" accept="${MonkeyConfig.esc(opt.accept || '*/*')}" />`,
  381. button: (name, opt) => `<button type="button" id="__MonkeyConfig_field_${MonkeyConfig.esc(name)}" name="${MonkeyConfig.esc(name)}">${MonkeyConfig.esc(opt.label || 'Click')}</button>`,
  382. group: (name, opt) => {try {
  383. return `<fieldset><legend>${MonkeyConfig.esc(opt.label || name)}</legend>${
  384. Object.entries(opt.params).map(([subName, subOpt]) =>
  385. MonkeyConfig.formatters.tr(subName, subOpt)
  386. ).join('')
  387. }</fieldset>`;} catch (e) {
  388. log(`Failed to render group for ${name}: ${e.message}`, 'error');
  389. return '';}},
  390. select: (name, opt) => {try {
  391. const choices = Array.isArray(opt.choices) ? Object.fromEntries(opt.choices.map(val => [val, val])) :
  392. opt.choices;
  393. return `<select id="__MonkeyConfig_field_${MonkeyConfig.esc(name)}" class="__MonkeyConfig_field_select" name="${MonkeyConfig.esc(name)}"${
  394. opt.multiple ? ' multiple="multiple"' : ''
  395. }>${
  396. Object.entries(choices).map(([val, text]) =>
  397. `<option value="${MonkeyConfig.esc(val)}">${MonkeyConfig.esc(text)}</option>`
  398. ).join('')
  399. }</select>`;} catch (e) {
  400. log(`Failed to render select for ${name}: ${e.message}`, 'error');
  401. return '';}}};
  402. MonkeyConfig.formatters = {
  403. tr: (name, opt) => {try {return `<tr>${
  404. ['checkbox', 'number', 'text'].includes(opt.type)? `<td id="__MonkeyConfig_parent_${MonkeyConfig.esc(name)}" colspan="2" class="__MonkeyConfig_inline">${MonkeyConfig.HTML._label(name, opt)} ${MonkeyConfig.HTML._field(name, opt)}</td>`
  405. : opt.type === 'group'? `<td colspan="2">${MonkeyConfig.HTML._field(name, opt)}</td>`
  406. : `<td>${MonkeyConfig.HTML._label(name, opt)}</td><td id="__MonkeyConfig_parent_${MonkeyConfig.esc(name)}">${MonkeyConfig.HTML._field(name, opt)}</td>`
  407. }</tr>`;} catch (e) {
  408. log(`Failed to format table row for ${name}: ${e.message}`, 'error');
  409. return '';}}};
  410. MonkeyConfig.res = {
  411. icons: {
  412. save: 'iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAABGdBTUEAAK/INwWK6QAAABl0RVh0U29mdHdhcmUAQWRvYmUgSW1hZ2VSZWFkeXHJZTwAAAGrSURBVDjLvZPZLkNhFIV75zjvYm7VGFNCqoZUJ+roKUUpjRuqp61Wq0NKDMelGGqOxBSUIBKXWtWGZxAvobr8lWjChRgSF//dv9be+9trCwAI/vIE/26gXmviW5bqnb8yUK028qZjPfoPWEj4Ku5HBspgAz941IXZeze8N1bottSo8BTZviVWrEh546EO03EXpuJOdG63otJbjBKHkEp/Ml6yNYYzpuezWL4s5VMtT8acCMQcb5XL3eJE8VgBlR7BeMGW9Z4yT9y1CeyucuhdTGDxfftaBO7G4L+zg91UocxVmCiy51NpiP3n2treUPujL8xhOjYOzZYsQWANyRYlU4Y9Br6oHd5bDh0bCpSOixJiWx71YY09J5pM/WEbzFcDmHvwwBu2wnikg+lEj4mwBe5bC5h1OUqcwpdC60dxegRmR06TyjCF9G9z+qM2uCJmuMJmaNZaUrCSIi6X+jJIBBYtW5Cge7cd7sgoHDfDaAvKQGAlRZYc6ltJlMxX03UzlaRlBdQrzSCwksLRbOpHUSb7pcsnxCCwngvM2Rm/ugUCi84fycr4l2t8Bb6iqTxSCgNIAAAAAElFTkSuQmCC',
  413. reset: 'iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAABGdBTUEAAK/INwWK6QAAABl0RVh0U29mdHdhcmUAQWRvYmUgSW1hZ2VSZWFkeXHJZTwAAAIJSURBVDjLpVM9aJNRFD35GsRSoUKKzQ/B0NJJF3EQlKrVgijSCBmC4NBFKihIcXBwEZdSHVoUwUInFUEkQ1DQ4CKiFsQsTrb5xNpgaZHw2Uog5t5zn0NJNFaw0guX97hwzuPcc17IOYfNlIdNVrhxufR6xJkZjAbSQGXjNAorqixSWFDV3KPhJ+UGLtSQMPryrDscPwLnAHOEOQc6gkbUpIagGmApWIb/pZRX4fjj889nWiSQtgYyBZ1BTUEj6AjPa0P71nb0Jfqwa+futIheHrzRn2yRQCUK/lOQhApBJVQJChHfnkCqOwWEQ+iORJHckUyX5ksvAEyGNuJC+s6xCRXNHNxzKMmQ4luwgjfvZp69uvr2+IZcyJ8rjIporrxURggetnV0QET3rrPxzMNM2+n7p678jUTrCiWhphAjVHR9DlR0WkSzf4IHxg5MSF0zXZEuVKWKSlCBCostS8zeG7oV64wPqxInbw86lbVXKEQ8mkAqmUJ4SxieeVhcnANFC02C7N2h69HO2IXeWC8MDj2JnqaFNAMd8f3HKjx6+LxQRmnOz1OZaxKIaF1VISYwB9ARZoQaYY6o1WpYCVYxt+zDn/XzVBv/MOWXW5J44ubRyVgkelFpmF/4BJVfOVDlVyqLVBZI5manPjajDOdcswfG9k/3X9v3/vfZv7rFBanriIo++J/f+BMT+YWS6hXl7QAAAABJRU5ErkJggg==',
  414. close: 'iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAACdklEQVR4nH2RzWucVRjFf899ZyaTNE0FC6K0LjIV/AJBF4IglcFWBTdON1WkDSS48A8QxBYrKIKgSzdiAonBKk0DxUVpQaFQwUU3opAaEmmxCEUqMk1m5r3Ph4smr50ontXlnOecc+9zBeCFr9Z3IRwI5+aFo5O/8z84fHp9nyTuC+fXC0cnb8mh02v3jBb8OSpOGYnbSufiq63l/zI//+VaZ3ctlkYEeiH0lPuTWjw0EkqBMSaZ8cLOthdXX9lpbi+uHtld2NKYKIIygqIerRThf/TNMXOyBo1QxpOdfW7+ahVycP5qZzzZmUYoWQM3p6dOuN8UgGfnV6Ymks+NFk4gCEHPEn9lXgbSnjrnhjRPdFWmLx1/ZFa2W56Z/XlqorC5ZiEEIMCmQgKaNSqub0E3p+nLM4/PssVVePqzH6fGC5trJsEjkC01AkSE0oOupukf3nhidtszFADw5KdXXpuox2IjpSF+4M7tLMeuvPnUwt18bWeAltotBSQFEVstAvnOkm/tnB+qeezjy51m4edqEagp5oa5oabUCJri3zz6yfdDX1w94eGPLh0ZaxRntpcIUOqdU6Mm1XDfgs1SOytvHVyuAg58+F1nV6NYqichIhARsgebpR9HpDdWl6//rWln9e32sky+f/GBZr12o1lLVXM2Y7NkZv1E+3OA1gffvj5aZ6FeFP/cRJ1etn3J3O+VCLI5ak5/oHQHWpkB1t5pf9Et7Vi/VNScbI5EEO57UwTXB2qoGv2sbOQ8c+3k4cq8jWsnDi1sDHSqnxVVo1dmImJFAPafOv9gRLSAG7+999IvO813Y/+751tB7E0iP10/9eLG328sUoT0ZNvDAAAAAElFTkSuQmCC',
  415. reload: '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',
  416. home: 'iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAABHNCSVQICAgIfAhkiAAAAAlwSFlzAAALEgAACxIB0t1+/AAAACB0RVh0U29mdHdhcmUATWFjcm9tZWRpYSBGaXJld29ya3MgTVi7kSokAAAAFnRFWHRDcmVhdGlvbiBUaW1lADExLzA1LzA33bqJ2wAAAlxJREFUeJx9U0tIVGEU/v7fe30wLQxCDKdykmrUCiqTIgrG1pGBMElto1VQyySMWhVl0qJNix6QGLhQWom6isRHE5SWBWLFNGqlmTWOd+bOf85pcfU6I9WBsznn+77z4BwlIsi1089e791QWHDNVqrOUggaJiQzJpFyTSzpmqsDZ46M5eJVrsC53rfdtlaNsa+/EE86cMnL2VqhrMRCKGDDMdTTf/boqTyBqcXl4ruvPk9O/VwODs0s4n8WClgotZDYF5Adt5siaQ0AN4Y/dv6NHA1vRntDdV7sU8pgLk3B5wumEwDUhf53Bw3L6NMPs+vI5WiPhMECdL2ZwqWhL3n5qkICMdcXhKPnH43NJasW0tk88p1IGCwCFmBXWSm22IS+xG8fYwRQTJV6Y1FBTTzp/IO85id3V+JmfYWPS7GCJlNjEUvF6raj4XK0RcIgETCL3wGLQERwonYbWASXX86AoWCIKrRh8lUvHqj0iJxbncEinqgIjm0vh/1jxhuDGDqTpWlbKwDA4Y5h0AqYRPDwxRgeD46vibHg+K0OaGcJSgRZ4mk957gTZSWW30UuuK1vBG19IyAWz1eOLhPcCYtcuNATulijJRSwfQFaGWEVnN5anbfMVdPpFEw226K7mg7FHEM9oYDld0DrwMTsdwEAVnoJWZae+dbmmAUADZsKmwe+OZPBIhUMPxhEcfx93tHsfzLqx7QCOOMk3Nl4M7Dumerv93cLc+N3o5BiBYa3XCUCi1zodMqrfCWa/0y5Vnuvdw+YrgtRHZEJGmK4jERWJGZEtc63NI3n4v8As6uX85AjWHEAAAAASUVORK5CYII='},
  417. stylesheets: {
  418. main: `:host, body { all: initial; font-family: Arial, sans-serif !important; display: block !important; isolation: isolate; }
  419. .__MonkeyConfig_container { display: flex !important; flex-direction: column !important; padding: 1em !important; font-size: __FONT_SIZE__ !important; color: __FONT_COLOR__ !important; background: #eee linear-gradient(180deg, #f8f8f8 0, #ddd 100%) !important; border-radius: 0.5em !important; box-shadow: 2px 2px 16px #000 !important; box-sizing: border-box !important; }
  420. .__MonkeyConfig_container h1 { border-bottom: solid 1px #999 !important; font-size: 120% !important; font-weight: normal !important; margin: 0 0 0.5em 0 !important; padding: 0 0 0.3em 0 !important; text-align: center !important; }
  421. .__MonkeyConfig_content { flex: 1 !important; overflow-y: auto !important; max-height: 60vh !important; }
  422. .__MonkeyConfig_top, .__MonkeyConfig_bottom { margin-bottom: 1em !important; }
  423. .__MonkeyConfig_top_columns, .__MonkeyConfig_bottom_columns { display: flex !important; justify-content: space-between !important; margin-bottom: 1em !important;}
  424. .__MonkeyConfig_left_top, .__MonkeyConfig_right_top, .__MonkeyConfig_left_bottom, .__MonkeyConfig_right_bottom { width: 48% !important; }
  425. .__MonkeyConfig_columns { display: flex !important; justify-content: space-between !important; margin-bottom: 1em !important; }
  426. .__MonkeyConfig_left_column, .__MonkeyConfig_right_column { width: 48% !important; }
  427. .__MonkeyConfig_container table { border-spacing: 0 !important; margin: 0 !important; width: 100% !important; }
  428. .__MonkeyConfig_container td { border: none !important; line-height: 100% !important; padding: 0.3em !important; text-align: left !important; vertical-align: middle !important; white-space: normal !important; }
  429. .__MonkeyConfig_container td.__MonkeyConfig_inline { display: flex !important; align-items: center !important; white-space: nowrap !important; }
  430. .__MonkeyConfig_container td.__MonkeyConfig_inline label { margin-right: 0.5em !important; flex-shrink: 0 !important; display: block !important; max-width: 100% !important; overflow-x: auto !important; white-space: nowrap !important; scrollbar-width: thin !important; }
  431. .__MonkeyConfig_container td.__MonkeyConfig_inline input[type="checkbox"] { flex-grow: 0 !important; margin: 0 0.3em 0 0 !important; display: inline-block !important; width: 11px !important; height: 11px !important; }
  432. .__MonkeyConfig_container td.__MonkeyConfig_inline input[type="number"] { flex-grow: 0 !important; width: 40px !important; height: 20px !important; }
  433. .__MonkeyConfig_buttons_container { margin-top: 1em !important; border-top: solid 1px #999 !important; padding-top: 0.6em !important; text-align: center !important; }
  434. .__MonkeyConfig_buttons_container table { width: auto !important; margin: 0 auto !important; }
  435. .__MonkeyConfig_buttons_container td { padding: 0.3em !important; }
  436. .__MonkeyConfig_container button { background: #ccc linear-gradient(180deg, #ddd 0, #ccc 45%, #bbb 50%, #aaa 100%) !important; border: solid 1px !important; border-radius: 0.5em !important; box-shadow: 0 0 1px #000 !important; padding: 3px 8px 3px 24px !important; white-space: nowrap !important; }
  437. .__MonkeyConfig_container button img { vertical-align: middle !important; }
  438. .__MonkeyConfig_container label { line-height: 120% !important; vertical-align: middle !important; display: inline-block !important; max-width: 100% !important; overflow-x: auto !important; white-space: nowrap !important; scrollbar-width: thin !important; }
  439. .__MonkeyConfig_container textarea { vertical-align: text-top !important; width: 100% !important; white-space: pre-wrap !important; resize: vertical !important; text-align: left !important; }
  440. .__MonkeyConfig_container input[type="text"], .__MonkeyConfig_container input[type="number"], .__MonkeyConfig_container input[type="color"] { background: #fff !important; }
  441. .__MonkeyConfig_container button:hover { background: #d2d2d2 linear-gradient(180deg, #e2e2e2 0, #d2d2d2 45%, #c2c2c2 50%, #b2b2b2 100%) !important; }
  442. @media (max-width: 500px) {
  443. .__MonkeyConfig_columns, .__MonkeyConfig_top_columns, .__MonkeyConfig_bottom_columns { flex-direction: column !important; }
  444. .__MonkeyConfig_left_column, .__MonkeyConfig_right_column, .__MonkeyConfig_left_top, .__MonkeyConfig_right_top, .__MonkeyConfig_left_bottom, .__MonkeyConfig_right_bottom { width: 100% !important; }
  445. .__MonkeyConfig_container label { animation: scroll-text 10s linear infinite; }
  446. @keyframes scroll-text { 0% { transform: translateX(0); } 100% { transform: translateX(-100%); } }}`}};

QingJ © 2025

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