Tampermonkey Config

Simple Tampermonkey script config library

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

脚本作者
PRO-2684
版本
0.7.0
创建日期
2023-07-05
最近更新
2024-01-26
许可证
GPL-3.0

GM_config

🪄 功能

简易的 Tampermonkey 脚本配置库。 (Greasy Fork) (GitHub)

🎉 特性

  • 配置修改后自动更新菜单(无论由用户或脚本修改)
  • 支持监听配置获取/修改事件
  • 自动/手动注册菜单
  • 自定义程度高
    • 自定义配置输入方式 (prop.input)
    • 自定义输入数据处理方式 (prop.processor)
    • 自定义菜单项展现方式 (prop.formatter)
  • 自动删除与默认值相同的用户配置,降低存储开销

🤔 权限

这个库需要以下权限:

// @grant        GM_setValue // 保存配置
// @grant        GM_getValue // 获取配置
// @grant        GM_deleteValue // 自动删除配置 (可选,给予后库会自动删除与默认值相同的用户配置)
// @grant        GM_registerMenuCommand // 注册菜单
// @grant        GM_unregisterMenuCommand // 更新菜单

若你复制粘贴了上述代码,记得删去注释,否则可能报错。若有,你需要删去 @grant none。如果你代码内使用了 window 对象,你可能需要 @grant unsafeWindow 然后 let window = unsafeWindow

📖 使用

配置描述

使用这个库的第一步是创建一个配置描述。配置描述是一个字典,它的每个属性 (除了可能的 $default 外) 都是一个配置项的 id。

$default

通过使用 $default,你可以方便地创建大量相同类型的配置项。若未在配置描述中提供 $default,则会对配置项中未指定的属性使用如下值:

{
    input: "prompt",
    processor: "same",
    formatter: "normal"
}

若你想要修改默认值,你可以在配置描述中提供 $default 从而覆盖上述默认值。例如:

const config_desc = {
    "$default": {
        value: true,
        input: "current",
        processor: "not",
        formatter: "boolean"
    },
    switch_true: {
        name: "Switch true"
    },
    switch_false: {
        name: "Switch false",
        value: false
    }
}

prop.name

配置项的显示名称。必须提供一个字符串。

prop.value

配置项的默认值,可以是任意值。你需要考虑其合法性,因为此库不会验证默认值的合法性。

prop.input

(prop, orig) => input

配置项的输入方式。可以提供一个字符串(内置输入方式),也可以是一个自定义函数(当菜单项被点击时触发)。它接受配置项的名称和当前值,返回用户输入值。若 prop.input$default.input 均未指定,将使用 prompt,即弹出对话框询问输入。注意,“用户输入值”也可以实际上并非由用户输入,而是由脚本提供的。例如内置输入方式 current

内置输入方式:

  • prompt:弹出对话框询问输入(默认)
  • current:使用当前值作为输入(常与 prop.processor=not 配合使用,用于开关;或与自定义的 processor 配合使用,构成生成器)

prop.processor

(input) => stored

配置项的输入值处理器。可以提供一个字符串(内置处理器),也可以是一个自定义函数。它接受用户输入的值,返回处理后的值。若用户输入的值不合法,处理器应该抛出错误。若 prop.processor$default.processor 均未指定,将默认使用 same 处理器,即直接返回用户输入。常见的使用情况是将用户输入的字符串转换为整数或者浮点数。

内置处理器:

  • same:直接返回用户输入的字符串
  • not:取反(常与 prop.input=current 配合使用,用于开关)
  • int:转换为整数
  • int_range-<min>-<max>:转换为整数,且限制在 [<min>, <max>] 范围内
    • 不建议省略 -,否则可能出错
    • <min><max> 可以是任意整数,若不提供则视为此端无限制
  • float:转换为浮点数
  • float_range-<min>-<max>:转换为浮点数,且限制在 [<min>, <max>] 范围内
    • 不建议省略 -,否则可能出错
    • <min><max> 可以是任意浮点数,若不提供则视为此端无限制

prop.formatter

(name, value) => string

配置项在菜单的展现方式。展现方式可以是一个字符串(内置展现方式),也可以是一个自定义函数。它接受配置项的名称和当前值,返回菜单项的显示文本。若 prop.formatter$default.formatter 均未指定,则使用 normal 展现方式。

内置展现方式:

  • normalname: value 的形式
  • boolean:针对布尔值的展现方式。true 显示为 name: ✔false 显示为 name: ✘

常用组合

const config_desc = {
    // 开关
    enabled: {
        name: "Enabled",
        value: true,
        input: "current",
        processor: "not",
        formatter: "boolean"
    },
    // 整数
    value: {
        name: "Value",
        value: -10,
        processor: "int"
        input: "prompt",
        formatter: "normal"
    },
    // 自然数
    price: {
        name: "Price",
        value: 114,
        processor: "int_range-1-",
    },
    // 浮点数以及正数基本一致,分别为 `float` 和 `float_range-0-`
    // 字符串
    name: {
        name: "Name",
        value: "Crazy Thur."
        // 省略的默认值:input="prompt", processor="same", formatter="normal"
    },
}

其它 Tampermonkey 提供的属性

支持 prop.accessKey, prop.autoClose, prop.title。后两者仅在支持的 TM 版本中被传给 TM (>=4.20.0)。详细信息请参考 Tampermonkey 文档

注册配置菜单

当你创建了一个配置描述后,你需要将它注册为一个配置菜单。你可以使用 GM_config 函数来注册配置菜单。它接受两个参数:

  • config_desc:配置描述
  • menu:是否自动显示菜单
    • 若为 true,则会自动显示菜单(默认)
    • 若为 false,需要用户点击 Show configuration 后才会显示配置菜单
const config = GM_config(config_desc, false); // *注册配置菜单*
console.log(config.price); // *可以开始使用了 🎉*

查询/修改配置

当你注册了一个配置菜单后,你就可以使用 GM_config 返回的对象来查询/修改配置了。例如:

console.log(config.price); // *查询配置*
config.price = 100; // *修改配置* (菜单项会自动更新)

监听配置的查询/修改

这个库提供了一个 GM_config_event 字符串,它的值表示配置项被查询/修改时会触发的事件。你可以使用 window.addEventListener 来监听这个事件。它的 detail 属性是一个对象,包含了配置变更的详情。例如:

window.addEventListener(GM_config_event, (e) => { // *监听配置查询/修改*
    console.log(config, e.detail);
});

e.detail 对象的属性如下:

  • prop:被查询/修改的配置项的 id
  • type:变更类型,可能的值有:
    • get:查询配置
    • set:修改配置
  • before:变更前的值
  • after:变更后的值

这个功能常用于在配置变化时实时更新脚本的功能。在库内部,自动更新菜单项的功能就是通过监听这个事件来实现的。

若页面内的 iframe 需要监听此事件,则 iframe 内需要使用 window.top 代替 window

总结:修改配置项过程

  1. 用户点击菜单项
  2. prop.name 和当前值作为参数传入 prop.input,获取用户输入值
  3. 将用户输入值作为参数传入 prop.processor,获取处理后的值
  4. 保存处理后的值
  5. 发出对应 detailGM_config_event 事件
  6. 更新菜单项(被 GM_config_event 事件触发)

👀 完整的例子

将以下测试代码安装为脚本,观察它是如何工作的:

// ==UserScript==
// @name         Test Config
// @namespace    http://tampermonkey.net/
// @version      0.3
// @description  This is an example to demostrate the usage of greasyfork.org/scripts/470224.
// @author       PRO
// @match        https://greasyfork.org/*
// @icon         https://greasyfork.org/vite/assets/blacklogo16-bc64b9f7.png
// @grant        GM_setValue
// @grant        GM_getValue
// @grant        GM_registerMenuCommand
// @grant        GM_unregisterMenuCommand
// @require      https://update.greasyfork.org/scripts/470224/1303666/Tampermonkey%20Config.js
// @license      gpl-3.0
// ==/UserScript==

(function() {
    'use strict';
    const config_desc = { // Config description
        password: {
            name: "Password", // Display name
            value: "tmp", // Default value
            input: "prompt", // How to get user input (Invoked when user clicks the menu command)
            // Built-in values:
            // "current": Current value will be passed to `processor` as user input (generator-like)
            // "prompt": Use `prompt` to get user input (default value)
            // <function>: Custom function to get user input, should return certain value to be processed by `processor`
            //     (prop, orig) => input
            processor: (v) => {
                if (v.length < 3) throw "Too short!";
                return v;
            }
        },
        enabled: {
            name: "Enabled",
            value: true,
            input: "current",
            processor: "not", // Process user inputs, throw error if invalid
            // Built-in processors:
            // "same": Return user input directly (default value)
            // "not": Invert boolean value
            // "int": Convert to integer
            // "int_range-min-max": Convert to integer in range [min, max], raise error if invalid ("" for no limit)
            // "float": Convert to float
            // "float_range-min-max": Convert to float in range [min, max], raise error if invalid ("" for no limit)
            // <function>: Custom function to process value
            //     (input) => stored
            formatter: "boolean", // Format value to be displayed in menu command
            // Built-in formatters:
            // "normal": `${name}: ${value}`
            // "boolean": `${name}: ${value ? "✔" : "✘"}`
            // <function>: Custom function to format value
            //     (name, value) => string
        },
        val: {
            name: "Float",
            value: 11.4,
            processor: "float_range-0-" // Convert to float in range [0, +∞)
        }
    }
    const config = GM_config(config_desc, false); // Register menu commands
    window.addEventListener(GM_config_event, (e) => { // Listen to config changes
        console.log(e.detail);
    });
    window.setTimeout(() => { // Change config values, and menu commands will be updated automatically
        config.val += 1; // Remember to validate the value before setting it
    }, 5000);
})();

或者,你也可以安装这个脚本来体验这个库的功能:Greasy Fork Enhance

⚠️ 注意

这个项目正处于早期发展阶段,接口可能会发生变化。如果你有建议或者在使用过程中遇到了问题,欢迎提出 issue 或者 PR。