Tampermonkey 配置

简易的 Tampermonkey 脚本配置库

目前为 2024-12-10 提交的版本。查看 最新版本

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

// ==UserScript==
// @name         Tampermonkey Config
// @name:zh-CN   Tampermonkey 配置
// @license      gpl-3.0
// @namespace    http://tampermonkey.net/
// @version      1.2.0
// @description  Simple Tampermonkey script config library
// @description:zh-CN  简易的 Tampermonkey 脚本配置库
// @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.0"}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,s=o.max??1/0;if(NaN!==i&&r<i)throw`Invalid value: ${e}, expected integer >= ${i}!`;if(NaN!==s&&r>s)throw`Invalid value: ${e}, expected integer <= ${s}!`;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,s=o.max??1/0;if(NaN!==i&&r<i)throw`Invalid value: ${e}, expected float >= ${i}!`;if(NaN!==s&&r>s)throw`Invalid value: ${e}, expected float <= ${s}!`;return r}};static#e={normal:(t,e,o)=>`${o.name}: ${e}`,boolean:(t,e,o)=>`${o.name}: ${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"},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.#s(!1,{prop:t,before:e,after:e,remote:!1}),e),folder:(t,e,o)=>{const r=GM_config.#n(t).pop();return this.down(r),this.#s(!1,{prop:t,before:e,after:e,remote:!1}),e}};#a={};#l=[];#c=null;#u={};get currentPath(){return[...this.#l]}get#p(){if(this.#c)return this.#c;let t=this.#r;for(const e of this.#l)t=t[e].items;return this.#c=t,t}constructor(t,e={}){function o(t,e,o,r){const i=this.#d(t).value;void 0===e&&(e=i),void 0===o&&(o=i),t in this.#u&&(this.#u[t]=o),this.#s(!0,{prop:t,before:e,after:o,remote:r})}function r(t,...e){const o=Object.assign(t.folderDisplay??{},...e.map((t=>t.folderDisplay??{})));return Object.assign(t,...e,{folderDisplay:o})}super(),this.#r=t,function t(e,i=[],s={}){const n=r({},s,e.$default??{});delete e.$default;for(const s in e){const a=[...i,s];e[s]=r({},n,GM_config.#o[e[s].type]??{},e[s]),"folder"===e[s].type?t.call(this,e[s].items,a,n):GM_addValueChangeListener(GM_config.#f(a),o.bind(this))}}.call(this,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 i={},s=t=>({has:(e,o)=>{const r=GM_config.#h(`${t}.${o}`);return void 0!==this.#d(r)},get:(e,o)=>{const r=GM_config.#h(`${t}.${o}`),n=this.#d(r);if(void 0!==n)return"folder"===n.type?(i[r]||(i[r]=new Proxy({},s(r))),i[r]):this.get(r)},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({},s("")),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#n(t){return t.split(".").filter((t=>t))}static#f(t){return t.join(".")}static#h(t){return GM_config.#f(GM_config.#n(t))}get(t){const e=GM_config.#h(t),o=this.#d(t).value,r=this.#y(e,o);return this.#s(!1,{prop:e,before:r,after:r,remote:!1}),r}set(t,e){const o=GM_config.#h(t),r=this.#d(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.#d(e)?.items??{}):Object.keys(this.#r)}#d(t){"string"==typeof t&&(t=GM_config.#n(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}#y(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)}#s(t,e){const o=new CustomEvent(t?"set":"get",{detail:e});return this.dispatchEvent(o)}up(){const t=this.#l.pop();return this.#m(`⬆️ Went up to ${GM_config.#f(this.#l)||"#root"}`),this.#g(),t??null}down(t){const e=this.#p;return t in e&&"folder"===e[t].type?(this.#l.push(t),this.#m(`⬇️ Went down to ${GM_config.#f(this.#l)}`),this.#g(),!0):(this.#m(`❌ Cannot go down to ${t} - not a folder`),!1)}#g(){this.#c=null;const t=this.#d(this.#l);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}`)}if(this.#l.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.#f([...this.#l,t]);this.#a[e]=this.#$(e)}}#$(t){const e=this.#d(t),{name:o,value:r,input:i,processor:s,formatter:n,accessKey:a,autoClose:l,title:c}=e,u=this.#y(t,r),p="function"==typeof i?i:this.#i[i],d="function"==typeof n?n:GM_config.#e[n],f={accessKey:GM_config.#v(a,t,o,u),autoClose:GM_config.#v(l,t,o,u),title:GM_config.#v(c,t,o,u),id:this.#a[t]},h=GM_registerMenuCommand(d(t,u,e),(()=>{let o;try{if(o=p(t,u,e),"function"==typeof s)o=s(t,o,e);else{if("string"!=typeof s)throw"Unknown processor format: "+typeof s;{const r=GM_config.#t[s];if(void 0===r)throw`Unknown processor: ${s}`;o=r(t,o,e)}}}catch(t){return void alert("⚠️ "+t)}o!==u&&this.set(t,o)}),f);return this.#m(`+ Registered menu command: prop="${t}", id=${h}, option=`,f),h}}

QingJ © 2025

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