diff --git a/.gitignore b/.gitignore index a7ed15c..bd08399 100644 --- a/.gitignore +++ b/.gitignore @@ -1,5 +1,9 @@ +.DS_Store +.rvmrc runner.js jquery.mustache.js +qooxdoo.mustache.js dojox yui3 -commonjs.mustache.js +requirejs.mustache.js + diff --git a/.travis.yml b/.travis.yml index d599f87..b52599e 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,5 +1,5 @@ rvm: - 1.9.2 before_script: - - sudo apt-get install xulrunner-1.9 + - sudo apt-get -y install xulrunner-2.0 - gem install rspec diff --git a/CHANGES.md b/CHANGES.md deleted file mode 100644 index 40d50b8..0000000 --- a/CHANGES.md +++ /dev/null @@ -1,49 +0,0 @@ -# mustache.js Changes - -## 0.3.1-dev-twitter-b (8/23/2011) - -* Cached regexes for improved performance - -## 0.3.1-dev-twitter (12/3/2010) - -* fixed double-rendering bug -* added Rhino test-runner alongside JavaScriptCore - -## 0.3.1 (??-??-????) - -## 0.3.0 (21-07-2010) - -* Improved whitespace handling. -* Make IMPLICIT ITERATORS a first class feature. -* Fix Rhino compat. -* CommonJS packaging is no longer a special case. -* DRY Rakefile. -* Allow whitespace around tag names. -* Fix partial scope. -* Fix Comments. -* Added inverted sections. -* Avoid double encoding of entities. -* Use sections to dereference subcontexts. -* Added higher order sections. - - -## 0.2.3 (28-03-2010) - -* Better error message for missing partials. -* Added more robust type detection. -* Parse pragmas only once. -* Throw exception when encountering an unknown pragma. -* Ignore undefined partial contexts. Returns verbatim partials. -* Added yui3 packaging. - - -## 0.2.2 (11-02-2010) - -* ctemplate compat: Partials are indicated by >, not <. -* Add support for {{%PRAGMA}} to enable features. -* Made array of strings an option. Enable with `{{%JSTACHE-ENABLE-STRING-ARRAYS}}`. -* mustache compat: Don't barf on unknown variables. -* Add `rake dojo` target to create a dojo package. -* Add streaming api. -* Rename JSTACHE-ENABLE-STRING-ARRAYS to IMPLICIT-ITERATOR. -* Add support for pragma options. diff --git a/README.md b/README.md index f86e7fd..a04f667 100644 --- a/README.md +++ b/README.md @@ -1,348 +1,418 @@ -# mustache.js — Logic-less templates with JavaScript +# mustache.js - Logic-less {{mustache}} templates with JavaScript > What could be more logical awesome than no logic at all? -For a list of implementations (other than JavaScript) and editor -plugins, see . +[mustache.js](http://github.com/janl/mustache.js) is an implementation of the +[Mustache](http://mustache.github.com/) template system in JavaScript. +[Mustache](http://mustache.github.com/) is a logic-less template syntax. It can +be used for HTML, config files, source code - anything. It works by expanding +tags in a template using values provided in a hash or object. -## Where to Use? +We call it "logic-less" because there are no if statements, else clauses, or for +loops. Instead there are only tags. Some tags are replaced with a value, some +nothing, and others a series of values. -You can use mustache.js rendering stuff in various scenarios. E.g. you can -render templates in your browser, or rendering server-side stuff with -[node.js][node.js], use it for rendering stuff in [CouchDB][couchdb]’s views. +For a language-agnostic overview of Mustache's template syntax, see the +`mustache(5)` [manpage](http://mustache.github.com/mustache.5.html). +## Where to use mustache.js? -## Who Uses Mustache? +You can use mustache.js to render templates in many various scenarios where you +can use JavaScript. For example, you can render templates in a browser, +server-side using [node](http://nodejs.org/), in [CouchDB](http://couchdb.apache.org/) +views, or in almost any other environment where you can use JavaScript. -An updated list is kept on the Github wiki. Add yourself, if you use -mustache.js: +## Who uses mustache.js? +An updated list of mustache.js users is kept [on the Github wiki](http://wiki.github.com/janl/mustache.js/beard-competition). +Add yourself or your company if you use mustache.js! ## Usage -A quick example how to use mustache.js: +Below is quick example how to use mustache.js: var view = { title: "Joe", - calc: function() { + calc: function () { return 2 + 4; } - } + }; - var template = "{{title}} spends {{calc}}"; + var output = Mustache.render("{{title}} spends {{calc}}", view); - var html = Mustache.to_html(template, view); +In this example, the `Mustache.render` function takes two parameters: 1) the +[mustache](http://mustache.github.com/) template and 2) a `view` object that +contains the data and code needed to render the template. -`template` is a simple string with mustache tags and `view` is a JavaScript -object containing the data and any code to render the template. +### CommonJS +mustache.js is usable without any modification in both browsers and [CommonJS](http://www.commonjs.org/) +environments like [node.js](http://nodejs.org/). To use it as a CommonJS module, +simply require the file, like this: -## Template Tag Types + var Mustache = require("mustache"); -There are several types of tags currently implemented in mustache.js. +## Templates -For a language-agnostic overview of Mustache’s template syntax, see the -`mustache(5)` manpage or . +A [mustache](http://mustache.github.com/) template is a string that contains +any number of mustache tags. Tags are indicated by the double mustaches that +surround them. `{{person}}` is a tag, as is `{{#person}}`. In both examples we +refer to `person` as the tag's key. -### Simple Tags +There are several types of tags available in mustache.js. -Tags are always surrounded by mustaches like this `{{foobar}}`. +### Variables - var view = {name: "Joe", say_hello: function(){ return "hello" }} +The most basic tag type is a simple variable. A `{{name}}` tag renders the value +of the `name` key in the current context. If there is no such key, nothing is +rendered. - template = "{{say_hello}}, {{name}}" +All variables are HTML-escaped by default. If you want to render unescaped HTML, +use the triple mustache: `{{{name}}}`. You can also use `&` to unescape a +variable. + +Template: -#### Accessing values in nested objects (Dot Notation) + * {{name}} + * {{age}} + * {{company}} + * {{{company}}} + * {{&company}} -To access data logically grouped into nested objects, specify a '.' delimited -path to the value. +View: - var contact = { - name: {first: "Bill", last: "Bobitybob" }, - age: 37 + { + "name": "Chris", + "company": "GitHub" } - template = "Hello, {{name.first}} {{name.last}}. You are {{age}} years old." +Output: -*NOTICE*: The dot notation feature was recently implemented for the 0.4 - release, which is not out as of Nov 9 2011. You can find the feature in the - current master branch of mustachejs. + * Chris + * + * <b>GitHub</b> + * GitHub + * GitHub -### Conditional Sections +JavaScript's dot notation may be used to access keys that are properties of +objects in a view. -Conditional sections begin with `{{#condition}}` and end with -`{{/condition}}`. When `condition` evaluates to true, the section is rendered, -otherwise the whole block will output nothing at all. `condition` may be a -function returning true/false or a simple boolean. +Template: - var view = {condition: function() { - // [...your code goes here...] - return true; - }} + * {{name.first}} {{name.last}} + * {{age}} - {{#condition}} - I will be visible if condition is true - {{/condition}} +View: + { + "name": { + "first": "Michael", + "last": "Jackson" + }, + "age": "RIP" + } -### Enumerable Sections +Output: -Enumerable Sections use the same syntax as condition sections do. -`{{#shopping_items}}` and `{{/shopping_items}}`. Actually the view decides how -mustache.js renders the section. If the view returns an array, it will -iterator over the items. Use `{{.}}` to access the current item inside the -enumeration section. + * Michael Jackson + * RIP - var view = {name: "Joe's shopping card", - items: ["bananas", "apples"]} +### Sections - var template = "{{name}}:
    {{#items}}
  • {{.}}
  • {{/items}}
" +Sections render blocks of text one or more times, depending on the value of the +key in the current context. - Outputs: - Joe's shopping card:
  • bananas
  • apples
+A section begins with a pound and ends with a slash. That is, `{{#person}}` +begins a `person` section, while `{{/person}}` ends it. The text between the two +tags is referred to as that section's "block". +The behavior of the section is determined by the value of the key. -### Higher Order Sections +#### False Values or Empty Lists -If a section key returns a function, it will be called and passed both the -unrendered block of text and a renderer convenience function. +If the `person` key exists and has a value of `null`, `undefined`, or `false`, +or is an empty list, the block will not be rendered. -Given this object: +Template: - "name": "Tater", - "bolder": function() { - return function(text, render) { - return "" + render(text) + '' - } - } + Shown. + {{#person}} + Never shown! + {{/person}} -And this template: +View: - {{#bolder}}Hi {{name}}.{{/bolder}} + { + "person": true + } -We'll get this output: +Output: - Hi Tater. + Shown. + +#### Non-Empty Lists -As you can see, we’re pre-processing the text in the block. This can be used -to implement caching, filters (like syntax highlighting), etc. +If the `person` key exists and is not `null`, `undefined`, or `false`, and is +not an empty list the block will be rendered one or more times. -You can use `this.name` to access the attribute `name` from your view. +When the value is a list, the block is rendered once for each item in the list. +The context of the block is set to the current item in the list for each +iteration. In this way we can loop over collections. -### Dereferencing Sections +Template: -If your data has components that are logically grouped into nested objects, -you may wish to dereference an object to access its values. + {{#stooges}} + {{name}} + {{/stooges}} -Given this object: +View: { - "name": "Bill", - "address": { - "street": "801 Streetly street", - "city": "Boston", - "state": "MA", - "zip" "02101" - } + "stooges": [ + { "name": "Moe" }, + { "name": "Larry" }, + { "name": "Curly" } + ] } -And this template: - -

Contact: {{name}}

- {{#address}} -

{{street}}

-

{{city}}, {{state}} {{zip}}

- {{/address}} +Output: -We'll get this output: + Moe + Larry + Curly -

Contact: Bill

-

801 Streetly street

-

Boston, MA 02101

+When looping over an array of strings, a `.` can be used to refer to the current +item in the list. -### Inverted Sections +Template: -An inverted section opens with `{{^section}}` instead of `{{#section}}` and -uses a boolean negative to evaluate. Empty arrays are considered falsy. + {{#musketeers}} + * {{.}} + {{/musketeers}} View: - var inverted_section = { - "repo": [] + { + "musketeers": ["Athos", "Aramis", "Porthos", "D'Artagnan"] } -Template: - - {{#repo}}{{name}}{{/repo}} - {{^repo}}No repos :({{/repo}} +Output: -Result: + * Athos + * Aramis + * Porthos + * D'Artagnan - No repos :( +If the value of a section variable is a function, it will be called in the +context of the current item in the list on each iteration. +Template: -### View Partials + {{#beatles}} + * {{name}} + {{/beatles}} -mustache.js supports a quite powerful but yet simple view partial mechanism. -Use the following syntax for partials: `{{>partial_name}}` +View: - var view = { - name: "Joe", - winnings: { - value: 1000, - taxed_value: function() { - return this.value - (this.value * 0.4); - } + { + "beatles": [ + { "firstName": "John", "lastName": "Lennon" }, + { "firstName": "Paul", "lastName": "McCartney" }, + { "firstName": "George", "lastName": "Harrison" }, + { "firstName": "Ringo", "lastName": "Starr" } + ], + "name": function () { + return this.firstName + " " + this.lastName; } - }; - - var template = "Welcome, {{name}}! {{>winnings}}" - var partials = { - winnings: "You just won ${{value}} (which is ${{taxed_value}} after tax)"}; - - var output = Mustache.to_html(template, view, partials) - - output will be: - Welcome, Joe! You just won $1000 (which is $600 after tax) - -You invoke a partial with `{{>winnings}}`. Invoking the partial `winnings` -will tell mustache.js to look for a object in the context's property -`winnings`. It will then use that object as the context for the template found -in `partials` for `winnings`. - -## Escaping - -mustache.js does escape all values when using the standard double mustache -syntax. Characters which will be escaped: `& \ " ' < >`. To disable escaping, -simply use triple mustaches like `{{{unescaped_variable}}}`. - -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`. + } +Output: -## Streaming + * John Lennon + * Paul McCartney + * George Harrison + * Ringo Starr -To stream template results out of mustache.js, you can pass an optional -`send()` callback to the `to_html()` call: +#### Functions - Mustache.to_html(template, view, partials, function(line) { - print(line); - }); +If the value of a section key is a function, it is called with the section's +literal block of text, un-rendered, as its first argument. The second argument +is a special rendering function that uses the current view as its view argument. +It is called in the context of the current view object. +Template: -## Pragmas + {{#bold}}Hi {{name}}.{{/bold}} -Pragma tags let you alter the behaviour of mustache.js. They have the format -of +View: - {{%PRAGMANAME}} + { + "name": "Tater", + "bold": function () { + return function (text, render) { + return "" + render(text) + ""; + } + } + } -and they accept options: +Output: - {{%PRAGMANAME option=value}} + Hi Tater. +### Inverted Sections -### IMPLICIT-ITERATOR +An inverted section opens with `{{^section}}` instead of `{{#section}}`. The +block of an inverted section is rendered only if the value of that section's tag +is `null`, `undefined`, `false`, or an empty list. -When using a block to iterate over an enumerable (Array), mustache.js expects -an objects as enumerable items. The implicit iterator pragma enables optional -behaviour of allowing literals as enumerable items. Consider this view: +Template: - var view = { - foo: [1, 2, 3, 4, 5, "french"] - }; + {{#repos}}{{name}}{{/repos}} + {{^repos}}No repos :({{/repos}} -The following template can iterate over the member `foo`: +View: - {{%IMPLICIT-ITERATOR}} - {{#foo}} - {{.}} - {{/foo}} + { + "repos": [] + } -If you don't like the dot in there, the pragma accepts an option to set your -own iteration marker: +Output: - {{%IMPLICIT-ITERATOR iterator=bob}} - {{#foo}} - {{bob}} - {{/foo}} + No repos :( -## More Examples and Documentation +### Comments -See `examples/` for more goodies and read the [original mustache docs][m] +Comments begin with a bang and are ignored. The following template: -## Command Line +

Today{{! ignore me }}.

-See `mustache(1)` man page or - -for command line docs. +Will render as follows: -Or just install it as a RubyGem: +

Today.

- $ gem install mustache - $ mustache -h +Comments may contain newlines. -[m]: http://github.com/defunkt/mustache/#readme -[node.js]: http://nodejs.org -[couchdb]: http://couchdb.apache.org +### Partials +Partials begin with a greater than sign, like {{> box}}. -## Plugins for jQuery, Dojo, Yui, CommonJS, qooxdoo +Partials are rendered at runtime (as opposed to compile time), so recursive +partials are possible. Just avoid infinite loops. -This repository lets you build modules for [jQuery][], [Dojo][], [Yui][] and -[CommonJS][] / [Node.js][] with the help of `rake`. +They also inherit the calling context. Whereas in ERB you may have this: -NOTE: The default `rake` task is only used for testing and require rspec to be -installed (see below). + <%= partial :next_more, :start => start, :size => size %> -Run `rake jquery` to get a jQuery compatible plugin file in the -`mustache-jquery/` directory. +Mustache requires only this: -Run `rake dojo` to get a Dojo compatible plugin file in the `mustache-dojo/` -directory. + {{> next_more}} -Run `rake yui` to get a Yui compatible plugin file in the `mustache-yui/` -directory. +Why? Because the `next_more.mustache` file will inherit the `size` and `start` +variables from the calling context. In this way you may want to think of +partials as includes, or template expansion, even though it's not literally true. -Run `rake commonjs` to get a CommonJS compatible plugin file in the -`mustache-commonjs/` directory which you can also use with [Node.js][]. +For example, this template and partial: -Run `rake qooxdoo` to get a qooxdoo compatible file named `qooxdoo.mustache.js`. + base.mustache: +

Names

+ {{#names}} + {{> user}} + {{/names}} -## Testing + user.mustache: + {{name}} -NOTE: You will need to install rspec first by running `gem install rspec`. +Can be thought of as a single, expanded template: -To run the mustache.js test suite, run `rake spec`. All specs will be run first with JavaScriptCore (using `jsc`) -and again with Rhino, using `java org.mozilla.javascript.tools.shell.Main`. +

Names

+ {{#names}} + {{name}} + {{/names}} -JavaScriptCore is used from the OSX default location: +In mustache.js an object of partials may be passed as the third argument to +`Mustache.render`. The object should be keyed by the name of the partial, and +its value should be the partial text. - /System/Library/Frameworks/JavaScriptCore.framework/Versions/A/Resources/jsc +### Set Delimiter -To install Rhino on OSX, follow [these instructions](Rhino Install). +Set Delimiter tags start with an equals sign and change the tag delimiters from +`{{` and `}}` to custom strings. -### Adding Tests +Consider the following contrived example: -Tests are located in the `examples/` directory. Adding a new test requires three files. Here's an example to add a test named "foo": + * {{ default_tags }} + {{=<% %>=}} + * <% erb_style_tags %> + <%={{ }}=%> + * {{ default_tags_again }} -`examples/foo.html` (the template): +Here we have a list with three items. The first item uses the default tag style, +the second uses ERB style as defined by the Set Delimiter tag, and the third +returns to the default style after yet another Set Delimiter declaration. - foo {{bar}} +According to [ctemplates](http://google-ctemplate.googlecode.com/svn/trunk/doc/howto.html), +this "is useful for languages like TeX, where double-braces may occur in the +text and are awkward to use for markup." -`examples/foo.js` (the view context): +Custom delimiters may not contain whitespace or the equals sign. - var foo = { - bar: "baz" - }; +## Streaming -`examples/foo.txt` (the expected output): +To stream template results out of mustache.js, you can pass an optional callback +to the call to `Mustache.render`: - foo baz + Mustache.render(template, view, partials, function (chunk) { + print(chunk); + }); -[jQuery]: http://jquery.com/ -[Dojo]: http://www.dojotoolkit.org/ -[Yui]: http://developer.yahoo.com/yui/ -[CommonJS]: http://www.commonjs.org/ -[Node.js]: http://nodejs.org/ -[Rhino Install]: http://michaux.ca/articles/installing-rhino-on-os-x +When the template is finished rendering, the callback will be called with `null` +after which it won't be called anymore for that rendering. + +## Plugins for JavaScript Libraries + +By default mustache.js may be used in a browser or any [CommonJS](http://www.commonjs.org/) +environment, including [node](http://nodejs.org/). Additionally, mustache.js may +be built specifically for several different client libraries and platforms, +including the following: + + - [jQuery](http://jquery.com/) + - [MooTools](http://mootools.net/) + - [Dojo](http://www.dojotoolkit.org/) + - [YUI](http://developer.yahoo.com/yui/) + - [RequireJS](http://requirejs.org/) + - [qooxdoo](http://qooxdoo.org/) + +These may be built using [Rake](http://rake.rubyforge.org/) and one of the +following commands: + + $ rake jquery + $ rake mootools + $ rake dojo + $ rake yui + $ rake requirejs + $ rake qooxdoo + +## Thanks + +Mustache.js wouldn't kick ass if it weren't for these fine souls: + + * Chris Wanstrath / defunkt + * Alexander Lang / langalex + * Sebastian Cohnen / tisba + * J Chris Anderson / jchris + * Tom Robinson / tlrobinson + * Aaron Quint / quirkey + * Douglas Crockford + * Nikita Vasilyev / NV + * Elise Wood / glytch + * Damien Mathieu / dmathieu + * Jakub Kuźma / qoobaa + * Will Leinweber / will + * dpree + * Jason Smith / jhs + * Aaron Gibralter / agibralter + * Ross Boucher / boucher + * Matt Sanford / mzsanford + * Ben Cherry / bcherry + * Michael Jackson / mjijackson diff --git a/Rakefile b/Rakefile index a19c5ea..61700ee 100644 --- a/Rakefile +++ b/Rakefile @@ -1,4 +1,5 @@ require 'rake' +require 'rake/clean' task :default => :spec @@ -7,53 +8,52 @@ task :spec do require 'rspec/core/rake_task' RSpec::Core::RakeTask.new(:spec) do |t| #t.spec_opts = ['--options', "\"#{File.dirname(__FILE__)}/spec/spec.opts\""] - t.pattern = 'test/*_spec.rb' + t.pattern = 'spec/*_spec.rb' end end +# Creates a task that uses the various template wrappers to make a wrapped +# output file. There is some extra complexity because Dojo and YUI use +# different final locations. def templated_build(name, opts={}) - # Create a rule that uses the .tmpl.{pre,post} stuff to make a final, - # wrapped, output file. - # There is some extra complexity because Dojo and YUI3 use different - # template files and final locations. short = name.downcase - source = "mustache-#{short}" + source = File.join("wrappers", short) dependencies = ["mustache.js"] + Dir.glob("#{source}/*.tpl.*") + target_js = opts[:location] ? "mustache.js" : "#{short}.mustache.js" + + CLEAN.include(opts[:location] ? opts[:location] : target_js) desc "Package for #{name}" task short.to_sym => dependencies do - target_js = opts[:location] ? "mustache.js" : "#{short}.mustache.js" - puts "Packaging for #{name}" - sh "mkdir -p #{opts[:location]}" if opts[:location] - sh "cat #{source}/#{target_js}.tpl.pre mustache.js \ - #{source}/#{target_js}.tpl.post > #{opts[:location] || '.'}/#{target_js}" - # extra - if opts[:extra] - sh "sed -e 's/{{version}}/#{version}/' #{source}/#{opts[:extra]} \ - > #{opts[:location]}/#{opts[:extra]}" - end + mkdir_p opts[:location] if opts[:location] + + files = [ + "#{source}/mustache.js.pre", + 'mustache.js', + "#{source}/mustache.js.post" + ] - puts "Done, see #{opts[:location] || '.'}/#{target_js}" + open("#{opts[:location] || '.'}/#{target_js}", 'w') do |f| + files.each {|file| f << File.read(file) } + end + puts "Done, see #{opts[:location] || '.'}/#{target_js}" end end -templated_build "CommonJS", :location => "lib", :extra => "package.json" templated_build "jQuery" -templated_build "qooxdoo" +templated_build "MooTools" templated_build "Dojo", :location => "dojox/string" templated_build "YUI3", :location => "yui3/mustache" -templated_build "requirejs" - - -def version - File.read("mustache.js").match('version: "([^\"]+)",$')[1] -end - +templated_build "RequireJS" +templated_build "qooxdoo" -desc "Remove temporary files." -task :clean do - sh "git clean -fdx" +task :minify do + # npm install uglify-js + mmjs = "mustache.min.js" + `echo "/*! Version: 0.5.1-dev */" > #{mmjs}` + `uglifyjs mustache.js >> #{mmjs}` + puts "Created #{mmjs}" end diff --git a/TESTING.md b/TESTING.md new file mode 100644 index 0000000..47d15e4 --- /dev/null +++ b/TESTING.md @@ -0,0 +1,62 @@ +## Running the mustache.js test suite + +The mustache.js test suite uses the [RSpec](http://rspec.info/) testing +framework. In order to run the tests you'll need to install [Ruby](http://ruby-lang.org/) +as well as the `rake`, `rspec` (>=2), and `json` [RubyGems](http://rubygems.org/). + +### How to install Ruby and the required gems from source + +Make sure you have the required tools to compile it: + + $ apt-get install build-essential libssl-dev libreadline5-dev zlib1g-dev + +Download and extract the Ruby source, and install it: + + $ wget ftp://ftp.ruby-lang.org/pub/ruby/stable-snapshot.tar.gz + $ tar xvzf stable-snapshot.tar.gz + $ cd ruby + $ ./configure && make && make install + +Download and extract RubyGems, and install it: + + $ wget http://production.cf.rubygems.org/rubygems/rubygems-1.8.12.tgz + $ tar xzvf rubygems-1.8.12.tgz + $ cd rubygems-1.8.12 + $ ruby setup.rb + +If you want to update RubyGems: + + $ gem update --system + +Install the required gems: + + $ gem install rake rspec json + +That's it! + +### How to run the tests + +The mustache.js test suite currently uses 4 different JavaScript runtime engines +to maximize portability across platforms and browsers. They are: + + * node + * SpiderMonkey (Mozilla, Firefox) + * JavaScriptCore (WebKit, Safari) + * Rhino (Mozilla, Java) + +When the test suite runs it will automatically determine which platforms are +available on your machine and run on all of them. The suite must run on at least +one platform in order to succeed. + +Once you have at least one JavaScript platform installed, you can run the test +suite with the following command: + + $ rake + +### How to create a test + +All test files live in the spec/_files directory. To create a new test: + + * Create a template file called `somename.mustache` + * Create a JavaScript file containing the view called `somename.js` + * Create a text file with the expected result called `somename.txt` diff --git a/TESTS_HELP.md b/TESTS_HELP.md deleted file mode 100644 index 82a03e5..0000000 --- a/TESTS_HELP.md +++ /dev/null @@ -1,54 +0,0 @@ -## How to run the tests - -To run the test, you need ruby and the following gems installed : rake, rspec (>=2), json - -### How to install ruby and the required gems from source - -Make sure you have the required tools to compile it - - # apt-get install build-essential libssl-dev libreadline5-dev zlib1g-dev - -Download ruby source and extract the source - - $ cd ~/ - $ wget ftp://ftp.ruby-lang.org/pub/ruby/stable-snapshot.tar.gz - $ tar xvzf stable-snapshot.tar.gz - -Install it - - $ ./configure && make - # make install - -download the last version of RubyGems from here -http://rubyforge.org/frs/?group_id=126 - -Extract the source - - $ tar xzvf rubygems-1.8.4.tgz - -Install it - - $ cd rubygems-1.8.4 - # ruby setup.rb - -If you want to update RubyGems - - # gem update --system - -Install the required gems - - # gem install rake rspec json - -That's it! - -### How to run the tests - - $ rake - -### How to create a test - -- Create a template file `somename.html` -- Create a javascript file with data and functions `somename.js` -- Create a file the expected result `somename.txt` - -Done! diff --git a/THANKS.md b/THANKS.md deleted file mode 100644 index 22418c8..0000000 --- a/THANKS.md +++ /dev/null @@ -1,22 +0,0 @@ -# Thanks - -Mustache.js wouldn't kick ass if it weren't for these fine souls: - - * Chris Wanstrath / defunkt - * Alexander Lang / langalex - * Sebastian Cohnen / tisba - * J Chris Anderson / jchris - * Tom Robinson / tlrobinson - * Aaron Quint / quirkey - * Douglas Crockford - * Nikita Vasilyev / NV - * Elise Wood / glytch - * Damien Mathieu / dmathieu - * Jakub Kuźma / qoobaa - * Will Leinweber / will - * dpree - * Jason Smith / jhs - * Aaron Gibralter / agibralter - * Ross Boucher / boucher - * Matt Sanford / mzsanford - * Ben Cherry / bcherry diff --git a/examples/array_of_partials_implicit_partial.js b/examples/array_of_partials_implicit_partial.js deleted file mode 100644 index fcdf3b0..0000000 --- a/examples/array_of_partials_implicit_partial.js +++ /dev/null @@ -1,3 +0,0 @@ -var partial_context = { - numbers: ['1', '2', '3', '4'] -}; diff --git a/examples/array_of_strings.html b/examples/array_of_strings.html deleted file mode 100644 index 2898705..0000000 --- a/examples/array_of_strings.html +++ /dev/null @@ -1 +0,0 @@ -{{#array_of_strings}}{{.}} {{/array_of_strings}} \ No newline at end of file diff --git a/examples/array_of_strings_options.html b/examples/array_of_strings_options.html deleted file mode 100644 index 6b28278..0000000 --- a/examples/array_of_strings_options.html +++ /dev/null @@ -1,2 +0,0 @@ -{{%IMPLICIT-ITERATOR iterator=rob}} -{{#array_of_strings_options}}{{rob}} {{/array_of_strings_options}} \ No newline at end of file diff --git a/examples/array_of_strings_options.js b/examples/array_of_strings_options.js deleted file mode 100644 index 2e29adf..0000000 --- a/examples/array_of_strings_options.js +++ /dev/null @@ -1 +0,0 @@ -var array_of_strings_options = {array_of_strings_options: ['hello', 'world']}; diff --git a/examples/array_of_strings_options.txt b/examples/array_of_strings_options.txt deleted file mode 100644 index 4a1f475..0000000 --- a/examples/array_of_strings_options.txt +++ /dev/null @@ -1 +0,0 @@ -hello world diff --git a/examples/array_partial.js b/examples/array_partial.js deleted file mode 100644 index 88d7592..0000000 --- a/examples/array_partial.js +++ /dev/null @@ -1,5 +0,0 @@ -var partial_context = { - partial: { - array: ['1', '2', '3', '4'] - } -}; \ No newline at end of file diff --git a/examples/complex.txt b/examples/complex.txt deleted file mode 100644 index b51ca10..0000000 --- a/examples/complex.txt +++ /dev/null @@ -1,6 +0,0 @@ -

Colors

- diff --git a/examples/empty_partial.2.html b/examples/empty_partial.2.html deleted file mode 100644 index b920295..0000000 --- a/examples/empty_partial.2.html +++ /dev/null @@ -1 +0,0 @@ -yo \ No newline at end of file diff --git a/examples/empty_partial.js b/examples/empty_partial.js deleted file mode 100644 index 9cc53c6..0000000 --- a/examples/empty_partial.js +++ /dev/null @@ -1,3 +0,0 @@ -var partial_context = { - foo: 1 -}; diff --git a/examples/empty_template.txt b/examples/empty_template.txt deleted file mode 100644 index 60652b6..0000000 --- a/examples/empty_template.txt +++ /dev/null @@ -1 +0,0 @@ -

Test

diff --git a/examples/error_not_found.txt b/examples/error_not_found.txt deleted file mode 100644 index 8b13789..0000000 --- a/examples/error_not_found.txt +++ /dev/null @@ -1 +0,0 @@ - diff --git a/examples/inverted_section.html b/examples/inverted_section.html deleted file mode 100644 index beec558..0000000 --- a/examples/inverted_section.html +++ /dev/null @@ -1,2 +0,0 @@ -{{#repo}}{{name}}{{/repo}} -{{^repo}}No repos :({{/repo}} diff --git a/examples/nesting.txt b/examples/nesting.txt deleted file mode 100644 index caf5afd..0000000 --- a/examples/nesting.txt +++ /dev/null @@ -1,4 +0,0 @@ - 1 - 2 - 3 - diff --git a/examples/template_partial.2.html b/examples/template_partial.2.html deleted file mode 100644 index 54bdd7d..0000000 --- a/examples/template_partial.2.html +++ /dev/null @@ -1 +0,0 @@ -Again, {{again}}! \ No newline at end of file diff --git a/examples/template_partial.js b/examples/template_partial.js deleted file mode 100644 index 68a4317..0000000 --- a/examples/template_partial.js +++ /dev/null @@ -1,8 +0,0 @@ -var partial_context = { - title: function() { - return "Welcome"; - }, - partial: { - again: "Goodbye" - } -} diff --git a/examples/two_in_a_row.html b/examples/two_in_a_row.html deleted file mode 100644 index dc38602..0000000 --- a/examples/two_in_a_row.html +++ /dev/null @@ -1 +0,0 @@ -{{greeting}}, {{name}}! \ No newline at end of file diff --git a/examples/two_sections.txt b/examples/two_sections.txt deleted file mode 100644 index 8b13789..0000000 --- a/examples/two_sections.txt +++ /dev/null @@ -1 +0,0 @@ - diff --git a/examples/unescaped.html b/examples/unescaped.html deleted file mode 100644 index 9982708..0000000 --- a/examples/unescaped.html +++ /dev/null @@ -1 +0,0 @@ -

{{{title}}}

\ No newline at end of file diff --git a/examples/unknown_pragma.html b/examples/unknown_pragma.html deleted file mode 100644 index 113810d..0000000 --- a/examples/unknown_pragma.html +++ /dev/null @@ -1 +0,0 @@ -{{%I-HAVE-THE-GREATEST-MUSTACHE}} diff --git a/examples/unknown_pragma.js b/examples/unknown_pragma.js deleted file mode 100644 index 68344a5..0000000 --- a/examples/unknown_pragma.js +++ /dev/null @@ -1 +0,0 @@ -var unknown_pragma = {}; diff --git a/examples/unknown_pragma.txt b/examples/unknown_pragma.txt deleted file mode 100644 index a34840d..0000000 --- a/examples/unknown_pragma.txt +++ /dev/null @@ -1 +0,0 @@ -ERROR: This implementation of mustache doesn't understand the 'I-HAVE-THE-GREATEST-MUSTACHE' pragma diff --git a/examples/view_partial.js b/examples/view_partial.js deleted file mode 100644 index 30ade55..0000000 --- a/examples/view_partial.js +++ /dev/null @@ -1,19 +0,0 @@ -var partial_context = { - greeting: function() { - return "Welcome"; - }, - - farewell: function() { - return "Fair enough, right?"; - }, - - partial: { - name: "Chris", - value: 10000, - taxed_value: function() { - return this.value - (this.value * 0.4); - }, - in_ca: true - } -}; - diff --git a/examples/whitespace_partial.js b/examples/whitespace_partial.js deleted file mode 100644 index 30ade55..0000000 --- a/examples/whitespace_partial.js +++ /dev/null @@ -1,19 +0,0 @@ -var partial_context = { - greeting: function() { - return "Welcome"; - }, - - farewell: function() { - return "Fair enough, right?"; - }, - - partial: { - name: "Chris", - value: 10000, - taxed_value: function() { - return this.value - (this.value * 0.4); - }, - in_ca: true - } -}; - diff --git a/lib/package.json b/lib/package.json deleted file mode 100644 index 4a4cbb1..0000000 --- a/lib/package.json +++ /dev/null @@ -1,8 +0,0 @@ -{ - "name": "mustache", - "author": "http://mustache.github.com/", - "description": "{{ mustache }} in JavaScript — Logic-less templates.", - "keywords": ["template"], - "version": "0.3.1-dev-twitter", - "main": "./mustache" -} diff --git a/mustache-commonjs/mustache.js.tpl.post b/mustache-commonjs/mustache.js.tpl.post deleted file mode 100644 index 885803f..0000000 --- a/mustache-commonjs/mustache.js.tpl.post +++ /dev/null @@ -1,7 +0,0 @@ - -exports.name = Mustache.name; -exports.version = Mustache.version; - -exports.to_html = function() { - return Mustache.to_html.apply(this, arguments); -}; diff --git a/mustache-commonjs/mustache.js.tpl.pre b/mustache-commonjs/mustache.js.tpl.pre deleted file mode 100644 index 5e33b34..0000000 --- a/mustache-commonjs/mustache.js.tpl.pre +++ /dev/null @@ -1,6 +0,0 @@ -/* - * CommonJS-compatible mustache.js module - * - * See http://github.com/janl/mustache.js for more info. - */ - diff --git a/mustache-commonjs/package.json b/mustache-commonjs/package.json deleted file mode 100644 index 74d3aba..0000000 --- a/mustache-commonjs/package.json +++ /dev/null @@ -1,7 +0,0 @@ -{ - "name": "mustache", - "author": "http://mustache.github.com/", - "description": "{{ mustache }} in JavaScript — Logic-less templates.", - "keywords": ["template"], - "version": "{{version}}" -} diff --git a/mustache-dojo/mustache.js.tpl.post b/mustache-dojo/mustache.js.tpl.post deleted file mode 100644 index d64667f..0000000 --- a/mustache-dojo/mustache.js.tpl.post +++ /dev/null @@ -1,4 +0,0 @@ - - dojox.mustache = dojo.hitch(Mustache, "to_html"); - -})(); \ No newline at end of file diff --git a/mustache-jquery/jquery.mustache.js.tpl.post b/mustache-jquery/jquery.mustache.js.tpl.post deleted file mode 100644 index 1fe2887..0000000 --- a/mustache-jquery/jquery.mustache.js.tpl.post +++ /dev/null @@ -1,6 +0,0 @@ - - $.mustache = function(template, view, partials) { - return Mustache.to_html(template, view, partials); - }; - -})(jQuery); diff --git a/mustache-yui3/mustache.js.tpl.post b/mustache-yui3/mustache.js.tpl.post deleted file mode 100644 index 373d827..0000000 --- a/mustache-yui3/mustache.js.tpl.post +++ /dev/null @@ -1,4 +0,0 @@ - - Y.mustache = Mustache.to_html; - -}, "0"); diff --git a/mustache.js b/mustache.js index 5677871..641cebd 100644 --- a/mustache.js +++ b/mustache.js @@ -1,422 +1,536 @@ -/* - mustache.js — Logic-less templates in JavaScript - - See http://mustache.github.com/ for more info. -*/ - -var Mustache = function() { - var regexCache = {}; - var Renderer = function() {}; - - Renderer.prototype = { - otag: "{{", - ctag: "}}", - pragmas: {}, - buffer: [], - pragmas_implemented: { - "IMPLICIT-ITERATOR": true - }, - context: {}, - - render: function(template, context, partials, in_recursion) { - // reset buffer & set context - if(!in_recursion) { - this.context = context; - this.buffer = []; // TODO: make this non-lazy - } - - // fail fast - if(!this.includes("", template)) { - if(in_recursion) { - return template; - } else { - this.send(template); - return; - } - } - - // get the pragmas together - template = this.render_pragmas(template); - - // render the template - var html = this.render_section(template, context, partials); - - // render_section did not find any sections, we still need to render the tags - if (html === false) { - html = this.render_tags(template, context, partials, in_recursion); - } - - if (in_recursion) { - return html; - } else { - this.sendLines(html); - } - }, - - /* - Sends parsed lines - */ - send: function(line) { - if(line !== "") { - this.buffer.push(line); - } - }, +/*! + * mustache.js - Logic-less {{mustache}} templates with JavaScript + * http://github.com/janl/mustache.js + */ +var Mustache = (typeof module !== "undefined" && module.exports) || {}; + +(function (exports) { + + exports.name = "mustache.js"; + exports.version = "0.5.0-dev"; + exports.tags = ["{{", "}}"]; + exports.parse = parse; + exports.compile = compile; + exports.render = render; + exports.clearCache = clearCache; + + // This is here for backwards compatibility with 0.4.x. + exports.to_html = function (template, view, partials, send) { + var result = render(template, view, partials); + + if (typeof send === "function") { + send(result); + } else { + return result; + } + }; - sendLines: function(text) { - if (text) { - var lines = text.split("\n"); - for (var i = 0; i < lines.length; i++) { - this.send(lines[i]); - } - } - }, - - /* - Looks for %PRAGMAS - */ - render_pragmas: function(template) { - // no pragmas - if(!this.includes("%", template)) { - return template; + var _toString = Object.prototype.toString; + var _isArray = Array.isArray; + var _forEach = Array.prototype.forEach; + var _trim = String.prototype.trim; + + var isArray; + if (_isArray) { + isArray = _isArray; + } else { + isArray = function (obj) { + return _toString.call(obj) === "[object Array]"; + }; + } + + var forEach; + if (_forEach) { + forEach = function (obj, callback, scope) { + return _forEach.call(obj, callback, scope); + }; + } else { + forEach = function (obj, callback, scope) { + for (var i = 0, len = obj.length; i < len; ++i) { + callback.call(scope, obj[i], i, obj); } + }; + } + + var spaceRe = /^\s*$/; + + function isWhitespace(string) { + return spaceRe.test(string); + } + + var trim; + if (_trim) { + trim = function (string) { + return string == null ? "" : _trim.call(string); + }; + } else { + var trimLeft, trimRight; + + if (isWhitespace("\xA0")) { + trimLeft = /^\s+/; + trimRight = /\s+$/; + } else { + // IE doesn't match non-breaking spaces with \s, thanks jQuery. + trimLeft = /^[\s\xA0]+/; + trimRight = /[\s\xA0]+$/; + } - var that = this; - var regex = this.getCachedRegex("render_pragmas", function(otag, ctag) { - return new RegExp(otag + "%([\\w-]+) ?([\\w]+=[\\w]+)?" + ctag, "g"); - }); + trim = function (string) { + return string == null ? "" : + String(string).replace(trimLeft, "").replace(trimRight, ""); + }; + } + + var escapeMap = { + "&": "&", + "<": "<", + ">": ">", + '"': '"', + "'": ''' + }; - return template.replace(regex, function(match, pragma, options) { - if(!that.pragmas_implemented[pragma]) { - throw({message: - "This implementation of mustache doesn't understand the '" + - pragma + "' pragma"}); - } - that.pragmas[pragma] = {}; - if(options) { - var opts = options.split("="); - that.pragmas[pragma][opts[0]] = opts[1]; - } - return ""; - // ignore unknown pragmas silently - }); - }, - - /* - Tries to find a partial in the curent scope and render it - */ - render_partial: function(name, context, partials) { - name = this.trim(name); - if(!partials || partials[name] === undefined) { - throw({message: "unknown_partial '" + name + "'"}); - } - if(typeof(context[name]) != "object") { - return this.render(partials[name], context, partials, true); - } - return this.render(partials[name], context[name], partials, true); - }, - - /* - Renders inverted (^) and normal (#) sections - */ - render_section: function(template, context, partials) { - if(!this.includes("#", template) && !this.includes("^", template)) { - // did not render anything, there were no sections - return false; - } + function escapeHTML(string) { + return String(string).replace(/&(?!\w+;)|[<>"']/g, function (s) { + return escapeMap[s] || s; + }); + } + + /** + * Adds the `template`, `line`, and `file` properties to the given error + * object and alters the message to provide more useful debugging information. + */ + function debug(e, template, line, file) { + file = file || "