Roo/Template.js
[roojs1] / Roo / Template.js
1 /*
2  * Based on:
3  * Ext JS Library 1.1.1
4  * Copyright(c) 2006-2007, Ext JS, LLC.
5  *
6  * Originally Released Under LGPL - original licence link has changed is not relivant.
7  *
8  * Fork - LGPL
9  * <script type="text/javascript">
10  */
11  
12 /**
13 * @class Roo.Template
14 * Represents an HTML fragment template. Templates can be precompiled for greater performance.
15 * For a list of available format functions, see {@link Roo.util.Format}.<br />
16 * Usage:
17 <pre><code>
18 var t = new Roo.Template({
19     html :  '&lt;div name="{id}"&gt;' + 
20         '&lt;span class="{cls}"&gt;{name:trim} {someval:this.myformat}{value:ellipsis(10)}&lt;/span&gt;' +
21         '&lt;/div&gt;',
22     myformat: function (value, allValues) {
23         return 'XX' + value;
24     }
25 });
26 t.append('some-element', {id: 'myid', cls: 'myclass', name: 'foo', value: 'bar'});
27 </code></pre>
28 * For more information see this blog post with examples: <a href="http://www.jackslocum.com/yui/2006/10/06/domhelper-create-elements-using-dom-html-fragments-or-templates/">DomHelper - Create Elements using DOM, HTML fragments and Templates</a>. 
29 * @constructor
30 * @param {Object} cfg - Configuration object.
31 */
32 Roo.Template = function(cfg){
33     // BC!
34     if(cfg instanceof Array){
35         cfg = cfg.join("");
36     }else if(arguments.length > 1){
37         cfg = Array.prototype.join.call(arguments, "");
38     }
39     
40     
41     if (typeof(cfg) == 'object') {
42         Roo.apply(this,cfg)
43     } else {
44         // bc
45         this.html = cfg;
46     }
47     if (this.url) {
48         this.load();
49     }
50     
51 };
52 Roo.Template.prototype = {
53     
54     /**
55      * @cfg {String} url  The Url to load the template from.
56      */
57     url : false,
58     /**
59      * @cfg {String} html  The HTML fragment or an array of fragments to join("") or multiple arguments to join("")
60      */
61     html : '',
62     /**
63      * Returns an HTML fragment of this template with the specified values applied.
64      * @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'})
65      * @return {String} The HTML fragment
66      */
67     applyTemplate : function(values){
68         try {
69             _t= this;
70             if (this.url) {
71                 this.afterLoad = function () { _t.applyTemplate(values); };
72                 this.load();
73                 return;
74             }
75             if(this.compiled){
76                 return this.compiled(values);
77             }
78             var useF = this.disableFormats !== true;
79             var fm = Roo.util.Format, tpl = this;
80             var fn = function(m, name, format, args){
81                 if(format && useF){
82                     if(format.substr(0, 5) == "this."){
83                         return tpl.call(format.substr(5), values[name], values);
84                     }else{
85                         if(args){
86                             // quoted values are required for strings in compiled templates, 
87                             // but for non compiled we need to strip them
88                             // quoted reversed for jsmin
89                             var re = /^\s*['"](.*)["']\s*$/;
90                             args = args.split(',');
91                             for(var i = 0, len = args.length; i < len; i++){
92                                 args[i] = args[i].replace(re, "$1");
93                             }
94                             args = [values[name]].concat(args);
95                         }else{
96                             args = [values[name]];
97                         }
98                         return fm[format].apply(fm, args);
99                     }
100                 }else{
101                     return values[name] !== undefined ? values[name] : "";
102                 }
103             };
104             return this.html.replace(this.re, fn);
105         } catch (e) {
106             Roo.log(e);
107             throw e;
108         }
109          
110     },
111     
112     loading : false,
113     
114     afterLoad : false,
115     
116     load : function ()
117     {
118          
119         if (this.loading) {
120             return;
121         }
122         var _t = this;
123         
124         this.loading = true;
125         this.compiled = false;
126         
127         var cx = new Roo.data.Connection();
128         cx.request({
129             url : this.url,
130             method : 'GET',
131             success : function (response) {
132                 _t.loading = false;
133                 _t.html = response.responseText;
134                 _t.url = false;
135                 _t.afterLoad && _t.afterLoad();
136             },
137             failure : function(response) {
138                 Roo.log("Template failed to load from " + url);
139                 _t.loading = false;
140             }
141         });
142     },
143
144     /**
145      * Sets the HTML used as the template and optionally compiles it.
146      * @param {String} html
147      * @param {Boolean} compile (optional) True to compile the template (defaults to undefined)
148      * @return {Roo.Template} this
149      */
150     set : function(html, compile){
151         this.html = html;
152         this.compiled = null;
153         if(compile){
154             this.compile();
155         }
156         return this;
157     },
158     
159     /**
160      * True to disable format functions (defaults to false)
161      * @type Boolean
162      */
163     disableFormats : false,
164     
165     /**
166     * The regular expression used to match template variables 
167     * @type RegExp
168     * @property 
169     */
170     re : /\{([\w-]+)(?:\:([\w\.]*)(?:\((.*?)?\))?)?\}/g,
171     
172     /**
173      * Compiles the template into an internal function, eliminating the RegEx overhead.
174      * @return {Roo.Template} this
175      */
176     compile : function(){
177         var fm = Roo.util.Format;
178         var useF = this.disableFormats !== true;
179         var sep = Roo.isGecko ? "+" : ",";
180         var fn = function(m, name, format, args){
181             if(format && useF){
182                 args = args ? ',' + args : "";
183                 if(format.substr(0, 5) != "this."){
184                     format = "fm." + format + '(';
185                 }else{
186                     format = 'this.call("'+ format.substr(5) + '", ';
187                     args = ", values";
188                 }
189             }else{
190                 args= ''; format = "(values['" + name + "'] == undefined ? '' : ";
191             }
192             return "'"+ sep + format + "values['" + name + "']" + args + ")"+sep+"'";
193         };
194         var body;
195         // branched to use + in gecko and [].join() in others
196         if(Roo.isGecko){
197             body = "this.compiled = function(values){ return '" +
198                    this.html.replace(/\\/g, '\\\\').replace(/(\r\n|\n)/g, '\\n').replace(/'/g, "\\'").replace(this.re, fn) +
199                     "';};";
200         }else{
201             body = ["this.compiled = function(values){ return ['"];
202             body.push(this.html.replace(/\\/g, '\\\\').replace(/(\r\n|\n)/g, '\\n').replace(/'/g, "\\'").replace(this.re, fn));
203             body.push("'].join('');};");
204             body = body.join('');
205         }
206         /**
207          * eval:var:values
208          * eval:var:fm
209          */
210         eval(body);
211         return this;
212     },
213     
214     // private function used to call members
215     call : function(fnName, value, allValues){
216         return this[fnName](value, allValues);
217     },
218     
219     /**
220      * Applies the supplied values to the template and inserts the new node(s) as the first child of el.
221      * @param {String/HTMLElement/Roo.Element} el The context element
222      * @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'})
223      * @param {Boolean} returnElement (optional) true to return a Roo.Element (defaults to undefined)
224      * @return {HTMLElement/Roo.Element} The new node or Element
225      */
226     insertFirst: function(el, values, returnElement){
227         return this.doInsert('afterBegin', el, values, returnElement);
228     },
229
230     /**
231      * Applies the supplied values to the template and inserts the new node(s) before el.
232      * @param {String/HTMLElement/Roo.Element} el The context element
233      * @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'})
234      * @param {Boolean} returnElement (optional) true to return a Roo.Element (defaults to undefined)
235      * @return {HTMLElement/Roo.Element} The new node or Element
236      */
237     insertBefore: function(el, values, returnElement){
238         return this.doInsert('beforeBegin', el, values, returnElement);
239     },
240
241     /**
242      * Applies the supplied values to the template and inserts the new node(s) after el.
243      * @param {String/HTMLElement/Roo.Element} el The context element
244      * @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'})
245      * @param {Boolean} returnElement (optional) true to return a Roo.Element (defaults to undefined)
246      * @return {HTMLElement/Roo.Element} The new node or Element
247      */
248     insertAfter : function(el, values, returnElement){
249         return this.doInsert('afterEnd', el, values, returnElement);
250     },
251     
252     /**
253      * Applies the supplied values to the template and appends the new node(s) to el.
254      * @param {String/HTMLElement/Roo.Element} el The context element
255      * @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'})
256      * @param {Boolean} returnElement (optional) true to return a Roo.Element (defaults to undefined)
257      * @return {HTMLElement/Roo.Element} The new node or Element
258      */
259     append : function(el, values, returnElement){
260         return this.doInsert('beforeEnd', el, values, returnElement);
261     },
262
263     doInsert : function(where, el, values, returnEl){
264         el = Roo.getDom(el);
265         var newNode = Roo.DomHelper.insertHtml(where, el, this.applyTemplate(values));
266         return returnEl ? Roo.get(newNode, true) : newNode;
267     },
268
269     /**
270      * Applies the supplied values to the template and overwrites the content of el with the new node(s).
271      * @param {String/HTMLElement/Roo.Element} el The context element
272      * @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'})
273      * @param {Boolean} returnElement (optional) true to return a Roo.Element (defaults to undefined)
274      * @return {HTMLElement/Roo.Element} The new node or Element
275      */
276     overwrite : function(el, values, returnElement){
277         el = Roo.getDom(el);
278         el.innerHTML = this.applyTemplate(values);
279         return returnElement ? Roo.get(el.firstChild, true) : el.firstChild;
280     }
281 };
282 /**
283  * Alias for {@link #applyTemplate}
284  * @method
285  */
286 Roo.Template.prototype.apply = Roo.Template.prototype.applyTemplate;
287
288 // backwards compat
289 Roo.DomHelper.Template = Roo.Template;
290
291 /**
292  * Creates a template from the passed element's value (<i>display:none</i> textarea, preferred) or innerHTML.
293  * @param {String/HTMLElement} el A DOM element or its id
294  * @returns {Roo.Template} The created template
295  * @static
296  */
297 Roo.Template.from = function(el){
298     el = Roo.getDom(el);
299     return new Roo.Template(el.value || el.innerHTML);
300 };