PageRenderTime 38ms CodeModel.GetById 17ms app.highlight 15ms RepoModel.GetById 1ms app.codeStats 1ms

/ext-4.1.0_b3/docs/source/XTemplateCompiler.html

https://bitbucket.org/srogerf/javascript
HTML | 376 lines | 316 code | 60 blank | 0 comment | 0 complexity | f11290d9525b8b268f6148a16ae20107 MD5 | raw file
  1<!DOCTYPE html>
  2<html>
  3<head>
  4  <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
  5  <title>The source code</title>
  6  <link href="../resources/prettify/prettify.css" type="text/css" rel="stylesheet" />
  7  <script type="text/javascript" src="../resources/prettify/prettify.js"></script>
  8  <style type="text/css">
  9    .highlight { display: block; background-color: #ddd; }
 10  </style>
 11  <script type="text/javascript">
 12    function highlight() {
 13      document.getElementById(location.hash.replace(/#/, "")).className = "highlight";
 14    }
 15  </script>
 16</head>
 17<body onload="prettyPrint(); highlight();">
 18  <pre class="prettyprint lang-js"><span id='Ext-XTemplateCompiler'>/**
 19</span> * This class compiles the XTemplate syntax into a function object. The function is used
 20 * like so:
 21 * 
 22 *      function (out, values, parent, xindex, xcount) {
 23 *          // out is the output array to store results
 24 *          // values, parent, xindex and xcount have their historical meaning
 25 *      }
 26 *
 27 * @markdown
 28 * @private
 29 */
 30Ext.define('Ext.XTemplateCompiler', {
 31    extend: 'Ext.XTemplateParser',
 32
 33    // Chrome really likes &quot;new Function&quot; to realize the code block (as in it is
 34    // 2x-3x faster to call it than using eval), but Firefox chokes on it badly.
 35    // IE and Opera are also fine with the &quot;new Function&quot; technique.
 36    useEval: Ext.isGecko,
 37
 38    // See http://jsperf.com/nige-array-append for quickest way to append to an array of unknown length
 39    // (Due to arbitrary code execution inside a template, we cannot easily track the length in  var)
 40    // On IE6 and 7 myArray[myArray.length]='foo' is better. On other browsers myArray.push('foo') is better.
 41    useIndex: Ext.isIE6 || Ext.isIE7,
 42
 43    useFormat: true,
 44    
 45    propNameRe: /^[\w\d\$]*$/,
 46
 47    compile: function (tpl) {
 48        var me = this,
 49            code = me.generate(tpl);
 50
 51        // When using &quot;new Function&quot;, we have to pass our &quot;Ext&quot; variable to it in order to
 52        // support sandboxing. If we did not, the generated function would use the global
 53        // &quot;Ext&quot;, not the &quot;Ext&quot; from our sandbox (scope chain).
 54        //
 55        return me.useEval ? me.evalTpl(code) : (new Function('Ext', code))(Ext);
 56    },
 57
 58    generate: function (tpl) {
 59        var me = this;
 60
 61        me.body = [
 62            'var c0=values, a0 = Ext.isArray(c0), p0=parent, n0=xcount, i0=xindex, v;\n'
 63        ];
 64        me.funcs = [
 65            // note: Ext here is properly sandboxed
 66            'var fm=Ext.util.Format;' 
 67        ];
 68        me.switches = [];
 69
 70        me.parse(tpl);
 71
 72        me.funcs.push(
 73            (me.useEval ? '$=' : 'return') + ' function (' + me.fnArgs + ') {',
 74                me.body.join(''),
 75            '}'
 76        );
 77
 78        var code = me.funcs.join('\n');
 79
 80        return code;
 81    },
 82
 83    //-----------------------------------
 84    // XTemplateParser callouts
 85
 86    doText: function (text) {
 87        var me = this,
 88            out = me.body;
 89
 90        text = text.replace(me.aposRe, &quot;\\'&quot;).replace(me.newLineRe, '\\n');
 91        if (me.useIndex) {
 92            out.push('out[out.length]=\'', text, '\'\n');
 93        } else {
 94            out.push('out.push(\'', text, '\')\n');
 95        }
 96    },
 97
 98    doExpr: function (expr) {
 99        var out = this.body;
100        out.push('if ((v=' + expr + ')!==undefined) out');
101        if (this.useIndex) {
102             out.push('[out.length]=String(v)\n');
103        } else {
104             out.push('.push(String(v))\n');
105        }
106    },
107
108    doTag: function (tag) {
109        this.doExpr(this.parseTag(tag));
110    },
111
112    doElse: function () {
113        this.body.push('} else {\n');
114    },
115
116    doEval: function (text) {
117        this.body.push(text, '\n');
118    },
119
120    doIf: function (action, actions) {
121        var me = this;
122
123        // If it's just a propName, use it directly in the if
124        if (me.propNameRe.test(action)) {
125            me.body.push('if (', me.parseTag(action), ') {\n');
126        }
127        // Otherwise, it must be an expression, and needs to be returned from an fn which uses with(values)
128        else {
129            me.body.push('if (', me.addFn(action), me.callFn, ') {\n');
130        }
131        if (actions.exec) {
132            me.doExec(actions.exec);
133        }
134    },
135
136    doElseIf: function (action, actions) {
137        var me = this;
138
139        // If it's just a propName, use it directly in the else if
140        if (me.propNameRe.test(action)) {
141            me.body.push('} else if (', me.parseTag(action), ') {\n');
142        }
143        // Otherwise, it must be an expression, and needs to be returned from an fn which uses with(values)
144        else {
145            me.body.push('} else if (', me.addFn(action), me.callFn, ') {\n');
146        }
147        if (actions.exec) {
148            me.doExec(actions.exec);
149        }
150    },
151
152    doSwitch: function (action) {
153        var me = this;
154
155        // If it's just a propName, use it directly in the switch
156        if (me.propNameRe.test(action)) {
157            me.body.push('switch (', me.parseTag(action), ') {\n');
158        }
159        // Otherwise, it must be an expression, and needs to be returned from an fn which uses with(values)
160        else {
161            me.body.push('switch (', me.addFn(action), me.callFn, ') {\n');
162        }
163        me.switches.push(0);
164    },
165
166    doCase: function (action) {
167        var me = this,
168            cases = Ext.isArray(action) ? action : [action],
169            n = me.switches.length - 1,
170            match, i;
171
172        if (me.switches[n]) {
173            me.body.push('break;\n');
174        } else {
175            me.switches[n]++;
176        }
177
178        for (i = 0, n = cases.length; i &lt; n; ++i) {
179            match = me.intRe.exec(cases[i]);
180            cases[i] = match ? match[1] : (&quot;'&quot; + cases[i].replace(me.aposRe,&quot;\\'&quot;) + &quot;'&quot;);
181        }
182
183        me.body.push('case ', cases.join(': case '), ':\n');
184    },
185
186    doDefault: function () {
187        var me = this,
188            n = me.switches.length - 1;
189
190        if (me.switches[n]) {
191            me.body.push('break;\n');
192        } else {
193            me.switches[n]++;
194        }
195
196        me.body.push('default:\n');
197    },
198
199    doEnd: function (type, actions) {
200        var me = this,
201            L = me.level-1;
202
203        if (type == 'for') {
204            /*
205            To exit a for loop we must restore the outer loop's context. The code looks
206            like this (which goes with that produced by doFor:
207
208                    for (...) { // the part generated by doFor
209                        ...  // the body of the for loop
210
211                        // ... any tpl for exec statement goes here...
212                    }
213                    parent = p1;
214                    values = r2;
215                    xcount = n1;
216                    xindex = i1
217            */
218            if (actions.exec) {
219                me.doExec(actions.exec);
220            }
221
222            me.body.push('}\n');
223            me.body.push('parent=p',L,';values=r',L+1,';xcount=n',L,';xindex=i',L,'\n');
224        } else if (type == 'if' || type == 'switch') {
225            me.body.push('}\n');
226        }
227    },
228
229    doFor: function (action, actions) {
230        var me = this,
231            s = me.addFn(action),
232            L = me.level,
233            up = L-1;
234
235        /*
236        We are trying to produce a block of code that looks like below. We use the nesting
237        level to uniquely name the control variables.
238
239            var c2 = f5.call(this, out, values, parent, xindex, xcount),
240                    // c2 is the context object for the for loop
241                a2 = Ext.isArray(c2),
242                    // a2 is the isArray result for the context
243                p2 = c1,
244                    // p2 is the parent context (of the outer for loop)
245                r2 = values
246                    // r2 is the values object to 
247
248            parent = a1 ? c1[i1] : p2 // set parent
249            // i2 is the loop index and n2 is the number (xcount) of this for loop
250            for (var i2 = 0, n2 = a2 ? c2.length : (c2 ? 1 : 0), xcount = n2; i2 &lt; n2; ++i2) {
251                values = a2 ? c2[i2] : c2   // adjust special vars to inner scope
252                xindex = i2 + 1           // xindex is 1-based
253
254        The body of the loop is whatever comes between the tpl and /tpl statements (which
255        is handled by doEnd).
256        */
257
258        me.body.push('var c',L,'=',s,me.callFn,', a',L,'=Ext.isArray(c',L,'), p',L,'=c',up,',r',L,'=values\n',
259            'parent=a',up,'?c',up,'[i',up,']:p',L,'\n',
260            'for (var i',L,'=0,n',L,'=a',L,'?c',L,'.length:(c',L,'?1:0), xcount=n',L,';i',L,'&lt;n'+L+';++i',L,'){\n',
261            'values=a',L,'?c',L,'[i',L,']:c',L,'\n',
262            'xindex=i',L,'+1\n');
263    },
264
265    doExec: function (action, actions) {
266        var me = this,
267            name = 'f' + me.funcs.length;
268
269        me.funcs.push('function ' + name + '(' + me.fnArgs + ') {',
270                            ' try { with(values) {',
271                            '  ' + action,
272                            ' }} catch(e) {}',
273                      '}');
274
275        me.body.push(name + me.callFn + '\n');
276    },
277
278    //-----------------------------------
279    // Internal
280
281    addFn: function (body) {
282        var me = this,
283            name = 'f' + me.funcs.length;
284
285        if (body === '.') {
286            me.funcs.push('function ' + name + '(' + me.fnArgs + ') {',
287                            ' return values',
288                       '}');
289        } else if (body === '..') {
290            me.funcs.push('function ' + name + '(' + me.fnArgs + ') {',
291                            ' return parent',
292                       '}');
293        } else {
294            me.funcs.push('function ' + name + '(' + me.fnArgs + ') {',
295                            ' try { with(values) {',
296                            '  return(' + body + ')',
297                            ' }} catch(e) {}',
298                       '}');
299        }
300
301        return name;
302    },
303
304    parseTag: function (tag) {
305        var m = this.tagRe.exec(tag),
306            name = m[1],
307            format = m[2],
308            args = m[3],
309            math = m[4],
310            v;
311
312        // name = &quot;.&quot; - Just use the values object.
313        if (name == '.') {
314            // filter to not include arrays/objects/nulls
315            v = 'Ext.Array.indexOf([&quot;string&quot;, &quot;number&quot;, &quot;boolean&quot;], typeof values) &gt; -1 || Ext.isDate(values) ? values : &quot;&quot;';
316        }
317        // name = &quot;#&quot; - Use the xindex
318        else if (name == '#') {
319            v = 'xindex';
320        }
321        else if (name.substr(0, 7) == &quot;parent.&quot;) {
322            v = name;
323        }
324        // compound Javascript property name (e.g., &quot;foo.bar&quot;)
325        else if (isNaN(name) &amp;&amp; name.indexOf('-') == -1 &amp;&amp; name.indexOf('.') != -1) {
326            v = &quot;values.&quot; + name;
327        }
328        // number or a '-' in it or a single word (maybe a keyword): use array notation
329        // (http://jsperf.com/string-property-access/4)
330        else {    
331            v = &quot;values['&quot; + name + &quot;']&quot;;
332        }
333
334        if (math) {
335            v = '(' + v + math + ')';
336        }
337
338        if (format &amp;&amp; this.useFormat) {
339            args = args ? ',' + args : &quot;&quot;;
340            if (format.substr(0, 5) != &quot;this.&quot;) {
341                format = &quot;fm.&quot; + format + '(';
342            } else {
343                format += '(';
344            }
345        } else {
346            return v;
347        }
348
349        return format + v + args + ')';
350    },
351
352    // @private
353    evalTpl: function ($) {
354
355        // We have to use eval to realize the code block and capture the inner func we also
356        // don't want a deep scope chain. We only do this in Firefox and it is also unhappy
357        // with eval containing a return statement, so instead we assign to &quot;$&quot; and return
358        // that. Because we use &quot;eval&quot;, we are automatically sandboxed properly.
359        eval($);
360        return $;
361    },
362
363    newLineRe: /\r\n|\r|\n/g,
364    aposRe: /[']/g,
365    intRe:  /^\s*(\d+)\s*$/,
366    tagRe:  /([\w-\.\#]+)(?:\:([\w\.]*)(?:\((.*?)?\))?)?(\s?[\+\-\*\/]\s?[\d\.\+\-\*\/\(\)]+)?/
367
368}, function () {
369    var proto = this.prototype;
370
371    proto.fnArgs = 'out,values,parent,xindex,xcount';
372    proto.callFn = '.call(this,' + proto.fnArgs + ')';
373});
374</pre>
375</body>
376</html>