Firefox拡張入門第8回(ローカライズ)

XULローカライズにはdtdファイルを使い、Javascriptローカライズにはpropertiesファイルを使います。

今回は、ローカライズした"hello,world."ボタン、つまり、"こんにちは、世界。"ボタンを作ってみましょう。
マッチFOXは"Strings.jsm"ライブラリをご用意していますので、簡単にpropertiesファイルを扱えます。
以下のファイルたちに手を加えてください。

<button oncommand="Sample.hello();" label="&Sample.sidebar.hello;"/>
const EXPORT = ["hello"];

function hello() {
  var strings = new Strings("chrome://sample/locale/sidebar.properties");
  alert(strings.get("hello"));
}
<!ENTITY Sample.sidebar.hello "Don't push me! Don't you dare push me!!">
<!ENTITY Sample.sidebar.hello "押すなよ!絶対に押すなよ!!">
hello = hello,world.
hello = こんにちは、世界。

完成です。
http://eva-lu-ator.net/~gemma/geocities/matchfox/stringsja.png

英語版Firefoxを起動して確認してみましょう。

firefox -UILocale "en-US" -no-remote

http://eva-lu-ator.net/~gemma/geocities/matchfox/strings.png

すばらしい。

Strings.jsm リファレンスマニュアル

function: Strings aPropertiesFile
新しいインスタンスを作ります。
aPropertiesFile StringBundleに使うpropertiesファイルのURI。文字列。

function: get aName &optional aArgs
ローカライズ文字列を取得します。
aName 項目名。文字列。
aArgs printfのような文字列フォーマットに渡す引数。文字列の配列。

アリの餌集めのシミュレーション

友人と、子どもにコンピュータや数学に興味をもってもらうには、という話をしていて、
アリの餌集めなら、子どもに身近だし、群知能ランダムウォークに繋がるし面白いんじゃないか、となった。
Processingでもいいけど、ブラウザのJavascriptCanvasを使うほうが簡単かな。

目標

  • 子どもに、電子アリのプログラミングを楽しんでもらうこと。
  • 子どもの創意工夫を邪魔しないこと。
  • 結果を現実のアリでも説明できること。

で、こんなモデルはどうだろう。

電子アリのモデル

  • 秒速1ドットで動ける。
  • 周囲1ドットの色(RGBA)を読める。
  • 描画できる。画面全体に渡って。全く好きなように。(現実のアリがフェロモンを出すことのモデル。)
  • 周囲1ドットの砂糖の数がわかる。
  • 砂糖を1つ運べる。
  • GPSを持っていて、常に現在の自分の位置がわかる。
  • すごい記憶力と、すごい計算力がある。(三角関数でも遺伝的アルゴリズムでも使い放題)
  • 他の電子アリのことは全くわからない。

出題方法

  • processという関数の中身をプログラムしてもらう。
  • グローバル変数などで、他の電子アリと直接に値をやりとりするのは禁止。
    • (グローバル変数を使うこと自体はOKだけど。とにかく、情報交換にはフェロモン描画を使ってください。)
  • process関数は1秒間に1回、各電子アリごとに呼ばれる。
function process(aAntData, aProbedImageData, aFoundSugarsCount) {
 //この中身を書いてね
 //描画にはCanvas要素を使ってね。グローバル変数ctxに Canvas.getContext("2d")が入ってるよ。
}

//引数aAntDataは以下のようなオブジェクト
{
    id:    2,    //ID
    x:     127,  //現在の x 座標
    y:     127,  //現在の y 座標
    carry: false //砂糖を運んでいるか
};

//引数aProbedImageDataは以下のような配列
aProbedImageData[-1][-1] == [0,0,0,0]      //左上の色。RGBA。これは黒。
aProbedImageData[ 0][-1] == [255,0,0,0]    //上の色。これは赤。
...
aProbedImageData[ 1][ 1] == [255,0,0,127]  //右下の色。これは半透明の赤。

//引数aFoundSugarsCountは周囲の砂糖の数。

//process関数は以下の値のいずれかを返すこと。
[-1,-1] //左上に動く
[ 0,-1] //上
[ 1,-1] //右上
[-1, 0] //左
[ 0, 0] //動かない
[ 1, 0] //右
[-1, 1] //左下
[ 0, 1] //下
[ 1, 1] //右下
"carry" //砂糖を拾う

実際に作ってみた

とりあえずランダムウォークする。
砂糖を見つけたら、まるで「あったぞーっ」て大声で叫ぶように、赤いフェロモンを出す。
それが赤のグラデーションになっているので、みんなで濃い赤に向かって進む。
ある程度濃い赤まで来たらランダムウォークで周りを歩いて他の砂糖を探す。

感想

  • ちょっと子どもにはプログラムが難しすぎるし、アリの行列をなかなか拝めない。
  • 好きに描画していいぞってのは面白いんだけどなー。
  • 現実のアリには、こういう大声で叫んで仲間を集めるのはいないのかな?
    • あ、天敵に見つかっちゃうか。

Firefox拡張入門第7回(独自プロトコルの定義 - ttp)

2ちゃんねるなどでリンク避けとして"ttp://..."を使うことがあります。
今回の拡張では、ttpプロトコルを定義して、"http://..."と同様に扱えるようにしましょう。

  1. ttpプロトコルを扱うXPCOMコンポーネントのクラスを登録します。
  2. そのクラスのnewURI関数で、URIの部品を受け取って、nsIURIのインスタンスを返します。(注)
  3. そのクラスのnewChannel関数で、↑で作ったnsIURIのインスタンスを受け取って、ttpプロトコルのためのnsIChannelのインスタンスを返します。ここではhttpプロトコルのnewChannel関数に処理を丸投げするので簡単です。

(注)"ここではhttpプロトコルのnewURI関数に処理を丸投げするので簡単です"・・・だと芸がないですもんね。

拡張のcomponentsディレクトリ以下に、jsファイルを置けば自動で認識してくれます。

//XPCOMコンポーネントを作るための便利ライブラリを使います。
Components.utils.import("resource://gre/modules/XPCOMUtils.jsm");

//プロトコルを扱うクラス
function TTProtocol() {}

TTProtocol.prototype = {
  //クラスの説明
  classDescription: "TTP Protocol",
  //XPCOMコンポーネントのID。 末尾の"...=ttp"に注目。
  contractID      : "@mozilla.org/network/protocol;1?name=ttp",
  //XPCOMコンポーネントのUUID。UUIDは各自で生成します。
  classID         : Components.ID("0bc360c0-a913-11de-8a39-0800200c9a66"),
  QueryInterface  : XPCOMUtils.generateQI([Components.interfaces.nsIProtocolHandler]),
  //プロコトルのスキーム。
  scheme: "ttp",
  //ポート番号は任意。HTTPと同様。
  defaultPort: -1,
  //プロトコルの設定。HTTPと同様。
  protocolFlags: Components.interfaces.nsIProtocolHandler.URI_STD |
                 Components.interfaces.nsIProtocolHandler.URI_LOADABLE_BY_ANYONE,
  //ポート番号は任意。
  allowPort: function TTProtocolAllowPort(aPort, aScheme) {
    return false;
  },
  //URIを処理する関数。
  //部品を受け取ってnsIURIのインスタンスを返します。
  newURI: function TTProtocolNewURI(aSpec, aCharset, aBaseURI) {
    var uri = Components.classes["@mozilla.org/network/standard-url;1"]
                .createInstance(Components.interfaces.nsIStandardURL);
    uri.init(Components.interfaces.nsIStandardURL.URLTYPE_STANDARD, -1,
             aSpec, aCharset, aBaseURI);
    uri.QueryInterface(Components.interfaces.nsIURI);
    return uri;
  },
  //通信チャンネルを作る関数。
  //↑で作ったnsIURIのインスタンスを受け取ってnsIChannelのインスタンスを返します。
  newChannel: function TTProtocolNewChannel(aURI) {
    var IOService = Components.classes["@mozilla.org/network/io-service;1"].
                      getService(Components.interfaces.nsIIOService);
    return IOService.newChannel("h" + aURI.spec, null, null);
  }
};

//コンポーネントの生成。
function NSGetModule(compMgr, fileSpec){
	return XPCOMUtils.generateModule([TTProtocol]);
}


特にclassIDには、各コンポーネント固有のUUIDが必要なのでUUID (GUID) Generator on the WEBで生成してください。

newURI関数

この関数は、絶対アドレスと相対アドレスの両方を扱う役目があります。
絶対アドレスのときは、aSpecに文字列"http://.../main.html"、aBaseURIにnullが入っています。
相対アドレスのときは、aSpecに文字列"../images/01.jpg", aBaseURIにnsIURIのインスタンスで、specが"http://.../main.html"なものが入っています。
newURI関数は、これらの部品をもとに、nsIURIのインスタンスを作って返します。

newURI関数のnsIStandardURL

この部品をイチから料理するのは面倒です。
特に"http://..."の処理は複雑で、":80"があったり、"user@domain"があったり・・・。
似たり寄ったりの処理を"ftp://..."や"file://..."でも使うので、このnsIStandardURLがあります。
今回の"ttp://..."はhttpと同じなので、コイツに任せればおしまいです。
さらに柔軟にURIを作りたいときは、nsISimpleURIを使うこともあるようです。

独自プロトコルの使いどころ

ブラウザの窓の中に、"CSSJavascriptを駆使したブラウザ的なページ"を作りはじめたら、
このテクニックが必要なサインです。ブラウザの中にブラウザを作っていませんか?
例えばGMailの画面は、gmailto:があるとスッキリする気がします。
Webブラウザの未来Web-based protocol handlersに期待しましょう。

Firefox拡張入門第6回(Database.jsmライブラリ)

マッチFOXに同梱しているDatabase.jsmライブラリは、SQLite用のORマッパです。tomblooが開発しました。

使用例

// Bookmarkモデルを生成。
var Bookmark = Entity({
    name : 'bookmarks',
    fields : {
        id           : 'INTEGER PRIMARY KEY',
        url          : 'TEXT UNIQUE NOT NULL',
        title        : 'TEXT',
        date         : 'TIMESTAMP NOT NULL',
        last_visited : 'TIMESTAMP',
        comment      : 'TEXT',
    }
})
// データベースのファイル("ProfD/hogehoge/hogehoge.sqlite")を取得。
function dbFile() {
  var pd = DirectoryService.get("ProfD", Ci.nsIFile);
  pd.append("hogehoge");
  if (!pd.exists() || !pd.isDirectory()) {
    pd.create(Ci.nsIFile.DIRECTORY_TYPE, 0755);
  }
  pd.append("hogehoge.sqlite");
  return pd;
}
// Databaseインスタンスを生成
var db = new Database(dbFile());
// BookmarkモデルにDatabaseインスタンスをセット
Bookmark.db = db;
// Bookmarkモデルを初期化
Bookmark.initialize();
...
//Bookmark.insert     作成、
//Bookmark.find       検索、
//Bookmark.update     更新、
//Bookmark.deleteById 削除、などなど
...
// データベースを閉じる
db.close();

TIMESTAMP型、LIST型

TIMESTAMP型を指定すると、JavascriptのDateオブジェクトを透過的に読み書きできます。
(SQLiteには日付型がないので実際にはINTEGER型で"Date.getTime()の値"をデータベースに保存しています)
同様に、LIST型を指定すると、JavascriptのArrayオブジェクトを透過的に読み書きできます。
(実際にはTEXT型で"Arrayから作ったCSV的な文字列"を保存しています。)
便利。

findByFoo, findByFooAndBar, countByFoo、countByFooAndBar,

モデルのFooやBarの値で、検索やカウントができます。これは__noSuchMethod__をフックして実現してます。
すごい。

リファレンスマニュアル

続きを読む

Firefox拡張入門第5回(Prefs.jsmライブラリ)

マッチFOXが生成するPrefs.jsmは、Firefox拡張でユーザ設定(about:configのアレ)を簡単に読み書きできるライブラリです。

ユーザ設定とは

ユーザ設定の項目の値には型があるので気をつけます。
  • boolean (真偽値)
  • integer (整数)
  • string (文字列)
  • など。
項目名が他人様のとカブらないように気をつけます。

"ブランチ"にまとめるように心がけましょう。
例えば拡張で使う項目名には"extensions."ブランチを使いましょう。

例: "javascript.options.strict"オプションを扱う

1. ライブラリを new する。

//ブランチを指定します。ピリオドで終わるのがポイント。
var prefs = new Prefs("javascript.options.");

2. trueにする

prefs.set("strict", true);

3. 取得する

var v = prefs.get("strict");

項目の型はライブラリが自動で判断してくれます(手動で指定もできます)。

リファレンスマニュアル

function: Prefs aBranchName
新しいインスタンスを作ります。
aBranchName ブランチ名。文字列。ブランチ名はピリオドで終わる必要があります。

function: get aPrefName &optional aDefaultValue aType
項目の値を取得します。
aPrefName 項目名。文字列。
aDefaultValue 取得に失敗したとき返す値。
aType 項目の型。文字列。下表1参照。

function: set aPrefName aValue &optional aType aRelFileRelativeToKey
項目の値をセットします。
aPrefName 項目名。文字列。
aValue セットする値。
aType 項目の型。文字列。下表2参照。
aRelFileRelativeToKey 相対ファイルパスの元ディレクトリ。"ProfD"など。文字列。

function: clear aPrefName
項目を削除します。
aPrefName 項目名。文字列。

function: getChildList &optional aStartingAt
ブランチ以下の項目名たちを配列で返します。
aStartingAt ブランチ名。文字列。

表1(getのaType)

aType 動作
真偽値 "boolean" getBoolPref(aPrefName)
整数 "integer" getIntPref(aPrefName)
文字列 "string" getComplexValue(aPrefName, Ci.nsISupportsString).data
地域化文字列 "localized" getComplexValue(aPrefName, Ci.nsIPrefLocalizedString).data
絶対ファイルパス "file" getComplexValue(aPrefName, Ci.nsILocalFile)
相対ファイルパス "relFile" getComplexValue(aPrefName, Ci.nsIRelativeFilePref)

表2(setのaType)

aType 動作
真偽値 "boolean" setBoolPref(aPrefName, !!aValue)
整数 "integer" setIntPref(aPrefName, +aValue)
文字列 "string" setComplexValue(aPrefName, Ci.nsISupportsString, nsISupportsStringのインスタンス)
地域化文字列 "localized" setComplexValue(aPrefName, Ci.nsIPrefLocalizedString, nsIPrefLocalizedStringのインスタンス
絶対ファイルパス "file" setComplexValue(aPrefName, Ci.nsILocalFile, aValue)
相対ファイルパス "relFile" setComplexValue(aPrefName, Ci.nsIRelativeFilePref, nsIRelativeFilePrefのインスタンス)

相対ファイルパスについてはFile I/O - MDC参照。

XMPPの概略(和訳)

XMPPはインスタントメッセージのプロトコルで、Google Talkが使っています。
今後はGoogle Waveの基盤としてますます重要性が増す要注目のテクノロジです。

そして!ついに!Google App EngineXMPPをサポートしました!

そこかしこでGAEのXMPPサポートに賞賛の声が。

XMPP標準化団体のXMPP Technologies: Overviewの和訳です。どぞー。

XMPPとは、Extensible(拡張性のある) Messaging(メッセージと) and Presence(プレゼンス) Protocol(プロトコル)です。このようなオープン技術の集まりでできています。

  • インスタントメッセージ
  • プレゼンス
  • 多人数チャット
  • 音声&ビデオ電話
  • 協調作業
  • コンテンツ・シンジケーション
  • XMLデータの一般化ルーティング

XMPPはもともとJabberというオープンソース・コミュニティで開発されたもので、その目的は、オープンで、セキュアで、スパムが無いこと、そして、当時のクローズドなインスタントメッセージサービスの中央集権を崩すことにありました。XMPPは、そういったサービスへの突破口を用意しています。

オープン
XMPPプロトコルは、自由、オープン、公開、簡潔を旨とします。さらに、様々な形で複数の実装があります(クライアント、サーバ、サーバコンポーネント、コードライブラリ)。
スタンダード
インターネット技術タスクフォース(IETF)が、インスタントメッセージとプレゼンスの技術として合意を得て策定した、XMLストリームプロトコルです。XMPPの仕様はRFC3920RFC3921として2004年に公開されました。また、XMPP標準化団体が続けてたくさんのXEPシリーズを公開しています。
実績
Jabber/XMPP技術ははじめ Jeremie Miller が1998年に開発したもので、今では大変安定しています。数百の開発者がこの技術で仕事をし、数千のJabberサーバがインターネット上で動き、数万の人々がXMPPをインスタントメッセージに使っています。Google Talkをはじめ、世界中の組織がXMPPサービスを展開しています。
分散
XMPPネットワークの設計は電子メールと似ています。つまり、誰でも自分のXMPPサーバを立ち上げることができ、個人や組織がコミュニケーションの活用を自己管理できるようになっています。
セキュア
XMPPサーバは外のネットワークから切り離せます(例えば企業のイントラネットなどで)。また、頑丈なセキュリティ(SASLとTLSによる)がXMPPコア仕様に織り込み済みです。そして、XMPPネットワークには事実上スパムはありません。さらに、XMPP開発者は精力的に、エンド・ツー・エンドの暗号化でセキュリティの壁をさらに高くしようと励んでいます。
拡張性
XMLのパワーによって、このプロトコルを土台にしてあなたオリジナルの機能を作ることができます。相互運用性を維持するため、定番の拡張はXEPシリーズとして公開していますが、特に公開が必須というわけでなく、内々でプライベートな拡張を持つことももちろんできます。
柔軟性
XMPPアプリケーションは単なるIMにとどまりません・・・
多様性
広範な企業やオープンソース・プロジェクトが、XMPPを使って、リアルタイム・アプリケーションおよびサービスの構築、運用をしています。XMPP技術は決してあなたを"ロック・イン(囲い込み)"しません。
      • -

以上です。