Upgrade to ExtJS 3.2.1 - Released 04/27/2010
[extjs.git] / docs / source / XTemplate.html
1 <html>
2 <head>
3   <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />    
4   <title>The source code</title>
5     <link href="../resources/prettify/prettify.css" type="text/css" rel="stylesheet" />
6     <script type="text/javascript" src="../resources/prettify/prettify.js"></script>
7 </head>
8 <body  onload="prettyPrint();">
9     <pre class="prettyprint lang-js">/*!
10  * Ext JS Library 3.2.1
11  * Copyright(c) 2006-2010 Ext JS, Inc.
12  * licensing@extjs.com
13  * http://www.extjs.com/license
14  */
15 <div id="cls-Ext.XTemplate"></div>/**
16  * @class Ext.XTemplate
17  * @extends Ext.Template
18  * <p>A template class that supports advanced functionality like:<div class="mdetail-params"><ul>
19  * <li>Autofilling arrays using templates and sub-templates</li>
20  * <li>Conditional processing with basic comparison operators</li>
21  * <li>Basic math function support</li>
22  * <li>Execute arbitrary inline code with special built-in template variables</li>
23  * <li>Custom member functions</li>
24  * <li>Many special tags and built-in operators that aren't defined as part of
25  * the API, but are supported in the templates that can be created</li>
26  * </ul></div></p>
27  * <p>XTemplate provides the templating mechanism built into:<div class="mdetail-params"><ul>
28  * <li>{@link Ext.DataView}</li>
29  * <li>{@link Ext.ListView}</li>
30  * <li>{@link Ext.form.ComboBox}</li>
31  * <li>{@link Ext.grid.TemplateColumn}</li>
32  * <li>{@link Ext.grid.GroupingView}</li>
33  * <li>{@link Ext.menu.Item}</li>
34  * <li>{@link Ext.layout.MenuLayout}</li>
35  * <li>{@link Ext.ColorPalette}</li>
36  * </ul></div></p>
37  *
38  * <p>For example usage {@link #XTemplate see the constructor}.</p>
39  *
40  * @constructor
41  * The {@link Ext.Template#Template Ext.Template constructor} describes
42  * the acceptable parameters to pass to the constructor. The following
43  * examples demonstrate all of the supported features.</p>
44  *
45  * <div class="mdetail-params"><ul>
46  *
47  * <li><b><u>Sample Data</u></b>
48  * <div class="sub-desc">
49  * <p>This is the data object used for reference in each code example:</p>
50  * <pre><code>
51 var data = {
52     name: 'Jack Slocum',
53     title: 'Lead Developer',
54     company: 'Ext JS, LLC',
55     email: 'jack@extjs.com',
56     address: '4 Red Bulls Drive',
57     city: 'Cleveland',
58     state: 'Ohio',
59     zip: '44102',
60     drinks: ['Red Bull', 'Coffee', 'Water'],
61     kids: [{
62         name: 'Sara Grace',
63         age:3
64     },{
65         name: 'Zachary',
66         age:2
67     },{
68         name: 'John James',
69         age:0
70     }]
71 };
72  * </code></pre>
73  * </div>
74  * </li>
75  *
76  *
77  * <li><b><u>Auto filling of arrays</u></b>
78  * <div class="sub-desc">
79  * <p>The <b><tt>tpl</tt></b> tag and the <b><tt>for</tt></b> operator are used
80  * to process the provided data object:
81  * <ul>
82  * <li>If the value specified in <tt>for</tt> is an array, it will auto-fill,
83  * repeating the template block inside the <tt>tpl</tt> tag for each item in the
84  * array.</li>
85  * <li>If <tt>for="."</tt> is specified, the data object provided is examined.</li>
86  * <li>While processing an array, the special variable <tt>{#}</tt>
87  * will provide the current array index + 1 (starts at 1, not 0).</li>
88  * </ul>
89  * </p>
90  * <pre><code>
91 &lt;tpl <b>for</b>=".">...&lt;/tpl>       // loop through array at root node
92 &lt;tpl <b>for</b>="foo">...&lt;/tpl>     // loop through array at foo node
93 &lt;tpl <b>for</b>="foo.bar">...&lt;/tpl> // loop through array at foo.bar node
94  * </code></pre>
95  * Using the sample data above:
96  * <pre><code>
97 var tpl = new Ext.XTemplate(
98     '&lt;p>Kids: ',
99     '&lt;tpl <b>for</b>=".">',       // process the data.kids node
100         '&lt;p>{#}. {name}&lt;/p>',  // use current array index to autonumber
101     '&lt;/tpl>&lt;/p>'
102 );
103 tpl.overwrite(panel.body, data.kids); // pass the kids property of the data object
104  * </code></pre>
105  * <p>An example illustrating how the <b><tt>for</tt></b> property can be leveraged
106  * to access specified members of the provided data object to populate the template:</p>
107  * <pre><code>
108 var tpl = new Ext.XTemplate(
109     '&lt;p>Name: {name}&lt;/p>',
110     '&lt;p>Title: {title}&lt;/p>',
111     '&lt;p>Company: {company}&lt;/p>',
112     '&lt;p>Kids: ',
113     '&lt;tpl <b>for="kids"</b>>',     // interrogate the kids property within the data
114         '&lt;p>{name}&lt;/p>',
115     '&lt;/tpl>&lt;/p>'
116 );
117 tpl.overwrite(panel.body, data);  // pass the root node of the data object
118  * </code></pre>
119  * <p>Flat arrays that contain values (and not objects) can be auto-rendered
120  * using the special <b><tt>{.}</tt></b> variable inside a loop.  This variable
121  * will represent the value of the array at the current index:</p>
122  * <pre><code>
123 var tpl = new Ext.XTemplate(
124     '&lt;p>{name}\&#39;s favorite beverages:&lt;/p>',
125     '&lt;tpl for="drinks">',
126        '&lt;div> - {.}&lt;/div>',
127     '&lt;/tpl>'
128 );
129 tpl.overwrite(panel.body, data);
130  * </code></pre>
131  * <p>When processing a sub-template, for example while looping through a child array,
132  * you can access the parent object's members via the <b><tt>parent</tt></b> object:</p>
133  * <pre><code>
134 var tpl = new Ext.XTemplate(
135     '&lt;p>Name: {name}&lt;/p>',
136     '&lt;p>Kids: ',
137     '&lt;tpl for="kids">',
138         '&lt;tpl if="age > 1">',
139             '&lt;p>{name}&lt;/p>',
140             '&lt;p>Dad: {<b>parent</b>.name}&lt;/p>',
141         '&lt;/tpl>',
142     '&lt;/tpl>&lt;/p>'
143 );
144 tpl.overwrite(panel.body, data);
145  * </code></pre>
146  * </div>
147  * </li>
148  *
149  *
150  * <li><b><u>Conditional processing with basic comparison operators</u></b>
151  * <div class="sub-desc">
152  * <p>The <b><tt>tpl</tt></b> tag and the <b><tt>if</tt></b> operator are used
153  * to provide conditional checks for deciding whether or not to render specific
154  * parts of the template. Notes:<div class="sub-desc"><ul>
155  * <li>Double quotes must be encoded if used within the conditional</li>
156  * <li>There is no <tt>else</tt> operator &mdash; if needed, two opposite
157  * <tt>if</tt> statements should be used.</li>
158  * </ul></div>
159  * <pre><code>
160 &lt;tpl if="age &gt; 1 &amp;&amp; age &lt; 10">Child&lt;/tpl>
161 &lt;tpl if="age >= 10 && age < 18">Teenager&lt;/tpl>
162 &lt;tpl <b>if</b>="this.isGirl(name)">...&lt;/tpl>
163 &lt;tpl <b>if</b>="id==\'download\'">...&lt;/tpl>
164 &lt;tpl <b>if</b>="needsIcon">&lt;img src="{icon}" class="{iconCls}"/>&lt;/tpl>
165 // no good:
166 &lt;tpl if="name == "Jack"">Hello&lt;/tpl>
167 // encode &#34; if it is part of the condition, e.g.
168 &lt;tpl if="name == &#38;quot;Jack&#38;quot;">Hello&lt;/tpl>
169  * </code></pre>
170  * Using the sample data above:
171  * <pre><code>
172 var tpl = new Ext.XTemplate(
173     '&lt;p>Name: {name}&lt;/p>',
174     '&lt;p>Kids: ',
175     '&lt;tpl for="kids">',
176         '&lt;tpl if="age > 1">',
177             '&lt;p>{name}&lt;/p>',
178         '&lt;/tpl>',
179     '&lt;/tpl>&lt;/p>'
180 );
181 tpl.overwrite(panel.body, data);
182  * </code></pre>
183  * </div>
184  * </li>
185  *
186  *
187  * <li><b><u>Basic math support</u></b>
188  * <div class="sub-desc">
189  * <p>The following basic math operators may be applied directly on numeric
190  * data values:</p><pre>
191  * + - * /
192  * </pre>
193  * For example:
194  * <pre><code>
195 var tpl = new Ext.XTemplate(
196     '&lt;p>Name: {name}&lt;/p>',
197     '&lt;p>Kids: ',
198     '&lt;tpl for="kids">',
199         '&lt;tpl if="age &amp;gt; 1">',  // <-- Note that the &gt; is encoded
200             '&lt;p>{#}: {name}&lt;/p>',  // <-- Auto-number each item
201             '&lt;p>In 5 Years: {age+5}&lt;/p>',  // <-- Basic math
202             '&lt;p>Dad: {parent.name}&lt;/p>',
203         '&lt;/tpl>',
204     '&lt;/tpl>&lt;/p>'
205 );
206 tpl.overwrite(panel.body, data);
207 </code></pre>
208  * </div>
209  * </li>
210  *
211  *
212  * <li><b><u>Execute arbitrary inline code with special built-in template variables</u></b>
213  * <div class="sub-desc">
214  * <p>Anything between <code>{[ ... ]}</code> is considered code to be executed
215  * in the scope of the template. There are some special variables available in that code:
216  * <ul>
217  * <li><b><tt>values</tt></b>: The values in the current scope. If you are using
218  * scope changing sub-templates, you can change what <tt>values</tt> is.</li>
219  * <li><b><tt>parent</tt></b>: The scope (values) of the ancestor template.</li>
220  * <li><b><tt>xindex</tt></b>: If you are in a looping template, the index of the
221  * loop you are in (1-based).</li>
222  * <li><b><tt>xcount</tt></b>: If you are in a looping template, the total length
223  * of the array you are looping.</li>
224  * <li><b><tt>fm</tt></b>: An alias for <tt>Ext.util.Format</tt>.</li>
225  * </ul>
226  * This example demonstrates basic row striping using an inline code block and the
227  * <tt>xindex</tt> variable:</p>
228  * <pre><code>
229 var tpl = new Ext.XTemplate(
230     '&lt;p>Name: {name}&lt;/p>',
231     '&lt;p>Company: {[values.company.toUpperCase() + ", " + values.title]}&lt;/p>',
232     '&lt;p>Kids: ',
233     '&lt;tpl for="kids">',
234        '&lt;div class="{[xindex % 2 === 0 ? "even" : "odd"]}">',
235         '{name}',
236         '&lt;/div>',
237     '&lt;/tpl>&lt;/p>'
238 );
239 tpl.overwrite(panel.body, data);
240  * </code></pre>
241  * </div>
242  * </li>
243  *
244  * <li><b><u>Template member functions</u></b>
245  * <div class="sub-desc">
246  * <p>One or more member functions can be specified in a configuration
247  * object passed into the XTemplate constructor for more complex processing:</p>
248  * <pre><code>
249 var tpl = new Ext.XTemplate(
250     '&lt;p>Name: {name}&lt;/p>',
251     '&lt;p>Kids: ',
252     '&lt;tpl for="kids">',
253         '&lt;tpl if="this.isGirl(name)">',
254             '&lt;p>Girl: {name} - {age}&lt;/p>',
255         '&lt;/tpl>',
256         // use opposite if statement to simulate 'else' processing:
257         '&lt;tpl if="this.isGirl(name) == false">',
258             '&lt;p>Boy: {name} - {age}&lt;/p>',
259         '&lt;/tpl>',
260         '&lt;tpl if="this.isBaby(age)">',
261             '&lt;p>{name} is a baby!&lt;/p>',
262         '&lt;/tpl>',
263     '&lt;/tpl>&lt;/p>',
264     {
265         // XTemplate configuration:
266         compiled: true,
267         disableFormats: true,
268         // member functions:
269         isGirl: function(name){
270             return name == 'Sara Grace';
271         },
272         isBaby: function(age){
273             return age < 1;
274         }
275     }
276 );
277 tpl.overwrite(panel.body, data);
278  * </code></pre>
279  * </div>
280  * </li>
281  *
282  * </ul></div>
283  *
284  * @param {Mixed} config
285  */
286 Ext.XTemplate = function(){
287     Ext.XTemplate.superclass.constructor.apply(this, arguments);
288
289     var me = this,
290         s = me.html,
291         re = /<tpl\b[^>]*>((?:(?=([^<]+))\2|<(?!tpl\b[^>]*>))*?)<\/tpl>/,
292         nameRe = /^<tpl\b[^>]*?for="(.*?)"/,
293         ifRe = /^<tpl\b[^>]*?if="(.*?)"/,
294         execRe = /^<tpl\b[^>]*?exec="(.*?)"/,
295         m,
296         id = 0,
297         tpls = [],
298         VALUES = 'values',
299         PARENT = 'parent',
300         XINDEX = 'xindex',
301         XCOUNT = 'xcount',
302         RETURN = 'return ',
303         WITHVALUES = 'with(values){ ';
304
305     s = ['<tpl>', s, '</tpl>'].join('');
306
307     while((m = s.match(re))){
308         var m2 = m[0].match(nameRe),
309             m3 = m[0].match(ifRe),
310             m4 = m[0].match(execRe),
311             exp = null,
312             fn = null,
313             exec = null,
314             name = m2 && m2[1] ? m2[1] : '';
315
316        if (m3) {
317            exp = m3 && m3[1] ? m3[1] : null;
318            if(exp){
319                fn = new Function(VALUES, PARENT, XINDEX, XCOUNT, WITHVALUES + RETURN +(Ext.util.Format.htmlDecode(exp))+'; }');
320            }
321        }
322        if (m4) {
323            exp = m4 && m4[1] ? m4[1] : null;
324            if(exp){
325                exec = new Function(VALUES, PARENT, XINDEX, XCOUNT, WITHVALUES +(Ext.util.Format.htmlDecode(exp))+'; }');
326            }
327        }
328        if(name){
329            switch(name){
330                case '.': name = new Function(VALUES, PARENT, WITHVALUES + RETURN + VALUES + '; }'); break;
331                case '..': name = new Function(VALUES, PARENT, WITHVALUES + RETURN + PARENT + '; }'); break;
332                default: name = new Function(VALUES, PARENT, WITHVALUES + RETURN + name + '; }');
333            }
334        }
335        tpls.push({
336             id: id,
337             target: name,
338             exec: exec,
339             test: fn,
340             body: m[1]||''
341         });
342        s = s.replace(m[0], '{xtpl'+ id + '}');
343        ++id;
344     }
345     for(var i = tpls.length-1; i >= 0; --i){
346         me.compileTpl(tpls[i]);
347     }
348     me.master = tpls[tpls.length-1];
349     me.tpls = tpls;
350 };
351 Ext.extend(Ext.XTemplate, Ext.Template, {
352     // private
353     re : /\{([\w-\.\#]+)(?:\:([\w\.]*)(?:\((.*?)?\))?)?(\s?[\+\-\*\\]\s?[\d\.\+\-\*\\\(\)]+)?\}/g,
354     // private
355     codeRe : /\{\[((?:\\\]|.|\n)*?)\]\}/g,
356
357     // private
358     applySubTemplate : function(id, values, parent, xindex, xcount){
359         var me = this,
360             len,
361             t = me.tpls[id],
362             vs,
363             buf = [];
364         if ((t.test && !t.test.call(me, values, parent, xindex, xcount)) ||
365             (t.exec && t.exec.call(me, values, parent, xindex, xcount))) {
366             return '';
367         }
368         vs = t.target ? t.target.call(me, values, parent) : values;
369         len = vs.length;
370         parent = t.target ? values : parent;
371         if(t.target && Ext.isArray(vs)){
372             for(var i = 0, len = vs.length; i < len; i++){
373                 buf[buf.length] = t.compiled.call(me, vs[i], parent, i+1, len);
374             }
375             return buf.join('');
376         }
377         return t.compiled.call(me, vs, parent, xindex, xcount);
378     },
379
380     // private
381     compileTpl : function(tpl){
382         var fm = Ext.util.Format,
383             useF = this.disableFormats !== true,
384             sep = Ext.isGecko ? "+" : ",",
385             body;
386
387         function fn(m, name, format, args, math){
388             if(name.substr(0, 4) == 'xtpl'){
389                 return "'"+ sep +'this.applySubTemplate('+name.substr(4)+', values, parent, xindex, xcount)'+sep+"'";
390             }
391             var v;
392             if(name === '.'){
393                 v = 'values';
394             }else if(name === '#'){
395                 v = 'xindex';
396             }else if(name.indexOf('.') != -1){
397                 v = name;
398             }else{
399                 v = "values['" + name + "']";
400             }
401             if(math){
402                 v = '(' + v + math + ')';
403             }
404             if (format && useF) {
405                 args = args ? ',' + args : "";
406                 if(format.substr(0, 5) != "this."){
407                     format = "fm." + format + '(';
408                 }else{
409                     format = 'this.call("'+ format.substr(5) + '", ';
410                     args = ", values";
411                 }
412             } else {
413                 args= ''; format = "("+v+" === undefined ? '' : ";
414             }
415             return "'"+ sep + format + v + args + ")"+sep+"'";
416         }
417
418         function codeFn(m, code){
419             // Single quotes get escaped when the template is compiled, however we want to undo this when running code.
420             return "'" + sep + '(' + code.replace(/\\'/g, "'") + ')' + sep + "'";
421         }
422
423         // branched to use + in gecko and [].join() in others
424         if(Ext.isGecko){
425             body = "tpl.compiled = function(values, parent, xindex, xcount){ return '" +
426                    tpl.body.replace(/(\r\n|\n)/g, '\\n').replace(/'/g, "\\'").replace(this.re, fn).replace(this.codeRe, codeFn) +
427                     "';};";
428         }else{
429             body = ["tpl.compiled = function(values, parent, xindex, xcount){ return ['"];
430             body.push(tpl.body.replace(/(\r\n|\n)/g, '\\n').replace(/'/g, "\\'").replace(this.re, fn).replace(this.codeRe, codeFn));
431             body.push("'].join('');};");
432             body = body.join('');
433         }
434         eval(body);
435         return this;
436     },
437
438     <div id="method-Ext.XTemplate-applyTemplate"></div>/**
439      * Returns an HTML fragment of this template with the specified values applied.
440      * @param {Object} values The template values. Can be an array if your params are numeric (i.e. {0}) or an object (i.e. {foo: 'bar'})
441      * @return {String} The HTML fragment
442      */
443     applyTemplate : function(values){
444         return this.master.compiled.call(this, values, {}, 1, 1);
445     },
446
447     <div id="method-Ext.XTemplate-compile"></div>/**
448      * Compile the template to a function for optimized performance.  Recommended if the template will be used frequently.
449      * @return {Function} The compiled function
450      */
451     compile : function(){return this;}
452
453     <div id="prop-Ext.XTemplate-re"></div>/**
454      * @property re
455      * @hide
456      */
457     <div id="prop-Ext.XTemplate-disableFormats"></div>/**
458      * @property disableFormats
459      * @hide
460      */
461     <div id="method-Ext.XTemplate-set"></div>/**
462      * @method set
463      * @hide
464      */
465
466 });
467 <div id="method-Ext.XTemplate-apply"></div>/**
468  * Alias for {@link #applyTemplate}
469  * Returns an HTML fragment of this template with the specified values applied.
470  * @param {Object/Array} values The template values. Can be an array if your params are numeric (i.e. {0}) or an object (i.e. {foo: 'bar'})
471  * @return {String} The HTML fragment
472  * @member Ext.XTemplate
473  * @method apply
474  */
475 Ext.XTemplate.prototype.apply = Ext.XTemplate.prototype.applyTemplate;
476
477 <div id="method-Ext.XTemplate-XTemplate.from"></div>/**
478  * Creates a template from the passed element's value (<i>display:none</i> textarea, preferred) or innerHTML.
479  * @param {String/HTMLElement} el A DOM element or its id
480  * @return {Ext.Template} The created template
481  * @static
482  */
483 Ext.XTemplate.from = function(el){
484     el = Ext.getDom(el);
485     return new Ext.XTemplate(el.value || el.innerHTML);
486 };
487 </pre>    
488 </body>
489 </html>