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     
48     
49 };
50 Roo.Template.prototype = {
51     
52     /**
53      * @cfg {String} url  The Url to load the template from.
54      */
55     url : false,
56     /**
57      * @cfg {String} html  The HTML fragment or an array of fragments to join("") or multiple arguments to join("")
58      */
59     html : '',
60     /**
61      * Returns an HTML fragment of this template with the specified values applied.
62      * @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'})
63      * @return {String} The HTML fragment
64      */
65     applyTemplate : function(values){
66         try {
67             if ()
68             if(this.compiled){
69                 return this.compiled(values);
70             }
71             var useF = this.disableFormats !== true;
72             var fm = Roo.util.Format, tpl = this;
73             var fn = function(m, name, format, args){
74                 if(format && useF){
75                     if(format.substr(0, 5) == "this."){
76                         return tpl.call(format.substr(5), values[name], values);
77                     }else{
78                         if(args){
79                             // quoted values are required for strings in compiled templates, 
80                             // but for non compiled we need to strip them
81                             // quoted reversed for jsmin
82                             var re = /^\s*['"](.*)["']\s*$/;
83                             args = args.split(',');
84                             for(var i = 0, len = args.length; i < len; i++){
85                                 args[i] = args[i].replace(re, "$1");
86                             }
87                             args = [values[name]].concat(args);
88                         }else{
89                             args = [values[name]];
90                         }
91                         return fm[format].apply(fm, args);
92                     }
93                 }else{
94                     return values[name] !== undefined ? values[name] : "";
95                 }
96             };
97             return this.html.replace(this.re, fn);
98         } catch (e) {
99             Roo.log(e);
100             throw e;
101         }
102          
103     },
104     
105     loading : false,
106     
107     afterLoad : false,
108     
109     load : function (url, success)
110     {
111         
112         this.afterLoad = success;
113         if (this.loading) {
114             return;
115         }
116         
117         this.loading = true;
118         this.compiled = false;
119         var _t = this;
120         var cx = new Roo.data.Connection();
121         cx.request({
122             url : url,
123             method : 'GET',
124             success : function (response) {
125                 _t.loading = false;
126                 _t.html = response.responseText;
127                 _t.afterLoad && _t.afterLoad();
128             },
129             failure : function(response) {
130                 Roo.log("Template failed to load from " + url);
131                 _t.loading = false;
132             }
133         });
134     },
135
136     /**
137      * Sets the HTML used as the template and optionally compiles it.
138      * @param {String} html
139      * @param {Boolean} compile (optional) True to compile the template (defaults to undefined)
140      * @return {Roo.Template} this
141      */
142     set : function(html, compile){
143         this.html = html;
144         this.compiled = null;
145         if(compile){
146             this.compile();
147         }
148         return this;
149     },
150     
151     /**
152      * True to disable format functions (defaults to false)
153      * @type Boolean
154      */
155     disableFormats : false,
156     
157     /**
158     * The regular expression used to match template variables 
159     * @type RegExp
160     * @property 
161     */
162     re : /\{([\w-]+)(?:\:([\w\.]*)(?:\((.*?)?\))?)?\}/g,
163     
164     /**
165      * Compiles the template into an internal function, eliminating the RegEx overhead.
166      * @return {Roo.Template} this
167      */
168     compile : function(){
169         var fm = Roo.util.Format;
170         var useF = this.disableFormats !== true;
171         var sep = Roo.isGecko ? "+" : ",";
172         var fn = function(m, name, format, args){
173             if(format && useF){
174                 args = args ? ',' + args : "";
175                 if(format.substr(0, 5) != "this."){
176                     format = "fm." + format + '(';
177                 }else{
178                     format = 'this.call("'+ format.substr(5) + '", ';
179                     args = ", values";
180                 }
181             }else{
182                 args= ''; format = "(values['" + name + "'] == undefined ? '' : ";
183             }
184             return "'"+ sep + format + "values['" + name + "']" + args + ")"+sep+"'";
185         };
186         var body;
187         // branched to use + in gecko and [].join() in others
188         if(Roo.isGecko){
189             body = "this.compiled = function(values){ return '" +
190                    this.html.replace(/\\/g, '\\\\').replace(/(\r\n|\n)/g, '\\n').replace(/'/g, "\\'").replace(this.re, fn) +
191                     "';};";
192         }else{
193             body = ["this.compiled = function(values){ return ['"];
194             body.push(this.html.replace(/\\/g, '\\\\').replace(/(\r\n|\n)/g, '\\n').replace(/'/g, "\\'").replace(this.re, fn));
195             body.push("'].join('');};");
196             body = body.join('');
197         }
198         /**
199          * eval:var:values
200          * eval:var:fm
201          */
202         eval(body);
203         return this;
204     },
205     
206     // private function used to call members
207     call : function(fnName, value, allValues){
208         return this[fnName](value, allValues);
209     },
210     
211     /**
212      * Applies the supplied values to the template and inserts the new node(s) as the first child of el.
213      * @param {String/HTMLElement/Roo.Element} el The context element
214      * @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'})
215      * @param {Boolean} returnElement (optional) true to return a Roo.Element (defaults to undefined)
216      * @return {HTMLElement/Roo.Element} The new node or Element
217      */
218     insertFirst: function(el, values, returnElement){
219         return this.doInsert('afterBegin', el, values, returnElement);
220     },
221
222     /**
223      * Applies the supplied values to the template and inserts the new node(s) before el.
224      * @param {String/HTMLElement/Roo.Element} el The context element
225      * @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'})
226      * @param {Boolean} returnElement (optional) true to return a Roo.Element (defaults to undefined)
227      * @return {HTMLElement/Roo.Element} The new node or Element
228      */
229     insertBefore: function(el, values, returnElement){
230         return this.doInsert('beforeBegin', el, values, returnElement);
231     },
232
233     /**
234      * Applies the supplied values to the template and inserts the new node(s) after el.
235      * @param {String/HTMLElement/Roo.Element} el The context element
236      * @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'})
237      * @param {Boolean} returnElement (optional) true to return a Roo.Element (defaults to undefined)
238      * @return {HTMLElement/Roo.Element} The new node or Element
239      */
240     insertAfter : function(el, values, returnElement){
241         return this.doInsert('afterEnd', el, values, returnElement);
242     },
243     
244     /**
245      * Applies the supplied values to the template and appends the new node(s) to el.
246      * @param {String/HTMLElement/Roo.Element} el The context element
247      * @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'})
248      * @param {Boolean} returnElement (optional) true to return a Roo.Element (defaults to undefined)
249      * @return {HTMLElement/Roo.Element} The new node or Element
250      */
251     append : function(el, values, returnElement){
252         return this.doInsert('beforeEnd', el, values, returnElement);
253     },
254
255     doInsert : function(where, el, values, returnEl){
256         el = Roo.getDom(el);
257         var newNode = Roo.DomHelper.insertHtml(where, el, this.applyTemplate(values));
258         return returnEl ? Roo.get(newNode, true) : newNode;
259     },
260
261     /**
262      * Applies the supplied values to the template and overwrites the content of el with the new node(s).
263      * @param {String/HTMLElement/Roo.Element} el The context element
264      * @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'})
265      * @param {Boolean} returnElement (optional) true to return a Roo.Element (defaults to undefined)
266      * @return {HTMLElement/Roo.Element} The new node or Element
267      */
268     overwrite : function(el, values, returnElement){
269         el = Roo.getDom(el);
270         el.innerHTML = this.applyTemplate(values);
271         return returnElement ? Roo.get(el.firstChild, true) : el.firstChild;
272     }
273 };
274 /**
275  * Alias for {@link #applyTemplate}
276  * @method
277  */
278 Roo.Template.prototype.apply = Roo.Template.prototype.applyTemplate;
279
280 // backwards compat
281 Roo.DomHelper.Template = Roo.Template;
282
283 /**
284  * Creates a template from the passed element's value (<i>display:none</i> textarea, preferred) or innerHTML.
285  * @param {String/HTMLElement} el A DOM element or its id
286  * @returns {Roo.Template} The created template
287  * @static
288  */
289 Roo.Template.from = function(el){
290     el = Roo.getDom(el);
291     return new Roo.Template(el.value || el.innerHTML);
292 };