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