PHP Classes

File: web/bundles/extjs/src/XTemplateCompiler.js

Recommend this page to a friend!
  Classes of william amed   Raptor 2   web/bundles/extjs/src/XTemplateCompiler.js   Download  
File: web/bundles/extjs/src/XTemplateCompiler.js
Role: Auxiliary data
Content type: text/plain
Description: Auxiliary data
Class: Raptor 2
Framework that takes routes from annotations
Author: By
Last change:
Date: 8 years ago
Size: 18,997 bytes
 

Contents

Class file image Download
/* This file is part of Ext JS 4.2 Copyright (c) 2011-2013 Sencha Inc Contact: http://www.sencha.com/contact GNU General Public License Usage This file may be used under the terms of the GNU General Public License version 3.0 as published by the Free Software Foundation and appearing in the file LICENSE included in the packaging of this file. Please review the following information to ensure the GNU General Public License version 3.0 requirements will be met: http://www.gnu.org/copyleft/gpl.html. If you are unsure which license is appropriate for your use, please contact the sales department at http://www.sencha.com/contact. Build date: 2013-05-16 14:36:50 (f9be68accb407158ba2b1be2c226a6ce1f649314) */ // @tag core /** * This class compiles the XTemplate syntax into a function object. The function is used * like so: * * function (out, values, parent, xindex, xcount) { * // out is the output array to store results * // values, parent, xindex and xcount have their historical meaning * } * * @markdown * @private */ Ext.define('Ext.XTemplateCompiler', { extend: 'Ext.XTemplateParser', // Chrome really likes "new Function" to realize the code block (as in it is // 2x-3x faster to call it than using eval), but Firefox chokes on it badly. // IE and Opera are also fine with the "new Function" technique. useEval: Ext.isGecko, // See http://jsperf.com/nige-array-append for quickest way to append to an array of unknown length // (Due to arbitrary code execution inside a template, we cannot easily track the length in var) // On IE6 to 8, myArray[myArray.length]='foo' is better. On other browsers myArray.push('foo') is better. useIndex: Ext.isIE8m, useFormat: true, propNameRe: /^[\w\d\$]*$/, compile: function (tpl) { var me = this, code = me.generate(tpl); // When using "new Function", we have to pass our "Ext" variable to it in order to // support sandboxing. If we did not, the generated function would use the global // "Ext", not the "Ext" from our sandbox (scope chain). // return me.useEval ? me.evalTpl(code) : (new Function('Ext', code))(Ext); }, generate: function (tpl) { var me = this, // note: Ext here is properly sandboxed definitions = 'var fm=Ext.util.Format,ts=Object.prototype.toString;', code; // Track how many levels we use, so that we only "var" each level's variables once me.maxLevel = 0; me.body = [ 'var c0=values, a0=' + me.createArrayTest(0) + ', p0=parent, n0=xcount, i0=xindex, k0, v;\n' ]; if (me.definitions) { if (typeof me.definitions === 'string') { me.definitions = [me.definitions, definitions ]; } else { me.definitions.push(definitions); } } else { me.definitions = [ definitions ]; } me.switches = []; me.parse(tpl); me.definitions.push( (me.useEval ? '$=' : 'return') + ' function (' + me.fnArgs + ') {', me.body.join(''), '}' ); code = me.definitions.join('\n'); // Free up the arrays. me.definitions.length = me.body.length = me.switches.length = 0; delete me.definitions; delete me.body; delete me.switches; return code; }, //----------------------------------- // XTemplateParser callouts doText: function (text) { var me = this, out = me.body; text = text.replace(me.aposRe, "\\'").replace(me.newLineRe, '\\n'); if (me.useIndex) { out.push('out[out.length]=\'', text, '\'\n'); } else { out.push('out.push(\'', text, '\')\n'); } }, doExpr: function (expr) { var out = this.body; out.push('if ((v=' + expr + ') != null) out'); // Coerce value to string using concatenation of an empty string literal. // See http://jsperf.com/tostringvscoercion/5 if (this.useIndex) { out.push('[out.length]=v+\'\'\n'); } else { out.push('.push(v+\'\')\n'); } }, doTag: function (tag) { var expr = this.parseTag(tag); if (expr) { this.doExpr(expr); } else { // if we cannot match on tagRe handle as plain text this.doText('{' + tag + '}'); } }, doElse: function () { this.body.push('} else {\n'); }, doEval: function (text) { this.body.push(text, '\n'); }, doIf: function (action, actions) { var me = this; // If it's just a propName, use it directly in the if if (action === '.') { me.body.push('if (values) {\n'); } else if (me.propNameRe.test(action)) { me.body.push('if (', me.parseTag(action), ') {\n'); } // Otherwise, it must be an expression, and needs to be returned from an fn which uses with(values) else { me.body.push('if (', me.addFn(action), me.callFn, ') {\n'); } if (actions.exec) { me.doExec(actions.exec); } }, doElseIf: function (action, actions) { var me = this; // If it's just a propName, use it directly in the else if if (action === '.') { me.body.push('else if (values) {\n'); } else if (me.propNameRe.test(action)) { me.body.push('} else if (', me.parseTag(action), ') {\n'); } // Otherwise, it must be an expression, and needs to be returned from an fn which uses with(values) else { me.body.push('} else if (', me.addFn(action), me.callFn, ') {\n'); } if (actions.exec) { me.doExec(actions.exec); } }, doSwitch: function (action) { var me = this; // If it's just a propName, use it directly in the switch if (action === '.') { me.body.push('switch (values) {\n'); } else if (me.propNameRe.test(action)) { me.body.push('switch (', me.parseTag(action), ') {\n'); } // Otherwise, it must be an expression, and needs to be returned from an fn which uses with(values) else { me.body.push('switch (', me.addFn(action), me.callFn, ') {\n'); } me.switches.push(0); }, doCase: function (action) { var me = this, cases = Ext.isArray(action) ? action : [action], n = me.switches.length - 1, match, i; if (me.switches[n]) { me.body.push('break;\n'); } else { me.switches[n]++; } for (i = 0, n = cases.length; i < n; ++i) { match = me.intRe.exec(cases[i]); cases[i] = match ? match[1] : ("'" + cases[i].replace(me.aposRe,"\\'") + "'"); } me.body.push('case ', cases.join(': case '), ':\n'); }, doDefault: function () { var me = this, n = me.switches.length - 1; if (me.switches[n]) { me.body.push('break;\n'); } else { me.switches[n]++; } me.body.push('default:\n'); }, doEnd: function (type, actions) { var me = this, L = me.level-1; if (type == 'for' || type == 'foreach') { /* To exit a for or foreach loop we must restore the outer loop's context. The code looks like this (which goes with that produced by doFor or doForEach): for (...) { // the part generated by doFor or doForEach ... // the body of the for loop // ... any tpl for exec statement goes here... } parent = p1; values = r2; xcount = n1; xindex = i1 */ if (actions.exec) { me.doExec(actions.exec); } me.body.push('}\n'); me.body.push('parent=p',L,';values=r',L+1,';xcount=n'+L+';xindex=i',L,'+1;xkey=k',L,';\n'); } else if (type == 'if' || type == 'switch') { me.body.push('}\n'); } }, doFor: function (action, actions) { var me = this, s, L = me.level, up = L-1, parentAssignment; // If it's just a propName, use it directly in the switch if (action === '.') { s = 'values'; } else if (me.propNameRe.test(action)) { s = me.parseTag(action); } // Otherwise, it must be an expression, and needs to be returned from an fn which uses with(values) else { s = me.addFn(action) + me.callFn; } /* We are trying to produce a block of code that looks like below. We use the nesting level to uniquely name the control variables. // Omit "var " if we have already been through level 2 var i2 = 0, n2 = 0, c2 = values['propName'], // c2 is the context object for the for loop a2 = Array.isArray(c2); r2 = values, // r2 is the values object p2, // p2 is the parent context (of the outer for loop) k2; // object key - not used by for loop but doEnd needs this to be declared // If iterating over the current data, the parent is always set to c2 p2 = parent = c2; // If iterating over a property in an object, set the parent to the object p2 = parent = a1 ? c1[i1] : c1 // set parent if (c2) { if (a2) { n2 = c2.length; } else if (c2.isMixedCollection) { c2 = c2.items; n2 = c2.length; } else if (c2.isStore) { c2 = c2.data.items; n2 = c2.length; } else { c2 = [ c2 ]; n2 = 1; } } // i2 is the loop index and n2 is the number (xcount) of this for loop for (xcount = n2; i2 < n2; ++i2) { values = c2[i2] // adjust special vars to inner scope xindex = i2 + 1 // xindex is 1-based The body of the loop is whatever comes between the tpl and /tpl statements (which is handled by doEnd). */ // Declare the vars for a particular level only if we have not already declared them. if (me.maxLevel < L) { me.maxLevel = L; me.body.push('var '); } if (action == '.') { parentAssignment = 'c' + L; } else { parentAssignment = 'a' + up + '?c' + up + '[i' + up + ']:c' + up; } me.body.push('i',L,'=0,n', L, '=0,c',L,'=',s,',a',L,'=', me.createArrayTest(L),',r',L,'=values,p',L,',k',L,';\n', 'p',L,'=parent=',parentAssignment,'\n', 'if (c',L,'){if(a',L,'){n', L,'=c', L, '.length;}else if (c', L, '.isMixedCollection){c',L,'=c',L,'.items;n',L,'=c',L,'.length;}else if(c',L,'.isStore){c',L,'=c',L,'.data.items;n',L,'=c',L,'.length;}else{c',L,'=[c',L,'];n',L,'=1;}}\n', 'for (xcount=n',L,';i',L,'<n'+L+';++i',L,'){\n', 'values=c',L,'[i',L,']'); if (actions.propName) { me.body.push('.', actions.propName); } me.body.push('\n', 'xindex=i',L,'+1\n'); if (actions.between) { me.body.push('if(xindex>1){ out.push("',actions.between,'"); } \n'); } }, doForEach: function (action, actions) { var me = this, s, L = me.level, up = L-1, parentAssignment; // If it's just a propName, use it directly in the switch if (action === '.') { s = 'values'; } else if (me.propNameRe.test(action)) { s = me.parseTag(action); } // Otherwise, it must be an expression, and needs to be returned from an fn which uses with(values) else { s = me.addFn(action) + me.callFn; } /* We are trying to produce a block of code that looks like below. We use the nesting level to uniquely name the control variables. // Omit "var " if we have already been through level 2 var i2 = -1, n2 = 0, c2 = values['propName'], // c2 is the context object for the for loop a2 = Array.isArray(c2); r2 = values, // r2 is the values object p2, // p2 is the parent context (of the outer for loop) k2; // k2 is the object key while looping // If iterating over the current data, the parent is always set to c2 p2 = parent = c2; // If iterating over a property in an object, set the parent to the object p2 = parent = a1 ? c1[i1] : c1 // set parent for(k2 in c2){ xindex = ++i + 1; // xindex is 1-based xkey = k2; values = c2[k2]; // values is the property value The body of the loop is whatever comes between the tpl and /tpl statements (which is handled by doEnd). */ // Declare the vars for a particular level only if we have not already declared them. if (me.maxLevel < L) { me.maxLevel = L; me.body.push('var '); } if (action == '.') { parentAssignment = 'c' + L; } else { parentAssignment = 'a' + up + '?c' + up + '[i' + up + ']:c' + up; } me.body.push('i',L,'=-1,n',L,'=0,c',L,'=',s,',a',L,'=',me.createArrayTest(L),',r',L,'=values,p',L,',k',L,';\n', 'p',L,'=parent=',parentAssignment,'\n', 'for(k',L,' in c',L,'){\n', 'xindex=++i',L,'+1;\n', 'xkey=k',L,';\n', 'values=c',L,'[k',L,'];'); if (actions.propName) { me.body.push('.', actions.propName); } if (actions.between) { me.body.push('if(xindex>1){ out.push("',actions.between,'"); } \n'); } }, createArrayTest: ('isArray' in Array) ? function(L) { return 'Array.isArray(c' + L + ')'; } : function(L) { return 'ts.call(c' + L + ')==="[object Array]"'; }, doExec: function (action, actions) { var me = this, name = 'f' + me.definitions.length; me.definitions.push('function ' + name + '(' + me.fnArgs + ') {', ' try { with(values) {', ' ' + action, ' }} catch(e) {', //<debug> 'Ext.log("XTemplate Error: " + e.message);', //</debug> '}', '}'); me.body.push(name + me.callFn + '\n'); }, //----------------------------------- // Internal addFn: function (body) { var me = this, name = 'f' + me.definitions.length; if (body === '.') { me.definitions.push('function ' + name + '(' + me.fnArgs + ') {', ' return values', '}'); } else if (body === '..') { me.definitions.push('function ' + name + '(' + me.fnArgs + ') {', ' return parent', '}'); } else { me.definitions.push('function ' + name + '(' + me.fnArgs + ') {', ' try { with(values) {', ' return(' + body + ')', ' }} catch(e) {', //<debug> 'Ext.log("XTemplate Error: " + e.message);', //</debug> '}', '}'); } return name; }, parseTag: function (tag) { var me = this, m = me.tagRe.exec(tag), name, format, args, math, v; if (!m) { return null; } name = m[1]; format = m[2]; args = m[3]; math = m[4]; // name = "." - Just use the values object. if (name == '.') { // filter to not include arrays/objects/nulls if (!me.validTypes) { me.definitions.push('var validTypes={string:1,number:1,boolean:1};'); me.validTypes = true; } v = 'validTypes[typeof values] || ts.call(values) === "[object Date]" ? values : ""'; } // name = "#" - Use the xindex else if (name == '#') { v = 'xindex'; } // name = "$" - Use the xkey else if (name == '$') { v = 'xkey'; } else if (name.substr(0, 7) == "parent.") { v = name; } // compound Javascript property name (e.g., "foo.bar") else if (isNaN(name) && name.indexOf('-') == -1 && name.indexOf('.') != -1) { v = "values." + name; } // number or a '-' in it or a single word (maybe a keyword): use array notation // (http://jsperf.com/string-property-access/4) else { v = "values['" + name + "']"; } if (math) { v = '(' + v + math + ')'; } if (format && me.useFormat) { args = args ? ',' + args : ""; if (format.substr(0, 5) != "this.") { format = "fm." + format + '('; } else { format += '('; } } else { return v; } return format + v + args + ')'; }, // @private evalTpl: function ($) { // We have to use eval to realize the code block and capture the inner func we also // don't want a deep scope chain. We only do this in Firefox and it is also unhappy // with eval containing a return statement, so instead we assign to "$" and return // that. Because we use "eval", we are automatically sandboxed properly. eval($); return $; }, newLineRe: /\r\n|\r|\n/g, aposRe: /[']/g, intRe: /^\s*(\d+)\s*$/, tagRe: /^([\w-\.\#\$]+)(?:\:([\w\.]*)(?:\((.*?)?\))?)?(\s?[\+\-\*\/]\s?[\d\.\+\-\*\/\(\)]+)?$/ }, function () { var proto = this.prototype; proto.fnArgs = 'out,values,parent,xindex,xcount,xkey'; proto.callFn = '.call(this,' + proto.fnArgs + ')'; });