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