No puede seleccionar más de 25 temas Los temas deben comenzar con una letra o número, pueden incluir guiones ('-') y pueden tener hasta 35 caracteres de largo.

229 líneas
6.3KB

  1. /*
  2. Shamless port of http://github.com/defunkt/mustache
  3. by Jan Lehnardt <jan@apache.org>, Alexander Lang <alex@upstream-berlin.com>,
  4. Sebastian Cohnen <sebastian.cohnen@googlemail.com>
  5. Thanks @defunkt for the awesome code.
  6. See http://github.com/defunkt/mustache for more info.
  7. */
  8. var Mustache = function() {
  9. var Renderer = function() {};
  10. Renderer.prototype = {
  11. otag: "{{",
  12. ctag: "}}",
  13. render: function(template, context) {
  14. // fail fast
  15. if(template.indexOf(this.otag) == -1) {
  16. return template;
  17. }
  18. var html = this.render_section(template, context);
  19. return this.render_tags(html, context);
  20. },
  21. create_context: function(_context) {
  22. if(this.is_object(_context)) {
  23. return _context;
  24. } else {
  25. return {'.': _context};
  26. }
  27. },
  28. is_object: function(a) {
  29. return a && typeof a == 'object'
  30. },
  31. /*
  32. Tries to find a partial in the global scope and render it
  33. */
  34. render_partial: function(name, context) {
  35. // FIXME: too hacky
  36. var evil_name = eval(name);
  37. switch(typeof evil_name) {
  38. case "string": // a string partial, we simply render
  39. return this.render(evil_name, context);
  40. case "object": // a view partial needs a `name_template` template
  41. var tpl = name + "_template";
  42. return this.render(eval(tpl), evil_name);
  43. default: // should not happen #famouslastwords
  44. throw("Unknown partial type.");
  45. }
  46. },
  47. /*
  48. Renders boolean and enumerable sections
  49. */
  50. render_section: function(template, context) {
  51. if(template.indexOf(this.otag + "#") == -1) {
  52. return template;
  53. }
  54. var that = this;
  55. var regex = new RegExp(this.otag + "\\#(.+)" + this.ctag +
  56. "\\s*([\\s\\S]+)" + this.otag + "\\/\\1" + this.ctag + "\\s*", "mg");
  57. // for each {{#foo}}{{/foo}} section do...
  58. return template.replace(regex, function(match, name, content) {
  59. var value = that.find(name, context);
  60. if(that.is_array(value)) { // Enumerable, Let's loop!
  61. return value.map(function(row) {
  62. return that.render(content, that.merge(context, that.create_context(row)));
  63. }).join('');
  64. } else if(value) { // boolean section
  65. return that.render(content, context);
  66. } else {
  67. return "";
  68. }
  69. }
  70. );
  71. },
  72. /*
  73. Replace {{foo}} and friends with values from our view
  74. */
  75. render_tags: function(template, context) {
  76. var lines = template.split("\n");
  77. var new_regex = function() {
  78. return new RegExp(that.otag +
  79. "(=|!|<|\\{)?([^\/#]+?)\\1?" +
  80. that.ctag + "+",
  81. "g");
  82. };
  83. // tit for tat
  84. var that = this;
  85. var regex = new_regex();
  86. for (var i=0; i < lines.length; i++) {
  87. lines[i] = lines[i].replace(regex,
  88. function (match,operator,name) {
  89. switch(operator) {
  90. case "!": // ignore comments
  91. return match;
  92. case "=": // set new delimiters, rebuild the replace regexp
  93. that.set_delimiters(name);
  94. regex = new_regex();
  95. // redo the line in order to get tags with the new delimiters on the same line
  96. i--;
  97. return "";
  98. case "<": // render partial
  99. return that.render_partial(name, context);
  100. case "{": // the triple mustache is unescaped
  101. return that.find(name, context);
  102. default: // escape the value
  103. return that.escape(that.find(name, context));
  104. }},
  105. this);
  106. };
  107. return lines.join("\n");
  108. },
  109. set_delimiters: function(delimiters) {
  110. var dels = delimiters.split(" ");
  111. this.otag = this.escape_regex(dels[0]);
  112. this.ctag = this.escape_regex(dels[1]);
  113. },
  114. escape_regex: function(text) {
  115. // thank you Simon Willison
  116. if(!arguments.callee.sRE) {
  117. var specials = [
  118. '/', '.', '*', '+', '?', '|',
  119. '(', ')', '[', ']', '{', '}', '\\'
  120. ];
  121. arguments.callee.sRE = new RegExp(
  122. '(\\' + specials.join('|\\') + ')', 'g'
  123. );
  124. }
  125. return text.replace(arguments.callee.sRE, '\\$1');
  126. },
  127. /*
  128. find `name` in current `context`. That is find me a value
  129. from the view object
  130. */
  131. find: function(name, context) {
  132. name = this.trim(name);
  133. if(typeof context[name] === "function") {
  134. return context[name].apply(context);
  135. }
  136. if(context[name] !== undefined) {
  137. return context[name];
  138. }
  139. throw("Can't find " + name + " in " + context);
  140. },
  141. // Utility methods
  142. /*
  143. Does away with nasty characters
  144. */
  145. escape: function(s) {
  146. return s.toString().replace(/[&"<>\\]/g, function(s) {
  147. switch(s) {
  148. case "&": return "&amp;";
  149. case "\\": return "\\\\";;
  150. case '"': return '\"';;
  151. case "<": return "&lt;";
  152. case ">": return "&gt;";
  153. default: return s;
  154. }
  155. });
  156. },
  157. /*
  158. Merges all properties of object `b` into object `a`.
  159. `b.property` overwrites a.property`
  160. */
  161. merge: function(a, b) {
  162. var _new = {};
  163. for(var name in a) {
  164. if(a.hasOwnProperty(name)) {
  165. _new[name] = a[name];
  166. }
  167. };
  168. for(var name in b) {
  169. if(b.hasOwnProperty(name)) {
  170. _new[name] = b[name];
  171. }
  172. };
  173. return _new;
  174. },
  175. /*
  176. Thanks Doug Crockford
  177. JavaScript — The Good Parts lists an alternative that works better with
  178. frames. Frames can suck it, we use the simple version.
  179. */
  180. is_array: function(a) {
  181. return (a &&
  182. typeof a === 'object' &&
  183. a.constructor === Array);
  184. },
  185. /*
  186. Gets rid of leading and trailing whitespace
  187. */
  188. trim: function(s) {
  189. return s.replace(/^\s*|\s*$/g, '');
  190. }
  191. };
  192. return({
  193. name: "mustache.js",
  194. version: "0.1",
  195. /*
  196. Turns a template and view into HTML
  197. */
  198. to_html: function(template, view) {
  199. return new Renderer().render(template, view);
  200. }
  201. });
  202. }();