XTemplateParser.js 8.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249
  1. /**
  2. * This class parses the XTemplate syntax and calls abstract methods to process the parts.
  3. * @private
  4. */
  5. Ext.define('Ext.XTemplateParser', {
  6. constructor: function (config) {
  7. Ext.apply(this, config);
  8. },
  9. /**
  10. * @property {Number} level The 'for' loop context level. This is adjusted up by one
  11. * prior to calling {@link #doFor} and down by one after calling the corresponding
  12. * {@link #doEnd} that closes the loop. This will be 1 on the first {@link #doFor}
  13. * call.
  14. */
  15. /**
  16. * This method is called to process a piece of raw text from the tpl.
  17. * @param {String} text
  18. * @method doText
  19. */
  20. // doText: function (text)
  21. /**
  22. * This method is called to process expressions (like `{[expr]}`).
  23. * @param {String} expr The body of the expression (inside "{[" and "]}").
  24. * @method doExpr
  25. */
  26. // doExpr: function (expr)
  27. /**
  28. * This method is called to process simple tags (like `{tag}`).
  29. * @method doTag
  30. */
  31. // doTag: function (tag)
  32. /**
  33. * This method is called to process `<tpl else>`.
  34. * @method doElse
  35. */
  36. // doElse: function ()
  37. /**
  38. * This method is called to process `{% text %}`.
  39. * @param {String} text
  40. * @method doEval
  41. */
  42. // doEval: function (text)
  43. /**
  44. * This method is called to process `<tpl if="action">`. If there are other attributes,
  45. * these are passed in the actions object.
  46. * @param {String} action
  47. * @param {Object} actions Other actions keyed by the attribute name (such as 'exec').
  48. * @method doIf
  49. */
  50. // doIf: function (action, actions)
  51. /**
  52. * This method is called to process `<tpl elseif="action">`. If there are other attributes,
  53. * these are passed in the actions object.
  54. * @param {String} action
  55. * @param {Object} actions Other actions keyed by the attribute name (such as 'exec').
  56. * @method doElseIf
  57. */
  58. // doElseIf: function (action, actions)
  59. /**
  60. * This method is called to process `<tpl switch="action">`. If there are other attributes,
  61. * these are passed in the actions object.
  62. * @param {String} action
  63. * @param {Object} actions Other actions keyed by the attribute name (such as 'exec').
  64. * @method doSwitch
  65. */
  66. // doSwitch: function (action, actions)
  67. /**
  68. * This method is called to process `<tpl case="action">`. If there are other attributes,
  69. * these are passed in the actions object.
  70. * @param {String} action
  71. * @param {Object} actions Other actions keyed by the attribute name (such as 'exec').
  72. * @method doCase
  73. */
  74. // doCase: function (action, actions)
  75. /**
  76. * This method is called to process `<tpl default>`.
  77. * @method doDefault
  78. */
  79. // doDefault: function ()
  80. /**
  81. * This method is called to process `</tpl>`. It is given the action type that started
  82. * the tpl and the set of additional actions.
  83. * @param {String} type The type of action that is being ended.
  84. * @param {Object} actions The other actions keyed by the attribute name (such as 'exec').
  85. * @method doEnd
  86. */
  87. // doEnd: function (type, actions)
  88. /**
  89. * This method is called to process `<tpl for="action">`. If there are other attributes,
  90. * these are passed in the actions object.
  91. * @param {String} action
  92. * @param {Object} actions Other actions keyed by the attribute name (such as 'exec').
  93. * @method doFor
  94. */
  95. // doFor: function (action, actions)
  96. /**
  97. * This method is called to process `<tpl exec="action">`. If there are other attributes,
  98. * these are passed in the actions object.
  99. * @param {String} action
  100. * @param {Object} actions Other actions keyed by the attribute name.
  101. * @method doExec
  102. */
  103. // doExec: function (action, actions)
  104. /**
  105. * This method is called to process an empty `<tpl>`. This is unlikely to need to be
  106. * implemented, so a default (do nothing) version is provided.
  107. * @method
  108. */
  109. doTpl: Ext.emptyFn,
  110. parse: function (str) {
  111. var me = this,
  112. len = str.length,
  113. aliases = { elseif: 'elif' },
  114. topRe = me.topRe,
  115. actionsRe = me.actionsRe,
  116. index, stack, s, m, t, prev, frame, subMatch, begin, end, actions,
  117. prop;
  118. me.level = 0;
  119. me.stack = stack = [];
  120. for (index = 0; index < len; index = end) {
  121. topRe.lastIndex = index;
  122. m = topRe.exec(str);
  123. if (!m) {
  124. me.doText(str.substring(index, len));
  125. break;
  126. }
  127. begin = m.index;
  128. end = topRe.lastIndex;
  129. if (index < begin) {
  130. me.doText(str.substring(index, begin));
  131. }
  132. if (m[1]) {
  133. end = str.indexOf('%}', begin+2);
  134. me.doEval(str.substring(begin+2, end));
  135. end += 2;
  136. } else if (m[2]) {
  137. end = str.indexOf(']}', begin+2);
  138. me.doExpr(str.substring(begin+2, end));
  139. end += 2;
  140. } else if (m[3]) { // if ('{' token)
  141. me.doTag(m[3]);
  142. } else if (m[4]) { // content of a <tpl xxxxxx xxx> tag
  143. actions = null;
  144. while ((subMatch = actionsRe.exec(m[4])) !== null) {
  145. s = subMatch[2] || subMatch[3];
  146. if (s) {
  147. s = Ext.String.htmlDecode(s); // decode attr value
  148. t = subMatch[1];
  149. t = aliases[t] || t;
  150. actions = actions || {};
  151. prev = actions[t];
  152. if (typeof prev == 'string') {
  153. actions[t] = [prev, s];
  154. } else if (prev) {
  155. actions[t].push(s);
  156. } else {
  157. actions[t] = s;
  158. }
  159. }
  160. }
  161. if (!actions) {
  162. if (me.elseRe.test(m[4])) {
  163. me.doElse();
  164. } else if (me.defaultRe.test(m[4])) {
  165. me.doDefault();
  166. } else {
  167. me.doTpl();
  168. stack.push({ type: 'tpl' });
  169. }
  170. }
  171. else if (actions['if']) {
  172. me.doIf(actions['if'], actions);
  173. stack.push({ type: 'if' });
  174. }
  175. else if (actions['switch']) {
  176. me.doSwitch(actions['switch'], actions);
  177. stack.push({ type: 'switch' });
  178. }
  179. else if (actions['case']) {
  180. me.doCase(actions['case'], actions);
  181. }
  182. else if (actions['elif']) {
  183. me.doElseIf(actions['elif'], actions);
  184. }
  185. else if (actions['for']) {
  186. ++me.level;
  187. // Extract property name to use from indexed item
  188. if (prop = me.propRe.exec(m[4])) {
  189. actions.propName = prop[1] || prop[2];
  190. }
  191. me.doFor(actions['for'], actions);
  192. stack.push({ type: 'for', actions: actions });
  193. }
  194. else if (actions.exec) {
  195. me.doExec(actions.exec, actions);
  196. stack.push({ type: 'exec', actions: actions });
  197. }
  198. /*
  199. else {
  200. // todo - error
  201. }
  202. */
  203. } else if (m[0].length === 5) {
  204. // if the length of m[0] is 5, assume that we're dealing with an opening tpl tag with no attributes (e.g. <tpl>...</tpl>)
  205. // in this case no action is needed other than pushing it on to the stack
  206. stack.push({ type: 'tpl' });
  207. } else {
  208. frame = stack.pop();
  209. me.doEnd(frame.type, frame.actions);
  210. if (frame.type == 'for') {
  211. --me.level;
  212. }
  213. }
  214. }
  215. },
  216. // Internal regexes
  217. topRe: /(?:(\{\%)|(\{\[)|\{([^{}]*)\})|(?:<tpl([^>]*)\>)|(?:<\/tpl>)/g,
  218. actionsRe: /\s*(elif|elseif|if|for|exec|switch|case|eval)\s*\=\s*(?:(?:"([^"]*)")|(?:'([^']*)'))\s*/g,
  219. propRe: /prop=(?:(?:"([^"]*)")|(?:'([^']*)'))/,
  220. defaultRe: /^\s*default\s*$/,
  221. elseRe: /^\s*else\s*$/
  222. });