diff --git a/README.md b/README.md index 6c46fb8..b7fa878 100644 --- a/README.md +++ b/README.md @@ -64,23 +64,27 @@ Enumerable Sections use the same syntax as condition sections do. `{{#shopping_i ### View Partials mustache.js supports a quite powerful but yet simple view partial mechanism. Use the following syntax for partials: `{{`. To disable escaping, simply use tripple mustaches like `{{{unescaped_variable}}}`. diff --git a/examples/template_partial.2.html b/examples/template_partial.2.html new file mode 100644 index 0000000..54bdd7d --- /dev/null +++ b/examples/template_partial.2.html @@ -0,0 +1 @@ +Again, {{again}}! \ No newline at end of file diff --git a/examples/template_partial.html b/examples/template_partial.html index ed5bd85..b5ecbb8 100644 --- a/examples/template_partial.html +++ b/examples/template_partial.html @@ -1,2 +1,2 @@

{{title}}

-{{Welcome -Again, Welcome! +Again, Goodbye! diff --git a/examples/view_partial.2.html b/examples/view_partial.2.html new file mode 100644 index 0000000..2fea632 --- /dev/null +++ b/examples/view_partial.2.html @@ -0,0 +1,5 @@ +Hello {{name}} +You have just won ${{value}}! +{{#in_ca}} +Well, ${{ taxed_value }}, after taxes. +{{/in_ca}} diff --git a/examples/view_partial.html b/examples/view_partial.html index 59aadfd..d7c6b49 100644 --- a/examples/view_partial.html +++ b/examples/view_partial.html @@ -1,3 +1,3 @@

{{greeting}}

-{{{{farewell}} \ No newline at end of file diff --git a/examples/view_partial.js b/examples/view_partial.js index ea7e505..6dc0a18 100644 --- a/examples/view_partial.js +++ b/examples/view_partial.js @@ -1,24 +1,19 @@ -var view_partial = { +var partial_context = { greeting: function() { return "Welcome"; }, farewell: function() { return "Fair enough, right?"; - } -}; - -var simple = { - name: "Chris", - value: 10000, - taxed_value: function() { - return this.value - (this.value * 0.4); }, - in_ca: true + + partial: { + name: "Chris", + value: 10000, + taxed_value: function() { + return this.value - (this.value * 0.4); + }, + in_ca: true + } }; -var simple_template = "Hello {{name}}\n" + -"You have just won ${{value}}!\n" + -"{{#in_ca}}\n" + -"Well, ${{ taxed_value }}, after taxes.\n" + -"{{/in_ca}}\n"; diff --git a/mustache.js b/mustache.js index 4de34d1..4f4b0c4 100644 --- a/mustache.js +++ b/mustache.js @@ -15,37 +15,33 @@ var Mustache = function() { otag: "{{", ctag: "}}", - render: function(template, context) { + render: function(template, context, partials) { // fail fast if(template.indexOf(this.otag) == -1) { return template; } - var html = this.render_section(template, context); - return this.render_tags(html, context); + var html = this.render_section(template, context, partials); + return this.render_tags(html, context, partials); }, /* Tries to find a partial in the global scope and render it */ - render_partial: function(name, context) { - // FIXME: too hacky - var evil_name = eval(name); - switch(typeof evil_name) { - case "string": // a string partial, we simply render - return this.render(evil_name, context); - case "object": // a view partial needs a `name_template` template - var tpl = name + "_template"; - return this.render(eval(tpl), evil_name); - default: // should not happen #famouslastwords - throw("Unknown partial type."); + render_partial: function(name, context, partials) { + if(typeof(context[name]) != "object") { + throw({message: "subcontext for '" + name + "' is not an object"}); + } + if(!partials || !partials[name]) { + throw({message: "unknown_partial"}); } + return this.render(partials[name], context[name], partials); }, /* Renders boolean and enumerable sections */ - render_section: function(template, context) { + render_section: function(template, context, partials) { if(template.indexOf(this.otag + "#") == -1) { return template; } @@ -59,10 +55,11 @@ var Mustache = function() { var value = that.find(name, context); if(that.is_array(value)) { // Enumerable, Let's loop! return that.map(value, function(row) { - return that.render(content, that.merge(context, that.create_context(row))); + return that.render(content, that.merge(context, + that.create_context(row)), partials); }).join(''); } else if(value) { // boolean section - return that.render(content, context); + return that.render(content, context, partials); } else { return ""; } @@ -72,7 +69,7 @@ var Mustache = function() { /* Replace {{foo}} and friends with values from our view */ - render_tags: function(template, context) { + render_tags: function(template, context, partials) { var lines = template.split("\n"); var new_regex = function() { @@ -96,7 +93,7 @@ var Mustache = function() { i--; return ""; case "<": // render partial - return that.render_partial(name, context); + return that.render_partial(name, context, partials); case "{": // the triple mustache is unescaped return that.find(name, context); default: // escape the value @@ -233,8 +230,8 @@ var Mustache = function() { /* Turns a template and view into HTML */ - to_html: function(template, view) { - return new Renderer().render(template, view); + to_html: function(template, view, partials) { + return new Renderer().render(template, view, partials); } }); }(); diff --git a/test/mustache_spec.rb b/test/mustache_spec.rb index a01de57..9b94840 100644 --- a/test/mustache_spec.rb +++ b/test/mustache_spec.rb @@ -7,6 +7,21 @@ testnames = Dir.glob(__DIR__ + '/../examples/*.js').map do |name| File.basename name, '.js' end +non_partials = testnames.select{|t| not t.include? "partial"} +partials = testnames.select{|t| t.include? "partial"} + +def load_test(dir, name, partial=false) + view = File.read(dir + "/../examples/#{name}.js") + template = File.read(dir + "/../examples/#{name}.html").to_json + expect = File.read(dir + "/../examples/#{name}.txt") + if not partial + [view, template, expect] + else + partial = File.read(dir + "/../examples/#{name}.2.html").to_json + [view, template, partial, expect] + end +end + describe "mustache" do before(:all) do @mustache = File.read(__DIR__ + "/../mustache.js") @@ -25,32 +40,54 @@ describe "mustache" do run_js(js).should == "ERROR: Can't find x in [object Object]\n" end - testnames.each do |testname| - describe testname do + non_partials.each do |testname| + describe testname do it "should generate the correct html" do - view = File.read(__DIR__ + "/../examples/#{testname}.js") - template = File.read(__DIR__ + "/../examples/#{testname}.html").to_json - expect = File.read(__DIR__ + "/../examples/#{testname}.txt") - + + view, template, expect = load_test(__DIR__, testname) runner = <<-JS - try { - #{@mustache} - #{view} - var template = #{template}; - var result = Mustache.to_html(template, #{testname}); - print(result); - } catch(e) { - print('ERROR: ' + e.message); - } + try { + #{@mustache} + #{view} + var template = #{template}; + var result = Mustache.to_html(template, #{testname}); + print(result); + } catch(e) { + print('ERROR: ' + e.message); + } JS - - + run_js(runner).should == expect end end end - + + partials.each do |testname| + describe testname do + it "should generate the correct html" do + + view, template, partial, expect = + load_test(__DIR__, testname, true) + + runner = <<-JS + try { + #{@mustache} + #{view}; + var template = #{template}; + var partials = {"partial": #{partial}}; + var result = Mustache.to_html(template, partial_context, partials); + print(result); + } catch(e) { + print('ERROR: ' + e.message); + } + JS + + run_js(runner).should == expect + end + end + end + def run_js(js) File.open("runner.js", 'w') {|f| f << js} `js runner.js`