2007年 12月 25日

拡張機能

必須拡張機能のうち、自分で書けそうなやつで userChrome.js にできそうなやつは自分で書いたほうがいいですね。バージョンアップで動かなくなったら自分で直せるし (拡張機能になってると直すのめんどい……)、ノウハウたまるし……

だんだんインストールしてる拡張が減っていきます。

コードのよみかた

last_char = s.charAt(s.length - 1)

何が解りにくいって、どの単語が変数でどの単語がプロパティでどの単語がメソッドで…という区別が付かない。

http://d.hatena.ne.jp/hama_shun/20071224/1198504421

区別がつかなくても読めはするはず? (むしろ、どれがプロパティで……どれがメソッドで……っていうのは定義とか考えるとめんどくさいよなぁ……)

このコードでまず重要なのは、s が何であるか、ということだけだから、そこから読めば疲れないんじゃないかなぁ。

  • last_char はこの行で代入されているので、この先を読むにあたっては、この行さえ理解できれば、この前でどんな使われかたをされていようが関係ないし、先を読むのに必要なだけなのでとりあえず無視できる。
  • charAt, length は、s のプロパティアクセスなのだから (ドット演算子があるから)、s が何かわかればそれのリファレンスを読めばなにかわかる。

このコードだけから推測すれば、charAt がある標準オブジェクトは String しかないので sString だと思われる (sString の s だとおもうし、last_char に代入してることからも、s は String だなぁというのが想像できる)


自分がこの行を読むときのプロセスは (かなり冗長にかくと)

  1. last_char に代入してるなぁ
  2. last_char って名前から右側でやってそうなことを想像
  3. → 最後の文字が代入されるはず?
  4. last_char という名前はいったん忘れる
  5. s.charAt(s.length - 1) を頭にいれる
    1. ドットでくぎる
    2. s ってなんだろ (前からさがす)
    3. (この場合は前に定義がないので) charAt をよんでるし s だから String か
    4. charAt は位置を引数にとって文字を返すメソッドか (String のリファレンスよむ)
    5. 引数が s.length - 1 か
      1. s.length は s の長さか (String のリファレンスよむ)
      2. 長さ - 1 だから最後の文字の位置か
    6. s.charAt(s.length - 1) は最後の文字を取得か
  6. last_char に代入か (もどってくる)
  7. 最後の文字を取得して last_char に代入か
  8. 「last_char は s の最後の文字」だけ覚えて次の行を読む……

脳内スタック多い人はもっと別の読みかたできそうだよあぁ……

userChrome.js で設定画面をつくる

XUL で UI つくりたいなぁってちょっと思ったんです。なんかいろいろやったんですがこうしたら一応できた。なんかもっと、スマートな方法がありそうだけど……

function openChromeWindow (xml, opts) {
	// create temporary content dir.
	var t = IO.getFile("Temp", "content" + Math.random() * 0xffff);
	t.createUnique(Components.interfaces.nsIFile.DIRECTORY_TYPE, 0700);

	// write manifest
	var f = IO.getFile("PrefD", "extensions");
	f.append("{1280606b-2510-4fe0-97ef-9b5a22eafe64}"); // userchromejs
	f.append("chrome.manifest");
	var prev = read(f);
	write(f, "content userchromejs file://" + t.path + "/\n");

	// write chrome xul
	var c = t.clone();
	c.append("temp.xul");
	c.create(Components.interfaces.nsIFile.NORMAL_FILE_TYPE, 0700);
	write(c, xml.toXMLString());

	// refresh chrome registry
	Components.classes["@mozilla.org/chrome/chrome-registry;1"]
	          .getService(Components.interfaces.nsIChromeRegistry)
	          .checkForNewChrome();

	// open
	window[opts.fun || "openDialog"]("chrome://userchromejs/content/temp.xul", opts.name || "temp", opts.opts || "chrome");

	// remove temp files and restore original
	t.remove(true);
	write(f, prev);

	// refresh chrome registry
	Components.classes["@mozilla.org/chrome/chrome-registry;1"]
	          .getService(Components.interfaces.nsIChromeRegistry)
	          .checkForNewChrome();


	function read (f) {
		var res = "", str, strm = IO.newInputStream(f, "text");
		while (str = strm.readString(4096)) res += str;
		strm.close();
		return res;
	}

	function write (f, str) {
		var strm  = IO.newOutputStream(f, "text");
		strm.writeString(str);
		strm.close();
	}
}

こんなふうにつかう

default xml namespace = "http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul";
openChromeWindow(
	<window>
		<script type="application/javascript">
			const prefs = Components.classes["@mozilla.org/preferences;1"]
			                        .getService(Components.interfaces.nsIPrefBranch);
			alert(prefs.getCharPref("browser.startup.homepage"));
		</script>
	</window>
);
  • 一つのウィンドウとして開きたい
  • chrome 特権を行使したい
    • chrome 特権を行使するには chrome: じゃないとだめらしい
    • chrome: は manifest かかないとだめらしい

nsIScriptableIO

nsIScriptableIO は XPCOM 直接使うよりは遙かにマシだけれど、なんかいまいちだよなぁ……さらにラッパを書きたくなるよ……


こう書きたい

var content = IO.openFile(f, "read text");
// 上と同じ
var content = IO.openFile(f, "read text", function (stream) {
    var res = [];
    while (str = strm.readString(4096)) res.push(str);
    return res.join("");
}); // 自動で close

IO.openFile(f, "write text", "content");
// or
IO.openFile(f, "write text", function (stream) {
    stream.writeString("content");
}); // 自動で close

なんであんなインターフェイスなんだろ……

E4X で processing-instruction をふくめて String に

E4X はデフォルトだと pi ノードを無視する。無視してほしくないときは

XML.ignoreProcessingInstructions = false;

する必要がある。でもって、これ true にしようが false にしようが

xml = <?xml-stylesheet href=""?>
      <window/>;

みたいなのは syntax エラーなので、こうする必要がある。

xml = <>
	<?xml-stylesheet href=""?>
	<window/>;
</>;
2007年 12月 23日

XUL/XPCOM のファイルのよみかき。

なんかすげーめんどくさいなぁ。Java 並のめんどくささ。しかも Java ほどセオリーっぽい書きかたがない…… (仕様変更がどうとか……)

実はラッパがあったりしないのかなぁ……

かるく書いてみたけど、こういうのが欲しい。ぜったい既にあると思うんだけど検索しにくくてみつからない……

function File () { this.initialize.apply(this, arguments) }
File.prototype = {
	initialize : function (path) {
		if (typeof path == "string") {
			this.file = Components.classes["@mozilla.org/file/local;1"]
			                      .createInstance(Components.interfaces.nsILocalFile);
			this.file.initWithPath(path);
		} else {
			this.file = path;
		}
		this.charset = "UTF-8";
	},

	read : function (cb) {
		if (!cb) cb = function (i) {
			var ret = [];
			var str = {};
			while (i.readString(4096, str) != 0) {
				ret.push(str.value);
			}
			return ret.join("");
		};
		var fistream = Components.classes["@mozilla.org/network/file-input-stream;1"]
		                         .createInstance(Components.interfaces.nsIFileInputStream);
		var cistream = Components.classes["@mozilla.org/intl/converter-input-stream;1"]
		                         .createInstance(Components.interfaces.nsIConverterInputStream);
		fistream.init(this.file, 0x01, 0444, 0);
		cistream.init(fistream, this.charset, 1024, Components.interfaces.nsIConverterInputStream.DEFAULT_REPLACEMENT_CHARACTER);
		var ret = cb.call(this, cistream, fistream);
		cistream.close();
		fistream.close();
		return ret;
	},
	write : function (cb) {
		if (typeof cb == "string") {
			var _str = cb;
			cb = function (i) {
				i.writeString(_str);
				return this;
			};
		}
		var fostream = Components.classes["@mozilla.org/network/file-output-stream;1"]
		                         .createInstance(Components.interfaces.nsIFileOutputStream);
		var costream = Components.classes["@mozilla.org/intl/converter-output-stream;1"]
		                         .createInstance(Components.interfaces.nsIConverterOutputStream);
		fostream.init(this.file, 0x02 | 0x08 | 0x20, 0664, 0); // write, create, truncate
		costream.init(fostream, this.charset, 4096, 0x0000);
		var ret = cb.call(this, costream, fostream);
		costream.close();
		fostream.close();
		return ret;
	}
};
File.TempFile = function (prefix, suffix) {
	var name = [prefix, Math.floor(Math.random() * 0xffffff).toString(16), suffix].join(".");
	var file = Components.classes["@mozilla.org/file/directory_service;1"]
	                     .getService(Components.interfaces.nsIProperties)
	                     .get("TmpD", Components.interfaces.nsIFile);
	file.append(name);
	file.createUnique(Components.interfaces.nsIFile.NORMAL_FILE_TYPE, 0664);

	Components.classes["@mozilla.org/uriloader/external-helper-app-service;1"]
	          .getService(Components.interfaces.nsPIExternalAppLauncher)
	          .deleteTemporaryFileOnExit(file);

	return new File(file);
}

var tf = File.TempFile("foobar", "txt");
alert(tf);
tf.write("foobar");
alert(tf.read());

Opera での with の挙動

var foo = { 
    bar : "bar"
};
(function () {
    with (foo) {
        alert("normal+"+bar);
        function baz () {
            alert("function def."+bar); // opera だと reference error
        }
        baz();

        (function () {
            alert("function exp."+bar); // <del>opera だと reference error</del>
        })();
    }
})();

Fx と Safari では大丈夫。これは Opera のバグっぽい? (Opera 9.24, 9.50 Beta 4591)

同じくスコープチェインをいじる try/catch は大丈夫だ。with だけか

(function () {
    try {
        throw "hello";
    } catch (foo) {
        alert(foo);
        (function () {
            alert(foo);
        })();
    }
})();

(Opera, Safari, Fx でちゃんとできる)

関数式のほうは勘違いでした。関数宣言のほうだけだめです……

Safari の XPath

./div/div のつもりで div/div って書くと INVALID っていわれる。ひどい……

textarea を好きなエディタで編集する userChrome.js

XPCOM あたりを触っていたのはこれを書くため……

コメント欄で教えてもらった IO オブジェクトを使うようにしたので Fx3 でしか動かないです。

It's All Text とか使ってたんですが、Fx3 に対応していないのと、なんかいろいろ挙動がいまいちだったので短く書きなおした感じです。

ちゃんと設定を読むようにしたよ。
http://coderepos.org/share/changeset/3529

最初に Ctrl-E したときにエディタを訊くよ。もしリセットしたかったら about:config をひらいて extensions.userchromejs.EditOnFavorite.editor を検索してリセットすればいいよ。

キーバインドも設定を読むようにしたいけど、ちょっとめんどうなのでまだ放置だよ

キーバインドも設定可能にしたよ。
http://coderepos.org/share/changeset/3531

extensions.userchromejs.EditOnFavorite.key に C-e とか C-; とか入れればそっちを使うはずだよ。

設定画面をつけたよ……

Tools -> userChrome.js -> EditOnFavorite で画面がひらくよ。OS X でしか確認してないけど prefwindow をつかっているからたぶん別の環境でも大丈夫だよ

2007年 12月 20日

Haiku のはてスタのリンク先を Haiku のユーザページにする GM

やっつけ

// ==UserScript==
// @name        Jump to haiku user page on clicking star
// @namespace   http://lowreal.net/
// @include     http://h.hatena.ne.jp/*
// ==/UserScript==

location.href = "javascript:"+encodeURIComponent(uneval(function () {
	Hatena.Star.User.prototype.userPage = function () {
		return "/" + this.name + "/";
	};
}))+"()";