拡張機能
必須拡張機能のうち、自分で書けそうなやつで userChrome.js にできそうなやつは自分で書いたほうがいいですね。バージョンアップで動かなくなったら自分で直せるし (拡張機能になってると直すのめんどい……)、ノウハウたまるし……
だんだんインストールしてる拡張が減っていきます。
必須拡張機能のうち、自分で書けそうなやつで userChrome.js にできそうなやつは自分で書いたほうがいいですね。バージョンアップで動かなくなったら自分で直せるし (拡張機能になってると直すのめんどい……)、ノウハウたまるし……
だんだんインストールしてる拡張が減っていきます。
last_char = s.charAt(s.length - 1)何が解りにくいって、どの単語が変数でどの単語がプロパティでどの単語がメソッドで…という区別が付かない。
区別がつかなくても読めはするはず? (むしろ、どれがプロパティで……どれがメソッドで……っていうのは定義とか考えるとめんどくさいよなぁ……)
このコードでまず重要なのは、s が何であるか、ということだけだから、そこから読めば疲れないんじゃないかなぁ。
last_char はこの行で代入されているので、この先を読むにあたっては、この行さえ理解できれば、この前でどんな使われかたをされていようが関係ないし、先を読むのに必要なだけなのでとりあえず無視できる。charAt, length は、s のプロパティアクセスなのだから (ドット演算子があるから)、s が何かわかればそれのリファレンスを読めばなにかわかる。このコードだけから推測すれば、charAt がある標準オブジェクトは String しかないので s は String だと思われる (s は String の s だとおもうし、last_char に代入してることからも、s は String だなぁというのが想像できる)
自分がこの行を読むときのプロセスは (かなり冗長にかくと)
脳内スタック多い人はもっと別の読みかたできそうだよあぁ……
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>
);
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 はデフォルトだと pi ノードを無視する。無視してほしくないときは
XML.ignoreProcessingInstructions = false;
する必要がある。でもって、これ true にしようが false にしようが
xml = <?xml-stylesheet href=""?>
<window/>;みたいなのは syntax エラーなので、こうする必要がある。
xml = <> <?xml-stylesheet href=""?> <window/>; </>;
なんかすげーめんどくさいなぁ。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());
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 でちゃんとできる)
関数式のほうは勘違いでした。関数宣言のほうだけだめです……
./div/div のつもりで div/div って書くと INVALID っていわれる。ひどい……
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 をつかっているからたぶん別の環境でも大丈夫だよ
やっつけ
// ==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 + "/";
};
}))+"()";