您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
Greasy Fork is available in English.
Enhanced Configuration Dialog Builder with column layout, custom styling, additional input types, and scrollable labels
当前为
此脚本不应直接安装。它是供其他脚本使用的外部库,要使用该库请加入元指令 // @require https://update.greasyfork.icu/scripts/548573/1655880/ModernMonkeyConfig%20Edition%20Enhanced%20mod2.js
// ==UserScript== // @name ModernMonkeyConfig Edition Enhanced mod2 // @noframes // @version 0.3.2 // @namespace http://odyniec.net/ // @include * // @description Enhanced Configuration Dialog Builder with column layout, custom styling, additional input types, and scrollable labels // @require https://cdnjs.cloudflare.com/ajax/libs/dompurify/3.2.5/purify.min.js#sha384-qSFej5dZNviyoPgYJ5+Xk4bEbX8AYddxAHPuzs1aSgRiXxJ3qmyWNaPsRkpv/+x5 // ==/UserScript== /* eslint-disable no-undef */ /* * ModernMonkeyConfig Edition Enhanced mod2 * Based on version 0.1.4 by Michal Wojciechowski (odyniec.net) * v0.1.4 - January 2020 - David Hosier (https://github.com/david-hosier/MonkeyConfig) * Enhanced by Scorpion - March 2025 * Additions: Column layout, font size/color customization, new input types (textarea, range, radio, file, button, group) * Modified: Checkbox, number, and text inputs aligned inline with labels - March 2025 * Fixed: Improved Shadow DOM and Optimized Iframe for consistent styling across sites - March 2025 * Enhanced: Scrollable labels, customizable checkbox/number sizes, new column options (left&top, right&top, left&bottom, right&bottom) - April 2025 * Security: Added Trusted Types support, DOMPurify sanitization, and robust error handling - May 2025 */ function MonkeyConfig(data) { let cfg = this, params = data.parameters || data.params, values = {}, storageKey, displayed, openLayer, shadowRoot, container, iframeFallback; function log(message, level = "info") { try { console[level](`[MonkeyConfig v0.3.2] ${message}`); } catch (e) { console.error(`[MonkeyConfig v0.3.2] Logging failed: ${e.message}`); } } let trustedPolicy; try { trustedPolicy = window.trustedTypes?.createPolicy("monkeyConfigPolicy", { createHTML: (input) => { if (typeof DOMPurify === "undefined") { log( "DOMPurify not available, falling back to basic sanitization", "warn" ); return input .replace(/<script\b[^<]*(?:(?!<\/script>)<[^<]*)*<\/script>/gi, "") .replace(/on\w+\s*=\s*".*?"/gi, ""); } return DOMPurify.sanitize(input, { RETURN_TRUSTED_TYPE: true }); }, }) || { createHTML: (input) => input }; } catch (e) { log(`Failed to create Trusted Types policy: ${e.message}`, "error"); trustedPolicy = { createHTML: (input) => input .replace(/<script\b[^<]*(?:(?!<\/script>)<[^<]*)*<\/script>/gi, "") .replace(/on\w+\s*=\s*".*?"/gi, ""), }; } function createTrustedHTML(htmlString) { try { return trustedPolicy.createHTML(htmlString); } catch (e) { log(`Failed to create TrustedHTML: ${e.message}`, "error"); return ""; } } function init() { try { data.buttons = data.buttons || [ "save", "reset", "close", "reload", "homepage", ]; storageKey = "_MonkeyConfig_" + (data.title || "Configuration").replace(/[^a-zA-Z0-9]/g, "_") + "_cfg"; let storedValues; try { storedValues = GM_getValue(storageKey) ? JSON.parse(GM_getValue(storageKey)) : {}; } catch (e) { log(`Failed to parse stored values: ${e.message}`, "error"); storedValues = {}; } cfg.shadowWidth = data.shadowWidth || storedValues.shadowWidth || "600px"; cfg.shadowHeight = data.shadowHeight || storedValues.shadowHeight || "400px"; cfg.iframeWidth = data.iframeWidth || storedValues.iframeWidth || "600px"; cfg.iframeHeight = data.iframeHeight || storedValues.iframeHeight || "400px"; cfg.shadowFontSize = data.shadowFontSize || storedValues.shadowFontSize || "14px"; cfg.shadowFontColor = data.shadowFontColor || storedValues.shadowFontColor || "#000000"; cfg.iframeFontSize = data.iframeFontSize || storedValues.iframeFontSize || "14px"; cfg.iframeFontColor = data.iframeFontColor || storedValues.iframeFontColor || "#000000"; cfg.title = data.title || (typeof GM_getMetadata === "function" ? GM_getMetadata("name") + " Configuration" : "Configuration"); for (let key in params) { const param = params[key]; values[key] = storedValues[key] ?? param.default ?? ""; } if (data.menuCommand) { try { GM_registerMenuCommand( data.menuCommand === true ? cfg.title : data.menuCommand, () => cfg.open() ); } catch (e) { log(`Failed to register menu command: ${e.message}`, "error"); } } cfg.open = open; cfg.close = close; cfg.get = (name) => values[name]; cfg.set = (name, value) => { try { values[name] = value; update(); } catch (e) { log(`Failed to set value for ${name}: ${e.message}`, "error"); } }; } catch (e) { log(`Initialization failed: ${e.message}`, "error"); } } function setDefaults() { try { for (let key in params) { if (params[key].default !== undefined) { values[key] = params[key].default; } } update(); } catch (e) { log(`Failed to set defaults: ${e.message}`, "error"); } } function render() { try { let html = `<div class="__MonkeyConfig_container"> <div style="position: absolute; top: 5px; right: 5px;"> <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;"> ${ MonkeyConfig.res.icons.close } </button> </div> <h1>${MonkeyConfig.esc( cfg.title )}</h1><div class="__MonkeyConfig_content"><div class="__MonkeyConfig_top">`; for (let key in params) { if (params[key].column === "top") { html += MonkeyConfig.formatters.tr(key, params[key]); } } html += `<div class="__MonkeyConfig_top_columns"><div class="__MonkeyConfig_left_top">`; for (let key in params) { if (params[key].column === "left&top") { html += MonkeyConfig.formatters.tr(key, params[key]); } } html += `</div><div class="__MonkeyConfig_right_top">`; for (let key in params) { if (params[key].column === "right&top") { html += MonkeyConfig.formatters.tr(key, params[key]); } } html += `</div></div>`; html += `</div><div class="__MonkeyConfig_columns"><div class="__MonkeyConfig_left_column">`; for (let key in params) { if (params[key].column === "left") { html += MonkeyConfig.formatters.tr(key, params[key]); } } html += `</div><div class="__MonkeyConfig_right_column">`; for (let key in params) { if (params[key].column === "right") { html += MonkeyConfig.formatters.tr(key, params[key]); } } html += `</div></div><table class="__MonkeyConfig_default">`; for (let key in params) { if (!params[key].column) { html += MonkeyConfig.formatters.tr(key, params[key]); } } html += `</table><div class="__MonkeyConfig_bottom">`; for (let key in params) { if (params[key].column === "bottom") { html += MonkeyConfig.formatters.tr(key, params[key]); } } html += `<div class="__MonkeyConfig_bottom_columns"><div class="__MonkeyConfig_left_bottom">`; for (let key in params) { if (params[key].column === "left&bottom") { html += MonkeyConfig.formatters.tr(key, params[key]); } } html += `</div><div class="__MonkeyConfig_right_bottom">`; for (let key in params) { if (params[key].column === "right&bottom") { html += MonkeyConfig.formatters.tr(key, params[key]); } } html += `</div></div>`; html += `</div></div><div class="__MonkeyConfig_buttons_container"><table><tr>`; data.buttons.forEach((btn) => { if (btn === "close") return; html += "<td>"; if (btn === "save") { 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;">${MonkeyConfig.res.icons.save} Save Without Reload</button>`; } else if (btn === "reset") { 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;">${MonkeyConfig.res.icons.reset} Reset</button>`; } else if (btn === "reload") { 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;">${MonkeyConfig.res.icons.reload} Save With Reload</button>`; } else if (btn === "homepage") { 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;">${MonkeyConfig.res.icons.home} Homepage</button>`; } html += "</td>"; }); html += "</tr></table></div></div>"; return createTrustedHTML(html); } catch (e) { log(`Failed to render HTML: ${e.message}`, "error"); return createTrustedHTML( "<div>Error rendering configuration dialog</div>" ); } } function update() { try { if (!displayed) return; const root = shadowRoot || (iframeFallback && iframeFallback.contentDocument); if (!root) { log("Root element not found for update", "error"); return; } for (let key in params) { const elem = root.querySelector(`[name="${MonkeyConfig.esc(key)}"]`), param = params[key]; if (!elem) { log(`Element for ${key} not found`, "warn"); continue; } if (param.type === "checkbox") { elem.checked = !!values[key]; elem.style.width = param.checkboxWidth || "11px"; elem.style.height = param.checkboxHeight || "11px"; } else if (param.type === "number") { elem.value = values[key] || param.default; elem.style.width = param.inputWidth || "50px"; elem.style.height = param.inputHeight || "15px"; } else if (param.type === "text") { elem.value = values[key] || param.default; elem.style.width = param.inputWidth || "100px"; elem.style.height = param.inputHeight || "15px"; } else if (param.type === "custom" && param.set) { try { param.set( values[key], root.querySelector( `#__MonkeyConfig_parent_${MonkeyConfig.esc(key)}` ) ); } catch (e) { log(`Failed to set custom param ${key}: ${e.message}`, "error"); } } else if ( ["text", "color", "textarea", "range"].includes(param.type) ) { elem.value = values[key] || param.default; } else if (param.type === "radio") { const radio = root.querySelector( `[name="${MonkeyConfig.esc(key)}"][value="${MonkeyConfig.esc( values[key] )}"]` ); if (radio) radio.checked = true; } else if (param.type === "file") { elem.value = ""; } else if (param.type === "select") { const currentValue = values[key]; if (elem.type === "checkbox") { const checkboxes = root.querySelectorAll( `input[name="${MonkeyConfig.esc(key)}"]` ); checkboxes.forEach((cb) => { cb.checked = currentValue.includes(cb.value); }); } else if (elem.multiple) { const options = root.querySelectorAll( `select[name="${MonkeyConfig.esc(key)}"] option` ); options.forEach((opt) => { opt.selected = currentValue.includes(opt.value); }); } else { elem.value = currentValue; } } const fontSize = shadowRoot ? cfg.shadowFontSize : cfg.iframeFontSize; const defaultFontColor = shadowRoot ? cfg.shadowFontColor : cfg.iframeFontColor; const labelFontColor = param.fontColor || defaultFontColor; elem.style.fontSize = fontSize; elem.style.color = labelFontColor; if (param.type === "checkbox" || param.type === "textarea") { elem.style.backgroundColor = "inherit"; elem.style.color = labelFontColor; } const label = root.querySelector( `label[for="__MonkeyConfig_field_${MonkeyConfig.esc(key)}"]` ); if (label) { label.style.fontSize = fontSize; label.style.color = labelFontColor; label.style.cssText += param.type === "textarea" ? "text-align:center;display:block;width:100%" : "text-align:left;display:inline-block;width:auto"; } } } catch (e) { log(`Failed to update UI: ${e.message}`, "error"); } } function saveClick() { try { const root = shadowRoot || (iframeFallback && iframeFallback.contentDocument); if (!root) { log("Root element not found for save", "error"); return; } for (let key in params) { const elem = root.querySelector(`[name="${MonkeyConfig.esc(key)}"]`), param = params[key]; if (!elem) { log(`Element for ${key} not found during save`, "warn"); continue; } if (param.type === "checkbox") { values[key] = elem.checked; } else if (param.type === "custom" && param.get) { try { values[key] = param.get( root.querySelector( `#__MonkeyConfig_parent_${MonkeyConfig.esc(key)}` ) ); } catch (e) { log(`Failed to get custom param ${key}: ${e.message}`, "error"); } } else if ( ["number", "text", "color", "textarea", "range"].includes(param.type) ) { values[key] = elem.value; } else if (param.type === "radio") { values[key] = root.querySelector(`[name="${MonkeyConfig.esc(key)}"]:checked`) ?.value || ""; } else if (param.type === "file") { values[key] = elem.dataset.value || values[key]; } else if (param.type === "select") { if (elem.type === "checkbox") { values[key] = Array.from( root.querySelectorAll( `input[name="${MonkeyConfig.esc(key)}"]:checked` ) ).map((input) => input.value); } else if (elem.multiple) { values[key] = Array.from( root.querySelectorAll( `select[name="${MonkeyConfig.esc(key)}"] option:selected` ) ).map((opt) => opt.value); } else { values[key] = elem.value; } } } const allValues = { ...values, shadowWidth: cfg.shadowWidth, shadowHeight: cfg.shadowHeight, iframeWidth: cfg.iframeWidth, iframeHeight: cfg.iframeHeight, shadowFontSize: cfg.shadowFontSize, shadowFontColor: cfg.shadowFontColor, iframeFontSize: cfg.iframeFontSize, iframeFontColor: cfg.iframeFontColor, }; try { GM_setValue(storageKey, JSON.stringify(allValues)); } catch (e) { log(`Failed to save values: ${e.message}`, "error"); } close(); if (data.onSave) { try { data.onSave(values); } catch (e) { log(`onSave callback failed: ${e.message}`, "error"); } } } catch (e) { log(`Save operation failed: ${e.message}`, "error"); } } function open() { if (window.self !== window.top) { log("Cannot open dialog in iframe", "warn"); return; } function openDone(root) { try { const saveBtn = root.querySelector("#__MonkeyConfig_button_save"); if (saveBtn) saveBtn.addEventListener("click", saveClick, false); const resetBtn = root.querySelector("#__MonkeyConfig_button_reset"); if (resetBtn) resetBtn.addEventListener("click", setDefaults, false); const closeBtn = root.querySelector("#__MonkeyConfig_button_close"); if (closeBtn) closeBtn.addEventListener("click", close, false); const reloadBtn = root.querySelector("#__MonkeyConfig_button_reload"); if (reloadBtn) reloadBtn.addEventListener( "click", () => { saveClick(); location.reload(); }, false ); const homepageBtn = root.querySelector( "#__MonkeyConfig_button_homepage" ); if (homepageBtn) homepageBtn.addEventListener( "click", () => window.open( "https://openai.com/", "_blank" ), false ); displayed = true; const checkboxes = root.querySelectorAll('input[type="checkbox"]'); checkboxes.forEach((cb) => { cb.style.width = cb.style.width || "11px"; cb.style.height = cb.style.height || "11px"; }); const numbers = root.querySelectorAll('input[type="number"]'); numbers.forEach((num) => { num.style.width = num.style.width || "40px"; num.style.height = num.style.height || "20px"; }); update(); } catch (e) { log(`Failed to initialize dialog: ${e.message}`, "error"); } } const body = document.querySelector("body") || document.documentElement; if (!body) { log("Body not found, cannot open dialog", "error"); return; } openLayer = document.createElement("div"); openLayer.className = "__MonkeyConfig_layer"; try { shadowRoot = openLayer.attachShadow({ mode: "open", }); } catch (e) { log(`Failed to attach Shadow DOM: ${e.message}`, "error"); shadowRoot = null; } const shadowWidth = cfg.shadowWidth || "600px"; const shadowHeight = cfg.shadowHeight || "300px"; log( `Preparing Shadow DOM with title: ${MonkeyConfig.esc( cfg.title )}, dimensions - Width: ${shadowWidth}, Height: ${shadowHeight}` ); const heightStyle = shadowHeight === "auto" ? "auto" : shadowHeight; if (shadowRoot) { try { shadowRoot.innerHTML = createTrustedHTML(` <style> :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; } h1 { font-size: 120% !important; font-weight: normal !important; margin: 0 !important; padding: 0 !important; } ${MonkeyConfig.res.stylesheets.main .replace(/__FONT_SIZE__/g, MonkeyConfig.esc(cfg.shadowFontSize)) .replace( /__FONT_COLOR__/g, MonkeyConfig.esc(cfg.shadowFontColor) )} .__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; } .__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; } </style> <div class="__MonkeyConfig_overlay"></div> ${render()}`); container = shadowRoot.querySelector(".__MonkeyConfig_container"); openLayer.style.cssText = "position: fixed !important; top: 0 !important; left: 0 !important; width: 100% !important; height: 100% !important; z-index: 2147483647 !important;"; body.appendChild(openLayer); log("Dialog appended to body via Shadow DOM"); const appliedWidth = container?.offsetWidth || "unknown"; const appliedHeight = container?.offsetHeight || "unknown"; log( `Actual applied dimensions - Width: ${appliedWidth}px, Height: ${appliedHeight}px` ); if ( !container || shadowRoot.querySelector(".__MonkeyConfig_overlay")?.offsetHeight === 0 ) { throw new Error("Shadow DOM rendering failed"); } openDone(shadowRoot); } catch (e) { log( `Shadow DOM failed: ${e.message}, switching to iframe fallback`, "warn" ); body.removeChild(openLayer); shadowRoot = null; } } if (!shadowRoot) { iframeFallback = document.createElement("iframe"); const iframeWidth = cfg.iframeWidth || "600px"; const iframeHeight = cfg.iframeHeight || "300px"; log( `Switching to iframe with dimensions - Width: ${iframeWidth}, Height: ${iframeHeight}` ); 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;`; body.appendChild(iframeFallback); const iframeDoc = iframeFallback.contentDocument; try { iframeDoc.open(); const iframeHTML = createTrustedHTML(`<!DOCTYPE html><html><head><style> 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; } html, body { background: #eee !important; display: block !important; isolation: isolate !important; } input, textarea, button, label, table, td, div, span { all: unset !important; } ${MonkeyConfig.res.stylesheets.main .replace(/__FONT_SIZE__/g, MonkeyConfig.esc(cfg.iframeFontSize)) .replace( /__FONT_COLOR__/g, MonkeyConfig.esc(cfg.iframeFontColor) )} .__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; } .__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; } .__MonkeyConfig_container h1 { font-size: 120% !important; font-weight: normal !important; margin: 0 !important; padding: 0 !important; display: block !important; } .__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; } .__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; } .__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; } .__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; } .__MonkeyConfig_container button:hover { background: #d2d2d2 linear-gradient(180deg, #e2e2e2 0, #d2d2d2 45%, #c2c2c2 50%, #b2b2b2 100%) !important; } .__MonkeyConfig_container label { display: inline-block !important; line-height: 120% !important; vertical-align: middle !important; } .__MonkeyConfig_container table { border-spacing: 0 !important; margin: 0 !important; width: 100% !important; display: table !important; } .__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; } </style></head><body><div class="__MonkeyConfig_overlay"></div>${render()}</body></html>`); iframeDoc.write(iframeHTML); iframeDoc.close(); openLayer = iframeFallback; openDone(iframeDoc); const iframeAppliedWidth = iframeFallback.offsetWidth || "unknown"; const iframeAppliedHeight = iframeFallback.offsetHeight || "unknown"; log( `Iframe actual applied dimensions - Width: ${iframeAppliedWidth}px, Height: ${iframeAppliedHeight}px` ); } catch (e) { log(`Iframe rendering failed: ${e.message}`, "error"); body.removeChild(iframeFallback); iframeFallback = null; } } } function close() { try { if (openLayer && openLayer.parentNode) { openLayer.parentNode.removeChild(openLayer); } openLayer = shadowRoot = iframeFallback = undefined; displayed = false; } catch (e) { log(`Failed to close dialog: ${e.message}`, "error"); } } init(); } MonkeyConfig.esc = (string) => { try { return String(string) .replace(/&/g, "&") .replace(/</g, "<") .replace(/>/g, ">") .replace(/"/g, """) .replace(/'/g, "'"); } catch (e) { log(`Failed to escape string: ${e.message}`, "error"); return ""; } }; MonkeyConfig.HTML = { _field: (name, opt) => { try { return opt.type && MonkeyConfig.HTML[opt.type] ? opt.html ? opt.html.replace( /\[FIELD\]/, MonkeyConfig.HTML[opt.type](name, opt) ) : MonkeyConfig.HTML[opt.type](name, opt) : ""; } catch (e) { log(`Failed to render field ${name}: ${e.message}`, "error"); return ""; } }, _label: (name, opt) => { try { return `<label for="__MonkeyConfig_field_${MonkeyConfig.esc(name)}"${ opt.labelAlign || opt.fontSize || opt.fontColor ? ` style="${[ opt.labelAlign && `text-align:${MonkeyConfig.esc(opt.labelAlign)}`, opt.fontSize && `font-size:${MonkeyConfig.esc(opt.fontSize)}`, opt.fontColor && `color:${MonkeyConfig.esc(opt.fontColor)}`, ] .filter(Boolean) .join(";")};"` : "" }>${MonkeyConfig.esc( opt.label || name.charAt(0).toUpperCase() + name.slice(1).replace(/_/g, " ") )}</label>`; } catch (e) { log(`Failed to render label for ${name}: ${e.message}`, "error"); return ""; } }, checkbox: (name) => `<input id="__MonkeyConfig_field_${MonkeyConfig.esc( name )}" type="checkbox" name="${MonkeyConfig.esc(name)}" />`, custom: (name, opt) => opt.html || "", 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")}" />`, text: (name) => `<input id="__MonkeyConfig_field_${MonkeyConfig.esc( name )}" type="text" class="__MonkeyConfig_field_text" name="${MonkeyConfig.esc( name )}" />`, color: (name) => `<input id="__MonkeyConfig_field_${MonkeyConfig.esc( name )}" type="color" class="__MonkeyConfig_field_text" name="${MonkeyConfig.esc( name )}" />`, 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>`, 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 )}" />`, radio: (name, opt) => { try { return Object.entries(opt.choices) .map( ([val, text]) => `<label><input type="radio" name="${MonkeyConfig.esc( name )}" value="${MonkeyConfig.esc(val)}" /> ${MonkeyConfig.esc( text )}</label><br/>` ) .join(""); } catch (e) { log(`Failed to render radio for ${name}: ${e.message}`, "error"); return ""; } }, file: (name, opt) => `<input id="__MonkeyConfig_field_${MonkeyConfig.esc( name )}" type="file" name="${MonkeyConfig.esc(name)}" accept="${MonkeyConfig.esc( opt.accept || "*/*" )}" />`, button: (name, opt) => `<button type="button" id="__MonkeyConfig_field_${MonkeyConfig.esc( name )}" name="${MonkeyConfig.esc(name)}">${MonkeyConfig.esc( opt.label || "Click" )}</button>`, group: (name, opt) => { try { return `<fieldset><legend>${MonkeyConfig.esc( opt.label || name )}</legend>${Object.entries(opt.params) .map(([subName, subOpt]) => MonkeyConfig.formatters.tr(subName, subOpt)) .join("")}</fieldset>`; } catch (e) { log(`Failed to render group for ${name}: ${e.message}`, "error"); return ""; } }, select: (name, opt) => { try { const choices = Array.isArray(opt.choices) ? Object.fromEntries(opt.choices.map((val) => [val, val])) : opt.choices; return `<select id="__MonkeyConfig_field_${MonkeyConfig.esc( name )}" class="__MonkeyConfig_field_select" name="${MonkeyConfig.esc(name)}"${ opt.multiple ? ' multiple="multiple"' : "" }>${Object.entries(choices) .map( ([val, text]) => `<option value="${MonkeyConfig.esc(val)}">${MonkeyConfig.esc( text )}</option>` ) .join("")}</select>`; } catch (e) { log(`Failed to render select for ${name}: ${e.message}`, "error"); return ""; } }, }; MonkeyConfig.formatters = { tr: (name, opt) => { try { return `<tr>${ ["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>` : opt.type === "group" ? `<td colspan="2">${MonkeyConfig.HTML._field(name, opt)}</td>` : `<td>${MonkeyConfig.HTML._label( name, opt )}</td><td id="__MonkeyConfig_parent_${MonkeyConfig.esc( name )}">${MonkeyConfig.HTML._field(name, opt)}</td>` }</tr>`; } catch (e) { log(`Failed to format table row for ${name}: ${e.message}`, "error"); return ""; } }, }; MonkeyConfig.res = { icons: { save: '<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M20 6 9 17l-5-5"/></svg>', reset: '<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M3 12a9 9 0 1 0 9-9 9.75 9.75 0 0 0-6.74 2.74L3 8"/><path d="M3 3v5h5"/></svg>', close: '<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M18 6 6 18"/><path d="m6 6 12 12"/></svg>', reload:'<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M21 12a9 9 0 0 0-9-9 9.75 9.75 0 0 0-6.74 2.74L3 8"/><path d="M3 3v5h5"/><path d="M3 12a9 9 0 0 0 9 9 9.75 9.75 0 0 0 6.74-2.74L21 16"/><path d="M16 16h5v5"/></svg>', home: '<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M15 21v-8a1 1 0 0 0-1-1h-4a1 1 0 0 0-1 1v8"/><path d="M3 10a2 2 0 0 1 .709-1.528l7-5.999a2 2 0 0 1 2.582 0l7 5.999A2 2 0 0 1 21 10v9a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2z"/></svg>' }, stylesheets: { main: ` /* إعادة تعيين القيم الأساسية */ :host, body { all: initial; font-family: ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, "Noto Sans", sans-serif !important; display: block !important; isolation: isolate !important; box-sizing: border-box !important; } *, *::before, *::after { box-sizing: border-box !important; } /* Overlay بتصميم Tailwind */ .__MonkeyConfig_overlay { position: fixed !important; inset: 0 !important; background-color: rgba(0, 0, 0, 0.5) !important; backdrop-filter: blur(4px) !important; -webkit-backdrop-filter: blur(4px) !important; z-index: 2147483646 !important; animation: fadeIn 0.15s ease-out !important; } @keyframes fadeIn { from { opacity: 0; } to { opacity: 1; } } @keyframes slideIn { from { opacity: 0; transform: translate(-50%, -50%) scale(0.95); } to { opacity: 1; transform: translate(-50%, -50%) scale(1); } } /* الحاوي الرئيسي */ .__MonkeyConfig_container { position: fixed !important; top: 50% !important; left: 50% !important; transform: translate(-50%, -50%) !important; z-index: 2147483647 !important; width: __WIDTH__ !important; height: __HEIGHT__ !important; max-width: calc(100vw - 2rem) !important; max-height: calc(100vh - 2rem) !important; background-color: white !important; border-radius: 0.75rem !important; /* rounded-xl */ box-shadow: 0 25px 50px -12px rgba(0, 0, 0, 0.25) !important; /* shadow-2xl */ overflow: hidden !important; display: flex !important; flex-direction: column !important; font-size: __FONT_SIZE__ !important; color: __FONT_COLOR__ !important; animation: slideIn 0.2s ease-out !important; } /* الهيدر */ .__MonkeyConfig_container h1 { background: linear-gradient(135deg, #667eea 0%, #764ba2 100%) !important; color: white !important; font-size: 1.25rem !important; /* text-xl */ font-weight: 600 !important; /* font-semibold */ margin: 0 !important; padding: 1.5rem 4rem 1.5rem 1.5rem !important; /* p-6 pr-16 */ border-bottom: 1px solid rgba(229, 231, 235, 0.2) !important; position: relative !important; text-shadow: 0 1px 2px rgba(0, 0, 0, 0.1) !important; } /* زر الإغلاق */ .__MonkeyConfig_container button#__MonkeyConfig_button_close { position: absolute !important; top: 1rem !important; /* top-4 */ right: 1rem !important; /* right-4 */ width: 2rem !important; /* w-8 */ height: 2rem !important; /* h-8 */ border: none !important; border-radius: 0.375rem !important; /* rounded-md */ background-color: rgba(255, 255, 255, 0.2) !important; color: white !important; cursor: pointer !important; display: flex !important; align-items: center !important; justify-content: center !important; transition: all 0.15s ease-in-out !important; backdrop-filter: blur(8px) !important; -webkit-backdrop-filter: blur(8px) !important; } .__MonkeyConfig_container button#__MonkeyConfig_button_close:hover { background-color: rgba(255, 255, 255, 0.3) !important; transform: scale(1.05) !important; } /* منطقة المحتوى */ .__MonkeyConfig_content { flex: 1 !important; overflow-y: auto !important; padding: 1.5rem !important; /* p-6 */ background-color: #f9fafb !important; /* bg-gray-50 */ } /* التمرير المخصص */ .__MonkeyConfig_content::-webkit-scrollbar { width: 0.5rem !important; /* w-2 */ } .__MonkeyConfig_content::-webkit-scrollbar-track { background-color: #f3f4f6 !important; /* bg-gray-100 */ border-radius: 0.25rem !important; /* rounded */ } .__MonkeyConfig_content::-webkit-scrollbar-thumb { background-color: #d1d5db !important; /* bg-gray-300 */ border-radius: 0.25rem !important; /* rounded */ } .__MonkeyConfig_content::-webkit-scrollbar-thumb:hover { background-color: #9ca3af !important; /* bg-gray-400 */ } /* التخطيطات */ .__MonkeyConfig_top, .__MonkeyConfig_bottom { margin-bottom: 1.5rem !important; /* mb-6 */ } .__MonkeyConfig_columns, .__MonkeyConfig_top_columns, .__MonkeyConfig_bottom_columns { display: grid !important; grid-template-columns: repeat(2, 1fr) !important; /* grid-cols-2 */ gap: 1.5rem !important; /* gap-6 */ margin-bottom: 1.5rem !important; /* mb-6 */ } .__MonkeyConfig_left_column, .__MonkeyConfig_right_column, .__MonkeyConfig_left_top, .__MonkeyConfig_right_top, .__MonkeyConfig_left_bottom, .__MonkeyConfig_right_bottom { background-color: white !important; border-radius: 0.5rem !important; /* rounded-lg */ padding: 1.5rem !important; /* p-6 */ box-shadow: 0 1px 3px 0 rgba(0, 0, 0, 0.1), 0 1px 2px 0 rgba(0, 0, 0, 0.06) !important; /* shadow-sm */ border: 1px solid #e5e7eb !important; /* border border-gray-200 */ } /* الجداول */ .__MonkeyConfig_container table { width: 100% !important; border-collapse: separate !important; border-spacing: 0 !important; background-color: white !important; border-radius: 0.5rem !important; /* rounded-lg */ overflow: hidden !important; box-shadow: 0 1px 3px 0 rgba(0, 0, 0, 0.1), 0 1px 2px 0 rgba(0, 0, 0, 0.06) !important; /* shadow-sm */ border: 1px solid #e5e7eb !important; /* border border-gray-200 */ margin-bottom: 1.5rem !important; /* mb-6 */ } .__MonkeyConfig_container td { padding: 1rem 1.5rem !important; /* px-6 py-4 */ border-bottom: 1px solid #f3f4f6 !important; /* border-b border-gray-100 */ vertical-align: middle !important; } .__MonkeyConfig_container tr:last-child td { border-bottom: none !important; } /* الحقول المضمنة */ .__MonkeyConfig_container td.__MonkeyConfig_inline { display: flex !important; align-items: center !important; justify-content: space-between !important; gap: 1rem !important; /* gap-4 */ } .__MonkeyConfig_container td.__MonkeyConfig_inline label { flex: 1 !important; font-weight: 500 !important; /* font-medium */ color: #374151 !important; /* text-gray-700 */ margin: 0 !important; } /* Labels عامة */ .__MonkeyConfig_container label { font-weight: 500 !important; /* font-medium */ color: #374151 !important; /* text-gray-700 */ line-height: 1.5 !important; cursor: pointer !important; display: block !important; margin-bottom: 0.5rem !important; /* mb-2 */ } /* حقول الإدخال */ .__MonkeyConfig_container input[type="text"], .__MonkeyConfig_container input[type="number"], .__MonkeyConfig_container input[type="color"], .__MonkeyConfig_container input[type="range"], .__MonkeyConfig_container select { border: 1px solid #d1d5db !important; /* border border-gray-300 */ border-radius: 0.375rem !important; /* rounded-md */ padding: 0.5rem 0.75rem !important; /* px-3 py-2 */ font-size: 0.875rem !important; /* text-sm */ line-height: 1.25rem !important; background-color: white !important; transition: all 0.15s ease-in-out !important; outline: none !important; } .__MonkeyConfig_container input[type="text"]:focus, .__MonkeyConfig_container input[type="number"]:focus, .__MonkeyConfig_container input[type="color"]:focus, .__MonkeyConfig_container select:focus { border-color: #3b82f6 !important; /* border-blue-500 */ box-shadow: 0 0 0 3px rgba(59, 130, 246, 0.1) !important; /* ring-blue-500/10 */ } .__MonkeyConfig_container input[type="number"] { width: 5rem !important; /* w-20 */ } .__MonkeyConfig_container input[type="text"] { min-width: 8rem !important; /* min-w-32 */ } /* Checkbox */ .__MonkeyConfig_container input[type="checkbox"] { width: 1.125rem !important; /* w-4.5 */ height: 1.125rem !important; /* h-4.5 */ accent-color: #3b82f6 !important; /* accent-blue-500 */ cursor: pointer !important; margin: 0 !important; flex-shrink: 0 !important; } /* Textarea */ .__MonkeyConfig_container textarea { width: 100% !important; border: 1px solid #d1d5db !important; /* border border-gray-300 */ border-radius: 0.375rem !important; /* rounded-md */ padding: 0.75rem !important; /* p-3 */ font-size: 0.875rem !important; /* text-sm */ line-height: 1.5 !important; background-color: white !important; resize: vertical !important; min-height: 5rem !important; /* min-h-20 */ font-family: inherit !important; transition: all 0.15s ease-in-out !important; outline: none !important; } .__MonkeyConfig_container textarea:focus { border-color: #3b82f6 !important; /* border-blue-500 */ box-shadow: 0 0 0 3px rgba(59, 130, 246, 0.1) !important; /* ring-blue-500/10 */ } /* Radio buttons */ .__MonkeyConfig_container input[type="radio"] { width: 1rem !important; /* w-4 */ height: 1rem !important; /* h-4 */ accent-color: #3b82f6 !important; /* accent-blue-500 */ cursor: pointer !important; margin-right: 0.5rem !important; /* mr-2 */ } .__MonkeyConfig_container input[type="radio"] + label { display: inline !important; margin-bottom: 0 !important; font-weight: 400 !important; /* font-normal */ cursor: pointer !important; } /* منطقة الأزرار */ .__MonkeyConfig_buttons_container { padding: 1.5rem !important; /* p-6 */ background-color: #f9fafb !important; /* bg-gray-50 */ border-top: 1px solid #e5e7eb !important; /* border-t border-gray-200 */ display: flex !important; justify-content: center !important; gap: 0.75rem !important; /* gap-3 */ flex-wrap: wrap !important; } .__MonkeyConfig_buttons_container table { background: none !important; box-shadow: none !important; border: none !important; margin: 0 !important; } .__MonkeyConfig_buttons_container td { padding: 0 0.375rem !important; /* px-1.5 */ border: none !important; } /* الأزرار */ .__MonkeyConfig_container button { display: inline-flex !important; align-items: center !important; gap: 0.5rem !important; /* gap-2 */ padding: 0.5rem 1rem !important; /* px-4 py-2 */ font-size: 0.875rem !important; /* text-sm */ font-weight: 500 !important; /* font-medium */ border-radius: 0.375rem !important; /* rounded-md */ border: none !important; cursor: pointer !important; transition: all 0.15s ease-in-out !important; text-decoration: none !important; outline: none !important; } /* أزرار مختلفة */ .__MonkeyConfig_container button#__MonkeyConfig_button_save { background-color: #3b82f6 !important; /* bg-blue-500 */ color: white !important; } .__MonkeyConfig_container button#__MonkeyConfig_button_save:hover { background-color: #2563eb !important; /* bg-blue-600 */ transform: translateY(-1px) !important; box-shadow: 0 4px 6px -1px rgba(0, 0, 0, 0.1) !important; /* shadow-md */ } .__MonkeyConfig_container button#__MonkeyConfig_button_reset { background-color: #f59e0b !important; /* bg-amber-500 */ color: white !important; } .__MonkeyConfig_container button#__MonkeyConfig_button_reset:hover { background-color: #d97706 !important; /* bg-amber-600 */ transform: translateY(-1px) !important; box-shadow: 0 4px 6px -1px rgba(0, 0, 0, 0.1) !important; /* shadow-md */ } .__MonkeyConfig_container button#__MonkeyConfig_button_reload { background-color: #10b981 !important; /* bg-emerald-500 */ color: white !important; } .__MonkeyConfig_container button#__MonkeyConfig_button_reload:hover { background-color: #059669 !important; /* bg-emerald-600 */ transform: translateY(-1px) !important; box-shadow: 0 4px 6px -1px rgba(0, 0, 0, 0.1) !important; /* shadow-md */ } .__MonkeyConfig_container button#__MonkeyConfig_button_homepage { background-color: #6b7280 !important; /* bg-gray-500 */ color: white !important; } .__MonkeyConfig_container button#__MonkeyConfig_button_homepage:hover { background-color: #4b5563 !important; /* bg-gray-600 */ transform: translateY(-1px) !important; box-shadow: 0 4px 6px -1px rgba(0, 0, 0, 0.1) !important; /* shadow-md */ } /* Select dropdown */ .__MonkeyConfig_container select { appearance: none !important; background-image: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' fill='none' viewBox='0 0 20 20'%3e%3cpath stroke='%236b7280' stroke-linecap='round' stroke-linejoin='round' stroke-width='1.5' d='m6 8 4 4 4-4'/%3e%3c/svg%3e") !important; background-position: right 0.5rem center !important; background-repeat: no-repeat !important; background-size: 1.5em 1.5em !important; padding-right: 2.5rem !important; min-width: 8rem !important; } /* Fieldset for groups */ .__MonkeyConfig_container fieldset { border: 1px solid #d1d5db !important; /* border border-gray-300 */ border-radius: 0.5rem !important; /* rounded-lg */ padding: 1rem !important; /* p-4 */ margin-bottom: 1rem !important; /* mb-4 */ background-color: white !important; } .__MonkeyConfig_container legend { font-weight: 600 !important; /* font-semibold */ color: #374151 !important; /* text-gray-700 */ padding: 0 0.5rem !important; /* px-2 */ } /* Responsive Design */ @media (max-width: 768px) { .__MonkeyConfig_columns, .__MonkeyConfig_top_columns, .__MonkeyConfig_bottom_columns { grid-template-columns: 1fr !important; /* grid-cols-1 */ gap: 1rem !important; /* gap-4 */ } .__MonkeyConfig_container { margin: 1rem !important; /* m-4 */ max-width: calc(100vw - 2rem) !important; max-height: calc(100vh - 2rem) !important; } .__MonkeyConfig_container h1 { font-size: 1.125rem !important; /* text-lg */ padding: 1rem 3rem 1rem 1rem !important; /* p-4 pr-12 */ } .__MonkeyConfig_content { padding: 1rem !important; /* p-4 */ } .__MonkeyConfig_buttons_container { padding: 1rem !important; /* p-4 */ flex-direction: column !important; } .__MonkeyConfig_container button { width: 100% !important; justify-content: center !important; } .__MonkeyConfig_container td.__MonkeyConfig_inline { flex-direction: column !important; align-items: stretch !important; gap: 0.5rem !important; /* gap-2 */ } .__MonkeyConfig_container td.__MonkeyConfig_inline label { margin-bottom: 0.25rem !important; /* mb-1 */ } } @media (max-width: 480px) { .__MonkeyConfig_container { border-radius: 0 !important; max-width: 100vw !important; max-height: 100vh !important; margin: 0 !important; } .__MonkeyConfig_container h1 { padding: 0.75rem 2.5rem 0.75rem 0.75rem !important; /* p-3 pr-10 */ } .__MonkeyConfig_content { padding: 0.75rem !important; /* p-3 */ } .__MonkeyConfig_buttons_container { padding: 0.75rem !important; /* p-3 */ } } /* Animation for buttons */ @keyframes buttonPress { 0% { transform: translateY(0) scale(1); } 50% { transform: translateY(1px) scale(0.98); } 100% { transform: translateY(0) scale(1); } } .__MonkeyConfig_container button:active { animation: buttonPress 0.1s ease-in-out !important; } /* Focus styles */ .__MonkeyConfig_container button:focus-visible { outline: 2px solid #3b82f6 !important; outline-offset: 2px !important; } .__MonkeyConfig_container input:focus-visible, .__MonkeyConfig_container textarea:focus-visible, .__MonkeyConfig_container select:focus-visible { outline: 2px solid #3b82f6 !important; outline-offset: 2px !important; } /* Loading state (optional) */ .__MonkeyConfig_container.loading { opacity: 0.7 !important; pointer-events: none !important; } /* Success message (optional) */ .__MonkeyConfig_success { position: absolute !important; top: 1rem !important; left: 50% !important; transform: translateX(-50%) !important; background-color: #10b981 !important; /* bg-emerald-500 */ color: white !important; padding: 0.5rem 1rem !important; /* px-4 py-2 */ border-radius: 0.375rem !important; /* rounded-md */ font-size: 0.875rem !important; /* text-sm */ z-index: 2147483648 !important; animation: slideDown 0.3s ease-out !important; } @keyframes slideDown { from { opacity: 0; transform: translateX(-50%) translateY(-1rem); } to { opacity: 1; transform: translateX(-50%) translateY(0); } } ` }, };