├── examples ├── empty_partial.2.html ├── error_not_found.txt ├── i18n.txt ├── two_sections.txt ├── apostrophe.txt ├── double_render.txt ├── empty_sections.txt ├── error_not_found.html ├── array_partial.html ├── empty_partial.txt ├── two_in_a_row.txt ├── unescaped.html ├── apostrophe.html ├── array_of_strings.txt ├── bug_11_eating_whitespace.txt ├── i18n.html ├── inverted_section.txt ├── two_sections.js ├── unescaped.txt ├── array_of_partials_partial.2.html ├── comments.txt ├── i18n.js ├── template_partial.2.html ├── two_in_a_row.html ├── array_of_strings_options.txt ├── bug_11_eating_whitespace.html ├── double_render.html ├── empty_sections.js ├── empty_template.js ├── error_not_found.js ├── partial_recursion.txt ├── unknown_pragma.js ├── array_of_partials_implicit_partial.2.html ├── empty_partial.html ├── empty_sections.html ├── higher_order_sections.txt ├── nesting.txt ├── template_partial.html ├── unknown_pragma.html ├── apostrophe.js ├── empty_partial.js ├── escaped.html ├── escaped.txt ├── template_partial.txt ├── array_of_strings.html ├── higher_order_sections.html ├── two_sections.html ├── empty_template.html ├── inverted_section.js ├── array_of_partials_partial.txt ├── empty_template.txt ├── nesting.html ├── partial_recursion.html ├── comments.html ├── recursion_with_same_names.txt ├── view_partial.html ├── array_of_partials_implicit_partial.txt ├── array_of_strings.js ├── bug_11_eating_whitespace.js ├── partial_recursion.2.html ├── simple.txt ├── array_partial.txt ├── inverted_section.html ├── null_string.txt ├── reuse_of_enumerables.txt ├── two_in_a_row.js ├── array_partial.2.html ├── whitespace_partial.html ├── double_render.js ├── array_of_partials_implicit_partial.js ├── array_of_partials_partial.html ├── array_partial.js ├── unescaped.js ├── array_of_partials_implicit_partial.html ├── array_of_strings_options.js ├── comments.js ├── null_string.html ├── array_of_partials_partial.js ├── nesting.js ├── unknown_pragma.txt ├── delimiters.html ├── escaped.js ├── recursion_with_same_names.html ├── simple.html ├── array_of_strings_options.html ├── reuse_of_enumerables.html ├── reuse_of_enumerables.js ├── view_partial.2.html ├── view_partial.txt ├── whitespace_partial.2.html ├── whitespace_partial.txt ├── template_partial.js ├── delimiters.txt ├── complex.txt ├── simple.js ├── section_as_context.txt ├── null_string.js ├── recursion_with_same_names.js ├── partial_recursion.js ├── section_as_context.html ├── delimiters.js ├── section_as_context.js ├── higher_order_sections.js ├── complex.html ├── view_partial.js ├── whitespace_partial.js └── complex.js ├── mustache-yui3 ├── mustache.js.tpl.pre └── mustache.js.tpl.post ├── mustache-requirejs ├── requirejs.mustache.js.tpl.post └── requirejs.mustache.js.tpl.pre ├── .gitignore ├── mustache-dojo ├── mustache.js.tpl.post └── mustache.js.tpl.pre ├── mustache-commonjs ├── mustache.js.tpl.pre ├── mustache.js.tpl.post └── package.json ├── mustache-jquery ├── jquery.mustache.js.tpl.post └── jquery.mustache.js.tpl.pre ├── lib └── package.json ├── THANKS.md ├── LICENSE ├── CHANGES.md ├── Rakefile ├── test └── mustache_spec.rb ├── README.md └── mustache.js /examples/empty_partial.2.html: -------------------------------------------------------------------------------- 1 | yo -------------------------------------------------------------------------------- /examples/error_not_found.txt: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /examples/i18n.txt: -------------------------------------------------------------------------------- 1 | foo BAR 2 | -------------------------------------------------------------------------------- /examples/two_sections.txt: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /examples/apostrophe.txt: -------------------------------------------------------------------------------- 1 | 'X 2 | -------------------------------------------------------------------------------- /examples/double_render.txt: -------------------------------------------------------------------------------- 1 | {{win}} 2 | -------------------------------------------------------------------------------- /examples/empty_sections.txt: -------------------------------------------------------------------------------- 1 | foo 2 | -------------------------------------------------------------------------------- /examples/error_not_found.html: -------------------------------------------------------------------------------- 1 | {{foo}} -------------------------------------------------------------------------------- /examples/array_partial.html: -------------------------------------------------------------------------------- 1 | {{>partial}} -------------------------------------------------------------------------------- /examples/empty_partial.txt: -------------------------------------------------------------------------------- 1 | hey 1 2 | yo 3 | -------------------------------------------------------------------------------- /examples/two_in_a_row.txt: -------------------------------------------------------------------------------- 1 | Welcome, Joe! 2 | -------------------------------------------------------------------------------- /examples/unescaped.html: -------------------------------------------------------------------------------- 1 |

{{{title}}}

-------------------------------------------------------------------------------- /examples/apostrophe.html: -------------------------------------------------------------------------------- 1 | {{apos}}{{control}} 2 | -------------------------------------------------------------------------------- /examples/array_of_strings.txt: -------------------------------------------------------------------------------- 1 | hello world 2 | -------------------------------------------------------------------------------- /examples/bug_11_eating_whitespace.txt: -------------------------------------------------------------------------------- 1 | yo foo 2 | -------------------------------------------------------------------------------- /examples/i18n.html: -------------------------------------------------------------------------------- 1 | {{_i}}foo {{bar}}{{/i}} 2 | -------------------------------------------------------------------------------- /examples/inverted_section.txt: -------------------------------------------------------------------------------- 1 | No repos :( 2 | -------------------------------------------------------------------------------- /examples/two_sections.js: -------------------------------------------------------------------------------- 1 | var two_sections = {}; -------------------------------------------------------------------------------- /examples/unescaped.txt: -------------------------------------------------------------------------------- 1 |

Bear > Shark

2 | -------------------------------------------------------------------------------- /examples/array_of_partials_partial.2.html: -------------------------------------------------------------------------------- 1 | {{i}} 2 | -------------------------------------------------------------------------------- /examples/comments.txt: -------------------------------------------------------------------------------- 1 |

A Comedy of Errors

2 | -------------------------------------------------------------------------------- /examples/i18n.js: -------------------------------------------------------------------------------- 1 | var i18n = { 2 | bar: "BAR" 3 | }; -------------------------------------------------------------------------------- /examples/template_partial.2.html: -------------------------------------------------------------------------------- 1 | Again, {{again}}! -------------------------------------------------------------------------------- /examples/two_in_a_row.html: -------------------------------------------------------------------------------- 1 | {{greeting}}, {{name}}! -------------------------------------------------------------------------------- /examples/array_of_strings_options.txt: -------------------------------------------------------------------------------- 1 | hello world 2 | -------------------------------------------------------------------------------- /examples/bug_11_eating_whitespace.html: -------------------------------------------------------------------------------- 1 | {{tag}} foo 2 | -------------------------------------------------------------------------------- /examples/double_render.html: -------------------------------------------------------------------------------- 1 | {{#foo}}{{bar}}{{/foo}} 2 | -------------------------------------------------------------------------------- /examples/empty_sections.js: -------------------------------------------------------------------------------- 1 | var empty_sections = {}; 2 | -------------------------------------------------------------------------------- /examples/empty_template.js: -------------------------------------------------------------------------------- 1 | var empty_template = {}; 2 | -------------------------------------------------------------------------------- /examples/error_not_found.js: -------------------------------------------------------------------------------- 1 | var error_not_found = {bar: 2}; -------------------------------------------------------------------------------- /examples/partial_recursion.txt: -------------------------------------------------------------------------------- 1 | 1 2 | 1.1 3 | 1.1.1 4 | -------------------------------------------------------------------------------- /examples/unknown_pragma.js: -------------------------------------------------------------------------------- 1 | var unknown_pragma = {}; 2 | -------------------------------------------------------------------------------- /examples/array_of_partials_implicit_partial.2.html: -------------------------------------------------------------------------------- 1 | {{.}} 2 | -------------------------------------------------------------------------------- /examples/empty_partial.html: -------------------------------------------------------------------------------- 1 | hey {{foo}} 2 | {{>partial}} 3 | -------------------------------------------------------------------------------- /examples/empty_sections.html: -------------------------------------------------------------------------------- 1 | {{#foo}}{{/foo}}foo{{#bar}}{{/bar}} 2 | -------------------------------------------------------------------------------- /examples/higher_order_sections.txt: -------------------------------------------------------------------------------- 1 | Hi Tater. To tinker? 2 | -------------------------------------------------------------------------------- /examples/nesting.txt: -------------------------------------------------------------------------------- 1 | 1 2 | 2 3 | 3 4 | 5 | -------------------------------------------------------------------------------- /examples/template_partial.html: -------------------------------------------------------------------------------- 1 |

{{title}}

2 | {{>partial}} -------------------------------------------------------------------------------- /examples/unknown_pragma.html: -------------------------------------------------------------------------------- 1 | {{%I-HAVE-THE-GREATEST-MUSTACHE}} 2 | -------------------------------------------------------------------------------- /examples/apostrophe.js: -------------------------------------------------------------------------------- 1 | var apostrophe = {'apos': "'", 'control':'X'}; 2 | -------------------------------------------------------------------------------- /examples/empty_partial.js: -------------------------------------------------------------------------------- 1 | var partial_context = { 2 | foo: 1 3 | }; 4 | -------------------------------------------------------------------------------- /examples/escaped.html: -------------------------------------------------------------------------------- 1 |

{{title}}

2 | But not {{entities}}. 3 | -------------------------------------------------------------------------------- /examples/escaped.txt: -------------------------------------------------------------------------------- 1 |

Bear > Shark

2 | But not ". 3 | -------------------------------------------------------------------------------- /examples/template_partial.txt: -------------------------------------------------------------------------------- 1 |

Welcome

2 | Again, Goodbye! 3 | -------------------------------------------------------------------------------- /mustache-yui3/mustache.js.tpl.pre: -------------------------------------------------------------------------------- 1 | YUI.add("mustache", function(Y) { 2 | -------------------------------------------------------------------------------- /examples/array_of_strings.html: -------------------------------------------------------------------------------- 1 | {{#array_of_strings}}{{.}} {{/array_of_strings}} -------------------------------------------------------------------------------- /examples/higher_order_sections.html: -------------------------------------------------------------------------------- 1 | {{#bolder}}Hi {{name}}.{{/bolder}} 2 | -------------------------------------------------------------------------------- /examples/two_sections.html: -------------------------------------------------------------------------------- 1 | {{#foo}} 2 | {{/foo}} 3 | {{#bar}} 4 | {{/bar}} 5 | -------------------------------------------------------------------------------- /mustache-requirejs/requirejs.mustache.js.tpl.post: -------------------------------------------------------------------------------- 1 | 2 | return Mustache; 3 | }); -------------------------------------------------------------------------------- /examples/empty_template.html: -------------------------------------------------------------------------------- 1 |

Test

-------------------------------------------------------------------------------- /examples/inverted_section.js: -------------------------------------------------------------------------------- 1 | var inverted_section = { 2 | "repo": [] 3 | } 4 | -------------------------------------------------------------------------------- /examples/array_of_partials_partial.txt: -------------------------------------------------------------------------------- 1 | Here is some stuff! 2 | 1 3 | 2 4 | 3 5 | 4 6 | -------------------------------------------------------------------------------- /examples/empty_template.txt: -------------------------------------------------------------------------------- 1 |

Test

2 | -------------------------------------------------------------------------------- /examples/nesting.html: -------------------------------------------------------------------------------- 1 | {{#foo}} 2 | {{#a}} 3 | {{b}} 4 | {{/a}} 5 | {{/foo}} 6 | -------------------------------------------------------------------------------- /examples/partial_recursion.html: -------------------------------------------------------------------------------- 1 | {{name}} 2 | {{#kids}} 3 | {{>partial}} 4 | {{/kids}} -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | runner.js 2 | jquery.mustache.js 3 | dojox 4 | yui3 5 | commonjs.mustache.js 6 | -------------------------------------------------------------------------------- /examples/comments.html: -------------------------------------------------------------------------------- 1 |

{{title}}{{! just something interesting... or not... }}

2 | -------------------------------------------------------------------------------- /examples/recursion_with_same_names.txt: -------------------------------------------------------------------------------- 1 | name 2 | desc 3 | t1 4 | 0 5 | t2 6 | 1 7 | -------------------------------------------------------------------------------- /examples/view_partial.html: -------------------------------------------------------------------------------- 1 |

{{greeting}}

2 | {{>partial}} 3 |

{{farewell}}

-------------------------------------------------------------------------------- /examples/array_of_partials_implicit_partial.txt: -------------------------------------------------------------------------------- 1 | Here is some stuff! 2 | 1 3 | 2 4 | 3 5 | 4 6 | -------------------------------------------------------------------------------- /examples/array_of_strings.js: -------------------------------------------------------------------------------- 1 | var array_of_strings = {array_of_strings: ['hello', 'world']}; 2 | -------------------------------------------------------------------------------- /examples/bug_11_eating_whitespace.js: -------------------------------------------------------------------------------- 1 | var bug_11_eating_whitespace = { 2 | tag: "yo" 3 | }; 4 | -------------------------------------------------------------------------------- /examples/partial_recursion.2.html: -------------------------------------------------------------------------------- 1 | {{name}} 2 | {{#children}} 3 | {{>partial}} 4 | {{/children}} -------------------------------------------------------------------------------- /examples/simple.txt: -------------------------------------------------------------------------------- 1 | Hello Chris 2 | You have just won $10000! 3 | Well, $6000, after taxes. 4 | -------------------------------------------------------------------------------- /mustache-yui3/mustache.js.tpl.post: -------------------------------------------------------------------------------- 1 | 2 | Y.mustache = Mustache.to_html; 3 | 4 | }, "0"); 5 | -------------------------------------------------------------------------------- /examples/array_partial.txt: -------------------------------------------------------------------------------- 1 | Here's a non-sense array of values 2 | 1 3 | 2 4 | 3 5 | 4 6 | 7 | -------------------------------------------------------------------------------- /examples/inverted_section.html: -------------------------------------------------------------------------------- 1 | {{#repo}}{{name}}{{/repo}} 2 | {{^repo}}No repos :({{/repo}} 3 | -------------------------------------------------------------------------------- /examples/null_string.txt: -------------------------------------------------------------------------------- 1 | Hello Elise 2 | glytch true 3 | binary false 4 | value 5 | numeric NaN 6 | -------------------------------------------------------------------------------- /examples/reuse_of_enumerables.txt: -------------------------------------------------------------------------------- 1 | t1 2 | 0 3 | t2 4 | 1 5 | t1 6 | 0 7 | t2 8 | 1 9 | -------------------------------------------------------------------------------- /examples/two_in_a_row.js: -------------------------------------------------------------------------------- 1 | var two_in_a_row = { 2 | name: "Joe", 3 | greeting: "Welcome" 4 | }; 5 | -------------------------------------------------------------------------------- /examples/array_partial.2.html: -------------------------------------------------------------------------------- 1 | Here's a non-sense array of values 2 | {{#array}} 3 | {{.}} 4 | {{/array}} -------------------------------------------------------------------------------- /examples/whitespace_partial.html: -------------------------------------------------------------------------------- 1 |

{{ greeting }}

2 | {{> partial }} 3 |

{{ farewell }}

-------------------------------------------------------------------------------- /mustache-dojo/mustache.js.tpl.post: -------------------------------------------------------------------------------- 1 | 2 | dojox.mustache = dojo.hitch(Mustache, "to_html"); 3 | 4 | })(); -------------------------------------------------------------------------------- /examples/double_render.js: -------------------------------------------------------------------------------- 1 | var double_render = { 2 | foo: true, 3 | bar: "{{win}}", 4 | win: "FAIL" 5 | }; -------------------------------------------------------------------------------- /examples/array_of_partials_implicit_partial.js: -------------------------------------------------------------------------------- 1 | var partial_context = { 2 | numbers: ['1', '2', '3', '4'] 3 | }; 4 | -------------------------------------------------------------------------------- /examples/array_of_partials_partial.html: -------------------------------------------------------------------------------- 1 | Here is some stuff! 2 | {{#numbers}} 3 | {{>partial}} 4 | {{/numbers}} 5 | -------------------------------------------------------------------------------- /examples/array_partial.js: -------------------------------------------------------------------------------- 1 | var partial_context = { 2 | partial: { 3 | array: ['1', '2', '3', '4'] 4 | } 5 | }; -------------------------------------------------------------------------------- /examples/unescaped.js: -------------------------------------------------------------------------------- 1 | var unescaped = { 2 | title: function() { 3 | return "Bear > Shark"; 4 | } 5 | }; 6 | -------------------------------------------------------------------------------- /examples/array_of_partials_implicit_partial.html: -------------------------------------------------------------------------------- 1 | Here is some stuff! 2 | {{#numbers}} 3 | {{>partial}} 4 | {{/numbers}} 5 | -------------------------------------------------------------------------------- /examples/array_of_strings_options.js: -------------------------------------------------------------------------------- 1 | var array_of_strings_options = {array_of_strings_options: ['hello', 'world']}; 2 | -------------------------------------------------------------------------------- /examples/comments.js: -------------------------------------------------------------------------------- 1 | var comments = { 2 | title: function() { 3 | return "A Comedy of Errors"; 4 | } 5 | }; 6 | -------------------------------------------------------------------------------- /examples/null_string.html: -------------------------------------------------------------------------------- 1 | Hello {{name}} 2 | glytch {{glytch}} 3 | binary {{binary}} 4 | value {{value}} 5 | numeric {{numeric}} -------------------------------------------------------------------------------- /examples/array_of_partials_partial.js: -------------------------------------------------------------------------------- 1 | var partial_context = { 2 | numbers: [{i: '1'}, {i: '2'}, {i: '3'}, {i: '4'}] 3 | }; 4 | -------------------------------------------------------------------------------- /examples/nesting.js: -------------------------------------------------------------------------------- 1 | var nesting = { 2 | foo: [ 3 | {a: {b: 1}}, 4 | {a: {b: 2}}, 5 | {a: {b: 3}} 6 | ] 7 | }; 8 | -------------------------------------------------------------------------------- /examples/unknown_pragma.txt: -------------------------------------------------------------------------------- 1 | ERROR: This implementation of mustache doesn't understand the 'I-HAVE-THE-GREATEST-MUSTACHE' pragma 2 | -------------------------------------------------------------------------------- /examples/delimiters.html: -------------------------------------------------------------------------------- 1 | {{=<% %>=}}* 2 | <% first %> 3 | * <% second %> 4 | <%=| |=%> 5 | * | third | 6 | |={{ }}=| 7 | * {{ fourth }} -------------------------------------------------------------------------------- /examples/escaped.js: -------------------------------------------------------------------------------- 1 | var escaped = { 2 | title: function() { 3 | return "Bear > Shark"; 4 | }, 5 | entities: """ 6 | }; 7 | -------------------------------------------------------------------------------- /examples/recursion_with_same_names.html: -------------------------------------------------------------------------------- 1 | {{ name }} 2 | {{ description }} 3 | 4 | {{#terms}} 5 | {{name}} 6 | {{index}} 7 | {{/terms}} 8 | -------------------------------------------------------------------------------- /examples/simple.html: -------------------------------------------------------------------------------- 1 | Hello {{name}} 2 | You have just won ${{value}}! 3 | {{#in_ca}} 4 | Well, ${{ taxed_value }}, after taxes. 5 | {{/in_ca}} -------------------------------------------------------------------------------- /examples/array_of_strings_options.html: -------------------------------------------------------------------------------- 1 | {{%IMPLICIT-ITERATOR iterator=rob}} 2 | {{#array_of_strings_options}}{{rob}} {{/array_of_strings_options}} -------------------------------------------------------------------------------- /examples/reuse_of_enumerables.html: -------------------------------------------------------------------------------- 1 | {{#terms}} 2 | {{name}} 3 | {{index}} 4 | {{/terms}} 5 | {{#terms}} 6 | {{name}} 7 | {{index}} 8 | {{/terms}} 9 | -------------------------------------------------------------------------------- /examples/reuse_of_enumerables.js: -------------------------------------------------------------------------------- 1 | var reuse_of_enumerables = { 2 | terms: [ 3 | {name: 't1', index: 0}, 4 | {name: 't2', index: 1}, 5 | ] 6 | }; -------------------------------------------------------------------------------- /examples/view_partial.2.html: -------------------------------------------------------------------------------- 1 | Hello {{name}} 2 | You have just won ${{value}}! 3 | {{#in_ca}} 4 | Well, ${{ taxed_value }}, after taxes. 5 | {{/in_ca}} 6 | -------------------------------------------------------------------------------- /examples/view_partial.txt: -------------------------------------------------------------------------------- 1 |

Welcome

2 | Hello Chris 3 | You have just won $10000! 4 | Well, $6000, after taxes. 5 | 6 |

Fair enough, right?

7 | -------------------------------------------------------------------------------- /examples/whitespace_partial.2.html: -------------------------------------------------------------------------------- 1 | Hello {{ name}} 2 | You have just won ${{value }}! 3 | {{# in_ca }} 4 | Well, ${{ taxed_value }}, after taxes. 5 | {{/ in_ca }} 6 | -------------------------------------------------------------------------------- /examples/whitespace_partial.txt: -------------------------------------------------------------------------------- 1 |

Welcome

2 | Hello Chris 3 | You have just won $10000! 4 | Well, $6000, after taxes. 5 | 6 |

Fair enough, right?

7 | -------------------------------------------------------------------------------- /examples/template_partial.js: -------------------------------------------------------------------------------- 1 | var partial_context = { 2 | title: function() { 3 | return "Welcome"; 4 | }, 5 | partial: { 6 | again: "Goodbye" 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /mustache-commonjs/mustache.js.tpl.pre: -------------------------------------------------------------------------------- 1 | /* 2 | * CommonJS-compatible mustache.js module 3 | * 4 | * See http://github.com/janl/mustache.js for more info. 5 | */ 6 | 7 | -------------------------------------------------------------------------------- /examples/delimiters.txt: -------------------------------------------------------------------------------- 1 | * 2 | It worked the first time. 3 | * And it worked the second time. 4 | * Then, surprisingly, it worked the third time. 5 | * Fourth time also fine!. 6 | -------------------------------------------------------------------------------- /examples/complex.txt: -------------------------------------------------------------------------------- 1 |

Colors

2 | 7 | -------------------------------------------------------------------------------- /examples/simple.js: -------------------------------------------------------------------------------- 1 | var simple = { 2 | name: "Chris", 3 | value: 10000, 4 | taxed_value: function() { 5 | return this.value - (this.value * 0.4); 6 | }, 7 | in_ca: true 8 | }; 9 | -------------------------------------------------------------------------------- /mustache-jquery/jquery.mustache.js.tpl.post: -------------------------------------------------------------------------------- 1 | 2 | $.mustache = function(template, view, partials) { 3 | return Mustache.to_html(template, view, partials); 4 | }; 5 | 6 | })(jQuery); 7 | -------------------------------------------------------------------------------- /examples/section_as_context.txt: -------------------------------------------------------------------------------- 1 |

this is an object

2 |

one of its attributes is a list

3 | 7 | -------------------------------------------------------------------------------- /mustache-requirejs/requirejs.mustache.js.tpl.pre: -------------------------------------------------------------------------------- 1 | /* 2 | Shameless port of a shameless port ^ 2 3 | @defunkt => @janl => @aq => @voodootikigod => @timruffles 4 | 5 | */ 6 | define(function(){ 7 | -------------------------------------------------------------------------------- /examples/null_string.js: -------------------------------------------------------------------------------- 1 | var null_string = { 2 | name: "Elise", 3 | glytch: true, 4 | binary: false, 5 | value: null, 6 | numeric: function() { 7 | return NaN; 8 | } 9 | }; 10 | -------------------------------------------------------------------------------- /examples/recursion_with_same_names.js: -------------------------------------------------------------------------------- 1 | var recursion_with_same_names = { 2 | name: 'name', 3 | description: 'desc', 4 | terms: [ 5 | {name: 't1', index: 0}, 6 | {name: 't2', index: 1}, 7 | ] 8 | }; -------------------------------------------------------------------------------- /mustache-commonjs/mustache.js.tpl.post: -------------------------------------------------------------------------------- 1 | 2 | exports.name = Mustache.name; 3 | exports.version = Mustache.version; 4 | 5 | exports.to_html = function() { 6 | return Mustache.to_html.apply(this, arguments); 7 | }; 8 | -------------------------------------------------------------------------------- /examples/partial_recursion.js: -------------------------------------------------------------------------------- 1 | var partial_context = { 2 | name: '1', 3 | kids: [ 4 | { 5 | name: '1.1', 6 | children: [ 7 | {name: '1.1.1'} 8 | ] 9 | } 10 | ] 11 | }; 12 | -------------------------------------------------------------------------------- /examples/section_as_context.html: -------------------------------------------------------------------------------- 1 | {{#a_object}} 2 |

{{title}}

3 |

{{description}}

4 | 9 | {{/a_object}} 10 | -------------------------------------------------------------------------------- /mustache-jquery/jquery.mustache.js.tpl.pre: -------------------------------------------------------------------------------- 1 | /* 2 | Shameless port of a shameless port 3 | @defunkt => @janl => @aq 4 | 5 | See http://github.com/defunkt/mustache for more info. 6 | */ 7 | 8 | ;(function($) { 9 | 10 | -------------------------------------------------------------------------------- /examples/delimiters.js: -------------------------------------------------------------------------------- 1 | var delimiters = { 2 | first: "It worked the first time.", 3 | second: "And it worked the second time.", 4 | third: "Then, surprisingly, it worked the third time.", 5 | fourth: "Fourth time also fine!." 6 | } 7 | -------------------------------------------------------------------------------- /examples/section_as_context.js: -------------------------------------------------------------------------------- 1 | var section_as_context = { 2 | a_object: { 3 | title: 'this is an object', 4 | description: 'one of its attributes is a list', 5 | a_list: [{label: 'listitem1'}, {label: 'listitem2'}] 6 | } 7 | }; 8 | -------------------------------------------------------------------------------- /mustache-commonjs/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "mustache", 3 | "author": "http://mustache.github.com/", 4 | "description": "{{ mustache }} in JavaScript — Logic-less templates.", 5 | "keywords": ["template"], 6 | "version": "{{version}}" 7 | } 8 | -------------------------------------------------------------------------------- /examples/higher_order_sections.js: -------------------------------------------------------------------------------- 1 | var higher_order_sections = { 2 | "name": "Tater", 3 | "helper": "To tinker?", 4 | "bolder": function() { 5 | return function(text, render) { 6 | return "" + render(text) + ' ' + this.helper; 7 | } 8 | } 9 | } -------------------------------------------------------------------------------- /mustache-dojo/mustache.js.tpl.pre: -------------------------------------------------------------------------------- 1 | /* 2 | Shameless port of a shameless port 3 | @defunkt => @janl => @aq => @voodootikigod 4 | 5 | See http://github.com/defunkt/mustache for more info. 6 | */ 7 | 8 | dojo.provide("dojox.mustache._base"); 9 | (function(){ 10 | -------------------------------------------------------------------------------- /lib/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "mustache", 3 | "author": "http://mustache.github.com/", 4 | "description": "{{ mustache }} in JavaScript — Logic-less templates.", 5 | "keywords": ["template"], 6 | "version": "0.3.1-dev-twitter", 7 | "main": "./mustache" 8 | } 9 | -------------------------------------------------------------------------------- /examples/complex.html: -------------------------------------------------------------------------------- 1 |

{{header}}

2 | {{#list}} 3 | 13 | {{/list}} 14 | {{#empty}} 15 |

The list is empty.

16 | {{/empty}} -------------------------------------------------------------------------------- /examples/view_partial.js: -------------------------------------------------------------------------------- 1 | var partial_context = { 2 | greeting: function() { 3 | return "Welcome"; 4 | }, 5 | 6 | farewell: function() { 7 | return "Fair enough, right?"; 8 | }, 9 | 10 | partial: { 11 | name: "Chris", 12 | value: 10000, 13 | taxed_value: function() { 14 | return this.value - (this.value * 0.4); 15 | }, 16 | in_ca: true 17 | } 18 | }; 19 | 20 | -------------------------------------------------------------------------------- /examples/whitespace_partial.js: -------------------------------------------------------------------------------- 1 | var partial_context = { 2 | greeting: function() { 3 | return "Welcome"; 4 | }, 5 | 6 | farewell: function() { 7 | return "Fair enough, right?"; 8 | }, 9 | 10 | partial: { 11 | name: "Chris", 12 | value: 10000, 13 | taxed_value: function() { 14 | return this.value - (this.value * 0.4); 15 | }, 16 | in_ca: true 17 | } 18 | }; 19 | 20 | -------------------------------------------------------------------------------- /examples/complex.js: -------------------------------------------------------------------------------- 1 | var complex = { 2 | header: function() { 3 | return "Colors"; 4 | }, 5 | item: [ 6 | {name: "red", current: true, url: "#Red"}, 7 | {name: "green", current: false, url: "#Green"}, 8 | {name: "blue", current: false, url: "#Blue"} 9 | ], 10 | link: function() { 11 | return this["current"] !== true; 12 | }, 13 | list: function() { 14 | return this.item.length !== 0; 15 | }, 16 | empty: function() { 17 | return this.item.length === 0; 18 | } 19 | }; 20 | -------------------------------------------------------------------------------- /THANKS.md: -------------------------------------------------------------------------------- 1 | # Thanks 2 | 3 | Mustache.js wouldn't kick ass if it weren't for these fine souls: 4 | 5 | * Chris Wanstrath / defunkt 6 | * Alexander Lang / langalex 7 | * Sebastian Cohnen / tisba 8 | * J Chris Anderson / jchris 9 | * Tom Robinson / tlrobinson 10 | * Aaron Quint / quirkey 11 | * Douglas Crockford 12 | * Nikita Vasilyev / NV 13 | * Elise Wood / glytch 14 | * Damien Mathieu / dmathieu 15 | * Jakub Kuźma / qoobaa 16 | * Will Leinweber / will 17 | * dpree 18 | * Jason Smith / jhs 19 | * Aaron Gibralter / agibralter 20 | * Ross Boucher / boucher 21 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2009 Chris Wanstrath (Ruby) 2 | Copyright (c) 2010 Jan Lehnardt (JavaScript) 3 | 4 | Permission is hereby granted, free of charge, to any person obtaining 5 | a copy of this software and associated documentation files (the 6 | "Software"), to deal in the Software without restriction, including 7 | without limitation the rights to use, copy, modify, merge, publish, 8 | distribute, sublicense, and/or sell copies of the Software, and to 9 | permit persons to whom the Software is furnished to do so, subject to 10 | the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be 13 | included in all copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 16 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 17 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 18 | NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE 19 | LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 20 | OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 21 | WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -------------------------------------------------------------------------------- /CHANGES.md: -------------------------------------------------------------------------------- 1 | # mustache.js Changes 2 | 3 | ## 0.3.1 (??-??-????) 4 | 5 | ## 0.3.0 (21-07-2010) 6 | 7 | * Improved whitespace handling. 8 | * Make IMPLICIT ITERATORS a first class feature. 9 | * Fix Rhino compat. 10 | * CommonJS packaging is no longer a special case. 11 | * DRY Rakefile. 12 | * Allow whitespace around tag names. 13 | * Fix partial scope. 14 | * Fix Comments. 15 | * Added inverted sections. 16 | * Avoid double encoding of entities. 17 | * Use sections to dereference subcontexts. 18 | * Added higher order sections. 19 | 20 | 21 | ## 0.2.3 (28-03-2010) 22 | 23 | * Better error message for missing partials. 24 | * Added more robust type detection. 25 | * Parse pragmas only once. 26 | * Throw exception when encountering an unknown pragma. 27 | * Ignore undefined partial contexts. Returns verbatim partials. 28 | * Added yui3 packaging. 29 | 30 | 31 | ## 0.2.2 (11-02-2010) 32 | 33 | * ctemplate compat: Partials are indicated by >, not <. 34 | * Add support for {{%PRAGMA}} to enable features. 35 | * Made array of strings an option. Enable with `{{%JSTACHE-ENABLE-STRING-ARRAYS}}`. 36 | * mustache compat: Don't barf on unknown variables. 37 | * Add `rake dojo` target to create a dojo package. 38 | * Add streaming api. 39 | * Rename JSTACHE-ENABLE-STRING-ARRAYS to IMPLICIT-ITERATOR. 40 | * Add support for pragma options. 41 | -------------------------------------------------------------------------------- /Rakefile: -------------------------------------------------------------------------------- 1 | require 'rake' 2 | require 'spec/rake/spectask' 3 | 4 | task :default => :spec 5 | 6 | Spec::Rake::SpecTask.new(:spec) do |t| 7 | #t.spec_opts = ['--options', "\"#{File.dirname(__FILE__)}/spec/spec.opts\""] 8 | t.spec_files = FileList['test/*_spec.rb'] 9 | end 10 | 11 | desc "Run all specs" 12 | task :spec 13 | 14 | def templated_build(name, opts={}) 15 | # Create a rule that uses the .tmpl.{pre,post} stuff to make a final, 16 | # wrapped, output file. 17 | # There is some extra complexity because Dojo and YUI3 use different 18 | # template files and final locations. 19 | short = name.downcase 20 | source = "mustache-#{short}" 21 | dependencies = ["mustache.js"] + Dir.glob("#{source}/*.tpl.*") 22 | 23 | desc "Package for #{name}" 24 | task short.to_sym => dependencies do 25 | target_js = opts[:location] ? "mustache.js" : "#{short}.mustache.js" 26 | 27 | puts "Packaging for #{name}" 28 | sh "mkdir -p #{opts[:location]}" if opts[:location] 29 | sh "cat #{source}/#{target_js}.tpl.pre mustache.js \ 30 | #{source}/#{target_js}.tpl.post > #{opts[:location] || '.'}/#{target_js}" 31 | 32 | # extra 33 | if opts[:extra] 34 | sh "sed -e 's/{{version}}/#{version}/' #{source}/#{opts[:extra]} \ 35 | > #{opts[:location]}/#{opts[:extra]}" 36 | end 37 | 38 | puts "Done, see #{opts[:location] || '.'}/#{target_js}" 39 | 40 | end 41 | end 42 | 43 | templated_build "CommonJS", :location => "lib", :extra => "package.json" 44 | templated_build "jQuery" 45 | templated_build "Dojo", :location => "dojox/string" 46 | templated_build "YUI3", :location => "yui3/mustache" 47 | templated_build "requirejs" 48 | 49 | 50 | def version 51 | File.read("mustache.js").match('version: "([^\"]+)",$')[1] 52 | end 53 | 54 | 55 | desc "Remove temporary files." 56 | task :clean do 57 | sh "git clean -fdx" 58 | end 59 | -------------------------------------------------------------------------------- /test/mustache_spec.rb: -------------------------------------------------------------------------------- 1 | require 'rubygems' 2 | require 'json' 3 | 4 | __DIR__ = File.dirname(__FILE__) 5 | 6 | testnames = Dir.glob(__DIR__ + '/../examples/*.js').map do |name| 7 | File.basename name, '.js' 8 | end 9 | 10 | non_partials = testnames.select{|t| not t.include? "partial"} 11 | partials = testnames.select{|t| t.include? "partial"} 12 | 13 | def load_test(dir, name, partial=false) 14 | view = File.read(dir + "/../examples/#{name}.js") 15 | template = File.read(dir + "/../examples/#{name}.html").to_json 16 | expect = File.read(dir + "/../examples/#{name}.txt") 17 | if not partial 18 | [view, template, expect] 19 | else 20 | partial = File.read(dir + "/../examples/#{name}.2.html").to_json 21 | [view, template, partial, expect] 22 | end 23 | end 24 | 25 | describe "mustache" do 26 | 27 | shared_examples_for "Mustache rendering" do 28 | 29 | before(:all) do 30 | mustache = File.read(__DIR__ + "/../mustache.js") 31 | stubbed_gettext = <<-JS 32 | // Stubbed gettext translation method for {{_i}}{{/i}} tags in Mustache. 33 | function _(text) { 34 | return text; 35 | } 36 | JS 37 | 38 | @boilerplate = <<-JS 39 | #{mustache} 40 | #{stubbed_gettext} 41 | JS 42 | end 43 | 44 | it "should return the same when invoked multiple times" do 45 | js = <<-JS 46 | #{@boilerplate} 47 | Mustache.to_html("x") 48 | print(Mustache.to_html("x")); 49 | JS 50 | run_js(@run_js, js).should == "x\n" 51 | 52 | end 53 | 54 | it "should clear the context after each run" do 55 | js = <<-JS 56 | #{@boilerplate} 57 | Mustache.to_html("{{#list}}{{x}}{{/list}}", {list: [{x: 1}]}) 58 | try { 59 | print(Mustache.to_html("{{#list}}{{x}}{{/list}}", {list: [{}]})); 60 | } catch(e) { 61 | print('ERROR: ' + e.message); 62 | } 63 | JS 64 | run_js(@run_js, js).should == "\n" 65 | end 66 | 67 | non_partials.each do |testname| 68 | describe testname do 69 | it "should generate the correct html" do 70 | 71 | view, template, expect = load_test(__DIR__, testname) 72 | 73 | runner = <<-JS 74 | try { 75 | #{@boilerplate} 76 | #{view} 77 | var template = #{template}; 78 | var result = Mustache.to_html(template, #{testname}); 79 | print(result); 80 | } catch(e) { 81 | print('ERROR: ' + e.message); 82 | } 83 | JS 84 | 85 | run_js(@run_js, runner).should == expect 86 | end 87 | it "should sendFun the correct html" do 88 | 89 | view, template, expect = load_test(__DIR__, testname) 90 | 91 | runner = <<-JS 92 | try { 93 | #{@boilerplate} 94 | #{view} 95 | var chunks = []; 96 | var sendFun = function(chunk) { 97 | if (chunk != "") { 98 | chunks.push(chunk); 99 | } 100 | } 101 | var template = #{template}; 102 | Mustache.to_html(template, #{testname}, null, sendFun); 103 | print(chunks.join("\\n")); 104 | } catch(e) { 105 | print('ERROR: ' + e.message); 106 | } 107 | JS 108 | 109 | run_js(@run_js, runner).strip.should == expect.strip 110 | end 111 | end 112 | end 113 | 114 | partials.each do |testname| 115 | describe testname do 116 | it "should generate the correct html" do 117 | 118 | view, template, partial, expect = 119 | load_test(__DIR__, testname, true) 120 | 121 | runner = <<-JS 122 | try { 123 | #{@boilerplate} 124 | #{view} 125 | var template = #{template}; 126 | var partials = {"partial": #{partial}}; 127 | var result = Mustache.to_html(template, partial_context, partials); 128 | print(result); 129 | } catch(e) { 130 | print('ERROR: ' + e.message); 131 | } 132 | JS 133 | 134 | run_js(@run_js, runner).should == expect 135 | end 136 | it "should sendFun the correct html" do 137 | 138 | view, template, partial, expect = 139 | load_test(__DIR__, testname, true) 140 | 141 | runner = <<-JS 142 | try { 143 | #{@boilerplate} 144 | #{view}; 145 | var template = #{template}; 146 | var partials = {"partial": #{partial}}; 147 | var chunks = []; 148 | var sendFun = function(chunk) { 149 | if (chunk != "") { 150 | chunks.push(chunk); 151 | } 152 | } 153 | Mustache.to_html(template, partial_context, partials, sendFun); 154 | print(chunks.join("\\n")); 155 | } catch(e) { 156 | print('ERROR: ' + e.message); 157 | } 158 | JS 159 | 160 | run_js(@run_js, runner).strip.should == expect.strip 161 | end 162 | end 163 | end 164 | end 165 | 166 | context "running in JavaScriptCore (WebKit, Safari)" do 167 | before(:each) do 168 | @run_js = :run_js_jsc 169 | end 170 | it_should_behave_like "Mustache rendering" 171 | end 172 | 173 | context "running in Rhino (Mozilla)" do 174 | before(:each) do 175 | @run_js = :run_js_rhino 176 | end 177 | 178 | it_should_behave_like "Mustache rendering" 179 | end 180 | 181 | def run_js(runner, js) 182 | send(runner, js) 183 | # run_js_rhino(js) 184 | # js_jsc = run_js_jsc(js) 185 | # js_rhino = run_js_rhino(js) 186 | # return js_jsc unless js_jsc != js_rhino 187 | end 188 | 189 | def run_js_jsc(js) 190 | File.open("runner.js", 'w') {|f| f << js} 191 | `jsc runner.js` 192 | end 193 | 194 | def run_js_rhino(js) 195 | File.open("runner.js", 'w') {|f| f << js} 196 | `java org.mozilla.javascript.tools.shell.Main runner.js` 197 | end 198 | end 199 | 200 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # mustache.js — Logic-less templates with JavaScript 2 | 3 | > What could be more logical awesome than no logic at all? 4 | 5 | For a list of implementations (other than JavaScript) and editor 6 | plugins, see . 7 | 8 | 9 | ## Where to Use? 10 | 11 | You can use mustache.js rendering stuff in various scenarios. E.g. you can 12 | render templates in your browser, or rendering server-side stuff with 13 | [node.js][node.js], use it for rendering stuff in [CouchDB][couchdb]’s views. 14 | 15 | 16 | ## Who Uses Mustache? 17 | 18 | An updated list is kept on the Github wiki. Add yourself, if you use 19 | mustache.js: 20 | 21 | 22 | ## Usage 23 | 24 | A quick example how to use mustache.js: 25 | 26 | var view = { 27 | title: "Joe", 28 | calc: function() { 29 | return 2 + 4; 30 | } 31 | } 32 | 33 | var template = "{{title}} spends {{calc}}"; 34 | 35 | var html = Mustache.to_html(template, view); 36 | 37 | `template` is a simple string with mustache tags and `view` is a JavaScript 38 | object containing the data and any code to render the template. 39 | 40 | 41 | ## Template Tag Types 42 | 43 | There are several types of tags currently implemented in mustache.js. 44 | 45 | For a language-agnostic overview of Mustache’s template syntax, see the 46 | `mustache(5)` manpage or . 47 | 48 | ### Simple Tags 49 | 50 | Tags are always surrounded by mustaches like this `{{foobar}}`. 51 | 52 | var view = {name: "Joe", say_hello: function(){ return "hello" }} 53 | 54 | template = "{{say_hello}}, {{name}}" 55 | 56 | 57 | ### Conditional Sections 58 | 59 | Conditional sections begin with `{{#condition}}` and end with 60 | `{{/condition}}`. When `condition` evaluates to true, the section is rendered, 61 | otherwise the whole block will output nothing at all. `condition` may be a 62 | function returning true/false or a simple boolean. 63 | 64 | var view = {condition: function() { 65 | // [...your code goes here...] 66 | return true; 67 | }} 68 | 69 | {{#condition}} 70 | I will be visible if condition is true 71 | {{/condition}} 72 | 73 | 74 | ### Enumerable Sections 75 | 76 | Enumerable Sections use the same syntax as condition sections do. 77 | `{{#shopping_items}}` and `{{/shopping_items}}`. Actually the view decides how 78 | mustache.js renders the section. If the view returns an array, it will 79 | iterator over the items. Use `{{.}}` to access the current item inside the 80 | enumeration section. 81 | 82 | var view = {name: "Joe's shopping card", 83 | items: ["bananas", "apples"]} 84 | 85 | var template = "{{name}}:
    {{#items}}
  • {{.}}
  • {{/items}}
" 86 | 87 | Outputs: 88 | Joe's shopping card:
  • bananas
  • apples
89 | 90 | 91 | ### Higher Order Sections 92 | 93 | If a section key returns a function, it will be called and passed both the 94 | unrendered block of text and a renderer convenience function. 95 | 96 | Given this JS: 97 | 98 | "name": "Tater", 99 | "bolder": function() { 100 | return function(text, render) { 101 | return "" + render(text) + '' 102 | } 103 | } 104 | 105 | And this template: 106 | 107 | {{#bolder}}Hi {{name}}.{{/bolder}} 108 | 109 | We'll get this output: 110 | 111 | Hi Tater. 112 | 113 | As you can see, we’re pre-processing the text in the block. This can be used 114 | to implement caching, filters (like syntax highlighting), etc. 115 | 116 | You can use `this.name` to access the attribute `name` from your view. 117 | 118 | ### Dereferencing Section 119 | 120 | If you have a nested object structure in your view, it can sometimes be easier 121 | to use sections like this: 122 | 123 | var objects = { 124 | a_object: { 125 | title: 'this is an object', 126 | description: 'one of its attributes is a list', 127 | a_list: [{label: 'listitem1'}, {label: 'listitem2'}] 128 | } 129 | }; 130 | 131 | This is our template: 132 | 133 | {{#a_object}} 134 |

{{title}}

135 |

{{description}}

136 |
    137 | {{#a_list}} 138 |
  • {{label}}
  • 139 | {{/a_list}} 140 |
141 | {{/a_object}} 142 | 143 | Here is the result: 144 | 145 |

this is an object

146 |

one of its attributes is a list

147 |
    148 |
  • listitem1
  • 149 |
  • listitem2
  • 150 |
151 | 152 | ### Inverted Sections 153 | 154 | An inverted section opens with `{{^section}}` instead of `{{#section}}` and 155 | uses a boolean negative to evaluate. Empty arrays are considered falsy. 156 | 157 | View: 158 | 159 | var inverted_section = { 160 | "repo": [] 161 | } 162 | 163 | Template: 164 | 165 | {{#repo}}{{name}}{{/repo}} 166 | {{^repo}}No repos :({{/repo}} 167 | 168 | Result: 169 | 170 | No repos :( 171 | 172 | 173 | ### View Partials 174 | 175 | mustache.js supports a quite powerful but yet simple view partial mechanism. 176 | Use the following syntax for partials: `{{>partial_name}}` 177 | 178 | var view = { 179 | name: "Joe", 180 | winnings: { 181 | value: 1000, 182 | taxed_value: function() { 183 | return this.value - (this.value * 0.4); 184 | } 185 | } 186 | }; 187 | 188 | var template = "Welcome, {{name}}! {{>winnings}}" 189 | var partials = { 190 | winnings: "You just won ${{value}} (which is ${{taxed_value}} after tax)"}; 191 | 192 | var output = Mustache.to_html(template, view, partials) 193 | 194 | output will be: 195 | Welcome, Joe! You just won $1000 (which is $600 after tax) 196 | 197 | You invoke a partial with `{{>winnings}}`. Invoking the partial `winnings` 198 | will tell mustache.js to look for a object in the context's property 199 | `winnings`. It will then use that object as the context for the template found 200 | in `partials` for `winnings`. 201 | 202 | ## Internationalization 203 | 204 | mustache.js supports i18n using the `{{_i}}{{/i}}` tags. When mustache.js encounters 205 | an internationalized section, it will call out to the standard global gettext function `_()` with the tag contents for a 206 | translation _before_ any rendering is done. For example: 207 | 208 | var template = "{{_i}}{{name}} is using mustache.js!{{/i}}" 209 | 210 | var view = { 211 | name: "Matt" 212 | }; 213 | 214 | var translationTable = { 215 | // Welsh, according to Google Translate 216 | "{{name}} is using mustache.js!": "Mae {{name}} yn defnyddio mustache.js!" 217 | }; 218 | 219 | function _(text) { 220 | return translationTable[text] || text; 221 | } 222 | 223 | alert(Mustache.to_html(template, view)); 224 | // alerts "Mae Matt yn defnyddio mustache.js!" 225 | 226 | ## Escaping 227 | 228 | mustache.js does escape all values when using the standard double mustache 229 | syntax. Characters which will be escaped: `& \ " ' < >`. To disable escaping, 230 | simply use triple mustaches like `{{{unescaped_variable}}}`. 231 | 232 | Example: Using `{{variable}}` inside a template for `5 > 2` will result in `5 > 2`, where as the usage of `{{{variable}}}` will result in `5 > 2`. 233 | 234 | 235 | ## Streaming 236 | 237 | To stream template results out of mustache.js, you can pass an optional 238 | `send()` callback to the `to_html()` call: 239 | 240 | Mustache.to_html(template, view, partials, function(line) { 241 | print(line); 242 | }); 243 | 244 | 245 | ## Pragmas 246 | 247 | Pragma tags let you alter the behaviour of mustache.js. They have the format 248 | of 249 | 250 | {{%PRAGMANAME}} 251 | 252 | and they accept options: 253 | 254 | {{%PRAGMANAME option=value}} 255 | 256 | 257 | ### IMPLICIT-ITERATOR 258 | 259 | When using a block to iterate over an enumerable (Array), mustache.js expects 260 | an objects as enumerable items. The implicit iterator pragma enables optional 261 | behaviour of allowing literals as enumerable items. Consider this view: 262 | 263 | var view = { 264 | foo: [1, 2, 3, 4, 5, "french"] 265 | }; 266 | 267 | The following template can iterate over the member `foo`: 268 | 269 | {{%IMPLICIT-ITERATOR}} 270 | {{#foo}} 271 | {{.}} 272 | {{/foo}} 273 | 274 | If you don't like the dot in there, the pragma accepts an option to set your 275 | own iteration marker: 276 | 277 | {{%IMPLICIT-ITERATOR iterator=bob}} 278 | {{#foo}} 279 | {{bob}} 280 | {{/foo}} 281 | 282 | ## F.A.Q. 283 | 284 | ### Why doesn’t Mustache allow dot notation like `{{variable.member}}`? 285 | 286 | The reason is given in the [mustache.rb 287 | bugtracker](http://github.com/defunkt/mustache/issues/issue/6). 288 | 289 | Mustache implementations strive to be template-compatible. 290 | 291 | 292 | ## More Examples and Documentation 293 | 294 | See `examples/` for more goodies and read the [original mustache docs][m] 295 | 296 | ## Command Line 297 | 298 | See `mustache(1)` man page or 299 | 300 | for command line docs. 301 | 302 | Or just install it as a RubyGem: 303 | 304 | $ gem install mustache 305 | $ mustache -h 306 | 307 | [m]: http://github.com/defunkt/mustache/#readme 308 | [node.js]: http://nodejs.org 309 | [couchdb]: http://couchdb.apache.org 310 | 311 | 312 | ## Plugins for jQuery, Dojo, Yui, CommonJS 313 | 314 | This repository lets you build modules for [jQuery][], [Dojo][], [Yui][] and 315 | [CommonJS][] / [Node.js][] with the help of `rake`: 316 | 317 | Run `rake jquery` to get a jQuery compatible plugin file in the 318 | `mustache-jquery/` directory. 319 | 320 | Run `rake dojo` to get a Dojo compatible plugin file in the `mustache-dojo/` 321 | directory. 322 | 323 | Run `rake yui` to get a Yui compatible plugin file in the `mustache-yui/` 324 | directory. 325 | 326 | Run `rake commonjs` to get a CommonJS compatible plugin file in the 327 | `mustache-commonjs/` directory which you can also use with [Node.js][]. 328 | 329 | [jQuery]: http://jquery.com/ 330 | [Dojo]: http://www.dojotoolkit.org/ 331 | [Yui]: http://developer.yahoo.com/yui/ 332 | [CommonJS]: http://www.commonjs.org/ 333 | [Node.js]: http://nodejs.org/ 334 | -------------------------------------------------------------------------------- /mustache.js: -------------------------------------------------------------------------------- 1 | /* 2 | mustache.js — Logic-less templates in JavaScript 3 | 4 | See http://mustache.github.com/ for more info. 5 | */ 6 | 7 | var Mustache = function() { 8 | var Renderer = function() {}; 9 | 10 | Renderer.prototype = { 11 | otag: "{{", 12 | ctag: "}}", 13 | pragmas: {}, 14 | buffer: [], 15 | pragmas_implemented: { 16 | "IMPLICIT-ITERATOR": true, 17 | "TRANSLATION-HINT": true 18 | }, 19 | context: {}, 20 | 21 | render: function(template, context, partials, in_recursion) { 22 | // reset buffer & set context 23 | if(!in_recursion) { 24 | this.context = context; 25 | this.buffer = []; // TODO: make this non-lazy 26 | } 27 | 28 | // fail fast 29 | if(!this.includes("", template)) { 30 | if(in_recursion) { 31 | return template; 32 | } else { 33 | this.send(template); 34 | return; 35 | } 36 | } 37 | 38 | // Branching or moving down the partial stack, save any translation mode info. 39 | if (this.pragmas['TRANSLATION-HINT']) { 40 | context['_mode'] = this.pragmas['TRANSLATION-HINT']['mode']; 41 | } 42 | 43 | template = this.render_pragmas(template); 44 | 45 | template = this.render_i18n(template, context, partials); 46 | 47 | var html = this.render_section(template, context, partials); 48 | 49 | if (html === template) { 50 | if (in_recursion) { 51 | return this.render_tags(html, context, partials, true); 52 | } 53 | 54 | this.render_tags(html, context, partials, false); 55 | } else { 56 | if(in_recursion) { 57 | return html; 58 | } else { 59 | var lines = html.split("\n"); 60 | for (var i = 0; i < lines.length; i++) { 61 | this.send(lines[i]); 62 | } 63 | return; 64 | } 65 | } 66 | }, 67 | 68 | /* 69 | Sends parsed lines 70 | */ 71 | send: function(line) { 72 | if(line != "") { 73 | this.buffer.push(line); 74 | } 75 | }, 76 | 77 | /* 78 | Looks for %PRAGMAS 79 | */ 80 | render_pragmas: function(template) { 81 | // no pragmas 82 | if(!this.includes("%", template)) { 83 | return template; 84 | } 85 | 86 | var that = this; 87 | var regex = new RegExp(this.otag + "%([\\w-]+) ?([\\w]+=[\\w]+)?" + 88 | this.ctag); 89 | return template.replace(regex, function(match, pragma, options) { 90 | if(!that.pragmas_implemented[pragma]) { 91 | throw({message: 92 | "This implementation of mustache doesn't understand the '" + 93 | pragma + "' pragma"}); 94 | } 95 | that.pragmas[pragma] = {}; 96 | if(options) { 97 | var opts = options.split("="); 98 | that.pragmas[pragma][opts[0]] = opts[1]; 99 | } 100 | return ""; 101 | // ignore unknown pragmas silently 102 | }); 103 | }, 104 | 105 | /* 106 | Tries to find a partial in the curent scope and render it 107 | */ 108 | render_partial: function(name, context, partials) { 109 | name = this.trim(name); 110 | if(!partials || partials[name] === undefined) { 111 | throw({message: "unknown_partial '" + name + "'"}); 112 | } 113 | if(typeof(context[name]) != "object") { 114 | return this.render(partials[name], context, partials, true); 115 | } 116 | return this.render(partials[name], context[name], partials, true); 117 | }, 118 | 119 | render_i18n: function(html, context, partials) { 120 | if (html.indexOf(this.otag + "_i") == -1) { 121 | return html; 122 | } 123 | var that = this; 124 | var regex = new RegExp(this.otag + "\\_i" + this.ctag + 125 | "\\s*([\\s\\S]+?)" + this.otag + "\\/i" + this.ctag, "mg"); 126 | 127 | // for each {{_i}}{{/i}} section do... 128 | return html.replace(regex, function(match, content) { 129 | var translation_mode = undefined; 130 | if (that.pragmas && that.pragmas["TRANSLATION-HINT"] && that.pragmas["TRANSLATION-HINT"]['mode']) { 131 | translation_mode = { _mode: that.pragmas["TRANSLATION-HINT"]['mode'] }; 132 | } else if (context['_mode']) { 133 | translation_mode = { _mode: context['_mode'] }; 134 | } 135 | 136 | return that.render(_(content, translation_mode), context, partials, true); 137 | }); 138 | }, 139 | 140 | /* 141 | Renders inverted (^) and normal (#) sections 142 | */ 143 | render_section: function(template, context, partials) { 144 | if(!this.includes("#", template) && !this.includes("^", template)) { 145 | return template; 146 | } 147 | 148 | var that = this; 149 | 150 | // This regex matches _the first_ section ({{#foo}}{{/foo}}), and captures the remainder 151 | var regex = new RegExp( 152 | "^([\\s\\S]*?)" + // all the crap at the beginning that is not {{*}} ($1) 153 | 154 | this.otag + // {{ 155 | "(\\^|\\#)\\s*(.+)\\s*" + // #foo (# == $2, foo == $3) 156 | this.ctag + // }} 157 | 158 | "\n*([\\s\\S]*?)" + // between the tag ($2). leading newlines are dropped 159 | 160 | this.otag + // {{ 161 | "\\/\\s*\\3\\s*" + // /foo (backreference to the opening tag). 162 | this.ctag + // }} 163 | 164 | "\\s*([\\s\\S]*)$", // everything else in the string ($4). leading whitespace is dropped. 165 | 166 | "g"); 167 | 168 | // for each {{#foo}}{{/foo}} section do... 169 | return template.replace(regex, function(match, before, type, name, content, after) { 170 | // before contains only tags, no sections 171 | var renderedBefore = before ? that.render_tags(before, context, partials, true) : "", 172 | 173 | // after may contain both sections and tags, so use full rendering function 174 | renderedAfter = after ? that.render(after, context, partials, true) : ""; 175 | 176 | var value = that.find(name, context); 177 | if(type == "^") { // inverted section 178 | if(!value || that.is_array(value) && value.length === 0) { 179 | // false or empty list, render it 180 | return renderedBefore + that.render(content, context, partials, true) + renderedAfter; 181 | } else { 182 | return renderedBefore + "" + renderedAfter; 183 | } 184 | } else if(type == "#") { // normal section 185 | if(that.is_array(value)) { // Enumerable, Let's loop! 186 | return renderedBefore + that.map(value, function(row) { 187 | return that.render(content, that.create_context(row), partials, true); 188 | }).join("") + renderedAfter; 189 | } else if(that.is_object(value)) { // Object, Use it as subcontext! 190 | return renderedBefore + that.render(content, that.create_context(value), 191 | partials, true) + renderedAfter; 192 | } else if(typeof value === "function") { 193 | // higher order section 194 | return renderedBefore + value.call(context, content, function(text) { 195 | return that.render(text, context, partials, true); 196 | }) + renderedAfter; 197 | } else if(value) { // boolean section 198 | return renderedBefore + that.render(content, context, partials, true) + renderedAfter; 199 | } else { 200 | return renderedBefore + "" + renderedAfter; 201 | } 202 | } 203 | }); 204 | }, 205 | 206 | /* 207 | Replace {{foo}} and friends with values from our view 208 | */ 209 | render_tags: function(template, context, partials, in_recursion) { 210 | // tit for tat 211 | var that = this; 212 | 213 | var new_regex = function() { 214 | return new RegExp(that.otag + "(=|!|>|\\{|%)?([^\\/#\\^]+?)\\1?" + 215 | that.ctag + "+", "g"); 216 | }; 217 | 218 | var regex = new_regex(); 219 | var tag_replace_callback = function(match, operator, name) { 220 | switch(operator) { 221 | case "!": // ignore comments 222 | return ""; 223 | case "=": // set new delimiters, rebuild the replace regexp 224 | that.set_delimiters(name); 225 | regex = new_regex(); 226 | return ""; 227 | case ">": // render partial 228 | return that.render_partial(name, context, partials); 229 | case "{": // the triple mustache is unescaped 230 | return that.find(name, context); 231 | default: // escape the value 232 | return that.escape(that.find(name, context)); 233 | } 234 | }; 235 | var lines = template.split("\n"); 236 | for(var i = 0; i < lines.length; i++) { 237 | lines[i] = lines[i].replace(regex, tag_replace_callback, this); 238 | if(!in_recursion) { 239 | this.send(lines[i]); 240 | } 241 | } 242 | 243 | if(in_recursion) { 244 | return lines.join("\n"); 245 | } 246 | }, 247 | 248 | set_delimiters: function(delimiters) { 249 | var dels = delimiters.split(" "); 250 | this.otag = this.escape_regex(dels[0]); 251 | this.ctag = this.escape_regex(dels[1]); 252 | }, 253 | 254 | escape_regex: function(text) { 255 | // thank you Simon Willison 256 | if(!arguments.callee.sRE) { 257 | var specials = [ 258 | '/', '.', '*', '+', '?', '|', 259 | '(', ')', '[', ']', '{', '}', '\\' 260 | ]; 261 | arguments.callee.sRE = new RegExp( 262 | '(\\' + specials.join('|\\') + ')', 'g' 263 | ); 264 | } 265 | return text.replace(arguments.callee.sRE, '\\$1'); 266 | }, 267 | 268 | /* 269 | find `name` in current `context`. That is find me a value 270 | from the view object 271 | */ 272 | find: function(name, context) { 273 | name = this.trim(name); 274 | 275 | // Checks whether a value is thruthy or false or 0 276 | function is_kinda_truthy(bool) { 277 | return bool === false || bool === 0 || bool; 278 | } 279 | 280 | var value; 281 | if(is_kinda_truthy(context[name])) { 282 | value = context[name]; 283 | } else if(is_kinda_truthy(this.context[name])) { 284 | value = this.context[name]; 285 | } 286 | 287 | if(typeof value === "function") { 288 | return value.apply(context); 289 | } 290 | if(value !== undefined) { 291 | return value; 292 | } 293 | // silently ignore unkown variables 294 | return ""; 295 | }, 296 | 297 | // Utility methods 298 | 299 | /* includes tag */ 300 | includes: function(needle, haystack) { 301 | return haystack.indexOf(this.otag + needle) != -1; 302 | }, 303 | 304 | /* 305 | Does away with nasty characters 306 | */ 307 | escape: function(s) { 308 | s = String(s === null ? "" : s); 309 | return s.replace(/&(?!\w+;)|["'<>\\]/g, function(s) { 310 | switch(s) { 311 | case "&": return "&"; 312 | case "\\": return "\\\\"; 313 | case '"': return '"'; 314 | case "'": return '''; 315 | case "<": return "<"; 316 | case ">": return ">"; 317 | default: return s; 318 | } 319 | }); 320 | }, 321 | 322 | // by @langalex, support for arrays of strings 323 | create_context: function(_context) { 324 | if(this.is_object(_context)) { 325 | return _context; 326 | } else { 327 | var iterator = "."; 328 | if(this.pragmas["IMPLICIT-ITERATOR"]) { 329 | iterator = this.pragmas["IMPLICIT-ITERATOR"].iterator; 330 | } 331 | var ctx = {}; 332 | ctx[iterator] = _context; 333 | return ctx; 334 | } 335 | }, 336 | 337 | is_object: function(a) { 338 | return a && typeof a == "object"; 339 | }, 340 | 341 | is_array: function(a) { 342 | return Object.prototype.toString.call(a) === '[object Array]'; 343 | }, 344 | 345 | /* 346 | Gets rid of leading and trailing whitespace 347 | */ 348 | trim: function(s) { 349 | return s.replace(/^\s*|\s*$/g, ""); 350 | }, 351 | 352 | /* 353 | Why, why, why? Because IE. Cry, cry cry. 354 | */ 355 | map: function(array, fn) { 356 | if (typeof array.map == "function") { 357 | return array.map(fn); 358 | } else { 359 | var r = []; 360 | var l = array.length; 361 | for(var i = 0; i < l; i++) { 362 | r.push(fn(array[i])); 363 | } 364 | return r; 365 | } 366 | } 367 | }; 368 | 369 | return({ 370 | name: "mustache.js", 371 | version: "0.3.1-dev-twitter", 372 | 373 | /* 374 | Turns a template and view into HTML 375 | */ 376 | to_html: function(template, view, partials, send_fun) { 377 | var renderer = new Renderer(); 378 | if(send_fun) { 379 | renderer.send = send_fun; 380 | } 381 | renderer.render(template, view || {}, partials); 382 | if(!send_fun) { 383 | return renderer.buffer.join("\n"); 384 | } 385 | } 386 | }); 387 | }(); 388 | --------------------------------------------------------------------------------