template.js 7.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250
  1. /*
  2. Copyright 2012, KISSY UI Library v1.20
  3. MIT Licensed
  4. build time: Jan 10 19:03
  5. */
  6. /**
  7. * @fileoverview KISSY Template Engine.
  8. * @author yyfrankyy@gmail.com
  9. */
  10. KISSY.add('template/base', function(S) {
  11. var // Template Cache
  12. templateCache = {},
  13. // start/end tag mark
  14. tagStartEnd = {
  15. '#': 'start',
  16. '/': 'end'
  17. },
  18. // static string
  19. KS_TEMPL_STAT_PARAM = 'KS_TEMPL_STAT_PARAM',
  20. KS_TEMPL_STAT_PARAM_REG = new RegExp(KS_TEMPL_STAT_PARAM, "g"),
  21. KS_TEMPL = 'KS_TEMPL',
  22. KS_DATA = 'KS_DATA_',
  23. KS_AS = 'as',
  24. // note : double quote for generated code
  25. PREFIX = '");',
  26. SUFFIX = KS_TEMPL + '.push("',
  27. PARSER_SYNTAX_ERROR = 'KISSY.Template: Syntax Error. ',
  28. PARSER_RENDER_ERROR = 'KISSY.Template: Render Error. ',
  29. PARSER_PREFIX = 'var ' + KS_TEMPL + '=[],' +
  30. KS_TEMPL_STAT_PARAM + '=false;with(',
  31. PARSER_MIDDLE = '||{}){try{' + KS_TEMPL + '.push("',
  32. PARSER_SUFFIX = '");}catch(e){' + KS_TEMPL + '=["' +
  33. PARSER_RENDER_ERROR + '" + e.message]}};return ' +
  34. KS_TEMPL + '.join("");',
  35. // restore double quote in logic template variable
  36. restoreQuote = function(str) {
  37. return str.replace(/\\"/g, '"');
  38. },
  39. // escape double quote in template
  40. escapeQuote = function(str) {
  41. return str.replace(/"/g, '\\"');
  42. },
  43. trim = S.trim,
  44. // build a static parser
  45. buildParser = function(tpl) {
  46. var _parser,
  47. _empty_index;
  48. return escapeQuote(trim(tpl)
  49. .replace(/[\r\t\n]/g, ' ')
  50. // escape escape ... . in case \ is consumed when run tpl parser function
  51. // '{{y}}\\x{{/y}}' =>tmpl.push('\x'); => tmpl.push('\\x');
  52. .replace(/\\/g, '\\\\'))
  53. .replace(/\{\{([#/]?)(?!\}\})([^}]*)\}\}/g,
  54. function(all, expr, body) {
  55. _parser = "";
  56. // must restore quote , if str is used as code directly
  57. body = restoreQuote(trim(body));
  58. //body = trim(body);
  59. // is an expression
  60. if (expr) {
  61. _empty_index = body.indexOf(' ');
  62. body = _empty_index === -1 ?
  63. [ body, '' ] :
  64. [
  65. body.substring(0, _empty_index),
  66. body.substring(_empty_index)
  67. ];
  68. var operator = body[0],
  69. fn,
  70. args = trim(body[1]),
  71. opStatement = Statements[operator];
  72. if (opStatement && tagStartEnd[expr]) {
  73. // get expression definition function/string
  74. fn = opStatement[tagStartEnd[expr]];
  75. _parser = String(S.isFunction(fn) ?
  76. fn.apply(this, args.split(/\s+/)) :
  77. fn.replace(KS_TEMPL_STAT_PARAM_REG, args));
  78. }
  79. }
  80. // return array directly
  81. else {
  82. _parser = KS_TEMPL +
  83. '.push(' +
  84. // prevent variable undefined error when look up in with ,simple variable substitution
  85. // with({}){alert(x);} => ReferenceError: x is not defined
  86. 'typeof (' + body + ') ==="undefined"?"":' + body +
  87. ');';
  88. }
  89. return PREFIX + _parser + SUFFIX;
  90. });
  91. },
  92. // expression
  93. Statements = {
  94. 'if': {
  95. start: 'if(typeof (' + KS_TEMPL_STAT_PARAM + ') !=="undefined" && ' + KS_TEMPL_STAT_PARAM + '){',
  96. end: '}'
  97. },
  98. 'else': {
  99. start: '}else{'
  100. },
  101. 'elseif': {
  102. start: '}else if(' + KS_TEMPL_STAT_PARAM + '){'
  103. },
  104. // KISSY.each function wrap
  105. 'each': {
  106. start: function(obj, as, v, k) {
  107. var _ks_value = '_ks_value',
  108. _ks_index = '_ks_index';
  109. if (as === KS_AS && v) {
  110. _ks_value = v || _ks_value,
  111. _ks_index = k || _ks_index;
  112. }
  113. return 'KISSY.each(' + obj +
  114. ', function(' + _ks_value +
  115. ', ' + _ks_index + '){';
  116. },
  117. end: '});'
  118. },
  119. // comments
  120. '!': {
  121. start: '/*' + KS_TEMPL_STAT_PARAM + '*/'
  122. }
  123. };
  124. /**
  125. * Template
  126. * @param {String} tpl template to be rendered.
  127. * @return {Object} return this for chain.
  128. */
  129. function Template(tpl) {
  130. if (!(templateCache[tpl])) {
  131. var _ks_data = S.guid(KS_DATA),
  132. func,
  133. o,
  134. _parser = [
  135. PARSER_PREFIX,
  136. _ks_data,
  137. PARSER_MIDDLE,
  138. o = buildParser(tpl),
  139. PARSER_SUFFIX
  140. ];
  141. try {
  142. func = new Function(_ks_data, _parser.join(""));
  143. } catch (e) {
  144. _parser[3] = PREFIX + SUFFIX +
  145. PARSER_SYNTAX_ERROR + ',' +
  146. e.message + PREFIX + SUFFIX;
  147. func = new Function(_ks_data, _parser.join(""));
  148. }
  149. templateCache[tpl] = {
  150. name: _ks_data,
  151. o:o,
  152. parser: _parser.join(""),
  153. render: func
  154. };
  155. }
  156. return templateCache[tpl];
  157. }
  158. S.mix(Template, {
  159. /**
  160. * Logging Compiled Template Codes
  161. * @param {String} tpl template string.
  162. */
  163. log: function(tpl) {
  164. if (tpl in templateCache) {
  165. if ('js_beautify' in window) {
  166. // S.log(js_beautify(templateCache[tpl].parser, {
  167. // indent_size: 4,
  168. // indent_char: ' ',
  169. // preserve_newlines: true,
  170. // braces_on_own_line: false,
  171. // keep_array_indentation: false,
  172. // space_after_anon_function: true
  173. // }), 'info');
  174. } else {
  175. S.log(templateCache[tpl].parser, 'info');
  176. }
  177. } else {
  178. Template(tpl);
  179. this.log(tpl);
  180. }
  181. },
  182. /**
  183. * add statement for extending template tags
  184. * @param {String} statement tag name.
  185. * @param {String} o extent tag object.
  186. */
  187. addStatement: function(statement, o) {
  188. if (S.isString(statement)) {
  189. Statements[statement] = o;
  190. } else {
  191. S.mix(Statements, statement);
  192. }
  193. }
  194. });
  195. return Template;
  196. });
  197. /**
  198. * 2011-09-20 note by yiminghe :
  199. * - code style change
  200. * - remove reg cache , ugly to see
  201. * - fix escape by escape
  202. * - expect(T('{{#if a=="a"}}{{b}}\\"{{/if}}').render({a:"a",b:"b"})).toBe('b\\"');
  203. */
  204. /**
  205. * @fileoverview KISSY.Template Node.
  206. * @author 文河<wenhe@taobao.com>
  207. */
  208. KISSY.add('template/node', function(S, Template, Node) {
  209. var $ = Node.all;
  210. S.mix(S, {
  211. tmpl: function(selector, data) {
  212. return $(Template($(selector).html()).render(data));
  213. }
  214. });
  215. }, {requires:["./base",'node']});
  216. KISSY.add("template", function(S, T) {
  217. S.Template = T;
  218. return T;
  219. }, {
  220. requires:["template/base","template/node"]
  221. });