Roo/XTemplate.js
[roojs1] / Roo / XTemplate.js
1 /*
2  * Based on:
3  * Ext JS Library 1.1.1
4  * Copyright(c) 2006-2007, Ext JS, LLC.
5  *
6  * Originally Released Under LGPL - original licence link has changed is not relivant.
7  *
8  * Fork - LGPL
9  * <script type="text/javascript">
10  */
11
12
13 /**
14  * @class Roo.XTemplate
15  * @extends Roo.Template
16  * Provides a template that can have nested templates for loops or conditionals. The syntax is:
17 <pre><code>
18 var t = new Roo.MasterTemplate(
19         '&lt;select name="{name}"&gt;',
20                 '&lt;tpl for="options"&gt;&lt;option value="{value:trim}"&gt;{text:ellipsis(10)}&lt;/option&gt;&lt;/tpl&gt;',
21         '&lt;/select&gt;'
22 );
23  
24 // then append, applying the master template values
25  </code></pre>
26  *
27  */
28 Roo.XTemplate = function()
29 {
30     Roo.XTemplate.superclass.constructor.apply(this, arguments);
31     if (this.html) {
32         this.compile();
33     }
34 };
35
36
37 Roo.extend(Roo.XTemplate, Roo.Template, {
38
39     /**
40      *
41      * basic tag replacing syntax
42      * WORD:WORD()
43      *
44      * // you can fake an object call by doing this
45      *  x.t:(test,tesT) 
46      * 
47      */
48     re : /\{([\w-\.]+)(?:\:([\w\.]*)(?:\((.*?)?\))?)?\}/g,
49
50     
51     compile: function()
52     {
53         var s = this.html;
54      
55         s = ['<tpl>', s, '</tpl>'].join('');
56     
57         var re = /<tpl\b[^>]*>((?:(?=([^<]+))\2|<(?!tpl\b[^>]*>))*?)<\/tpl>/;
58     
59         var nameRe = /^<tpl\b[^>]*?for="(.*?)"/;
60         var ifRe   = /^<tpl\b[^>]*?if="(.*?)"/;
61         var execRe = /^<tpl\b[^>]*?exec="(.*?)"/;
62         var m, id = 0;
63         var tpls = [];
64     
65         while(true == !!(m = s.match(re))){
66            var m2 = m[0].match(nameRe);
67            var m3 = m[0].match(ifRe);
68            var m4 = m[0].match(execRe);
69            var exp = null,
70                 fn = null,
71                 exec = null;
72            var name = m2 && m2[1] ? m2[1] : '';
73            if(m3){
74                 // if - puts fn into test..
75                 exp = m3 && m3[1] ? m3[1] : null;
76                 if(exp){
77                    fn = new Function('values', 'parent', 'with(values){ return '+(Roo.util.Format.htmlDecode(exp))+'; }');
78                 }
79            }
80            if(m4){
81                 // exec - calls a function... returns empty if true is  returned.
82                exp = m4 && m4[1] ? m4[1] : null;
83                if(exp){
84                    exec = new Function('values', 'parent', 'with(values){ '+(Roo.util.Format.htmlDecode(exp))+'; }');
85                }
86            }
87            if(name){
88                 // for = 
89                switch(name){
90                    case '.':  name = new Function('values', 'parent', 'with(values){ return values; }'); break;
91                    case '..': name = new Function('values', 'parent', 'with(values){ return parent; }'); break;
92                    default:   name = new Function('values', 'parent', 'with(values){ return '+name+'; }');
93                }
94            }
95            tpls.push({
96                 id: id,
97                 target: name,
98                 exec: exec,
99                 test: fn,
100                 body: m[1]||''
101             });
102            s = s.replace(m[0], '{xtpl'+ id + '}');
103            ++id;
104         }
105         for(var i = tpls.length-1; i >= 0; --i){
106             this.compileTpl(tpls[i]);
107         }
108         this.master = tpls[tpls.length-1];
109         this.tpls = tpls;
110         return this;
111     },
112     
113     applySubTemplate : function(id, values, parent)
114     {
115         var t = this.tpls[id];
116         if(t.test && !t.test.call(this, values, parent)){
117             return '';
118         }
119         if(t.exec && t.exec.call(this, values, parent)){
120             return '';
121         }
122         var vs = t.target ? t.target.call(this, values, parent) : values;
123         parent = t.target ? values : parent;
124         if(t.target && vs instanceof Array){
125             var buf = [];
126             for(var i = 0, len = vs.length; i < len; i++){
127                 buf[buf.length] = t.compiled.call(this, vs[i], parent);
128             }
129             return buf.join('');
130         }
131         return t.compiled.call(this, vs, parent);
132     },
133
134     compileTpl : function(tpl)
135     {
136         var fm = Roo.util.Format;
137         var useF = this.disableFormats !== true;
138         var sep = Roo.isGecko ? "+" : ",";
139         var fn = function(m, name, format, args){
140             //["{TEST:(a,b,c)}", "TEST", "", "a,b,c", 0, "{TEST:(a,b,c)}"]
141             if (typeof(format) == 'undefined') {
142                 format= 'htmlEncode';
143             }
144             if (format == 'raw' ) {
145                 format = false;
146             }
147             
148             if(name.substr(0, 4) == 'xtpl'){
149                 return "'"+ sep +'this.applySubTemplate('+name.substr(4)+', values, parent)'+sep+"'";
150             }
151             
152             var v;
153             //if(name.indexOf('.') != -1){
154                 v = name;
155             //}else{
156             //    v = "values['" + name + "']";
157             //}
158             if(format && useF){
159                 
160                 args = args ? ',' + args : "";
161                  
162                 if(format.substr(0, 5) != "this."){
163                     format = "fm." + format + '(';
164                 }else{
165                     format = 'this.call("'+ format.substr(5) + '", ';
166                     args = ", values";
167                 }
168                 return "'"+ sep + format + v + args + ")"+sep+"'";
169             }
170              
171             if (args.length) {
172                 // called with xxyx.yuu:(test,test)
173                 // change to ()
174                 return "'"+ sep + "("+v+" === undefined ? '' : " + v + '(' +  args + "))"+sep+"'";
175             }
176             // raw.. - :raw modifier..
177             return "'"+ sep + "("+v+" === undefined ? '' : " + v + ")"+sep+"'";
178             
179         };
180         var body;
181         // branched to use + in gecko and [].join() in others
182         if(Roo.isGecko){
183             body = "tpl.compiled = function(values, parent){ with(values) { return '" +
184                    tpl.body.replace(/(\r\n|\n)/g, '\\n').replace(/'/g, "\\'").replace(this.re, fn) +
185                     "';};};";
186         }else{
187             body = ["tpl.compiled = function(values, parent){ with (values) { return ['"];
188             body.push(tpl.body.replace(/(\r\n|\n)/g, '\\n').replace(/'/g, "\\'").replace(this.re, fn));
189             body.push("'].join('');};};");
190             body = body.join('');
191         }
192         
193         /** eval:var:zzzzzzz */
194         eval(body);
195         Roo.log(body.replace(/\\n/,'\n'));
196         
197         return this;
198     },
199
200     applyTemplate : function(values){
201         return this.master.compiled.call(this, values, {});
202         //var s = this.subs;
203     },
204
205     apply : function(){
206         return this.applyTemplate.apply(this, arguments);
207     }
208
209  });
210
211 Roo.XTemplate.from = function(el){
212     el = Roo.getDom(el);
213     return new Roo.XTemplate(el.value || el.innerHTML);
214 };