Upgrade to ExtJS 4.0.0 - Released 04/26/2011
[extjs.git] / src / Template.js
1 /**
2  * @class Ext.Template
3  * <p>Represents an HTML fragment template. Templates may be {@link #compile precompiled}
4  * for greater performance.</p>
5  * An instance of this class may be created by passing to the constructor either
6  * a single argument, or multiple arguments:
7  * <div class="mdetail-params"><ul>
8  * <li><b>single argument</b> : String/Array
9  * <div class="sub-desc">
10  * The single argument may be either a String or an Array:<ul>
11  * <li><tt>String</tt> : </li><pre><code>
12 var t = new Ext.Template("&lt;div>Hello {0}.&lt;/div>");
13 t.{@link #append}('some-element', ['foo']);
14    </code></pre>
15  * <li><tt>Array</tt> : </li>
16  * An Array will be combined with <code>join('')</code>.
17 <pre><code>
18 var t = new Ext.Template([
19     '&lt;div name="{id}"&gt;',
20         '&lt;span class="{cls}"&gt;{name:trim} {value:ellipsis(10)}&lt;/span&gt;',
21     '&lt;/div&gt;',
22 ]);
23 t.{@link #compile}();
24 t.{@link #append}('some-element', {id: 'myid', cls: 'myclass', name: 'foo', value: 'bar'});
25    </code></pre>
26  * </ul></div></li>
27  * <li><b>multiple arguments</b> : String, Object, Array, ...
28  * <div class="sub-desc">
29  * Multiple arguments will be combined with <code>join('')</code>.
30  * <pre><code>
31 var t = new Ext.Template(
32     '&lt;div name="{id}"&gt;',
33         '&lt;span class="{cls}"&gt;{name} {value}&lt;/span&gt;',
34     '&lt;/div&gt;',
35     // a configuration object:
36     {
37         compiled: true,      // {@link #compile} immediately
38     }
39 );
40    </code></pre>
41  * <p><b>Notes</b>:</p>
42  * <div class="mdetail-params"><ul>
43  * <li>For a list of available format functions, see {@link Ext.util.Format}.</li>
44  * <li><code>disableFormats</code> reduces <code>{@link #apply}</code> time
45  * when no formatting is required.</li>
46  * </ul></div>
47  * </div></li>
48  * </ul></div>
49  * @param {Mixed} config
50  */
51
52 Ext.define('Ext.Template', {
53
54     /* Begin Definitions */
55
56     requires: ['Ext.core.DomHelper', 'Ext.util.Format'],
57
58     statics: {
59         /**
60          * Creates a template from the passed element's value (<i>display:none</i> textarea, preferred) or innerHTML.
61          * @param {String/HTMLElement} el A DOM element or its id
62          * @param {Object} config A configuration object
63          * @return {Ext.Template} The created template
64          * @static
65          */
66         from: function(el, config) {
67             el = Ext.getDom(el);
68             return new this(el.value || el.innerHTML, config || '');
69         }
70     },
71
72     /* End Definitions */
73
74     constructor: function(html) {
75         var me = this,
76             args = arguments,
77             buffer = [],
78             i = 0,
79             length = args.length,
80             value;
81
82         me.initialConfig = {};
83
84         if (length > 1) {
85             for (; i < length; i++) {
86                 value = args[i];
87                 if (typeof value == 'object') {
88                     Ext.apply(me.initialConfig, value);
89                     Ext.apply(me, value);
90                 } else {
91                     buffer.push(value);
92                 }
93             }
94             html = buffer.join('');
95         } else {
96             if (Ext.isArray(html)) {
97                 buffer.push(html.join(''));
98             } else {
99                 buffer.push(html);
100             }
101         }
102
103         // @private
104         me.html = buffer.join('');
105
106         if (me.compiled) {
107             me.compile();
108         }
109     },
110     isTemplate: true,
111     /**
112      * @cfg {Boolean} disableFormats true to disable format functions in the template. If the template doesn't contain format functions, setting
113      * disableFormats to true will reduce apply time (defaults to false)
114      */
115     disableFormats: false,
116
117     re: /\{([\w\-]+)(?:\:([\w\.]*)(?:\((.*?)?\))?)?\}/g,
118     /**
119      * Returns an HTML fragment of this template with the specified values applied.
120      * @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'})
121      * @return {String} The HTML fragment
122      * @hide repeat doc
123      */
124     applyTemplate: function(values) {
125         var me = this,
126             useFormat = me.disableFormats !== true,
127             fm = Ext.util.Format,
128             tpl = me;
129
130         if (me.compiled) {
131             return me.compiled(values);
132         }
133         function fn(m, name, format, args) {
134             if (format && useFormat) {
135                 if (args) {
136                     args = [values[name]].concat(Ext.functionFactory('return ['+ args +'];')());
137                 } else {
138                     args = [values[name]];
139                 }
140                 if (format.substr(0, 5) == "this.") {
141                     return tpl[format.substr(5)].apply(tpl, args);
142                 }
143                 else {
144                     return fm[format].apply(fm, args);
145                 }
146             }
147             else {
148                 return values[name] !== undefined ? values[name] : "";
149             }
150         }
151         return me.html.replace(me.re, fn);
152     },
153
154     /**
155      * Sets the HTML used as the template and optionally compiles it.
156      * @param {String} html
157      * @param {Boolean} compile (optional) True to compile the template (defaults to undefined)
158      * @return {Ext.Template} this
159      */
160     set: function(html, compile) {
161         var me = this;
162         me.html = html;
163         me.compiled = null;
164         return compile ? me.compile() : me;
165     },
166
167     compileARe: /\\/g,
168     compileBRe: /(\r\n|\n)/g,
169     compileCRe: /'/g,
170     /**
171      * Compiles the template into an internal function, eliminating the RegEx overhead.
172      * @return {Ext.Template} this
173      * @hide repeat doc
174      */
175     compile: function() {
176         var me = this,
177             fm = Ext.util.Format,
178             useFormat = me.disableFormats !== true,
179             body, bodyReturn;
180
181         function fn(m, name, format, args) {
182             if (format && useFormat) {
183                 args = args ? ',' + args: "";
184                 if (format.substr(0, 5) != "this.") {
185                     format = "fm." + format + '(';
186                 }
187                 else {
188                     format = 'this.' + format.substr(5) + '(';
189                 }
190             }
191             else {
192                 args = '';
193                 format = "(values['" + name + "'] == undefined ? '' : ";
194             }
195             return "'," + format + "values['" + name + "']" + args + ") ,'";
196         }
197
198         bodyReturn = me.html.replace(me.compileARe, '\\\\').replace(me.compileBRe, '\\n').replace(me.compileCRe, "\\'").replace(me.re, fn);
199         body = "this.compiled = function(values){ return ['" + bodyReturn + "'].join('');};";
200         eval(body);
201         return me;
202     },
203
204     /**
205      * Applies the supplied values to the template and inserts the new node(s) as the first child of el.
206      * @param {Mixed} el The context element
207      * @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'})
208      * @param {Boolean} returnElement (optional) true to return a Ext.core.Element (defaults to undefined)
209      * @return {HTMLElement/Ext.core.Element} The new node or Element
210      */
211     insertFirst: function(el, values, returnElement) {
212         return this.doInsert('afterBegin', el, values, returnElement);
213     },
214
215     /**
216      * Applies the supplied values to the template and inserts the new node(s) before el.
217      * @param {Mixed} el The context element
218      * @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'})
219      * @param {Boolean} returnElement (optional) true to return a Ext.core.Element (defaults to undefined)
220      * @return {HTMLElement/Ext.core.Element} The new node or Element
221      */
222     insertBefore: function(el, values, returnElement) {
223         return this.doInsert('beforeBegin', el, values, returnElement);
224     },
225
226     /**
227      * Applies the supplied values to the template and inserts the new node(s) after el.
228      * @param {Mixed} el The context element
229      * @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'})
230      * @param {Boolean} returnElement (optional) true to return a Ext.core.Element (defaults to undefined)
231      * @return {HTMLElement/Ext.core.Element} The new node or Element
232      */
233     insertAfter: function(el, values, returnElement) {
234         return this.doInsert('afterEnd', el, values, returnElement);
235     },
236
237     /**
238      * Applies the supplied <code>values</code> to the template and appends
239      * the new node(s) to the specified <code>el</code>.
240      * <p>For example usage {@link #Template see the constructor}.</p>
241      * @param {Mixed} el The context element
242      * @param {Object/Array} values
243      * The template values. Can be an array if the params are numeric (i.e. <code>{0}</code>)
244      * or an object (i.e. <code>{foo: 'bar'}</code>).
245      * @param {Boolean} returnElement (optional) true to return an Ext.core.Element (defaults to undefined)
246      * @return {HTMLElement/Ext.core.Element} The new node or Element
247      */
248     append: function(el, values, returnElement) {
249         return this.doInsert('beforeEnd', el, values, returnElement);
250     },
251
252     doInsert: function(where, el, values, returnEl) {
253         el = Ext.getDom(el);
254         var newNode = Ext.core.DomHelper.insertHtml(where, el, this.applyTemplate(values));
255         return returnEl ? Ext.get(newNode, true) : newNode;
256     },
257
258     /**
259      * Applies the supplied values to the template and overwrites the content of el with the new node(s).
260      * @param {Mixed} el The context element
261      * @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'})
262      * @param {Boolean} returnElement (optional) true to return a Ext.core.Element (defaults to undefined)
263      * @return {HTMLElement/Ext.core.Element} The new node or Element
264      */
265     overwrite: function(el, values, returnElement) {
266         el = Ext.getDom(el);
267         el.innerHTML = this.applyTemplate(values);
268         return returnElement ? Ext.get(el.firstChild, true) : el.firstChild;
269     }
270 }, function() {
271
272     /**
273      * Alias for {@link #applyTemplate}
274      * Returns an HTML fragment of this template with the specified <code>values</code> applied.
275      * @param {Object/Array} values
276      * The template values. Can be an array if the params are numeric (i.e. <code>{0}</code>)
277      * or an object (i.e. <code>{foo: 'bar'}</code>).
278      * @return {String} The HTML fragment
279      * @member Ext.Template
280      * @method apply
281      */
282     this.createAlias('apply', 'applyTemplate');
283 });