Greasemonkeyスクリプトに設定画面を追加するライブラリ USConfig

重要なお知らせ

USConfig の開発は終了しました。(2011-01-11)



GitHub に置いておいたので興味のある人は使ってみて下さい。特に、同種のライブラリである GM_config を既に使っていて、

  • コントロールのレイアウトが自由にできない
  • コントロールの位置が揃わない
  • 設定画面がたまに出ない

といった点に不満を感じている方にお勧めです。

開発の経緯

上に挙げた GM_config に対する不満点を解消した上で、GM_config を置き換えるべく開発しました。目標は GM_config の対抗馬となること、そしてより使いやすいライブラリを提供することです。

Greasemonkeyスクリプトに設定画面を付加する方法としては他に jQuery を利用する方法などもあるようですが、導入の敷居などを考えると @require するだけで使える GM_config や USConfig の存在意義はそれなりにあると考えています。

互換性

GM_config を置き換えるために開発したものですが、使い方、中身ともにまったく別物となっています。なので、GM_config からの移行にはソース(ダイアログ定義部分)の書き換えが必要になります。

しかし、最終的に設定を GM_setValue() で保存し、またそれを GM_getValue() でロードする部分は変わりませんので、ライブラリの置き換えを適切に行えば、クライアントが保存している設定を継承できます。詳しくは以下に続く「使い方」の「GM_config からの移行」を見て下さい。

なお、現在のところ cross-browser対応は(その名前に反し)できていません。Greasemonkeyスクリプトがそのメタデータブロックにおいて @require して使うことを想定しています。GM_config の方がいつの間にか cross-browser対応を果たしていたので追随したいところですが、あまり優先度は高くないです。

使い方

サンプルを付けておいたのでそれを見て下さい、で終わりたいところですが、せっかくですので簡単に使い方を説明します。

USConfig による設定ダイアログの定義は以下のような感じになります↓

■samples/basic.user.js

Config.define('usc_basic', function() { with (this.builder) {

  var options_1 = ["BSD", "Linux", "Mac", "Windows"];
  var options_2 = ["CentOS", "Debian", "Fedora", "Ubuntu"];

  dialog(
    "Title",
    { width: 500, height: 400 },

    section(
      "Section 1",
      "This is a description of section 1. [optional]",

      grid(
        // buttons
        button("Button 1", 'button_1',
          function() { alert("Button 1 was clicked!"); }),

        button("Button 2", 'button_2',
          function() { alert("Button 2 was clicked!"); }),

        button("Button 3", 'button_3',
          function() { alert("Button 3 was clicked!"); })
      ),
      grid(
        // checkboxes
        checkbox("Checkbox 1", 'checkbox_1', true ),
        checkbox("Checkbox 2", 'checkbox_2', false),
        checkbox("Checkbox 3", 'checkbox_3', true), '\n',
        checkbox("Checkbox 4", 'checkbox_4', false),
        checkbox("Checkbox 5", 'checkbox_5', true ),
        checkbox("Checkbox 6", 'checkbox_6', false)
      ),
      grid(
        // radio buttons
        radio("Radio 1", 'radio_1', options_1, "Linux"), '\n',
        radio("Radio 2", 'radio_2', options_1, "Mac")
      ),
      grid(
        // select controls
        select("Select 1", 'select_1', options_2, "Ubuntu"),
        select("Select 2", 'select_2', options_2, "Debian")
      )
    ),
    section(
      "Section 2",
      "This is a description of section 2. [optional]",

      grid(
        // text fields
        text("Text 1", 'text_1', "String"),
        text("Text 2", 'text_2', "String"), '\n',
        integer("Integer 1", 'integer_1', 100),
        integer("Integer 2", 'integer_2', 100), '\n',
        number( "Number 1", 'number_1', 3.14),
        number( "Number 2", 'number_2', 3.14)
      ),
      grid(
        // text area
        textarea("Textarea 1", 'textarea_1', "String", { label: 'top' }),
        textarea("Textarea 2", 'textarea_2', "String", { label: 'top' })
      ),
      grid(
        // static texts
        staticText("This is a static text 1"),
        staticText("This is a static text 2"),
        staticText("This is a static text 3")
      )
    )
  );
}}, {
  aftersave: function() {
    var msg = "Seved settings are:\n\n";
    for (var id in this.settings) {
      msg = msg + id + " = " + this.settings[id] + "\n";
    }
    alert(msg);
  },
});

GM_registerMenuCommand("USConfig Sample", Config.open);

 ↓



@require

メタデータブロックにて @require すれば使えるようになります。この辺の手軽さはウリのひとつです。*1

@require に際しては安定動作を確認したバージョンを自分の管理下にあるサーバーに配置し、それを @require することを推奨。GitHub上にあるものを使う場合はサンプルのように必ずタグで特定のバージョンを指定のこと。間違っても master branch の HEAD を @require したりしないようにして下さい。

ダイアログを定義する

ダイアログの定義は Config.define() によって行います。

Config.define('dialog_name', function() { with (this.builder) {

  dialog(
    "Title",
    { width: 500, height: 400 },

    section(
      "Section 1",
      "This is a description of section 1. [optional]",

      grid(...),
      grid(...)
    ),
    section(...)
  );
}}, {
  aftersave: function() {...},
});
  1. ダイアログの名前
  2. ダイアログの定義関数
  3. コールバック関数などのオプション( → 指定可能なオプション一覧

を指定してダイアログを定義します。

function() { with (this.builder) {...}}

の部分がダイアログ定義関数であり、with の内側は DSLっぽく記述できるようになっています。ここにいくつかの section() を定義し、その内側にいくつかの grid()、さらに内側に button(), checkbox(), radio(), select(), text(), integer(), number(), textarea() などを記述することでダイアログを定義します。((これらはすべて Builderオブジェクトのメソッドですが、with (this.builder) {...} によってレシーバを省略できるようにすることで DSL的な記述ができるようにしています。))

section は設定項目のカテゴリであり、grid はレイアウト上のコントロールのグループです。同種のコントロールを同じ grid() にまとめると、ラベルの右端、コントロールの左端がいい感じに揃うようになっています。(※サンプル画像参照*2

checkbox() をはじめとする設定項目のコントロールを生成するメソッドには

  1. ラベル
  2. 設定項目ID
  3. デフォルト値

を指定します。(※他にもHTML要素に設定する属性やツールチップ文字列などを指定できたりしますが詳細はソースを見て下さい(;^ω^))

メニューへ登録する

GM_registerMenuCommand() を使ってメニューに登録する点は GM_config と同じです。要は Config.open() を呼び出せば定義したダイアログを表示させることができます。*3

GM_registerMenuCommand("USConfig Sample", Config.open);
設定をロードする

Config.load() でローカルに保存されている設定をロードすることができます。*4返値は「設定項目ID → 値」な関連をまとめたハッシュ(オブジェクト)です。

var settings = Config.load();
GM_config から移行する

Config.define() に渡すオプションで saveKey に 'GM_config' を指定すると GM_config で管理していた設定を継承できます。(注:1.2以前の GM_config で単一ダイアログのものの場合。また設定項目の ID は GM_config と同じものを使う必要があります。*5

Config.define('dialog_name', function() { with (this.builder) {
  dialog(...);
}}, {
  saveKey: 'GM_config',
  aftersave: function() {...},
});

ユーザー側の設定が失われたりすることがないよう、移行に際しては十分なテストを行って下さい。

今後の展開

cross-browser対応したいと思っています。現在は Firefox をメインに使っておりその優先順位はさほど高くありませんが、そのうちやる予定です。

cross-browser対応は、「そのうちやるかも」程度の優先順位です。

*1:その一方、Greasemonkey を前提しているため cross-browser対応を考えるときに面倒なことになる。

*2:このレイアウトを GM_config でやるのは至難のワザだろうと思います。

*3:定義したダイアログがひとつの場合。複数のダイアログを定義した場合は Config.open() に表示するダイアログの名前(Config.define() において設定したもの)を指定する必要がある。

*4:定義したダイアログがひとつの場合。複数のダイアログを定義した場合は Config.load() に取得する設定の名前(Config.define() において設定したもの)を指定する必要がある。

*5:それ以外の場合は何という名前で GM_setValue() されているか各自で調べて下さい。