JavaScript で HTML::Template

Perl module の HTML::Template みたいなことをする JavaScript library を書いてみた。とはいっても E4X を使ってるので Firefox 限定なんだけど。普通に E4X 便利なので他の browser も実装するようになるとよいね。

使い方は http://search.cpan.org/~samtregar/HTML-Template-2.9/Template.pm を読んでもらった方が早いと思うんだけど ( ちなみにこのひとの author icon がステキすぎる ) 、大体以下のような感じ。あー、 TMPL_INCLUDE は存在を忘れてた。

// Template definition. It's easy to use CDATA section like below.
var template = <><![CDATA[
<table>
    <caption><TMPL_VAR NAME="caption" /></caption>
    <thead>
        <tr>
            <td>No.</td>
            <td>Name</td>
            <td>Description</td>
        </tr>
    </thead>
    <tbody>
        <TMPL_LOOP NAME="tableList">
        <tr>
            <td><TMPL_VAR NAME="name" /></td>
            <td><TMPL_VAR NAME="description" /></td>
        </tr>
        </TMPL_LOOP>
    </tbody>
</table>
]]></>;

// Data definition.
var data = {
    caption: 'sample',
    tableList: [
        {
            name:        'マッチョ',
            description: 'おれよりマッチョなやつに会いに行く。',
        },
        {
            name:        '真ん中のひと',
            description: '痛んぶらーが雑誌のオマケにつくようになったね !!',
        },
        {
            name:        '悪神さま',
            description: 'いまどこに…',
        },
    ],
};

// Generate.
var result = HTMLTemplateFactory(template).output(data);

ここからうだうだ。個人的に HTML::Template の使い勝手の良さと導入のしやすさは異常と考えてて、 JavaScript でも innerHTML 目当ての文字列操作とか DOM でちまちまとかじゃなくてもっとこう直感的になんとかならんかなーっていうのがあったのがひとつ。で、この前 ( http://d.hatena.ne.jp/janus_wel/20081202/1228232450 ) E4X でそれっぽいことができたのでうまくやれば本家 HTML::Template と syntax 同じにできるんじゃね ? と思ってたのがもうひとつ。で、きっかけがあったので試しに書いてみたらうまくいってしまったという経緯で。

おおざっぱな内部の流れは以下のような感じ。

  1. 指定された template から E4X が通る形に文字列操作 ( 置換 ) を行う。
  2. 得られた E4X code の前後に 'return <>' と '</<;' をくっつけてそれを元に Function constructor で関数生成 ( この関数は cache される ) 。
  3. 指定された data を生成した関数に通して XML nodes を出力する。
  4. ( 同じ template に対して違う data を指定した場合、上記で cache されているので 1 回目より高速に処理される。新しく template を指定すると cache は破棄される。 )

文字列操作のところがキモなんだけど細かいところは source を読んでもらうとして、大体のカラクリを理解するには上記の使い方で出した table 要素の template と下に挙げる E4X として解釈できる code を見比べるとわかりやすいかもしれない。 TMPL_LOOP に相当する行が 1 行で完結していることと TMPL_LOOP をくぐると scope chain がひとつふえるところが大切。これに気付いたときは胃腸炎で体力が落ちてることも忘れてみなぎったね。ああみなぎったさ。

<table>
    <caption>{p0.caption}</caption>
    <thead>
        <tr>
            <td>No.</td>
            <td>Name</td>
            <td>Description</td>
        </tr>
    </thead>
    <tbody>
        {tmplLoop(p0.tableList, function (p1) {
        <tr>
            <td>{p1.name}</td>
            <td>{p1.description}</td>
        </tr>
        })}
    </tbody>
</table>