| @@ -0,0 +1,10 @@ | |||||
| require 'rake/testtask' | |||||
| task :default => :test | |||||
| Rake::TestTask.new do |t| | |||||
| t.libs << 'lib' | |||||
| t.pattern = 'test/**/*_test.rb' | |||||
| t.verbose = false | |||||
| end | |||||
| @@ -0,0 +1 @@ | |||||
| <h1>{{title}}{{! just something interesting... or not... }}</h1> | |||||
| @@ -0,0 +1,5 @@ | |||||
| var comments = { | |||||
| title: function() { | |||||
| return "A Comedy of Errors"; | |||||
| } | |||||
| }; | |||||
| @@ -0,0 +1 @@ | |||||
| <h1>A Comedy of Errors{{! just something interesting... or not... }}</h1> | |||||
| @@ -0,0 +1,16 @@ | |||||
| <h1>{{header}}</h1> | |||||
| {{#list}} | |||||
| <ul> | |||||
| {{#item}} | |||||
| {{#current}} | |||||
| <li><strong>{{name}}</strong></li> | |||||
| {{/current}} | |||||
| {{#link}} | |||||
| <li><a href="{{url}}">{{name}}</a></li> | |||||
| {{/link}} | |||||
| {{/item}} | |||||
| </ul> | |||||
| {{/list}} | |||||
| {{#empty}} | |||||
| <p>The list is empty.</p> | |||||
| {{/empty}} | |||||
| @@ -0,0 +1,25 @@ | |||||
| var complex = { | |||||
| header: function() { | |||||
| return "Colors"; | |||||
| }, | |||||
| item: [ | |||||
| {name: "red", current: true, url: "#Red"}, | |||||
| {name: "green", current: false, url: "#Green"}, | |||||
| {name: "blue", current: false, url: "#Blue"} | |||||
| ], | |||||
| link: function() { | |||||
| var v = this["current"] === true; | |||||
| // print("link() returns " + v); | |||||
| return v; | |||||
| }, | |||||
| list: function() { | |||||
| var v = this.item.length !== 0; | |||||
| // print("list() returns " + v); | |||||
| return v; | |||||
| }, | |||||
| empty: function() { | |||||
| var v = this.item.length === 0; | |||||
| // print("empty() returns " + v); | |||||
| return v; | |||||
| } | |||||
| }; | |||||
| @@ -0,0 +1 @@ | |||||
| <h1>Colors</h1> <ul> <li><strong>red</strong></li> <li><a href="#Green">green</a></li> <li><a href="#Blue">blue</a></li> </ul> | |||||
| @@ -0,0 +1 @@ | |||||
| <h1>{{title}}</h1> | |||||
| @@ -0,0 +1,14 @@ | |||||
| $LOAD_PATH.unshift File.dirname(__FILE__) + '/../lib' | |||||
| require 'mustache' | |||||
| class Escaped < Mustache | |||||
| self.path = File.dirname(__FILE__) | |||||
| def title | |||||
| "Bear > Shark" | |||||
| end | |||||
| end | |||||
| if $0 == __FILE__ | |||||
| puts Escaped.to_html | |||||
| end | |||||
| @@ -0,0 +1 @@ | |||||
| Again, {{title}}! | |||||
| @@ -0,0 +1,5 @@ | |||||
| Hello {{name}} | |||||
| You have just won ${{value}}! | |||||
| {{#in_ca}} | |||||
| Well, ${{ taxed_value }}, after taxes. | |||||
| {{/in_ca}} | |||||
| @@ -0,0 +1,8 @@ | |||||
| var simple = { | |||||
| name: "Chris", | |||||
| value: 10000, | |||||
| taxed_value: function() { | |||||
| return this.value - (this.value * 0.4); | |||||
| }, | |||||
| in_ca: true | |||||
| }; | |||||
| @@ -0,0 +1,26 @@ | |||||
| $LOAD_PATH.unshift File.dirname(__FILE__) + '/../lib' | |||||
| require 'mustache' | |||||
| class Simple < Mustache | |||||
| self.path = File.dirname(__FILE__) | |||||
| def name | |||||
| "Chris" | |||||
| end | |||||
| def value | |||||
| 10_000 | |||||
| end | |||||
| def taxed_value | |||||
| value - (value * 0.4) | |||||
| end | |||||
| def in_ca | |||||
| true | |||||
| end | |||||
| end | |||||
| if $0 == __FILE__ | |||||
| puts Simple.to_html | |||||
| end | |||||
| @@ -0,0 +1,3 @@ | |||||
| Hello Chris | |||||
| You have just won $10000! | |||||
| Well, $6000, after taxes. | |||||
| @@ -0,0 +1,2 @@ | |||||
| <h1>{{title}}</h1> | |||||
| {{<inner_partial}} | |||||
| @@ -0,0 +1,14 @@ | |||||
| $LOAD_PATH.unshift File.dirname(__FILE__) + '/../lib' | |||||
| require 'mustache' | |||||
| class TemplatePartial < Mustache | |||||
| self.path = File.dirname(__FILE__) | |||||
| def title | |||||
| "Welcome" | |||||
| end | |||||
| end | |||||
| if $0 == __FILE__ | |||||
| puts TemplatePartial.to_html | |||||
| end | |||||
| @@ -0,0 +1 @@ | |||||
| <h1>{{{title}}}</h1> | |||||
| @@ -0,0 +1,14 @@ | |||||
| $LOAD_PATH.unshift File.dirname(__FILE__) + '/../lib' | |||||
| require 'mustache' | |||||
| class Unescaped < Mustache | |||||
| self.path = File.dirname(__FILE__) | |||||
| def title | |||||
| "Bear > Shark" | |||||
| end | |||||
| end | |||||
| if $0 == __FILE__ | |||||
| puts Unescaped.to_html | |||||
| end | |||||
| @@ -0,0 +1,3 @@ | |||||
| <h1>{{greeting}}</h1> | |||||
| {{<simple}} | |||||
| <h3>{{farewell}}</h3> | |||||
| @@ -0,0 +1,18 @@ | |||||
| $LOAD_PATH.unshift File.dirname(__FILE__) + '/../lib' | |||||
| require 'mustache' | |||||
| class ViewPartial < Mustache | |||||
| self.path = File.dirname(__FILE__) | |||||
| def greeting | |||||
| "Welcome" | |||||
| end | |||||
| def farewell | |||||
| "Fair enough, right?" | |||||
| end | |||||
| end | |||||
| if $0 == __FILE__ | |||||
| puts ViewPartial.to_html | |||||
| end | |||||
| @@ -0,0 +1,110 @@ | |||||
| /* | |||||
| Shamless port of http://github.com/defunkt/mustache | |||||
| by Jan Lehnardt <jan@apache.org> | |||||
| Thanks @defunkt for the awesome code | |||||
| TBD: License | |||||
| ChangeLog: | |||||
| - 04.10.2009: Ininitial port at http://devhouseberlin.de/ | |||||
| */ | |||||
| var Mustache = { | |||||
| name: "mustache.js", | |||||
| version: "0.1", | |||||
| debug: true, | |||||
| stack: " ", | |||||
| context: {}, | |||||
| to_html: function(view, template) { | |||||
| return this.render(template, view); | |||||
| }, | |||||
| render: function(template, view) { | |||||
| this.stack = this.stack + " "; | |||||
| // fail fast | |||||
| if(template.indexOf("{{") == -1) { | |||||
| return template; | |||||
| } | |||||
| this.context = context = this.merge((this.context || {}), view); | |||||
| var html = this.render_section(template); | |||||
| // restore context, recursion might have messed it up | |||||
| // this.context = context; | |||||
| return this.render_tags(html); | |||||
| }, | |||||
| merge: function(a, b) { | |||||
| for(var name in b) { | |||||
| if(b.hasOwnProperty(name)) { | |||||
| a[name] = b[name]; | |||||
| } | |||||
| } | |||||
| return a; | |||||
| }, | |||||
| render_section: function(template) { | |||||
| if(template.indexOf("{{#") == -1) { | |||||
| return template; | |||||
| } | |||||
| var that = this; | |||||
| return template.replace(/\{\{\#(.+)\}\}\s*(.+)\s*\{\{\/\1\}\}\s*/mg, | |||||
| function(match, name, content) { | |||||
| print(match); | |||||
| print(name); | |||||
| print(content); | |||||
| var value = that.find(name); | |||||
| if(that.is_array(value)) { | |||||
| return value.map(function(row) { | |||||
| return that.render(content, row); | |||||
| }).join(''); | |||||
| } else if(value) { | |||||
| return that.render(content); | |||||
| } else { | |||||
| return ""; | |||||
| } | |||||
| } | |||||
| ); | |||||
| }, | |||||
| is_array: function(a) { | |||||
| return (a && | |||||
| typeof a === 'object' && | |||||
| a.constructor === Array); | |||||
| }, | |||||
| render_tags: function(template) { | |||||
| // values | |||||
| var that = this; | |||||
| return template.replace(/\{\{(!|<|\{)?([^\/#]+?)\1?\}\}+/mg, | |||||
| function (match, operator, name) { | |||||
| switch(operator) { | |||||
| case "!": // ignore comments | |||||
| return match; | |||||
| // TODO: partials | |||||
| // case "<": // render partial | |||||
| // return this.render_partial() | |||||
| case '{': // the triple mustache is unescaped | |||||
| return that.find(name); | |||||
| default: // escape the value | |||||
| return that.find(name); | |||||
| } | |||||
| }, this); | |||||
| }, | |||||
| find: function(name) { | |||||
| name = this.trim(name); | |||||
| // print(this.stack + "find(" + name + ")"); | |||||
| var context = this.context; | |||||
| if(typeof context[name] === "function") { | |||||
| return context[name].apply(context); | |||||
| } | |||||
| if(context[name] !== undefined) { | |||||
| return context[name]; | |||||
| } | |||||
| throw("Can't find " + name + " in " + context); | |||||
| }, | |||||
| trim: function(s) { | |||||
| return s.replace(/^\s*|\s*$/g, ''); | |||||
| }, | |||||
| }; | |||||
| @@ -0,0 +1,140 @@ | |||||
| /* | |||||
| Shamless port of http://github.com/defunkt/mustache | |||||
| by Jan Lehnardt <jan@apache.org> | |||||
| Thanks @defunkt for the awesome code | |||||
| TBD: License | |||||
| ChangeLog: | |||||
| - 04.10.2009: Ininitial port at http://devhouseberlin.de/ | |||||
| */ | |||||
| var Mustache = { | |||||
| name: "mustache.js", | |||||
| version: "0.1", | |||||
| debug: true, | |||||
| stack: " ", | |||||
| context: {}, | |||||
| to_html: function(view, template) { | |||||
| return this.render(template, view); | |||||
| }, | |||||
| render: function(template, view) { | |||||
| this.stack = this.stack + " "; | |||||
| // fail fast | |||||
| if(template.indexOf("{{") == -1) { | |||||
| return template; | |||||
| } | |||||
| this.context = context = this.merge((this.context || {}), view); | |||||
| var html = this.render_section(template); | |||||
| // restore context, recursion might have messed it up | |||||
| // this.context = context; | |||||
| return this.render_tags(html); | |||||
| }, | |||||
| merge: function(a, b) { | |||||
| for(var name in b) { | |||||
| if(b.hasOwnProperty(name)) { | |||||
| a[name] = b[name]; | |||||
| } | |||||
| } | |||||
| return a; | |||||
| }, | |||||
| render_section: function(template) { | |||||
| if(template.indexOf("{{#") == -1) { | |||||
| return template; | |||||
| } | |||||
| var that = this; | |||||
| return template.replace(/\{\{\#(.+)\}\}\s*(.+)\s*\{\{\/\1\}\}\s*/mg, | |||||
| function(match, name, content) { | |||||
| print(match); | |||||
| print(name); | |||||
| print(content); | |||||
| var value = that.find(name); | |||||
| if(that.is_array(value)) { | |||||
| return value.map(function(row) { | |||||
| return that.render(content, row); | |||||
| }).join(''); | |||||
| } else if(value) { | |||||
| return that.render(content); | |||||
| } else { | |||||
| return ""; | |||||
| } | |||||
| } | |||||
| ); | |||||
| }, | |||||
| is_array: function(a) { | |||||
| return (a && | |||||
| typeof a === 'object' && | |||||
| a.constructor === Array); | |||||
| }, | |||||
| render_tags: function(template) { | |||||
| // values | |||||
| var that = this; | |||||
| return template.replace(/\{\{(!|<|\{)?([^\/#]+?)\1?\}\}+/mg, | |||||
| function (match, operator, name) { | |||||
| switch(operator) { | |||||
| case "!": // ignore comments | |||||
| return match; | |||||
| // TODO: partials | |||||
| // case "<": // render partial | |||||
| // return this.render_partial() | |||||
| case '{': // the triple mustache is unescaped | |||||
| return that.find(name); | |||||
| default: // escape the value | |||||
| return that.find(name); | |||||
| } | |||||
| }, this); | |||||
| }, | |||||
| find: function(name) { | |||||
| name = this.trim(name); | |||||
| // print(this.stack + "find(" + name + ")"); | |||||
| var context = this.context; | |||||
| if(typeof context[name] === "function") { | |||||
| return context[name].apply(context); | |||||
| } | |||||
| if(context[name] !== undefined) { | |||||
| return context[name]; | |||||
| } | |||||
| throw("Can't find " + name + " in " + context); | |||||
| }, | |||||
| trim: function(s) { | |||||
| return s.replace(/^\s*|\s*$/g, ''); | |||||
| }, | |||||
| }; | |||||
| var complex = { | |||||
| header: function() { | |||||
| return "Colors"; | |||||
| }, | |||||
| item: [ | |||||
| {name: "red", current: true, url: "#Red"}, | |||||
| {name: "green", current: false, url: "#Green"}, | |||||
| {name: "blue", current: false, url: "#Blue"} | |||||
| ], | |||||
| link: function() { | |||||
| var v = this["current"] === true; | |||||
| // print("link() returns " + v); | |||||
| return v; | |||||
| }, | |||||
| list: function() { | |||||
| var v = this.item.length !== 0; | |||||
| // print("list() returns " + v); | |||||
| return v; | |||||
| }, | |||||
| empty: function() { | |||||
| var v = this.item.length === 0; | |||||
| // print("empty() returns " + v); | |||||
| return v; | |||||
| } | |||||
| }; | |||||
| var template = "<h1>{{header}}<\/h1>\n{{#list}}\n <ul>\n {{#item}}\n {{#current}}\n <li><strong>{{name}}<\/strong><\/li>\n {{\/current}}\n {{#link}}\n <li><a href=\"{{url}}\">{{name}}<\/a><\/li>\n {{\/link}}\n {{\/item}}\n <\/ul>\n{{\/list}}\n{{#empty}}\n <p>The list is empty.<\/p>\n{{\/empty}}"; | |||||
| var result = Mustache.to_html(complex, template); | |||||
| print(result); | |||||
| @@ -0,0 +1,34 @@ | |||||
| # mustache.js runs ruby tests, sorta | |||||
| # run with mustache_test.rb testname | |||||
| require 'json' | |||||
| testname = ARGV[0] | |||||
| puts "doing #{testname}" | |||||
| view = File.read("examples/#{testname}.js") | |||||
| template = File.read("examples/#{testname}.html").to_json | |||||
| expect = File.read("examples/#{testname}.txt") | |||||
| mustache = File.read("mustache.js") | |||||
| runner = <<-JS | |||||
| #{mustache} | |||||
| #{view} | |||||
| var template = #{template}; | |||||
| var result = Mustache.to_html(#{testname}, template); | |||||
| print(result); | |||||
| JS | |||||
| File.open("runner.js", 'w') {|f| f << runner} | |||||
| result = `js runner.js` | |||||
| if(result == expect) | |||||
| puts "." | |||||
| else | |||||
| puts "Error in #{testname}" | |||||
| puts "Expected" | |||||
| puts expect | |||||
| puts "Actual" | |||||
| puts result | |||||
| end | |||||