commit extjs-2.2.1
[extjs.git] / source / core / Template.js
1 /*\r
2  * Ext JS Library 2.2.1\r
3  * Copyright(c) 2006-2009, Ext JS, LLC.\r
4  * licensing@extjs.com\r
5  * \r
6  * http://extjs.com/license\r
7  */\r
8 \r
9 /**\r
10 * @class Ext.Template\r
11 * Represents an HTML fragment template. Templates can be precompiled for greater performance.\r
12 * For a list of available format functions, see {@link Ext.util.Format}.<br />\r
13 * Usage:\r
14 <pre><code>\r
15 var t = new Ext.Template(\r
16     '&lt;div name="{id}"&gt;',\r
17         '&lt;span class="{cls}"&gt;{name:trim} {value:ellipsis(10)}&lt;/span&gt;',\r
18     '&lt;/div&gt;'\r
19 );\r
20 t.append('some-element', {id: 'myid', cls: 'myclass', name: 'foo', value: 'bar'});\r
21 </code></pre>\r
22 * @constructor\r
23 * @param {String/Array} html The HTML fragment or an array of fragments to join("") or multiple arguments to join("")\r
24 */\r
25 Ext.Template = function(html){\r
26     var a = arguments;\r
27     if(Ext.isArray(html)){\r
28         html = html.join("");\r
29     }else if(a.length > 1){\r
30         var buf = [];\r
31         for(var i = 0, len = a.length; i < len; i++){\r
32             if(typeof a[i] == 'object'){\r
33                 Ext.apply(this, a[i]);\r
34             }else{\r
35                 buf[buf.length] = a[i];\r
36             }\r
37         }\r
38         html = buf.join('');\r
39     }\r
40     /**@private*/\r
41     this.html = html;\r
42     if(this.compiled){\r
43         this.compile();\r
44     }\r
45 };\r
46 Ext.Template.prototype = {\r
47     /**\r
48      * Returns an HTML fragment of this template with the specified values applied.\r
49      * @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'})\r
50      * @return {String} The HTML fragment\r
51      */\r
52     applyTemplate : function(values){\r
53         if(this.compiled){\r
54             return this.compiled(values);\r
55         }\r
56         var useF = this.disableFormats !== true;\r
57         var fm = Ext.util.Format, tpl = this;\r
58         var fn = function(m, name, format, args){\r
59             if(format && useF){\r
60                 if(format.substr(0, 5) == "this."){\r
61                     return tpl.call(format.substr(5), values[name], values);\r
62                 }else{\r
63                     if(args){\r
64                         // quoted values are required for strings in compiled templates,\r
65                         // but for non compiled we need to strip them\r
66                         // quoted reversed for jsmin\r
67                         var re = /^\s*['"](.*)["']\s*$/;\r
68                         args = args.split(',');\r
69                         for(var i = 0, len = args.length; i < len; i++){\r
70                             args[i] = args[i].replace(re, "$1");\r
71                         }\r
72                         args = [values[name]].concat(args);\r
73                     }else{\r
74                         args = [values[name]];\r
75                     }\r
76                     return fm[format].apply(fm, args);\r
77                 }\r
78             }else{\r
79                 return values[name] !== undefined ? values[name] : "";\r
80             }\r
81         };\r
82         return this.html.replace(this.re, fn);\r
83     },\r
84 \r
85     /**\r
86      * Sets the HTML used as the template and optionally compiles it.\r
87      * @param {String} html\r
88      * @param {Boolean} compile (optional) True to compile the template (defaults to undefined)\r
89      * @return {Ext.Template} this\r
90      */\r
91     set : function(html, compile){\r
92         this.html = html;\r
93         this.compiled = null;\r
94         if(compile){\r
95             this.compile();\r
96         }\r
97         return this;\r
98     },\r
99 \r
100     /**\r
101      * True to disable format functions (defaults to false)\r
102      * @type Boolean\r
103      */\r
104     disableFormats : false,\r
105 \r
106     /**\r
107     * The regular expression used to match template variables\r
108     * @type RegExp\r
109     * @property\r
110     */\r
111     re : /\{([\w-]+)(?:\:([\w\.]*)(?:\((.*?)?\))?)?\}/g,\r
112 \r
113     /**\r
114      * Compiles the template into an internal function, eliminating the RegEx overhead.\r
115      * @return {Ext.Template} this\r
116      */\r
117     compile : function(){\r
118         var fm = Ext.util.Format;\r
119         var useF = this.disableFormats !== true;\r
120         var sep = Ext.isGecko ? "+" : ",";\r
121         var fn = function(m, name, format, args){\r
122             if(format && useF){\r
123                 args = args ? ',' + args : "";\r
124                 if(format.substr(0, 5) != "this."){\r
125                     format = "fm." + format + '(';\r
126                 }else{\r
127                     format = 'this.call("'+ format.substr(5) + '", ';\r
128                     args = ", values";\r
129                 }\r
130             }else{\r
131                 args= ''; format = "(values['" + name + "'] == undefined ? '' : ";\r
132             }\r
133             return "'"+ sep + format + "values['" + name + "']" + args + ")"+sep+"'";\r
134         };\r
135         var body;\r
136         // branched to use + in gecko and [].join() in others\r
137         if(Ext.isGecko){\r
138             body = "this.compiled = function(values){ return '" +\r
139                    this.html.replace(/\\/g, '\\\\').replace(/(\r\n|\n)/g, '\\n').replace(/'/g, "\\'").replace(this.re, fn) +\r
140                     "';};";\r
141         }else{\r
142             body = ["this.compiled = function(values){ return ['"];\r
143             body.push(this.html.replace(/\\/g, '\\\\').replace(/(\r\n|\n)/g, '\\n').replace(/'/g, "\\'").replace(this.re, fn));\r
144             body.push("'].join('');};");\r
145             body = body.join('');\r
146         }\r
147         eval(body);\r
148         return this;\r
149     },\r
150 \r
151     // private function used to call members\r
152     call : function(fnName, value, allValues){\r
153         return this[fnName](value, allValues);\r
154     },\r
155 \r
156     /**\r
157      * Applies the supplied values to the template and inserts the new node(s) as the first child of el.\r
158      * @param {Mixed} el The context element\r
159      * @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'})\r
160      * @param {Boolean} returnElement (optional) true to return a Ext.Element (defaults to undefined)\r
161      * @return {HTMLElement/Ext.Element} The new node or Element\r
162      */\r
163     insertFirst: function(el, values, returnElement){\r
164         return this.doInsert('afterBegin', el, values, returnElement);\r
165     },\r
166 \r
167     /**\r
168      * Applies the supplied values to the template and inserts the new node(s) before el.\r
169      * @param {Mixed} el The context element\r
170      * @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'})\r
171      * @param {Boolean} returnElement (optional) true to return a Ext.Element (defaults to undefined)\r
172      * @return {HTMLElement/Ext.Element} The new node or Element\r
173      */\r
174     insertBefore: function(el, values, returnElement){\r
175         return this.doInsert('beforeBegin', el, values, returnElement);\r
176     },\r
177 \r
178     /**\r
179      * Applies the supplied values to the template and inserts the new node(s) after el.\r
180      * @param {Mixed} el The context element\r
181      * @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'})\r
182      * @param {Boolean} returnElement (optional) true to return a Ext.Element (defaults to undefined)\r
183      * @return {HTMLElement/Ext.Element} The new node or Element\r
184      */\r
185     insertAfter : function(el, values, returnElement){\r
186         return this.doInsert('afterEnd', el, values, returnElement);\r
187     },\r
188 \r
189     /**\r
190      * Applies the supplied values to the template and appends the new node(s) to el.\r
191      * @param {Mixed} el The context element\r
192      * @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'})\r
193      * @param {Boolean} returnElement (optional) true to return a Ext.Element (defaults to undefined)\r
194      * @return {HTMLElement/Ext.Element} The new node or Element\r
195      */\r
196     append : function(el, values, returnElement){\r
197         return this.doInsert('beforeEnd', el, values, returnElement);\r
198     },\r
199 \r
200     doInsert : function(where, el, values, returnEl){\r
201         el = Ext.getDom(el);\r
202         var newNode = Ext.DomHelper.insertHtml(where, el, this.applyTemplate(values));\r
203         return returnEl ? Ext.get(newNode, true) : newNode;\r
204     },\r
205 \r
206     /**\r
207      * Applies the supplied values to the template and overwrites the content of el with the new node(s).\r
208      * @param {Mixed} el The context element\r
209      * @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'})\r
210      * @param {Boolean} returnElement (optional) true to return a Ext.Element (defaults to undefined)\r
211      * @return {HTMLElement/Ext.Element} The new node or Element\r
212      */\r
213     overwrite : function(el, values, returnElement){\r
214         el = Ext.getDom(el);\r
215         el.innerHTML = this.applyTemplate(values);\r
216         return returnElement ? Ext.get(el.firstChild, true) : el.firstChild;\r
217     }\r
218 };\r
219 /**\r
220  * Alias for {@link #applyTemplate}\r
221  * Returns an HTML fragment of this template with the specified values applied.\r
222  * @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'})\r
223  * @return {String} The HTML fragment\r
224  * @member Ext.Template\r
225  * @method apply\r
226  */\r
227 Ext.Template.prototype.apply = Ext.Template.prototype.applyTemplate;\r
228 \r
229 // backwards compat\r
230 Ext.DomHelper.Template = Ext.Template;\r
231 \r
232 /**\r
233  * Creates a template from the passed element's value (<i>display:none</i> textarea, preferred) or innerHTML.\r
234  * @param {String/HTMLElement} el A DOM element or its id\r
235  * @param {Object} config A configuration object\r
236  * @return {Ext.Template} The created template\r
237  * @static\r
238  */\r
239 Ext.Template.from = function(el, config){\r
240     el = Ext.getDom(el);\r
241     return new Ext.Template(el.value || el.innerHTML, config || '');\r
242 };