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