Tampermonkey Config

Simple Tampermonkey script config library

Este script no debería instalarse directamente. Es una biblioteca que utilizan otros scripts mediante la meta-directiva de inclusión // @require https://update.greasyfork.org/scripts/470224/1667265/Tampermonkey%20Config.js

Tendrás que instalar una extensión para tu navegador como Tampermonkey, Greasemonkey o Violentmonkey si quieres utilizar este script.

Necesitarás instalar una extensión como Tampermonkey o Violentmonkey para instalar este script.

Necesitarás instalar una extensión como Tampermonkey o Violentmonkey para instalar este script.

Necesitarás instalar una extensión como Tampermonkey o Userscripts para instalar este script.

Necesitará instalar una extensión como Tampermonkey para instalar este script.

Necesitarás instalar una extensión para administrar scripts de usuario si quieres instalar este script.

(Ya tengo un administrador de scripts de usuario, déjame instalarlo)

Necesitará instalar una extensión como Stylus para instalar este estilo.

Necesitará instalar una extensión como Stylus para instalar este estilo.

Necesitará instalar una extensión como Stylus para instalar este estilo.

Necesitará instalar una extensión del gestor de estilos de usuario para instalar este estilo.

Necesitará instalar una extensión del gestor de estilos de usuario para instalar este estilo.

Necesitará instalar una extensión del gestor de estilos de usuario para instalar este estilo.

(Ya tengo un administrador de estilos de usuario, déjame instalarlo)

// ==UserScript==
// @name         Tampermonkey Config
// @name:zh-CN   Tampermonkey 配置
// @license      gpl-3.0
// @namespace    http://tampermonkey.net/
// @version      1.2.2
// @description  Simple yet powerful config lib for userscripts
// @description:zh-CN  简单而又强大的用户脚本配置库
// @author       PRO
// @match        *
// @grant        GM_setValue
// @grant        GM_getValue
// @grant        GM_deleteValue
// @grant        GM_registerMenuCommand
// @grant        GM_unregisterMenuCommand
// @grant        GM_addValueChangeListener
// ==/UserScript==
class GM_config extends EventTarget{static get version(){return"1.2.2"}static#t={same:(t,e,o)=>e,not:(t,e,o)=>!e,int:(t,e,o)=>{const r=parseInt(e);if(isNaN(r))throw`Invalid value: ${e}, expected integer!`;const i=o.min??-1/0,n=o.max??1/0;if(NaN!==i&&r<i)throw`Invalid value: ${e}, expected integer >= ${i}!`;if(NaN!==n&&r>n)throw`Invalid value: ${e}, expected integer <= ${n}!`;return r},float:(t,e,o)=>{const r=parseFloat(e);if(isNaN(r))throw`Invalid value: ${e}, expected float!`;const i=o.min??-1/0,n=o.max??1/0;if(NaN!==i&&r<i)throw`Invalid value: ${e}, expected float >= ${i}!`;if(NaN!==n&&r>n)throw`Invalid value: ${e}, expected float <= ${n}!`;return r},enum:(t,e,o)=>(e+1)%o.options.length};static#e={normal:(t,e,o)=>`${o.name}: ${e}`,boolean:(t,e,o)=>`${o.name}: ${e?"✔":"✘"}`,enum:(t,e,o)=>`${o.name}: ${o.options[e]}`,name_only:(t,e,o)=>o.name,folder:(t,e,o)=>`${o.folderDisplay.prefix}${o.name}${o.folderDisplay.suffix}`};static#o={str:{value:"",input:"prompt",processor:"same",formatter:"normal"},bool:{value:!1,input:"current",processor:"not",formatter:"boolean"},int:{value:0,input:"prompt",processor:"int",formatter:"normal"},float:{value:0,input:"prompt",processor:"float",formatter:"normal"},enum:{options:["A","B","C"],value:0,input:"current",processor:"enum",formatter:"enum"},action:{value:null,input:"action",processor:"same",formatter:"name_only",autoClose:!0},folder:{value:null,items:{},input:"folder",processor:"same",formatter:"folder",autoClose:!1}};proxy={};debug=!1;#r={};#i={prompt:(t,e,o)=>{const r=window.prompt(`🤔 New value for ${o.name}:`,e);return null===r?e:r},current:(t,e,o)=>e,action:(t,e,o)=>(this.#n(!1,{prop:t,before:e,after:e,remote:!1}),e),folder:(t,e,o)=>{const r=GM_config.#s(t).pop();return this.down(r),this.#n(!1,{prop:t,before:e,after:e,remote:!1}),e}};#a={};#c=[];#l=null;#u={};get currentPath(){return[...this.#c]}get#p(){if(this.#l)return this.#l;let t=this.#r;for(const e of this.#c)t=t[e].items;return this.#l=t,t}constructor(t,e={}){super(),this.#r=t,this.#d(this.#r,[],{input:"prompt",processor:"same",formatter:"normal",folderDisplay:{prefix:"",suffix:" >",parentText:"< Back",parentTitle:"Return to parent folder"}}),this.debug=e.debug??this.debug;const o={},r=t=>({has:(e,o)=>{const r=GM_config.#h(`${t}.${o}`);return void 0!==this.#f(r)},get:(e,i)=>{const n=GM_config.#h(`${t}.${i}`),s=this.#f(n);if(void 0!==s)return"folder"===s.type?(o[n]||(o[n]=new Proxy({},r(n))),o[n]):this.get(n)},set:(e,o,r)=>this.set(`${t}.${o}`,r),ownKeys:e=>this.list(t),getOwnPropertyDescriptor:(t,e)=>({enumerable:!0,configurable:!0})});if(this.proxy=new Proxy({},r("")),window===window.top){if(e.immediate??1)this.#g();else{const t=GM_registerMenuCommand("Show configuration",(()=>{this.#g()}),{autoClose:!1,title:"Show configuration options for this script"});this.#m(`+ Registered menu command: prop="Show configuration", id=${t}`),this.#a.null=t}this.addEventListener("set",(t=>{if(t.detail.before!==t.detail.after){this.#m(`🔧 "${t.detail.prop}" changed from ${t.detail.before} to ${t.detail.after}, remote: ${t.detail.remote}`);void 0!==this.#a[t.detail.prop]?this.#$(t.detail.prop):this.#m(`+ Skipped updating menu since it's not registered: prop="${t.detail.prop}"`)}})),this.addEventListener("get",(t=>{this.#m(`🔍 "${t.detail.prop}" requested, value is ${t.detail.after}`)}))}}static#v(t,...e){return"function"==typeof t?t(...e):t}static#s(t){return t.split(".").filter((t=>t))}static#y(t){return t.join(".")}static#h(t){return GM_config.#y(GM_config.#s(t))}static extend(t,e){return!(t in GM_config.#o)&&(GM_config.#o[t]=e,!0)}get(t){const e=GM_config.#h(t),o=this.#f(t).value,r=this.#M(e,o);return this.#n(!1,{prop:e,before:r,after:r,remote:!1}),r}set(t,e){const o=GM_config.#h(t),r=this.#f(o);if(void 0===r)return!1;return e===r.value&&"function"==typeof GM_deleteValue?(GM_deleteValue(o),this.#m(`🗑️ "${o}" deleted`)):GM_setValue(o,e),!0}list(t){const e=GM_config.#h(t??"");return e?Object.keys(this.#f(e)?.items??{}):Object.keys(this.#r)}up(){const t=this.#c.pop();return this.#m(`⬆️ Went up to ${GM_config.#y(this.#c)||"#root"}`),this.#g(),t??null}down(t){const e=this.#p;return t in e&&"folder"===e[t].type?(this.#c.push(t),this.#m(`⬇️ Went down to ${GM_config.#y(this.#c)}`),this.#g(),!0):(this.#m(`❌ Cannot go down to ${t} - not a folder`),!1)}#f(t){"string"==typeof t&&(t=GM_config.#s(t));let e=this.#r;for(const o of t.slice(0,-1))e=e?.[o]?.items;return e?e[t[t.length-1]]:void 0}#M(t,e){if(t in this.#u)return this.#u[t];const o=GM_getValue(t,e);return this.#u[t]=o,o}#m(...t){this.debug&&console.log("[GM_config]",...t)}#n(t,e){const o=new CustomEvent(t?"set":"get",{detail:e});return this.dispatchEvent(o)}#_(){for(const t in this.#a){const e=this.#a[t];GM_unregisterMenuCommand(e),delete this.#a[t],this.#m(`- Unregistered menu command: prop="${t}", id=${e}`)}}#g(){this.#l=null;const t=this.#f(this.#c);if(this.#_(),this.#c.length){const e=GM_registerMenuCommand(t.folderDisplay.parentText,(()=>{this.up()}),{autoClose:!1,title:t.folderDisplay.parentTitle});this.#a.null=e,this.#m(`+ Registered menu command: prop=null, id=${e}`)}for(const t in this.#p){const e=GM_config.#y([...this.#c,t]);this.#a[e]=this.#$(e)}}#$(t){const e=this.#f(t);if(e.hidden)return void this.#m(`○ Skipped registering menu command: prop="${t}" (hidden)`);const{value:o,input:r,processor:i,formatter:n,accessKey:s,autoClose:a,title:c}=e,l=this.#M(t,o),u="function"==typeof r?r:this.#i[r],p="function"==typeof n?n:GM_config.#e[n],d={accessKey:GM_config.#v(s,t,l,e),autoClose:GM_config.#v(a,t,l,e),title:GM_config.#v(c,t,l,e),id:this.#a[t]},h=GM_registerMenuCommand(p(t,l,e),(()=>{let o;try{if(o=u(t,l,e),"function"==typeof i)o=i(t,o,e);else{if("string"!=typeof i)throw"Unknown processor format: "+typeof i;{const r=GM_config.#t[i];if(void 0===r)throw`Unknown processor: ${i}`;o=r(t,o,e)}}}catch(t){return void alert("⚠️ "+t)}o!==l&&this.set(t,o)}),d);return this.#m(`+ Registered menu command: prop="${t}", id=${h}, option=`,d),h}#G(t,e,o,r){const i=this.#f(t).value;void 0===e&&(e=i),void 0===o&&(o=i),t in this.#u&&(this.#u[t]=o),this.#n(!0,{prop:t,before:e,after:o,remote:r})}#d(t,e=[],o={}){function r(t,...e){const o=Object.assign(t.folderDisplay??{},...e.map((t=>t.folderDisplay??{})));return Object.assign(t,...e,{folderDisplay:o})}const i=r({},o,t.$default??{});delete t.$default;for(const o in t){const n=[...e,o];t[o]=r({},i,GM_config.#o[t[o].type]??{},t[o]),"folder"===t[o].type?this.#d(t[o].items,n,i):GM_addValueChangeListener(GM_config.#y(n),this.#G.bind(this))}}}