Du kannst nicht mehr als 25 Themen auswählen Themen müssen entweder mit einem Buchstaben oder einer Ziffer beginnen. Sie können Bindestriche („-“) enthalten und bis zu 35 Zeichen lang sein.

306 Zeilen
8.1KB

  1. /*
  2. Shameless port of http://github.com/defunkt/mustache
  3. by Jan Lehnardt <jan@apache.org>,
  4. Alexander Lang <alex@upstream-berlin.com>,
  5. Sebastian Cohnen <sebastian.cohnen@googlemail.com>
  6. Thanks @defunkt for the awesome code.
  7. See http://github.com/defunkt/mustache for more info.
  8. */
  9. var Mustache = function() {
  10. var Renderer = function() {};
  11. Renderer.prototype = {
  12. otag: "{{",
  13. ctag: "}}",
  14. pragmas: {},
  15. buffer: [],
  16. pragmas_parsed: false,
  17. pragmas_implemented: {
  18. "IMPLICIT-ITERATOR": true
  19. },
  20. render: function(template, context, partials, in_recursion) {
  21. // fail fast
  22. if(template.indexOf(this.otag) == -1) {
  23. if(in_recursion) {
  24. return template;
  25. } else {
  26. this.send(template);
  27. return;
  28. }
  29. }
  30. if(!in_recursion) {
  31. this.buffer = [];
  32. }
  33. if(!this.pragmas_parsed) {
  34. template = this.render_pragmas(template);
  35. }
  36. var html = this.render_section(template, context, partials);
  37. if(in_recursion) {
  38. return this.render_tags(html, context, partials, in_recursion);
  39. }
  40. this.render_tags(html, context, partials, in_recursion);
  41. },
  42. /*
  43. Sends parsed lines
  44. */
  45. send: function(line) {
  46. if(line != "") {
  47. this.buffer.push(line);
  48. }
  49. },
  50. /*
  51. Looks for %PRAGMAS
  52. */
  53. render_pragmas: function(template) {
  54. this.pragmas_parsed = true;
  55. // no pragmas
  56. if(template.indexOf(this.otag + "%") == -1) {
  57. return template;
  58. }
  59. var that = this;
  60. var regex = new RegExp(this.otag + "%([\\w_-]+) ?([\\w]+=[\\w]+)?"
  61. + this.ctag);
  62. return template.replace(regex, function(match, pragma, options) {
  63. if(!that.pragmas_implemented[pragma]) {
  64. throw({message: "This implementation of mustache doesn't understand the '"
  65. + pragma + "' pragma"});
  66. }
  67. that.pragmas[pragma] = {};
  68. if(options) {
  69. var opts = options.split("=");
  70. that.pragmas[pragma][opts[0]] = opts[1];
  71. }
  72. return "";
  73. // ignore unknown pragmas silently
  74. });
  75. },
  76. /*
  77. Tries to find a partial in the global scope and render it
  78. */
  79. render_partial: function(name, context, partials) {
  80. if(typeof(context[name]) != "object") {
  81. throw({message: "subcontext for '" + name + "' is not an object"});
  82. }
  83. if(!partials || !partials[name]) {
  84. throw({message: "unknown_partial '" + name + "'"});
  85. }
  86. return this.render(partials[name], context[name], partials, true);
  87. },
  88. /*
  89. Renders boolean and enumerable sections
  90. */
  91. render_section: function(template, context, partials) {
  92. if(template.indexOf(this.otag + "#") == -1) {
  93. return template;
  94. }
  95. var that = this;
  96. // CSW - Added "+?" so it finds the tighest bound, not the widest
  97. var regex = new RegExp(this.otag + "\\#(.+)" + this.ctag +
  98. "\\s*([\\s\\S]+?)" + this.otag + "\\/\\1" + this.ctag + "\\s*", "mg");
  99. // for each {{#foo}}{{/foo}} section do...
  100. return template.replace(regex, function(match, name, content) {
  101. var value = that.find(name, context);
  102. if(that.is_array(value)) { // Enumerable, Let's loop!
  103. return that.map(value, function(row) {
  104. return that.render(content, that.merge(context,
  105. that.create_context(row)), partials, true);
  106. }).join("");
  107. } else if(value) { // boolean section
  108. return that.render(content, context, partials, true);
  109. } else {
  110. return "";
  111. }
  112. });
  113. },
  114. /*
  115. Replace {{foo}} and friends with values from our view
  116. */
  117. render_tags: function(template, context, partials, in_recursion) {
  118. // tit for tat
  119. var that = this;
  120. var new_regex = function() {
  121. return new RegExp(that.otag + "(=|!|>|\\{|%)?([^\/#]+?)\\1?" +
  122. that.ctag + "+", "g");
  123. };
  124. var regex = new_regex();
  125. var lines = template.split("\n");
  126. for (var i=0; i < lines.length; i++) {
  127. lines[i] = lines[i].replace(regex, function(match, operator, name) {
  128. switch(operator) {
  129. case "!": // ignore comments
  130. return match;
  131. case "=": // set new delimiters, rebuild the replace regexp
  132. that.set_delimiters(name);
  133. regex = new_regex();
  134. return "";
  135. case ">": // render partial
  136. return that.render_partial(name, context, partials);
  137. case "{": // the triple mustache is unescaped
  138. return that.find(name, context);
  139. default: // escape the value
  140. return that.escape(that.find(name, context));
  141. }
  142. }, this);
  143. if(!in_recursion) {
  144. this.send(lines[i]);
  145. }
  146. }
  147. if(in_recursion) {
  148. return lines.join("\n");
  149. }
  150. },
  151. set_delimiters: function(delimiters) {
  152. var dels = delimiters.split(" ");
  153. this.otag = this.escape_regex(dels[0]);
  154. this.ctag = this.escape_regex(dels[1]);
  155. },
  156. escape_regex: function(text) {
  157. // thank you Simon Willison
  158. if(!arguments.callee.sRE) {
  159. var specials = [
  160. '/', '.', '*', '+', '?', '|',
  161. '(', ')', '[', ']', '{', '}', '\\'
  162. ];
  163. arguments.callee.sRE = new RegExp(
  164. '(\\' + specials.join('|\\') + ')', 'g'
  165. );
  166. }
  167. return text.replace(arguments.callee.sRE, '\\$1');
  168. },
  169. /*
  170. find `name` in current `context`. That is find me a value
  171. from the view object
  172. */
  173. find: function(name, context) {
  174. name = this.trim(name);
  175. if(typeof context[name] === "function") {
  176. return context[name].apply(context);
  177. }
  178. if(context[name] !== undefined) {
  179. return context[name];
  180. }
  181. // silently ignore unkown variables
  182. return "";
  183. },
  184. // Utility methods
  185. /*
  186. Does away with nasty characters
  187. */
  188. escape: function(s) {
  189. return ((s == null) ? "" : s).toString().replace(/[&"<>\\]/g, function(s) {
  190. switch(s) {
  191. case "&": return "&amp;";
  192. case "\\": return "\\\\";;
  193. case '"': return '\"';;
  194. case "<": return "&lt;";
  195. case ">": return "&gt;";
  196. default: return s;
  197. }
  198. });
  199. },
  200. /*
  201. Merges all properties of object `b` into object `a`.
  202. `b.property` overwrites a.property`
  203. */
  204. merge: function(a, b) {
  205. var _new = {};
  206. for(var name in a) {
  207. if(a.hasOwnProperty(name)) {
  208. _new[name] = a[name];
  209. }
  210. };
  211. for(var name in b) {
  212. if(b.hasOwnProperty(name)) {
  213. _new[name] = b[name];
  214. }
  215. };
  216. return _new;
  217. },
  218. // by @langalex, support for arrays of strings
  219. create_context: function(_context) {
  220. if(this.is_object(_context)) {
  221. return _context;
  222. } else if(this.pragmas["IMPLICIT-ITERATOR"]) {
  223. var iterator = this.pragmas["IMPLICIT-ITERATOR"].iterator || ".";
  224. var ctx = {};
  225. ctx[iterator] = _context
  226. return ctx;
  227. }
  228. },
  229. is_object: function(a) {
  230. return a && typeof a == "object";
  231. },
  232. is_array: function(a) {
  233. return Object.prototype.toString.call(a) === '[object Array]';
  234. },
  235. /*
  236. Gets rid of leading and trailing whitespace
  237. */
  238. trim: function(s) {
  239. return s.replace(/^\s*|\s*$/g, "");
  240. },
  241. /*
  242. Why, why, why? Because IE. Cry, cry cry.
  243. */
  244. map: function(array, fn) {
  245. if (typeof array.map == "function") {
  246. return array.map(fn)
  247. } else {
  248. var r = [];
  249. var l = array.length;
  250. for(i=0;i<l;i++) {
  251. r.push(fn(array[i]));
  252. }
  253. return r;
  254. }
  255. }
  256. };
  257. return({
  258. name: "mustache.js",
  259. version: "0.2.3-dev",
  260. /*
  261. Turns a template and view into HTML
  262. */
  263. to_html: function(template, view, partials, send_fun) {
  264. var renderer = new Renderer();
  265. if(send_fun) {
  266. renderer.send = send_fun;
  267. }
  268. renderer.render(template, view, partials);
  269. if(!send_fun) {
  270. return renderer.buffer.join("\n");
  271. }
  272. }
  273. });
  274. }();