openerp-dev-web team mailing list archive
-
openerp-dev-web team
-
Mailing list archive
-
Message #03904
lp:~openerp-dev/openobject-client-web/proto61-qweb-cleanups-xmo into lp:~openerp-dev/openobject-client-web/trunk-proto61
Xavier (Open ERP) has proposed merging lp:~openerp-dev/openobject-client-web/proto61-qweb-cleanups-xmo into lp:~openerp-dev/openobject-client-web/trunk-proto61.
Requested reviews:
OpenERP R&D Team (openerp-dev)
For more details, see:
https://code.launchpad.net/~openerp-dev/openobject-client-web/proto61-qweb-cleanups-xmo/+merge/52661
Some cleanups of qweb while I was reading it
--
https://code.launchpad.net/~openerp-dev/openobject-client-web/proto61-qweb-cleanups-xmo/+merge/52661
Your team OpenERP R&D Team is requested to review the proposed merge of lp:~openerp-dev/openobject-client-web/proto61-qweb-cleanups-xmo into lp:~openerp-dev/openobject-client-web/trunk-proto61.
=== removed file 'addons/base/static/qweb/qweb.html'
--- addons/base/static/qweb/qweb.html 2011-02-23 17:51:09 +0000
+++ addons/base/static/qweb/qweb.html 1970-01-01 00:00:00 +0000
@@ -1,65 +0,0 @@
-<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
-<html>
-<head>
- <title>qweb test</title>
- <script type="text/javascript" src="jquery.pack.js"></script>
- <script type="text/javascript" src="qweb.js"></script>
- <script type="text/javascript">
- var debug=function(s){
- out="";
- if(typeof(s)=="object") {
- for(var i in s) {
- out+=i+" : "+s+"\n"
- }
- out=s+" {\n"+out+"}"
- } else {
- out=s+"";
- }
- $("#output")[0].innerHTML+=out.replace(/&/g,"&").replace(/</g,"<").replace(/>/g,">")+"<br/>\n"
- }
- $(function(){
- QWeb.add_template("qweb.xml");
- var v = {
- "loop" : [ {"status":"biesse","name":"Fab"}, {"status":"malin","name":"Antony"}, {"status":"chat","name":"Schtroumph"} ],
- "caca":"Youhou !! <\"men & man\"> !!",
- "string_vide": "",
- "string_one": "1",
- "number_one": 1,
- "dict" : {
- "caca":"Youhou !! <\"men & man\"> !!",
- "string_vide": "",
- "string_one": "1",
- "number_one": 1
- }
- }
- //debug(QWeb.render("test", v));
-
- var a = {"lastname" : "Meghazi", "firstname": "Fabien", "email" : "agr@xxxxxxxxxxxx", "status" : "GROSSE BIESSSSSSE"}
- var aa = new Array();
- for (var i = 0; i < 500; i++) {
- aa.push(a);
- }
- v["tableau"] = aa;
-
- iter = 1;
- start = Date.now()
- var tmp = ""
- for (var i = 0; i < iter; i++) {
- tmp += QWeb.render("table", v);
- }
- stop = Date.now()
- diff = stop - start;
- debug(iter + " iterations in " + (diff / 1000) + " sec");
- $("#table").html(tmp);
- })
- </script>
-</head>
-<body>
-
-<pre id="output" style="padding: 4px; border: 1px solid #000;"></pre>
-<div id="table">
-
-</div>
-
-</body>
-</html>
=== modified file 'addons/base/static/qweb/qweb.js'
--- addons/base/static/qweb/qweb.js 2011-03-07 13:55:15 +0000
+++ addons/base/static/qweb/qweb.js 2011-03-09 11:32:42 +0000
@@ -3,7 +3,6 @@
// QWeb javascript
//---------------------------------------------------------
-{
/*
TODO
@@ -37,331 +36,324 @@
DONE
we reintroduced t-att-id, no more t-esc-id because of the new convention t-att="["id","val"]"
*/
-}
-
-var QWeb={
- templates:{},
- prefix:"t",
- reg:"",
- tag:{},
- att:{},
- eval_object:function(e,v) {
- // TODO: Currently this will also replace and, or, ... in strings. Try
- // 'hi boys and girls' != '' and 1 == 1 -- will be replaced to : 'hi boys && girls' != '' && 1 == 1
- // try to find a solution without tokenizing
- e = e.replace(/\Wand\W/g, " && ");
- e = e.replace(/\Wor\W/g, " and ");
- e = e.replace(/\Wgt\W/g, " > ");
- e = e.replace(/\Wgte\W/g, " >= ");
- e = e.replace(/\Wlt\W/g, " < ");
- e = e.replace(/\Wlte\W/g, " <= ");
- if(v[e]!=undefined) {
- return v[e]
- } else {
- with(v) return eval(e);
- }
- },
- eval_str:function(e,v){
- var r=this.eval_object(e,v)
- r=(typeof(r)=="undefined"||r==null) ? "" : r.toString()
- return e=="0" ? v["0"] : r
- },
- eval_format:function(e,v){
- var i,m,r,src=e.split(/#/)
- r=src[0]
- for(i=1; i<src.length; i++) {
- if(m=src[i].match(/^{(.*)}(.*)/)) {
- r+=this.eval_str(m[1],v)+m[2]
- } else {
- r+="#"+src[i]
- }
- }
- return r
- },
- eval_bool:function(e,v){
- return this.eval_object(e,v)?true:false;
- },
- trim : function(v, mode) {
- if (!v || !mode) return v;
- if (mode == 'both') {
- return v.replace(/^\s*|\s*$/g, "");
- } else if (mode == "left") {
- return v.replace(/^\s*/, "");
- } else if (mode == "right") {
- return v.replace(/\s*$/, "");
- } else {
- return v;
- }
- },
- escape_text:function(s){
- return s.replace(/&/g,"&").replace(/</g,"<").replace(/>/g,">")
- },
- escape_att:function(s){
- return s.replace(/&/g,"&").replace(/</g,"<").replace(/>/g,">").replace(/"/g,""")
- },
- render_node : function(e, v, inner_trim) {
- var r=""
- if(e.nodeType==3) {
- r = inner_trim ? this.trim(e.data, inner_trim) : e.data;
- } else if(e.nodeType==1) {
- var g_att={};
- var t_att={};
- var t_render=null;
- var a=e.attributes;
- for(var i=0; i<a.length; i++) {
- var an=a[i].name,av=a[i].value;
- var m,n;
- if(m=an.match(this.reg)) {
- n=m[1]
- if(n=="eval") {
- n=m[2].substring(1)
- av=this.eval_str(av, v)
- }
- if(f=this.att[n]) {
- this[f](e,t_att,g_att,v,m[2],av)
- } else if(f=this.tag[n]) {
- t_render=f
- }
- t_att[n]=av
- } else {
- g_att[an]=av
- }
- }
- if (inner_trim && !t_att["trim"]) {
- t_att["trim"] = "inner " + inner_trim;
- }
- if (t_render) {
- r = this[t_render](e, t_att, g_att, v)
- } else {
- r = this.render_element(e, t_att, g_att, v)
- }
- }
- return r;
- },
- render_element:function(e,t_att,g_att,v){
- var inner = "", ec = e.childNodes, trim = t_att["trim"], inner_trim;
- if (trim) {
- if (trim.match(/(^|\W)(inner)($|\W)/)) {
- inner_trim = true;
- trim = "both";
- }
- var tm = trim.match(/(^|\W)(both|left|right)($|\W)/);
- if (tm) trim = tm[2];
- }
- for (var i=0; i<ec.length; i++) {
- inner += inner_trim ? this.trim(this.render_node(ec[i], v, inner_trim ? trim : null), trim) : this.render_node(ec[i], v, inner_trim ? trim : null);
- }
- if (trim && !inner_trim) {
- inner = this.trim(inner, trim);
- }
- if(e.tagName==this.prefix) {
- return inner;
- } else {
- var att="";
- for(var an in g_att) {
- av=g_att[an]
- att+=" "+an+'="'+this.escape_att(av)+'"'
- }
- return inner.length ? "<" + e.tagName + att + ">" + inner + "</" + e.tagName + ">" : "<" + e.tagName + att + "/>";
- }
- },
- render_att_att:function(e,t_att,g_att,v,ext,av){
- if(ext) {
- g_att[ext.substring(1)]=this.eval_str(av,v)
- } else {
- o=this.eval_object(av,v)
- g_att[o[0]]=o[1]
- }
- },
- render_att_attf:function(e,t_att,g_att,v,ext,av){
- g_att[ext.substring(1)]=this.eval_format(av,v)
- },
- render_tag_raw:function(e,t_att,g_att,v){
- return this.eval_str(t_att["raw"], v);
- },
- render_tag_rawf:function(e,t_att,g_att,v){
- return this.eval_format(t_att["raw"], v);
- },
- render_tag_esc:function(e,t_att,g_att,v){
- return this.escape_text(this.eval_str(t_att["esc"], v));
- },
- render_tag_escf:function(e,t_att,g_att,v){
- return this.escape_text(this.eval_format(t_att["esc"], v));
- },
- render_tag_if:function(e,t_att,g_att,v){
- return this.eval_bool(t_att["if"],v) ? this.render_element(e, t_att, g_att, v) : ""
- },
- render_tag_set:function(e,t_att,g_att,v){
- var ev=t_att["value"]
- if(ev && ev.constructor!=Function) {
- v[t_att["set"]]=this.eval_object(ev,v)
- } else {
- v[t_att["set"]]=this.render_element(e, t_att, g_att, v)
- }
- return ""
- },
- render_tag_call:function(e,t_att,g_att,v){
- var d=v;
- if(!t_att["import"]) {
- d = {}
- for(var i in v) {
- d[i]=v[i]
- }
- }
- d["0"]=this.render_element(e, t_att, g_att, d)
- return this.render(t_att["call"],d)
- },
- render_tag_js:function(e,t_att,g_att,v){
- var r=this.eval_str(this.render_element(e, t_att, g_att, v),v)
- return t_att["js"]!="quiet" ? r : ""
- },
- render_tag_foreach:function(e,t_att,g_att,v){
- var expr=t_att["foreach"]
- var enu=this.eval_object(expr,v)
- var ru=[];
- if(enu) {
- var val=t_att['as'] || expr.replace(/[^a-zA-Z0-9]/g,'_')
- d={}
- for(var i in v) {
- d[i]=v[i]
- }
- d[val+"_all"]=enu
- val_value = val+"_value"
- val_index = val+"_index"
- val_first = val+"_first"
- val_last = val+"_last"
- val_parity = val+"_parity"
- var size=enu.length
- if(size) {
- d[val+"_size"]=size
- for (var i=0; i<size; i++) {
- var cur=enu[i]
- d[val_value]=cur
- d[val_index]=i
- d[val_first]=i==0
- d[val_last]=i+1==size
- d[val_parity]=(i%2==1 ? 'odd' : 'even')
- if (cur.constructor==Object) {
- for(var j in cur) {
- d[j]=cur[j]
- }
- }
- d[val]=cur
- var r = this.render_element(e,t_att,g_att,d);
- ru.push(r)
- }
- } else {
- index=0
- for(cur in enu) {
- d[val_value]=cur
- d[val_index]=index
- d[val_first]=index==0
- d[val_parity]=(index%2==1 ? 'odd' : 'even')
- d[val]=cur
- ru.push(this.render_element(e,t_att,g_att,d))
- index+=1
- }
- }
- return ru.join("")
- } else {
- return "qweb: foreach "+expr+" not found."
- }
- },
- hash:function(){
- var l=[]
- for(var i in this) {
- if(m=i.match(/render_tag_(.*)/)) {
- this.tag[m[1]]=i
- l.push(m[1])
- } else if(m=i.match(/render_att_(.*)/)) {
- this.att[m[1]]=i
- l.push(m[1])
- }
- }
- l.sort(function(a,b){return a.length>b.length?-1:1})
- var s="^"+this.prefix+"-(eval|"+l.join("|")+"|.*)(.*)$"
- this.reg=new RegExp(s);
- },
- load_xml:function(s){
- var xml;
- if(s[0]=="<") {
- /*
- manque ca pour sarrisa
- if(window.DOMParser){
- mozilla
- if(!window.DOMParser){
- var doc = Sarissa.getDomDocument();
- doc.loadXML(sXml);
- return doc;
- };
- };
- */
- } else {
- var w=window,r=w.XMLHttpRequest,j;
- if(r)r=new r();else for(j in{"Msxml2":1,"Microsoft":1})try{r=new ActiveXObject(j+".XMLHTTP");break}catch(e){}
- if(r) {
- r.open("GET", s, false);
- r.send(null);
- //if ie r.setRequestHeader("If-Modified-Since", "Sat, 1 Jan 2000 00:00:00 GMT");
- xml=r.responseXML;
- /*
- TODO
- if intsernetexploror
- getdomimplmentation() for try catch
- responseXML.getImplet
- d=domimple()
- d.preserverWhitespace=1
- d.loadXML()
-
- xml.preserverWhitespace=1
- xml.loadXML(r.reponseText)
- */
- return xml;
- }
- }
- },
- add_template:function(e){
- // TODO: keep sources so we can implement reload()
- this.hash()
- if(e.constructor==String) {
- e=this.load_xml(e)
- }
- var ec=[];
- if(e.documentElement) {
- ec=e.documentElement.childNodes
- } else if(e.childNodes) {
- ec=e.childNodes
- }
- for (var i=0; i<ec.length; i++) {
- var n=ec[i];
- if(n.nodeType==1) {
- var name=n.getAttribute(this.prefix+"-name")
- this.templates[name]=n;
- }
- }
- },
- render:function(name,v){
- if(e=this.templates[name]) {
- return this.render_node(e,v)
- } else {
- return "template "+name+" not found";
- }
- },
- dump:function(o){
- var r="";
- if(typeof(o)=="object") {
- for(var i in o) {
- r+=i+" : "+this.dump(s)+"\n"
- }
- r=s+"{\n"+r+"}\n"
- } else {
- r=s+"";
- }
- return r;
- },
- debug:function(s){
- var r=this.dump(s)
- $("#debug")[0].append(this.escape_text(r)+"<br/>\n")
- }
-}
+
+var QWeb = {
+ templates:{},
+ prefix:"t",
+ reg:new RegExp(),
+ tag:{},
+ att:{},
+ eval_object:function(e, v) {
+ // TODO: Currently this will also replace and, or, ... in strings. Try
+ // 'hi boys and girls' != '' and 1 == 1 -- will be replaced to : 'hi boys && girls' != '' && 1 == 1
+ // try to find a solution without tokenizing
+ e = e.replace(/\Wand\W/g, " && ");
+ e = e.replace(/\Wor\W/g, " and ");
+ e = e.replace(/\Wgt\W/g, " > ");
+ e = e.replace(/\Wgte\W/g, " >= ");
+ e = e.replace(/\Wlt\W/g, " < ");
+ e = e.replace(/\Wlte\W/g, " <= ");
+ if (v[e] != undefined) {
+ return v[e];
+ } else {
+ with (v) return eval(e);
+ }
+ },
+ eval_str:function(e, v) {
+ var r = this.eval_object(e, v);
+ r = (typeof(r) == "undefined" || r == null) ? "" : r.toString();
+ return e == "0" ? v["0"] : r;
+ },
+ eval_format:function(e, v) {
+ var m, src = e.split(/#/), r = src[0];
+ for (var i = 1; i < src.length; i++) {
+ if (m = src[i].match(/^{(.*)}(.*)/)) {
+ r += this.eval_str(m[1], v) + m[2];
+ } else {
+ r += "#" + src[i];
+ }
+ }
+ return r;
+ },
+ eval_bool:function(e, v) {
+ return this.eval_object(e, v) ? true : false;
+ },
+ trim : function(v, mode) {
+ if (!v || !mode) return v;
+ switch (mode) {
+ case 'both':
+ return v.replace(/^\s*|\s*$/g, "");
+ case "left":
+ return v.replace(/^\s*/, "");
+ case "right":
+ return v.replace(/\s*$/, "");
+ }
+ },
+ escape_text:function(s) {
+ return s.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">");
+ },
+ escape_att:function(s) {
+ return s.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">").replace(/"/g, """);
+ },
+ render_node : function(e, v, inner_trim) {
+ if (e.nodeType == 3) {
+ return inner_trim ? this.trim(e.data, inner_trim) : e.data;
+ }
+ if (e.nodeType == 1) {
+ var g_att = {};
+ var t_att = {};
+ var t_render = null;
+ var a = e.attributes;
+ for (var i = 0; i < a.length; i++) {
+ var an = a[i].name,av = a[i].value;
+ var m;
+ if (m = an.match(this.reg)) {
+ var n = m[1];
+ if (n == "eval") {
+ n = m[2].substring(1);
+ av = this.eval_str(av, v);
+ }
+ var f;
+ if (f = this.att[n]) {
+ this[f](e, t_att, g_att, v, m[2], av);
+ } else if (f = this.tag[n]) {
+ t_render = f;
+ }
+ t_att[n] = av;
+ } else {
+ g_att[an] = av;
+ }
+ }
+ if (inner_trim && !t_att["trim"]) {
+ t_att["trim"] = "inner " + inner_trim;
+ }
+ if (t_render) {
+ return this[t_render](e, t_att, g_att, v);
+ }
+ return this.render_element(e, t_att, g_att, v);
+ }
+ return "";
+ },
+ render_element:function(e, t_att, g_att, v) {
+ var inner = "", ec = e.childNodes, trim = t_att["trim"], inner_trim;
+ if (trim) {
+ if (trim.match(/(^|\W)(inner)($|\W)/)) {
+ inner_trim = true;
+ trim = "both";
+ }
+ var tm = trim.match(/(^|\W)(both|left|right)($|\W)/);
+ if (tm) trim = tm[2];
+ }
+ for (var i = 0; i < ec.length; i++) {
+ inner += inner_trim ? this.trim(this.render_node(ec[i], v, inner_trim ? trim : null), trim) : this.render_node(ec[i], v, inner_trim ? trim : null);
+ }
+ if (trim && !inner_trim) {
+ inner = this.trim(inner, trim);
+ }
+ if (e.tagName == this.prefix) {
+ return inner;
+ }
+ var att = "";
+ for (var an in g_att) {
+ att += " " + an + '="' + this.escape_att(g_att[an]) + '"';
+ }
+ return inner.length ? "<" + e.tagName + att + ">" + inner + "</" + e.tagName + ">" : "<" + e.tagName + att + "/>";
+ },
+ render_att_att:function(e, t_att, g_att, v, ext, av) {
+ if (ext) {
+ g_att[ext.substring(1)] = this.eval_str(av, v);
+ } else {
+ var o = this.eval_object(av, v);
+ g_att[o[0]] = o[1];
+ }
+ },
+ render_att_attf:function(e, t_att, g_att, v, ext, av) {
+ g_att[ext.substring(1)] = this.eval_format(av, v);
+ },
+ render_tag_raw:function(e, t_att, g_att, v) {
+ return this.eval_str(t_att["raw"], v);
+ },
+ render_tag_rawf:function(e, t_att, g_att, v) {
+ return this.eval_format(t_att["raw"], v);
+ },
+ render_tag_esc:function(e, t_att, g_att, v) {
+ return this.escape_text(this.eval_str(t_att["esc"], v));
+ },
+ render_tag_escf:function(e, t_att, g_att, v) {
+ return this.escape_text(this.eval_format(t_att["esc"], v));
+ },
+ render_tag_if:function(e, t_att, g_att, v) {
+ return this.eval_bool(t_att["if"], v) ? this.render_element(e, t_att, g_att, v) : "";
+ },
+ render_tag_set:function(e, t_att, g_att, v) {
+ var ev = t_att["value"];
+ if (ev && ev.constructor != Function) {
+ v[t_att["set"]] = this.eval_object(ev, v);
+ } else {
+ v[t_att["set"]] = this.render_element(e, t_att, g_att, v);
+ }
+ return "";
+ },
+ render_tag_call:function(e, t_att, g_att, v) {
+ var d = v;
+ if (!t_att["import"]) {
+ d = {};
+ for (var i in v) {
+ d[i] = v[i];
+ }
+ }
+ d["0"] = this.render_element(e, t_att, g_att, d);
+ return this.render(t_att["call"], d);
+ },
+ render_tag_js:function(e, t_att, g_att, v) {
+ var r = this.eval_str(this.render_element(e, t_att, g_att, v), v);
+ return t_att["js"] != "quiet" ? r : "";
+ },
+ render_tag_foreach:function(e, t_att, g_att, v) {
+ var expr = t_att["foreach"];
+ var enu = this.eval_object(expr, v);
+ var ru = [];
+ if (enu) {
+ var val = t_att['as'] || expr.replace(/[^a-zA-Z0-9]/g, '_');
+ var d = {};
+ for (var i in v) {
+ d[i] = v[i];
+ }
+ d[val + "_all"] = enu;
+ var val_value = val + "_value",
+ val_index = val + "_index",
+ val_first = val + "_first",
+ val_last = val + "_last",
+ val_parity = val + "_parity";
+ var size = enu.length;
+ if (size) {
+ d[val + "_size"] = size;
+ for (var j = 0; j < size; j++) {
+ var cur = enu[j];
+ d[val_value] = cur;
+ d[val_index] = j;
+ d[val_first] = j == 0;
+ d[val_last] = j + 1 == size;
+ d[val_parity] = (j % 2 == 1 ? 'odd' : 'even');
+ if (cur.constructor == Object) {
+ for (var k in cur) {
+ d[k] = cur[k];
+ }
+ }
+ d[val] = cur;
+ var r = this.render_element(e, t_att, g_att, d);
+ ru.push(r);
+ }
+ } else {
+ var index = 0;
+ for (cur in enu) {
+ d[val_value] = cur;
+ d[val_index] = index;
+ d[val_first] = index == 0;
+ d[val_parity] = (index % 2 == 1 ? 'odd' : 'even');
+ d[val] = cur;
+ ru.push(this.render_element(e, t_att, g_att, d));
+ index += 1;
+ }
+ }
+ return ru.join("");
+ } else {
+ return "qweb: foreach " + expr + " not found.";
+ }
+ },
+ hash:function() {
+ var l = [], m;
+ for (var i in this) {
+ if (m = i.match(/render_tag_(.*)/)) {
+ this.tag[m[1]] = i;
+ l.push(m[1]);
+ } else if (m = i.match(/render_att_(.*)/)) {
+ this.att[m[1]] = i;
+ l.push(m[1]);
+ }
+ }
+ l.sort(function(a, b) {
+ return a.length > b.length ? -1 : 1;
+ });
+ var s = "^" + this.prefix + "-(eval|" + l.join("|") + "|.*)(.*)$";
+ this.reg = new RegExp(s);
+ },
+ /**
+ * returns the correct XMLHttpRequest instance for the browser, or null if
+ * it was not able to build any XHR instance.
+ *
+ * @returns XMLHttpRequest|MSXML2.XMLHTTP.3.0|null
+ */
+ get_xhr:function () {
+ if (window.XMLHttpRequest) {
+ return new window.XMLHttpRequest();
+ }
+ try {
+ return new ActiveXObject('MSXML2.XMLHTTP.3.0');
+ } catch(e) {
+ return null;
+ }
+ },
+ load_xml:function(s) {
+ var xml;
+ if (s[0] == "<") {
+ /*
+ manque ca pour sarrisa
+ if(window.DOMParser){
+ mozilla
+ if(!window.DOMParser){
+ var doc = Sarissa.getDomDocument();
+ doc.loadXML(sXml);
+ return doc;
+ };
+ };
+ */
+ } else {
+ var req = this.get_xhr();
+ if (req) {
+ req.open("GET", s, false);
+ req.send(null);
+ //if ie r.setRequestHeader("If-Modified-Since", "Sat, 1 Jan 2000 00:00:00 GMT");
+ xml = req.responseXML;
+ /*
+ TODO
+ if intsernetexploror
+ getdomimplmentation() for try catch
+ responseXML.getImplet
+ d=domimple()
+ d.preserverWhitespace=1
+ d.loadXML()
+
+ xml.preserverWhitespace=1
+ xml.loadXML(r.reponseText)
+ */
+ return xml;
+ }
+ }
+ },
+ add_template:function(e) {
+ // TODO: keep sources so we can implement reload()
+ this.hash();
+ if (e.constructor == String) {
+ e = this.load_xml(e);
+ }
+ var ec = e.documentElement ? e.documentElement.childNodes
+ : e.childNodes ? e.childNodes
+ : [];
+ for (var i = 0; i < ec.length; i++) {
+ var n = ec[i];
+ if (n.nodeType == 1) {
+ var name = n.getAttribute(this.prefix + "-name");
+ this.templates[name] = n;
+ }
+ }
+ },
+ render:function(name, v) {
+ var e;
+ if (e = this.templates[name]) {
+ return this.render_node(e, v);
+ }
+ return "template " + name + " not found";
+ }
+};