27 |
jpm |
1 |
/*
|
|
|
2 |
* Ext JS Library 2.0.2
|
|
|
3 |
* Copyright(c) 2006-2008, Ext JS, LLC.
|
|
|
4 |
* licensing@extjs.com
|
|
|
5 |
*
|
|
|
6 |
* http://extjs.com/license
|
|
|
7 |
*/
|
|
|
8 |
|
|
|
9 |
/**
|
|
|
10 |
* @class Ext.XTemplate
|
|
|
11 |
* @extends Ext.Template
|
|
|
12 |
* <p>A template class that supports advanced functionality like autofilling arrays, conditional processing with
|
|
|
13 |
* basic comparison operators, sub-templates, basic math function support, special built-in template variables,
|
|
|
14 |
* inline code execution and more. XTemplate also provides the templating mechanism built into {@link Ext.DataView}.</p>
|
|
|
15 |
* <p>XTemplate supports many special tags and built-in operators that aren't defined as part of the API, but are
|
|
|
16 |
* supported in the templates that can be created. The following examples demonstrate all of the supported features.
|
|
|
17 |
* This is the data object used for reference in each code example:</p>
|
|
|
18 |
* <pre><code>
|
|
|
19 |
var data = {
|
|
|
20 |
name: 'Jack Slocum',
|
|
|
21 |
title: 'Lead Developer',
|
|
|
22 |
company: 'Ext JS, LLC',
|
|
|
23 |
email: 'jack@extjs.com',
|
|
|
24 |
address: '4 Red Bulls Drive',
|
|
|
25 |
city: 'Cleveland',
|
|
|
26 |
state: 'Ohio',
|
|
|
27 |
zip: '44102',
|
|
|
28 |
drinks: ['Red Bull', 'Coffee', 'Water'],
|
|
|
29 |
kids: [{
|
|
|
30 |
name: 'Sara Grace',
|
|
|
31 |
age:3
|
|
|
32 |
},{
|
|
|
33 |
name: 'Zachary',
|
|
|
34 |
age:2
|
|
|
35 |
},{
|
|
|
36 |
name: 'John James',
|
|
|
37 |
age:0
|
|
|
38 |
}]
|
|
|
39 |
};
|
|
|
40 |
</code></pre>
|
|
|
41 |
* <p><b>Auto filling of arrays and scope switching</b><br/>Using the <tt>tpl</tt> tag and the <tt>for</tt> operator,
|
|
|
42 |
* you can switch to the scope of the object specified by <tt>for</tt> and access its members to populate the teamplte.
|
|
|
43 |
* If the variable in <tt>for</tt> is an array, it will auto-fill, repeating the template block inside the <tt>tpl</tt>
|
|
|
44 |
* tag for each item in the array:</p>
|
|
|
45 |
* <pre><code>
|
|
|
46 |
var tpl = new Ext.XTemplate(
|
|
|
47 |
'<p>Name: {name}</p>',
|
|
|
48 |
'<p>Title: {title}</p>',
|
|
|
49 |
'<p>Company: {company}</p>',
|
|
|
50 |
'<p>Kids: ',
|
|
|
51 |
'<tpl for="kids">',
|
|
|
52 |
'<p>{name}</p>',
|
|
|
53 |
'</tpl></p>'
|
|
|
54 |
);
|
|
|
55 |
tpl.overwrite(panel.body, data);
|
|
|
56 |
</code></pre>
|
|
|
57 |
* <p><b>Access to parent object from within sub-template scope</b><br/>When processing a sub-template, for example while
|
|
|
58 |
* looping through a child array, you can access the parent object's members via the <tt>parent</tt> object:</p>
|
|
|
59 |
* <pre><code>
|
|
|
60 |
var tpl = new Ext.XTemplate(
|
|
|
61 |
'<p>Name: {name}</p>',
|
|
|
62 |
'<p>Kids: ',
|
|
|
63 |
'<tpl for="kids">',
|
|
|
64 |
'<tpl if="age > 1">',
|
|
|
65 |
'<p>{name}</p>',
|
|
|
66 |
'<p>Dad: {parent.name}</p>',
|
|
|
67 |
'</tpl>',
|
|
|
68 |
'</tpl></p>'
|
|
|
69 |
);
|
|
|
70 |
tpl.overwrite(panel.body, data);
|
|
|
71 |
</code></pre>
|
|
|
72 |
* <p><b>Array item index and basic math support</b> <br/>While processing an array, the special variable <tt>{#}</tt>
|
|
|
73 |
* will provide the current array index + 1 (starts at 1, not 0). Templates also support the basic math operators
|
|
|
74 |
* + - * and / that can be applied directly on numeric data values:</p>
|
|
|
75 |
* <pre><code>
|
|
|
76 |
var tpl = new Ext.XTemplate(
|
|
|
77 |
'<p>Name: {name}</p>',
|
|
|
78 |
'<p>Kids: ',
|
|
|
79 |
'<tpl for="kids">',
|
|
|
80 |
'<tpl if="age > 1">',
|
|
|
81 |
'<p>{#}: {name}</p>', // <-- Auto-number each item
|
|
|
82 |
'<p>In 5 Years: {age+5}</p>', // <-- Basic math
|
|
|
83 |
'<p>Dad: {parent.name}</p>',
|
|
|
84 |
'</tpl>',
|
|
|
85 |
'</tpl></p>'
|
|
|
86 |
);
|
|
|
87 |
tpl.overwrite(panel.body, data);
|
|
|
88 |
</code></pre>
|
|
|
89 |
* <p><b>Auto-rendering of flat arrays</b> <br/>Flat arrays that contain values (and not objects) can be auto-rendered
|
|
|
90 |
* using the special <tt>{.}</tt> variable inside a loop. This variable will represent the value of
|
|
|
91 |
* the array at the current index:</p>
|
|
|
92 |
* <pre><code>
|
|
|
93 |
var tpl = new Ext.XTemplate(
|
|
|
94 |
'<p>{name}\'s favorite beverages:</p>',
|
|
|
95 |
'<tpl for="drinks">',
|
|
|
96 |
'<div> - {.}</div>',
|
|
|
97 |
'</tpl>'
|
|
|
98 |
);
|
|
|
99 |
tpl.overwrite(panel.body, data);
|
|
|
100 |
</code></pre>
|
|
|
101 |
* <p><b>Basic conditional logic</b> <br/>Using the <tt>tpl</tt> tag and the <tt>if</tt>
|
|
|
102 |
* operator you can provide conditional checks for deciding whether or not to render specific parts of the template.
|
|
|
103 |
* Note that there is no <tt>else</tt> operator — if needed, you should use two opposite <tt>if</tt> statements.
|
|
|
104 |
* Properly-encoded attributes are required as seen in the following example:</p>
|
|
|
105 |
* <pre><code>
|
|
|
106 |
var tpl = new Ext.XTemplate(
|
|
|
107 |
'<p>Name: {name}</p>',
|
|
|
108 |
'<p>Kids: ',
|
|
|
109 |
'<tpl for="kids">',
|
|
|
110 |
'<tpl if="age &gt; 1">', // <-- Note that the > is encoded
|
|
|
111 |
'<p>{name}</p>',
|
|
|
112 |
'</tpl>',
|
|
|
113 |
'</tpl></p>'
|
|
|
114 |
);
|
|
|
115 |
tpl.overwrite(panel.body, data);
|
|
|
116 |
</code></pre>
|
|
|
117 |
* <p><b>Ability to execute arbitrary inline code</b> <br/>In an XTemplate, anything between {[ ... ]} is considered
|
|
|
118 |
* code to be executed in the scope of the template. There are some special variables available in that code:
|
|
|
119 |
* <ul>
|
|
|
120 |
* <li><b><tt>values</tt></b>: The values in the current scope. If you are using scope changing sub-templates, you
|
|
|
121 |
* can change what <tt>values</tt> is.</li>
|
|
|
122 |
* <li><b><tt>parent</tt></b>: The scope (values) of the ancestor template.</li>
|
|
|
123 |
* <li><b><tt>xindex</tt></b>: If you are in a looping template, the index of the loop you are in (1-based).</li>
|
|
|
124 |
* <li><b><tt>xcount</tt></b>: If you are in a looping template, the total length of the array you are looping.</li>
|
|
|
125 |
* <li><b><tt>fm</tt></b>: An alias for <tt>Ext.util.Format</tt>.</li>
|
|
|
126 |
* </ul>
|
|
|
127 |
* This example demonstrates basic row striping using an inline code block and the <tt>xindex</tt> variable:</p>
|
|
|
128 |
* <pre><code>
|
|
|
129 |
var tpl = new Ext.XTemplate(
|
|
|
130 |
'<p>Name: {name}</p>',
|
|
|
131 |
'<p>Company: {[company.toUpperCase() + ', ' + title]}</p>',
|
|
|
132 |
'<p>Kids: ',
|
|
|
133 |
'<tpl for="kids">',
|
|
|
134 |
'<div class="{[xindex % 2 === 0 ? "even" : "odd"]}">,
|
|
|
135 |
'{name}',
|
|
|
136 |
'</div>',
|
|
|
137 |
'</tpl></p>'
|
|
|
138 |
);
|
|
|
139 |
tpl.overwrite(panel.body, data);
|
|
|
140 |
</code></pre>
|
|
|
141 |
* <p><b>Template member functions</b> <br/>One or more member functions can be defined directly on the config
|
|
|
142 |
* object passed into the XTemplate constructor for more complex processing:</p>
|
|
|
143 |
* <pre><code>
|
|
|
144 |
var tpl = new Ext.XTemplate(
|
|
|
145 |
'<p>Name: {name}</p>',
|
|
|
146 |
'<p>Kids: ',
|
|
|
147 |
'<tpl for="kids">',
|
|
|
148 |
'<tpl if="this.isGirl(name)">',
|
|
|
149 |
'<p>Girl: {name} - {age}</p>',
|
|
|
150 |
'</tpl>',
|
|
|
151 |
'<tpl if="this.isGirl(name) == false">',
|
|
|
152 |
'<p>Boy: {name} - {age}</p>',
|
|
|
153 |
'</tpl>',
|
|
|
154 |
'<tpl if="this.isBaby(age)">',
|
|
|
155 |
'<p>{name} is a baby!</p>',
|
|
|
156 |
'</tpl>',
|
|
|
157 |
'</tpl></p>', {
|
|
|
158 |
isGirl: function(name){
|
|
|
159 |
return name == 'Sara Grace';
|
|
|
160 |
},
|
|
|
161 |
isBaby: function(age){
|
|
|
162 |
return age < 1;
|
|
|
163 |
}
|
|
|
164 |
});
|
|
|
165 |
tpl.overwrite(panel.body, data);
|
|
|
166 |
</code></pre>
|
|
|
167 |
* @constructor
|
|
|
168 |
* @param {String/Array/Object} parts The HTML fragment or an array of fragments to join(""), or multiple arguments
|
|
|
169 |
* to join("") that can also include a config object
|
|
|
170 |
*/
|
|
|
171 |
Ext.XTemplate = function(){
|
|
|
172 |
Ext.XTemplate.superclass.constructor.apply(this, arguments);
|
|
|
173 |
var s = this.html;
|
|
|
174 |
|
|
|
175 |
s = ['<tpl>', s, '</tpl>'].join('');
|
|
|
176 |
|
|
|
177 |
var re = /<tpl\b[^>]*>((?:(?=([^<]+))\2|<(?!tpl\b[^>]*>))*?)<\/tpl>/;
|
|
|
178 |
|
|
|
179 |
var nameRe = /^<tpl\b[^>]*?for="(.*?)"/;
|
|
|
180 |
var ifRe = /^<tpl\b[^>]*?if="(.*?)"/;
|
|
|
181 |
var execRe = /^<tpl\b[^>]*?exec="(.*?)"/;
|
|
|
182 |
var m, id = 0;
|
|
|
183 |
var tpls = [];
|
|
|
184 |
|
|
|
185 |
while(m = s.match(re)){
|
|
|
186 |
var m2 = m[0].match(nameRe);
|
|
|
187 |
var m3 = m[0].match(ifRe);
|
|
|
188 |
var m4 = m[0].match(execRe);
|
|
|
189 |
var exp = null, fn = null, exec = null;
|
|
|
190 |
var name = m2 && m2[1] ? m2[1] : '';
|
|
|
191 |
if(m3){
|
|
|
192 |
exp = m3 && m3[1] ? m3[1] : null;
|
|
|
193 |
if(exp){
|
|
|
194 |
fn = new Function('values', 'parent', 'xindex', 'xcount', 'with(values){ return '+(Ext.util.Format.htmlDecode(exp))+'; }');
|
|
|
195 |
}
|
|
|
196 |
}
|
|
|
197 |
if(m4){
|
|
|
198 |
exp = m4 && m4[1] ? m4[1] : null;
|
|
|
199 |
if(exp){
|
|
|
200 |
exec = new Function('values', 'parent', 'xindex', 'xcount', 'with(values){ '+(Ext.util.Format.htmlDecode(exp))+'; }');
|
|
|
201 |
}
|
|
|
202 |
}
|
|
|
203 |
if(name){
|
|
|
204 |
switch(name){
|
|
|
205 |
case '.': name = new Function('values', 'parent', 'with(values){ return values; }'); break;
|
|
|
206 |
case '..': name = new Function('values', 'parent', 'with(values){ return parent; }'); break;
|
|
|
207 |
default: name = new Function('values', 'parent', 'with(values){ return '+name+'; }');
|
|
|
208 |
}
|
|
|
209 |
}
|
|
|
210 |
tpls.push({
|
|
|
211 |
id: id,
|
|
|
212 |
target: name,
|
|
|
213 |
exec: exec,
|
|
|
214 |
test: fn,
|
|
|
215 |
body: m[1]||''
|
|
|
216 |
});
|
|
|
217 |
s = s.replace(m[0], '{xtpl'+ id + '}');
|
|
|
218 |
++id;
|
|
|
219 |
}
|
|
|
220 |
for(var i = tpls.length-1; i >= 0; --i){
|
|
|
221 |
this.compileTpl(tpls[i]);
|
|
|
222 |
}
|
|
|
223 |
this.master = tpls[tpls.length-1];
|
|
|
224 |
this.tpls = tpls;
|
|
|
225 |
};
|
|
|
226 |
Ext.extend(Ext.XTemplate, Ext.Template, {
|
|
|
227 |
// private
|
|
|
228 |
re : /\{([\w-\.\#]+)(?:\:([\w\.]*)(?:\((.*?)?\))?)?(\s?[\+\-\*\\]\s?[\d\.\+\-\*\\\(\)]+)?\}/g,
|
|
|
229 |
// private
|
|
|
230 |
codeRe : /\{\[((?:\\\]|.|\n)*?)\]\}/g,
|
|
|
231 |
|
|
|
232 |
// private
|
|
|
233 |
applySubTemplate : function(id, values, parent, xindex, xcount){
|
|
|
234 |
var t = this.tpls[id];
|
|
|
235 |
if(t.test && !t.test.call(this, values, parent, xindex, xcount)){
|
|
|
236 |
return '';
|
|
|
237 |
}
|
|
|
238 |
if(t.exec && t.exec.call(this, values, parent, xindex, xcount)){
|
|
|
239 |
return '';
|
|
|
240 |
}
|
|
|
241 |
var vs = t.target ? t.target.call(this, values, parent) : values;
|
|
|
242 |
parent = t.target ? values : parent;
|
|
|
243 |
if(t.target && Ext.isArray(vs)){
|
|
|
244 |
var buf = [];
|
|
|
245 |
for(var i = 0, len = vs.length; i < len; i++){
|
|
|
246 |
buf[buf.length] = t.compiled.call(this, vs[i], parent, i+1, len);
|
|
|
247 |
}
|
|
|
248 |
return buf.join('');
|
|
|
249 |
}
|
|
|
250 |
return t.compiled.call(this, vs, parent, xindex, xcount);
|
|
|
251 |
},
|
|
|
252 |
|
|
|
253 |
// private
|
|
|
254 |
compileTpl : function(tpl){
|
|
|
255 |
var fm = Ext.util.Format;
|
|
|
256 |
var useF = this.disableFormats !== true;
|
|
|
257 |
var sep = Ext.isGecko ? "+" : ",";
|
|
|
258 |
var fn = function(m, name, format, args, math){
|
|
|
259 |
if(name.substr(0, 4) == 'xtpl'){
|
|
|
260 |
return "'"+ sep +'this.applySubTemplate('+name.substr(4)+', values, parent, xindex, xcount)'+sep+"'";
|
|
|
261 |
}
|
|
|
262 |
var v;
|
|
|
263 |
if(name === '.'){
|
|
|
264 |
v = 'values';
|
|
|
265 |
}else if(name === '#'){
|
|
|
266 |
v = 'xindex';
|
|
|
267 |
}else if(name.indexOf('.') != -1){
|
|
|
268 |
v = name;
|
|
|
269 |
}else{
|
|
|
270 |
v = "values['" + name + "']";
|
|
|
271 |
}
|
|
|
272 |
if(math){
|
|
|
273 |
v = '(' + v + math + ')';
|
|
|
274 |
}
|
|
|
275 |
if(format && useF){
|
|
|
276 |
args = args ? ',' + args : "";
|
|
|
277 |
if(format.substr(0, 5) != "this."){
|
|
|
278 |
format = "fm." + format + '(';
|
|
|
279 |
}else{
|
|
|
280 |
format = 'this.call("'+ format.substr(5) + '", ';
|
|
|
281 |
args = ", values";
|
|
|
282 |
}
|
|
|
283 |
}else{
|
|
|
284 |
args= ''; format = "("+v+" === undefined ? '' : ";
|
|
|
285 |
}
|
|
|
286 |
return "'"+ sep + format + v + args + ")"+sep+"'";
|
|
|
287 |
};
|
|
|
288 |
var codeFn = function(m, code){
|
|
|
289 |
return "'"+ sep +'('+code+')'+sep+"'";
|
|
|
290 |
};
|
|
|
291 |
|
|
|
292 |
var body;
|
|
|
293 |
// branched to use + in gecko and [].join() in others
|
|
|
294 |
if(Ext.isGecko){
|
|
|
295 |
body = "tpl.compiled = function(values, parent, xindex, xcount){ return '" +
|
|
|
296 |
tpl.body.replace(/(\r\n|\n)/g, '\\n').replace(/'/g, "\\'").replace(this.re, fn).replace(this.codeRe, codeFn) +
|
|
|
297 |
"';};";
|
|
|
298 |
}else{
|
|
|
299 |
body = ["tpl.compiled = function(values, parent, xindex, xcount){ return ['"];
|
|
|
300 |
body.push(tpl.body.replace(/(\r\n|\n)/g, '\\n').replace(/'/g, "\\'").replace(this.re, fn).replace(this.codeRe, codeFn));
|
|
|
301 |
body.push("'].join('');};");
|
|
|
302 |
body = body.join('');
|
|
|
303 |
}
|
|
|
304 |
eval(body);
|
|
|
305 |
return this;
|
|
|
306 |
},
|
|
|
307 |
|
|
|
308 |
/**
|
|
|
309 |
* Alias of {@link #applyTemplate}.
|
|
|
310 |
*/
|
|
|
311 |
apply : function(values){
|
|
|
312 |
return this.master.compiled.call(this, values, {}, 1, 1);
|
|
|
313 |
},
|
|
|
314 |
|
|
|
315 |
/**
|
|
|
316 |
* Returns an HTML fragment of this template with the specified values applied.
|
|
|
317 |
* @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'})
|
|
|
318 |
* @return {String} The HTML fragment
|
|
|
319 |
*/
|
|
|
320 |
applyTemplate : function(values){
|
|
|
321 |
return this.master.compiled.call(this, values, {}, 1, 1);
|
|
|
322 |
},
|
|
|
323 |
|
|
|
324 |
/**
|
|
|
325 |
* Compile the template to a function for optimized performance. Recommended if the template will be used frequently.
|
|
|
326 |
* @return {Function} The compiled function
|
|
|
327 |
*/
|
|
|
328 |
compile : function(){return this;}
|
|
|
329 |
|
|
|
330 |
/**
|
|
|
331 |
* @property re
|
|
|
332 |
* @hide
|
|
|
333 |
*/
|
|
|
334 |
/**
|
|
|
335 |
* @property disableFormats
|
|
|
336 |
* @hide
|
|
|
337 |
*/
|
|
|
338 |
/**
|
|
|
339 |
* @method set
|
|
|
340 |
* @hide
|
|
|
341 |
*/
|
|
|
342 |
|
|
|
343 |
});
|
|
|
344 |
|
|
|
345 |
/**
|
|
|
346 |
* Creates a template from the passed element's value (<i>display:none</i> textarea, preferred) or innerHTML.
|
|
|
347 |
* @param {String/HTMLElement} el A DOM element or its id
|
|
|
348 |
* @return {Ext.Template} The created template
|
|
|
349 |
* @static
|
|
|
350 |
*/
|
|
|
351 |
Ext.XTemplate.from = function(el){
|
|
|
352 |
el = Ext.getDom(el);
|
|
|
353 |
return new Ext.XTemplate(el.value || el.innerHTML);
|
|
|
354 |
};
|