naoyaさん のJemplateの記事に触発されて埋め込みjavascriptを実装してみました。

同様の実装として Ajax Pages があるのですが、よりシンプルに実装してみました。コードにして50行。

サンプルファイルダウンロード

これは今作っているフレームワークの一部です。シンプルですがわりと使えるかと。 実際の動作はサンプルファイルを見ていただくとして

lyase_view.jsの説明どおり

 1/*
 2 *   using innerHTML
 3 *   -------------
 4 *   //in HTML
 5 *   <textarea id="template" style="display:none">
 6 *     The value of x is:< %= context.x%>
 7 *   </textarea>
 8 *   //code
 9 *   document.write(Lyase.View.render({element:"template"}, {x : 10}));
10 *
11 *   using a template file
12 *   -------------
13 *   //in /template.jhtml
14 *   The value of x is:< %= context.x%>
15 *   //code
16 *   document.write(Lyase.View.render({file:"/template.jhtml"}, {x : 10}));
17 *
18 *   Of course, you can embed more complex codes.
19 *   //in HTML, with prototype.js
20 *   <textarea id="template" style="display:none">
21 *     < % context.list.each(function(pair){%>
22 *          The value of < %= pair.name %> is: < %= pair.value%>
23 *     < % }) %>
24 *   </textarea>
25 *   //code
26 *   document.write(Lyase.View.render({element:"template"},
27 *     {list :[{name : "x", value : 10}, {name : "y", value : 20}]}));
28 */

こんな感じでjavascriptのコードが読み込めます。

また

 1<h1>< %= context.title %></h1>
 2 この文章はLyase.Viewを使用して< %= context.fileName %>の内容を表示しています。
 3
 4
 5以下のようにクロージャも問題なく使用できます。
 6
 7< % var c = "," %>
 8< % context.list.each(function(num){ %>
 9  < %= num+c %>
10
11< % })%>
12
13< %= render({file :"./sample2.jhtml"}, context)%> <!--ココに注目--!>

このようにテンプレート内で別のテンプレートを呼び出すことも可能です。

テンプレートには文字列、textareaなどのinnerHTML、テンプレートファイルが使用できます。 prototype.jsのAjax.Updaterあたりをハックすれば結構使えるものになるんじゃないかなあ、と思ってみたり。


Typoでのlive search文字化けについては多くのブログで解決策が示されています。

最新でもやっぱり文字化けしているわけで。 僕も直してみました。

 1Index: app/views/shared/_search.rhtml
 2===================================================================
 3--- app/views/shared/_search.rhtml      (revision 850)
 4+++ app/views/shared/_search.rhtml      (working copy)
 5@@ -7,4 +7,4 @@
 6     :loading => "Element.show('search_spinner')",
 7     :complete => "Element.hide('search_spinner')",
 8     :update => "search-results",
 9-    :with => "'q=' + escape($F('q'))" -%>
10+    :with => "'q=' + encodeURIComponent($F('q'))" -%>

という感じで1行直せばイケルはずです。(ブラウザはあまり確認してませんが・・・) あ、このファイルはテーマにも含まれてますので注意です。 てゆーかなんでencodeURIComponentじゃなくてescapeなんだろ・・・ ただこの方式だとキャッシュを再構築しないとダメなんでちょっとイヤだなー・・・。


Mochikitも好きですが、普段はRails使いということもあってprototype.jsを使っています。

そういえばjavascriptのライブラリでOOなもの、ということで初めて出会ったのは jsolait でした。 当時の印象としては「んー確かにがんばってるんだけど・・・ちょっと大げさっていうか・・・」って感じでした。

そして去年、prototype.jsに出会うわけですが「class-style OO」とうたっている部分がかなりシンプル(すぎ)で衝撃でした。

ただ、やっぱりjavascriptにおいて「継承」というのはやっかいな問題です。 というわけで誰か作ってると思うんですが、より簡単に継承できるライブラリを作ってみました。

 1Class.def = function(definition) {
 2  var klass = Class.create();
 3  if(definition.__extend__)
 4    Class.inherit(klass, definition.__extend__);
 5  if(definition.__static__)
 6    Object.extend(klass, definition.__static__);
 7  if(definition.__include__) {
 8    if(definition.__include__.constructor != Array)
 9      definition.__include__ = [definition.__include__];
10    definition.__include__.each(function(include){
11      Object.extend(klass.prototype, include);
12    });
13  }
14  ["__static__","__extend__","__include__"].each(function(n){delete definition[n];}
15  Object.extend(klass.prototype, definition);
16  return klass;
17}    
18
19Class.inherit = function(child, parent) {
20  Object.extend(child, parent);
21  child.__super__ = parent.prototype;
22  var dummy = function(){};
23  dummy.prototype = parent.prototype;
24  child.prototype = new dummy();
25  child.prototype.constructor = child;
26  return child;
27}

こんだけです。 使い方はサンプルをみるほうがわかりやすいと思うので、サンプルをどーぞ。

 1var IncludedClass = Class.def({
 2  __static__ : {
 3    includedMethod : function() {
 4      return "includedMethod:"+this.value;
 5    }
 6  }
 7});    
 8
 9var AClass = Class.def({__include__ : IncludedClass,
10  __static__ : {
11    A : "A static"
12  },
13
14  initialize : function(val) {
15    if(!val) throw "error";
16    this.val = val;
17    this.klass = "AClass";
18  },
19
20  inspect : function() {
21    return "< "+this.klass+" : "+this.val+">";
22  }
23});
24
25var BClass = Class.def({ __extend__ : AClass,
26  __static__ : {
27    B : "B static"
28  },
29
30  initialize : function(val) {
31    BClass.__super__.initialize.call(this, val);
32    this.klass = "BClass";
33  }
34});
35
36var CClass = Class.def({ __extend__ : BClass,
37  __static__ : {
38    C : "C static"
39  },
40
41    initialize : function(val) {
42      CClass.__super__.initialize.call(this, val);
43      this.klass = "CClass";
44    }
45});
46
47var a = new AClass("aaaaaaa");
48var b = new BClass("bbbbbbb");
49var c = new CClass("ccccccc");
50alert("a inspect:"+a.inspect()); //=> a inspect:
51alert("b inspect:"+b.inspect()); //=> b inspect:
52alert("c inspect:"+c.inspect()); //=> c inspect:
53alert("includedMethod:"+a.includedMethod()); //=> includedMethod:included aaaaaa
54alert("CClass.A:"+CClass.A); //=> CClass.A:A static
55alert("CClass.B:"+CClass.B); //=> CClass.B:B static
56alert("CClass.C:"+CClass.C); //=> CClass.C:C static
57alert("b.constructor = a.constructor:"+(b.constructor == a.constructor).toString()); //=>b.constructor = a.constructor:false
58alert("b instanceof BClass:"+(b instanceof BClass).toString()); //=>b instanceof BClass:true
59alert("b instanceof AClass:"+(b instanceof AClass).toString()); //=>b instanceof AClass:true
60alert("c instanceof BClass:"+(c instanceof BClass).toString()); //=>c instanceof BClass:true
61alert("c instanceof AClass:"+(c instanceof AClass).toString()); //=>c instanceof AClass:true</div>

継承の処理がかなり楽になっていると思います。 テキトーに場当たりで単純にnewを使って継承していたりすると

「親クラスのコンストラクタに必須な引数があって、それがないとエラーでるから継承できねーよ、しゃーない適当に引数付けとくか」

ということになったりするもんですが(俺はよくなってますw)このライブラリを使えば大丈夫。 また、こんな変な書き方イヤだよ!!って方でも継承の部分はClass.inheritに独立させてあるのでそこだけつかえばオッケーです。

一応動作は確認していますがおかしいところあったら報告していただけるとありがたいです。