4 <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
5 <title>The source code</title>
6 <link href="../resources/prettify/prettify.css" type="text/css" rel="stylesheet" />
7 <script type="text/javascript" src="../resources/prettify/prettify.js"></script>
8 <style type="text/css">
9 .highlight { display: block; background-color: #ddd; }
11 <script type="text/javascript">
12 function highlight() {
13 document.getElementById(location.hash.replace(/#/, "")).className = "highlight";
17 <body onload="prettyPrint(); highlight();">
18 <pre class="prettyprint lang-js"><span id='Ext-XTemplate'>/**
19 </span> * A template class that supports advanced functionality like:
21 * - Autofilling arrays using templates and sub-templates
22 * - Conditional processing with basic comparison operators
23 * - Basic math function support
24 * - Execute arbitrary inline code with special built-in template variables
25 * - Custom member functions
26 * - Many special tags and built-in operators that aren't defined as part of the API, but are supported in the templates that can be created
28 * XTemplate provides the templating mechanism built into:
30 * - {@link Ext.view.View}
32 * The {@link Ext.Template} describes the acceptable parameters to pass to the constructor. The following examples
33 * demonstrate all of the supported features.
37 * This is the data object used for reference in each code example:
40 * name: 'Tommy Maintz',
41 * title: 'Lead Developer',
42 * company: 'Sencha Inc.',
43 * email: 'tommy@sencha.com',
44 * address: '5 Cups Drive',
48 * drinks: ['Coffee', 'Soda', 'Water'],
65 * # Auto filling of arrays
67 * The **tpl** tag and the **for** operator are used to process the provided data object:
69 * - If the value specified in for is an array, it will auto-fill, repeating the template block inside the tpl
70 * tag for each item in the array.
71 * - If for="." is specified, the data object provided is examined.
72 * - While processing an array, the special variable {#} will provide the current array index + 1 (starts at 1, not 0).
76 * <tpl for=".">...</tpl> // loop through array at root node
77 * <tpl for="foo">...</tpl> // loop through array at foo node
78 * <tpl for="foo.bar">...</tpl> // loop through array at foo.bar node
80 * Using the sample data above:
82 * var tpl = new Ext.XTemplate(
84 * '<tpl for=".">', // process the data.kids node
85 * '<p>{#}. {name}</p>', // use current array index to autonumber
86 * '</tpl></p>'
88 * tpl.overwrite(panel.body, data.kids); // pass the kids property of the data object
90 * An example illustrating how the **for** property can be leveraged to access specified members of the provided data
91 * object to populate the template:
93 * var tpl = new Ext.XTemplate(
94 * '<p>Name: {name}</p>',
95 * '<p>Title: {title}</p>',
96 * '<p>Company: {company}</p>',
98 * '<tpl for="kids">', // interrogate the kids property within the data
99 * '<p>{name}</p>',
100 * '</tpl></p>'
102 * tpl.overwrite(panel.body, data); // pass the root node of the data object
104 * Flat arrays that contain values (and not objects) can be auto-rendered using the special **`{.}`** variable inside a
105 * loop. This variable will represent the value of the array at the current index:
107 * var tpl = new Ext.XTemplate(
108 * '<p>{name}\'s favorite beverages:</p>',
109 * '<tpl for="drinks">',
110 * '<div> - {.}</div>',
113 * tpl.overwrite(panel.body, data);
115 * When processing a sub-template, for example while looping through a child array, you can access the parent object's
116 * members via the **parent** object:
118 * var tpl = new Ext.XTemplate(
119 * '<p>Name: {name}</p>',
121 * '<tpl for="kids">',
122 * '<tpl if="age &gt; 1">',
123 * '<p>{name}</p>',
124 * '<p>Dad: {parent.name}</p>',
126 * '</tpl></p>'
128 * tpl.overwrite(panel.body, data);
130 * # Conditional processing with basic comparison operators
132 * The **tpl** tag and the **if** operator are used to provide conditional checks for deciding whether or not to render
133 * specific parts of the template. Notes:
135 * - Double quotes must be encoded if used within the conditional
136 * - There is no else operator -- if needed, two opposite if statements should be used.
140 * <tpl if="age > 1 && age < 10">Child</tpl>
141 * <tpl if="age >= 10 && age < 18">Teenager</tpl>
142 * <tpl if="this.isGirl(name)">...</tpl>
143 * <tpl if="id==\'download\'">...</tpl>
144 * <tpl if="needsIcon"><img src="{icon}" class="{iconCls}"/></tpl>
146 * <tpl if="name == "Tommy"">Hello</tpl>
147 * // encode " if it is part of the condition, e.g.
148 * <tpl if="name == &quot;Tommy&quot;">Hello</tpl>
150 * Using the sample data above:
152 * var tpl = new Ext.XTemplate(
153 * '<p>Name: {name}</p>',
155 * '<tpl for="kids">',
156 * '<tpl if="age &gt; 1">',
157 * '<p>{name}</p>',
159 * '</tpl></p>'
161 * tpl.overwrite(panel.body, data);
163 * # Basic math support
165 * The following basic math operators may be applied directly on numeric data values:
171 * var tpl = new Ext.XTemplate(
172 * '<p>Name: {name}</p>',
174 * '<tpl for="kids">',
175 * '<tpl if="age &gt; 1">', // <-- Note that the > is encoded
176 * '<p>{#}: {name}</p>', // <-- Auto-number each item
177 * '<p>In 5 Years: {age+5}</p>', // <-- Basic math
178 * '<p>Dad: {parent.name}</p>',
180 * '</tpl></p>'
182 * tpl.overwrite(panel.body, data);
184 * # Execute arbitrary inline code with special built-in template variables
186 * Anything between `{[ ... ]}` is considered code to be executed in the scope of the template. There are some special
187 * variables available in that code:
189 * - **values**: The values in the current scope. If you are using scope changing sub-templates,
190 * you can change what values is.
191 * - **parent**: The scope (values) of the ancestor template.
192 * - **xindex**: If you are in a looping template, the index of the loop you are in (1-based).
193 * - **xcount**: If you are in a looping template, the total length of the array you are looping.
195 * This example demonstrates basic row striping using an inline code block and the xindex variable:
197 * var tpl = new Ext.XTemplate(
198 * '<p>Name: {name}</p>',
199 * '<p>Company: {[values.company.toUpperCase() + ", " + values.title]}</p>',
201 * '<tpl for="kids">',
202 * '<div class="{[xindex % 2 === 0 ? "even" : "odd"]}">',
205 * '</tpl></p>'
207 * tpl.overwrite(panel.body, data);
209 * # Template member functions
211 * One or more member functions can be specified in a configuration object passed into the XTemplate constructor for
212 * more complex processing:
214 * var tpl = new Ext.XTemplate(
215 * '<p>Name: {name}</p>',
217 * '<tpl for="kids">',
218 * '<tpl if="this.isGirl(name)">',
219 * '<p>Girl: {name} - {age}</p>',
221 * // use opposite if statement to simulate 'else' processing:
222 * '<tpl if="this.isGirl(name) == false">',
223 * '<p>Boy: {name} - {age}</p>',
225 * '<tpl if="this.isBaby(age)">',
226 * '<p>{name} is a baby!</p>',
228 * '</tpl></p>',
230 * // XTemplate configuration:
231 * disableFormats: true,
232 * // member functions:
233 * isGirl: function(name){
234 * return name == 'Sara Grace';
236 * isBaby: function(age){
241 * tpl.overwrite(panel.body, data);
243 Ext.define('Ext.XTemplate', {
245 /* Begin Definitions */
247 extend: 'Ext.Template',
249 /* End Definitions */
251 argsRe: /<tpl\b[^>]*>((?:(?=([^<]+))\2|<(?!tpl\b[^>]*>))*?)<\/tpl>/,
252 nameRe: /^<tpl\b[^>]*?for="(.*?)"/,
253 ifRe: /^<tpl\b[^>]*?if="(.*?)"/,
254 execRe: /^<tpl\b[^>]*?exec="(.*?)"/,
255 constructor: function() {
256 this.callParent(arguments);
271 WITHVALUES = 'with(values){ ',
272 m, matchName, matchIf, matchExec, exp, fn, exec, name, i;
274 html = ['<tpl>', html, '</tpl>'].join('');
276 while ((m = html.match(argsRe))) {
280 matchName = m[0].match(nameRe);
281 matchIf = m[0].match(ifRe);
282 matchExec = m[0].match(execRe);
284 exp = matchIf ? matchIf[1] : null;
286 fn = Ext.functionFactory(VALUES, PARENT, XINDEX, XCOUNT, WITHVALUES + 'try{' + RETURN + Ext.String.htmlDecode(exp) + ';}catch(e){return;}}');
289 exp = matchExec ? matchExec[1] : null;
291 exec = Ext.functionFactory(VALUES, PARENT, XINDEX, XCOUNT, WITHVALUES + Ext.String.htmlDecode(exp) + ';}');
294 name = matchName ? matchName[1] : null;
298 } else if (name === '..') {
301 name = Ext.functionFactory(VALUES, PARENT, 'try{' + WITHVALUES + RETURN + name + ';}}catch(e){return;}');
312 html = html.replace(m[0], '{xtpl' + id + '}');
316 for (i = tpls.length - 1; i >= 0; --i) {
317 me.compileTpl(tpls[i]);
319 me.master = tpls[tpls.length - 1];
324 applySubTemplate: function(id, values, parent, xindex, xcount) {
325 var me = this, t = me.tpls[id];
326 return t.compiled.call(me, values, parent, xindex, xcount);
329 <span id='Ext-XTemplate-cfg-codeRe'> /**
330 </span> * @cfg {RegExp} codeRe
331 * The regular expression used to match code variables. Default: matches {[expression]}.
333 codeRe: /\{\[((?:\\\]|.|\n)*?)\]\}/g,
335 <span id='Ext-XTemplate-cfg-compiled'> /**
336 </span> * @cfg {Boolean} compiled
337 * Only applies to {@link Ext.Template}, XTemplates are compiled automatically.
340 re: /\{([\w-\.\#]+)(?:\:([\w\.]*)(?:\((.*?)?\))?)?(\s?[\+\-\*\/]\s?[\d\.\+\-\*\/\(\)]+)?\}/g,
343 compileTpl: function(tpl) {
344 var fm = Ext.util.Format,
346 useFormat = me.disableFormats !== true,
347 body, bodyReturn, evaluatedFn;
349 function fn(m, name, format, args, math) {
351 // name is what is inside the {}
352 // Name begins with xtpl, use a Sub Template
353 if (name.substr(0, 4) == 'xtpl') {
354 return "',this.applySubTemplate(" + name.substr(4) + ", values, parent, xindex, xcount),'";
356 // name = "." - Just use the values object.
358 // filter to not include arrays/objects/nulls
359 v = 'Ext.Array.indexOf(["string", "number", "boolean"], typeof values) > -1 || Ext.isDate(values) ? values : ""';
362 // name = "#" - Use the xindex
363 else if (name == '#') {
366 else if (name.substr(0, 7) == "parent.") {
369 // name has a . in it - Use object literal notation, starting from values
370 else if (name.indexOf('.') != -1) {
371 v = "values." + name;
374 // name is a property of values
376 v = "values['" + name + "']";
379 v = '(' + v + math + ')';
381 if (format && useFormat) {
382 args = args ? ',' + args : "";
383 if (format.substr(0, 5) != "this.") {
384 format = "fm." + format + '(';
387 format = 'this.' + format.substr(5) + '(';
392 format = "(" + v + " === undefined ? '' : ";
394 return "'," + format + v + args + "),'";
397 function codeFn(m, code) {
398 // Single quotes get escaped when the template is compiled, however we want to undo this when running code.
399 return "',(" + code.replace(me.compileARe, "'") + "),'";
402 bodyReturn = tpl.body.replace(me.compileBRe, '\\n').replace(me.compileCRe, "\\'").replace(me.re, fn).replace(me.codeRe, codeFn);
403 body = "evaluatedFn = function(values, parent, xindex, xcount){return ['" + bodyReturn + "'].join('');};";
406 tpl.compiled = function(values, parent, xindex, xcount) {
412 if (tpl.test && !tpl.test.call(me, values, parent, xindex, xcount)) {
416 vs = tpl.target ? tpl.target.call(me, values, parent) : values;
421 parent = tpl.target ? values : parent;
422 if (tpl.target && Ext.isArray(vs)) {
426 for (i = 0; i < length; i++) {
427 buffer[buffer.length] = evaluatedFn.call(me, vs[i], parent, i + 1, length);
428 tpl.exec.call(me, vs[i], parent, i + 1, length);
431 for (i = 0; i < length; i++) {
432 buffer[buffer.length] = evaluatedFn.call(me, vs[i], parent, i + 1, length);
435 return buffer.join('');
439 tpl.exec.call(me, vs, parent, xindex, xcount);
441 return evaluatedFn.call(me, vs, parent, xindex, xcount);
447 // inherit docs from Ext.Template
448 applyTemplate: function(values) {
449 return this.master.compiled.call(this, values, {}, 1, 1);
452 <span id='Ext-XTemplate-method-compile'> /**
453 </span> * Does nothing. XTemplates are compiled automatically, so this function simply returns this.
454 * @return {Ext.XTemplate} this
456 compile: function() {
460 // re-create the alias, inheriting it from Ext.Template doesn't work as intended.
461 this.createAlias('apply', 'applyTemplate');