たまに18歳未満の人や心臓の弱い人にはお勧めできない情報が含まれることもあるかもしれない、甘くなくて酸っぱくてしょっぱいチラシの裏。RSSによる簡単な更新情報を利用したりすると、ハッピーになるかも知れませんしそうでないかも知れません。
の動向はもえじら組ブログで。
宣伝。日経LinuxにてLinuxの基礎?を紹介する漫画「シス管系女子」を連載させていただいています。
以下の特設サイトにて、単行本まんがでわかるLinux シス管系女子の試し読みが可能!
PHPとかerbのようなテンプレートをJavaScriptで。という話に書いたやつの続き。
大切な事なので3回言います。
<% for (var i = 0; i < 3; i++) { %>
今日は<%= today %>です。
<% } %>
オーケー?
こういう文字列をテンプレートとして解釈したい場面は多分よくあると思う。この例だと、today
という変数をどっか外部から与えて値を埋め込むことになる。で、この変数をどうやって渡したらええねん、と。
グローバル変数をがんがんに使って構わないのであれば、先にグローバル変数としてtoday
を定義しておけばいい。eval()
で実行されるコードの中からも普通に参照できる。
しかしアドオンのコードのように、個々のグローバル関数やグローバル変数の寿命が長い事が予想されるスクリプトでは、この手は使えない。
this.
を付けるparseTemplate()
の仕様としてはあくまで「書かれたコード片は第2引数で渡されたオブジェクトをthisとして実行されます」という風にしておいて、テンプレート風に書かれている方の文字列内のJavaScriptコード片では必ずthis.today
という風に書くようにする、という方法もある。
しかしいちいちthis.
を付けるのは面倒だし、そもそも僕がこのような記法を知ったきっかけであるERBでは、self.
なんて書く必要がなかった。同じような物は同じように使えた方がいい。なのでこの方法も使いたくない。
var
で宣言し直す先のエントリに当初書いていたコードでは、parseTemplate()
の第2引数で渡したオブジェクト(ハッシュ)のプロパティを走査してそれをvar
で変数として宣言し直す、ということをしてた。外の名前空間をなるべく汚さないための苦肉の策だ。
if (aContext && typeof aContext == 'object') {
for (var prop in aContext) {
if (!aContext.hasOwnProperty(prop)) continue;
__parseTemplate__codes.unshift('var '+prop+' = aContext.'+prop+';');
}
}
こんな感じ。
でも、これだとエラーになりそうな場合がけっこう考えられる。例えばハッシュのキーが01234
という文字列だと、実行されるJavaScriptのコードはvar 01234 = aContext.01234;
となってしまい、eval()
にかけた時点でSyntaxError: missing variable name
と怒られてしまう。
なので、こういうエラーの元になる名前のプロパティを除外して、安全な物だけをvar
で宣言する、ということをやりたかった。言い換えると、そのプロパティの名前を構成している文字列がJavaScriptの識別子として妥当かどうかを判別したかった。
ということでやっと、このエントリの本題に入る。
UxU 0.5.11に入れ損ねた。次版で標準のヘルパーメソッドに入れるけど、割といいかげんな実装で、たいした規模じゃないから、テストケースの中に直接書いて使ってもいいと思う。
function parseTemplate(aCode, aContext) {
var __parseTemplate__codes = [];
aCode.split('%>').forEach(function(aPart) {
var strPart, codePart;
[strPart, codePart] = aPart.split('<%');
__parseTemplate__codes.push('__parseTemplate__results.push('+
strPart.toSource()+
');');
if (!codePart) return;
if (codePart.charAt(0) == '=') {
__parseTemplate__codes.push('__parseTemplate__results.push(('+
codePart.substring(1)+
') || "");');
}
else {
__parseTemplate__codes.push(codePart);
}
});
var __parseTemplate__results = [];
with(aContext|| {}) {
eval('(function() { '+__parseTemplate__codes.join('\n')+' }).call(aContext|| {})');
}
return __parseTemplate__results.join('');
}
var source = <![CDATA[
大切な事なので3回言います。
<% for (var i = 0; i < 3; i++) { %>
今日は<%= today %>です。
<% } %>
オーケー?
]]>.toString();
var params = {
today : (new Date()).toString()
};
var result = parseTemplate(source, params);
レガシーだけどクロスブラウザな書き方だったら、こうか。
function parseTemplate(aCode, aContext) {
var __parseTemplate__codes = [];
aCode = aCode.split('%>');
var strPart, codePart;
for (var i in aCode) {
aCode[i] = aCode[i].split('<%');
strPart = aCode[i][0];
codePart = aCode[i].length == 1 ? null : aCode[i][1] ;
__parseTemplate__codes.push('__parseTemplate__results.push(unescape("'+
escape(strPart)+
'"));');
if (!codePart) continue;
if (codePart.charAt(0) == '=') {
__parseTemplate__codes.push('__parseTemplate__results.push(('+
codePart.substring(1)+
') || "");');
}
else {
__parseTemplate__codes.push(codePart);
}
}
var __parseTemplate__results = [];
with(aContext|| {}) {
eval('(function() { '+__parseTemplate__codes.join('\n')+' }).call(aContext|| {})');
}
return __parseTemplate__results.join('');
}
var source = '大切な事なので3回言います。\n'+
'<% for (var i = 0; i < 3; i++) { %>\n'+
' 今日は<%= today %>です。\n'+
'<% } %>\n'+
'オーケー?';
var params = {
today : (new Date()).toString()
};
var result = parseTemplate(source, params);
「会社ブログ(みんなで書いてる)でそろそろ実機動画公開していいんじゃないすかねー」「まあもうそろそろ公開しても大丈夫そうな品質になってきたかなー」という感じのことを社内で言ってた(僕は横で見てただけですが)んだけど、本家に先を越されてしまいました。
まあこれ見て分かる通り、春のOSCのバージョンに比べればだいぶ現実的な速度で動くようになってます。池添さん曰く、エミュレータより実機の方が快適じゃん!(まだ遅いけど)とのことでした。今は画面を毎回キッチリ再描画してるからスクロールが遅いけど、モバイル向けに描画キャッシュ的な物を使うようにするとかの手直しをすればもっと速くなる(逆に言うと、今はスクロール中もアニメーションGIFとかが動いてたりして、それでこの速度が出るんだからある意味スゴイとも言える)という風な話も聞きました。
横で見てて思うけど、Windows Mobile(Windows CE)対応は結構基礎の所から見直して頑張ってる感じですね。「アドオンに対応」のバグも、調べてみたらnsIFileとかの物凄く基盤な部分がWindows CE向けにはまだ実装されてなかったせいだった、とかそんな感じで……XULやJavaScriptの話しか分からない僕には、手伝いたくてもまだ手が出せない感じです。
FUEL の Window/Tab 周りのハンドラは Firefox 3 では使うな - 8時40分が超えられない - subtech
書籍の中で詳しく解説しといてナンですが、アレをほんとに使う人がいたのか!ということにむしろ驚いてますよってなもんです。
まあ言う人に言わせれば、NSPRやらNeckoやらのよくわからない物が何層も積み重なった上にあるFirefoxを使うということ自体が「しんじらんねー」な感じなんでしょうけども。
not enough memory - snj: 最近,Firefoxはもっと拡張間で連携プレーした方が良いんじゃないかって思ってきた....
リンク先に書かれてる話からは少しズレる。どちらかというとこの前書いた互換性の話の方だ。
アドオン同士で連携するには、機能が使いやすくまとまってるということが当然必要だけど、それだけでなく、外部から安心して使えるということも非常に重要だと思う。公開されていて、今後のアップデートでも後方互換性が保証されていないと、安心して使えない。「ソースを掘り返してこういう機能を見つけたから使ってみてるけど、これって知らんうちに削除されてしまったりしないか?」という不安があってはいけない。機能が「今のバージョンにあるかないか」ではなく、「今後もあり続けるのかどうか」が重要だと思う。
ツリー型タブは以前からいくつかAPIを公開してはいたけど、基盤になる処理をもっと公開APIとして表に出すことにした。公開した物は原則として後方互換性を維持していく方針でいる。
マルチプルタブハンドラの方も地味にAPIを整備している。
もう僕は疲れたんで、この辺使ってよろしくやってくれってことで……
スペース区切りで入力したらAND検索する、という風なことができないかなーと思ってソースを見てみたら、Firefox 3からは正規表現リテラル(「/foobar/i」という風に書けば正規表現としてそのまま解釈される)やワイルドカード(「*」で0個以上の任意の文字にマッチする)を利用できるようになっていたということに今更気付いた。
今まで目grepしてた苦労はなんだったんだ。
マルチプルタブハンドラについてRockridge氏ほかから「メニューをカスタマイズできるようにしてくれ」という要望が挙がっていたのだけれども、Menu Editorという素晴らしいアドオンがあるのに自前で同じような機能を再実装するのは徒労感しか無いなあと思ったので、開き直って、マルチプルタブハンドラの設定ダイアログを以下のようにしてみた。
Menu EditorのAPIをよく分かってないので(ていうかそもそも公開APIなのかどうかも知らない)、タブ選択時のメニューをカスタマイズできるようにするためにちょっと強引な方法を使ってる。
で、同じようなこと(他のアドオンの有無を調べた上で設定を開く)を何度も書きたくなかったので、設定ダイアログに加えた変更の要点を他のアドオンと連携しやすくするためのライブラリとして分離してみた。
if (window['piro.sakura.ne.jp'].extensions.isInstalled('my.extension.id@example.com') &&
window['piro.sakura.ne.jp'].extensions.isEnabled('my.extension.id@example.com'))
window['piro.sakura.ne.jp'].extensions.goToOptions('my.extension.id@example.com');
アドオンがインストールされているかどうか・有効か無効かを調べるだけならFUELで事足りるので、あまり使い出がないといえば使い出がない。まあThunderbird 2あたりでだったらニーズがあるかもだけど。
ちなみにFUELで書く場合、アドオンがインストールされているかどうかはApplication.extensions.has('my.extension.id@example.com')
、有効か無効かはApplication.extensions.get('my.extension.id@example.com').enabled
で分かる。設定ダイアログのChrome URLを調べるAPIはなくて、それでこのライブラリを作ることにした次第です。
感謝されても喜べないとか書いたけど、マルチプルタブハンドラのAMOでのレビューでAPIのことを誉められたのは、最近あった事の中では特に嬉しいと感じた出来事だ。
マルチプルタブハンドラは「複数のタブをまとめて操作する」機能を提供するアドオンだけれども、公開して多分すぐくらいから「こんな機能も付けてくれ」「あんな機能も付けてくれ」「このアドオンと連携してくれ」とかそんな感じの要望が出たと思う。そうなる事は最初から結構想像できていて、だから、タブの選択状態の制御や選択状態によるタブの取得といった事をできるAPIを開発初期から公開していた。他のアドオン作者の人がこれを見て、マルチプルタブハンドラ用のコードを組み込んでくれたら嬉しいなあ、という風に思っていた。
しかし実際には、MozillaZine Forum(本家の方)で宣伝するわけでもなく、AMOに同じだけの情報を載せられるでもなく、せいぜい分割ブラウザやツリー型タブなどの自作アドオンで細々と使う程度だった。このまま誰にも知られずに終わってしまうんだろうなあ、と思っていた。
なので、Paolo Amadini氏のこのコメントは非常に嬉しかった。
For developers, MTH provides a programming interface that makes it really easy to add new functions that work on multiple tabs; I used this interface in MAF, to provide an easy way to save multiple pages in a single file. (開発者向けに、MTHは複数のタブのために働く新しい機能を非常に簡単に追加できるようにするプログラミングインターフェースを備えている。複数のページを1つのファイルに簡単に保存できる手段を提供するために、私はMAFでこのインターフェースを使った。)
そこに目を付けて貰えた事が嬉しかっただけでなく、MAFという(僕にとっては)ずっと技術的に凄い事をやっているアドオンにマルチプルタブハンドラのためのコードを含めてもらえたということが、嬉しかった。今まで他のアドオンとの連携といえば、ずっとマイナーで世界の隅っこで細々開発してるだけの立場である自分の方から頑張って他のアドオンのコードをなんとか読み解いて連携のためのハックを考える、という事しかやってこなかったので、向こうの方で解決してもらえたということが嬉しかった。
Tab Killer 2.0.2009050701 MinefieldとShiretoko用にアップデートした。
こんな物でも要望がたまに来るからFirefoxのユーザ層って趣味の幅が広いなと思う。
分割ブラウザ更新した。Firefox 2捨てで、Firefox 3以降専用にした。おかげでJavaScriptコードモジュールとかの便利技術をためらいなく使えるようになった。最近になってアドオンを作り始めた人達がほんと羨ましい。あんな糞みたいな苦労をしなくても遙かに簡単に同じ事ができてより素晴らしい物を作れるんだから。羨ましいを通り越して妬ましくすらある。
更新履歴的には「ShiretokoとMinefieldで動くようにしました」で済ませてるけど、Firefox 2→Firefox 3で色々変わってた部分にかなり見落としがあったのを相当数直してる。ペイン内でのズームとかドラッグ&ドロップとか。
機能的な変更は以下の点くらい。
あと、地味にFirefox 3.5以降のプライベートブラウジングに対応してみた。プライバシーついでに、プライバシー情報の消去関係の機能とも統合を図ってる。この辺のノウハウはまたどっかで文書化したい(それか、勉強会のネタにするとか)。