[Pkg-javascript-commits] [mustache.js] 07/31: Imported Upstream version 0.4.0

Martín Ferrari tincho at debian.org
Sun Nov 13 22:20:13 UTC 2016


This is an automated email from the git hooks/post-receive script.

tincho pushed a commit to branch master
in repository mustache.js.

commit 357153bb10a5a44972ad5e386fb681ca1d83ae6f
Author: David Paleino <dapal at debian.org>
Date:   Wed Feb 22 18:15:27 2012 +0100

    Imported Upstream version 0.4.0
---
 .gitignore                                         |   7 +-
 .travis.yml                                        |   5 +
 CHANGES.md                                         |  11 +
 LICENSE                                            |   2 +
 README.md                                          | 191 +++++++-----
 Rakefile                                           |  56 ++--
 TESTING.md                                         |  62 ++++
 THANKS.md                                          |  20 --
 mustache-commonjs/mustache.js.tpl.post             |   7 -
 mustache-commonjs/package.json                     |   7 -
 mustache-dojo/mustache.js.tpl.post                 |   5 -
 mustache.js                                        | 324 ++++++++++++++-------
 spec/_files/ampersand_escape.js                    |   3 +
 spec/_files/ampersand_escape.mustache              |   1 +
 spec/_files/ampersand_escape.txt                   |   1 +
 spec/_files/apostrophe.js                          |   1 +
 spec/_files/apostrophe.mustache                    |   1 +
 spec/_files/apostrophe.txt                         |   1 +
 .../array_of_partials_implicit_partial.2.mustache  |   0
 .../_files}/array_of_partials_implicit_partial.js  |   0
 .../array_of_partials_implicit_partial.mustache    |   0
 .../_files}/array_of_partials_implicit_partial.txt |   0
 .../_files/array_of_partials_partial.2.mustache    |   0
 .../_files}/array_of_partials_partial.js           |   0
 .../_files/array_of_partials_partial.mustache      |   0
 .../_files}/array_of_partials_partial.txt          |   0
 {examples => spec/_files}/array_of_strings.js      |   0
 .../_files/array_of_strings.mustache               |   0
 {examples => spec/_files}/array_of_strings.txt     |   0
 .../_files}/array_of_strings_options.js            |   0
 .../_files/array_of_strings_options.mustache       |   0
 .../_files}/array_of_strings_options.txt           |   0
 .../_files/array_partial.2.mustache                |   0
 {examples => spec/_files}/array_partial.js         |   0
 .../_files/array_partial.mustache                  |   0
 {examples => spec/_files}/array_partial.txt        |   0
 .../_files}/bug_11_eating_whitespace.js            |   0
 .../_files/bug_11_eating_whitespace.mustache       |   0
 .../_files}/bug_11_eating_whitespace.txt           |   0
 {examples => spec/_files}/comments.js              |   0
 .../comments.html => spec/_files/comments.mustache |   0
 {examples => spec/_files}/comments.txt             |   0
 {examples => spec/_files}/complex.js               |   0
 .../complex.html => spec/_files/complex.mustache   |   0
 {examples => spec/_files}/complex.txt              |   0
 {examples => spec/_files}/delimiters.js            |   0
 .../_files/delimiters.mustache                     |   0
 {examples => spec/_files}/delimiters.txt           |   0
 spec/_files/dot_notation.js                        |  23 ++
 spec/_files/dot_notation.mustache                  |   9 +
 spec/_files/dot_notation.txt                       |   9 +
 spec/_files/double_render.js                       |   5 +
 spec/_files/double_render.mustache                 |   1 +
 spec/_files/double_render.txt                      |   1 +
 .../_files/empty_partial.2.mustache                |   0
 {examples => spec/_files}/empty_partial.js         |   0
 .../_files/empty_partial.mustache                  |   0
 {examples => spec/_files}/empty_partial.txt        |   0
 spec/_files/empty_sections.js                      |   1 +
 spec/_files/empty_sections.mustache                |   1 +
 spec/_files/empty_sections.txt                     |   1 +
 {examples => spec/_files}/empty_template.js        |   0
 .../_files/empty_template.mustache                 |   0
 {examples => spec/_files}/empty_template.txt       |   0
 {examples => spec/_files}/error_not_found.js       |   0
 .../_files/error_not_found.mustache                |   0
 {examples => spec/_files}/error_not_found.txt      |   0
 {examples => spec/_files}/escaped.js               |   0
 .../escaped.html => spec/_files/escaped.mustache   |   0
 {examples => spec/_files}/escaped.txt              |   0
 {examples => spec/_files}/higher_order_sections.js |   0
 .../_files/higher_order_sections.mustache          |   0
 .../_files}/higher_order_sections.txt              |   0
 {examples => spec/_files}/inverted_section.js      |   0
 .../_files/inverted_section.mustache               |   0
 {examples => spec/_files}/inverted_section.txt     |   0
 spec/_files/keys_with_questionmarks.js             |   5 +
 spec/_files/keys_with_questionmarks.mustache       |   3 +
 spec/_files/keys_with_questionmarks.txt            |   1 +
 spec/_files/nesting.js                             |   7 +
 spec/_files/nesting.mustache                       |   5 +
 spec/_files/nesting.txt                            |   4 +
 {examples => spec/_files}/null_string.js           |   0
 .../_files/null_string.mustache                    |   0
 {examples => spec/_files}/null_string.txt          |   0
 .../_files/partial_recursion.2.mustache            |   0
 {examples => spec/_files}/partial_recursion.js     |   0
 .../_files/partial_recursion.mustache              |   0
 {examples => spec/_files}/partial_recursion.txt    |   0
 .../_files}/recursion_with_same_names.js           |   0
 .../_files/recursion_with_same_names.mustache      |   0
 .../_files}/recursion_with_same_names.txt          |   0
 {examples => spec/_files}/reuse_of_enumerables.js  |   0
 .../_files/reuse_of_enumerables.mustache           |   0
 {examples => spec/_files}/reuse_of_enumerables.txt |   0
 {examples => spec/_files}/section_as_context.js    |   0
 .../_files/section_as_context.mustache             |   0
 {examples => spec/_files}/section_as_context.txt   |   0
 {examples => spec/_files}/simple.js                |   0
 .../simple.html => spec/_files/simple.mustache     |   0
 {examples => spec/_files}/simple.txt               |   0
 .../_files/template_partial.2.mustache             |   0
 {examples => spec/_files}/template_partial.js      |   0
 .../_files/template_partial.mustache               |   0
 {examples => spec/_files}/template_partial.txt     |   0
 {examples => spec/_files}/two_in_a_row.js          |   0
 .../_files/two_in_a_row.mustache                   |   0
 {examples => spec/_files}/two_in_a_row.txt         |   0
 spec/_files/two_sections.js                        |   1 +
 spec/_files/two_sections.mustache                  |   4 +
 .../_files/two_sections.txt                        |   0
 {examples => spec/_files}/unescaped.js             |   0
 .../_files/unescaped.mustache                      |   0
 {examples => spec/_files}/unescaped.txt            |   0
 {examples => spec/_files}/unknown_pragma.js        |   0
 .../_files/unknown_pragma.mustache                 |   0
 {examples => spec/_files}/unknown_pragma.txt       |   0
 .../_files/view_partial.2.mustache                 |   0
 {examples => spec/_files}/view_partial.js          |   0
 .../_files/view_partial.mustache                   |   0
 {examples => spec/_files}/view_partial.txt         |   0
 .../_files/whitespace_partial.2.mustache           |   0
 {examples => spec/_files}/whitespace_partial.js    |   0
 .../_files/whitespace_partial.mustache             |   0
 {examples => spec/_files}/whitespace_partial.txt   |   0
 spec/mustache_spec.rb                              | 276 ++++++++++++++++++
 test/mustache_spec.rb                              | 156 ----------
 wrappers/commonjs/mustache.js.tpl.post             |   8 +
 .../commonjs}/mustache.js.tpl.pre                  |   0
 wrappers/commonjs/package.json                     |   8 +
 wrappers/dojo/mustache.js.tpl.post                 |   4 +
 .../dojo}/mustache.js.tpl.pre                      |   4 +-
 .../jquery}/jquery.mustache.js.tpl.post            |   0
 .../jquery}/jquery.mustache.js.tpl.pre             |   0
 wrappers/qooxdoo/qooxdoo.mustache.js.tpl.post      |   9 +
 wrappers/qooxdoo/qooxdoo.mustache.js.tpl.pre       | 127 ++++++++
 wrappers/requirejs/requirejs.mustache.js.tpl.post  |   3 +
 wrappers/requirejs/requirejs.mustache.js.tpl.pre   |   6 +
 .../yui3}/mustache.js.tpl.post                     |   0
 .../yui3}/mustache.js.tpl.pre                      |   0
 140 files changed, 975 insertions(+), 412 deletions(-)

diff --git a/.gitignore b/.gitignore
index a7ed15c..c44d96c 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,5 +1,10 @@
+.DS_Store
+.rvmrc
 runner.js
+lib
 jquery.mustache.js
+qooxdoo.mustache.js
 dojox
 yui3
-commonjs.mustache.js
+requirejs.mustache.js
+
diff --git a/.travis.yml b/.travis.yml
new file mode 100644
index 0000000..b52599e
--- /dev/null
+++ b/.travis.yml
@@ -0,0 +1,5 @@
+rvm:
+  - 1.9.2
+before_script:
+  - sudo apt-get -y install xulrunner-2.0
+  - gem install rspec
diff --git a/CHANGES.md b/CHANGES.md
index 60be170..40d50b8 100644
--- a/CHANGES.md
+++ b/CHANGES.md
@@ -1,5 +1,16 @@
 # 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.
diff --git a/LICENSE b/LICENSE
index e9f38ab..395e263 100644
--- a/LICENSE
+++ b/LICENSE
@@ -1,3 +1,5 @@
+The MIT License
+
 Copyright (c) 2009 Chris Wanstrath (Ruby)
 Copyright (c) 2010 Jan Lehnardt (JavaScript)
 
diff --git a/README.md b/README.md
index 7434356..578df7c 100644
--- a/README.md
+++ b/README.md
@@ -1,50 +1,55 @@
-# 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 <http://mustache.github.com/>.
+[mustache.js](http://github.com/janl/mustache.js) is an implementation of the
+[Mustache](http://mustache.github.com/) templating 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: <http://wiki.github.com/janl/mustache.js/beard-competition>
+## 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() {
         return 2 + 4;
       }
-    }
-
-    var template = "{{title}} spends {{calc}}";
+    };
 
-    var html = Mustache.to_html(template, view);
+    var html = Mustache.to_html("{{title}} spends {{calc}}", view);
 
-`template` is a simple string with mustache tags and `view` is a JavaScript
-object containing the data and any code to render the template.
+In this example, the `Mustache.to_html` 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 Tag Types
 
 There are several types of tags currently implemented in mustache.js.
 
-For a language-agnostic overview of Mustache’s template syntax, see the
-`mustache(5)` manpage or <http://mustache.github.com/mustache.5.html>.
-
 ### Simple Tags
 
 Tags are always surrounded by mustaches like this `{{foobar}}`.
@@ -53,12 +58,27 @@ Tags are always surrounded by mustaches like this `{{foobar}}`.
 
     template = "{{say_hello}}, {{name}}"
 
+#### Accessing values in nested objects (Dot Notation)
+
+To access data logically grouped into nested objects, specify a '.' delimited
+path to the value.
+
+    var contact = {
+      name: {first: "Bill", last: "Bobitybob" },
+      age: 37
+    }
+
+    template = "Hello, {{name.first}} {{name.last}}. You are {{age}} years old."
+
+*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.
 
 ### Conditional Sections
 
 Conditional sections begin with `{{#condition}}` and end with
 `{{/condition}}`. When `condition` evaluates to true, the section is rendered,
-otherwise the hole block will output nothing at all. `condition` may be a
+otherwise the whole block will output nothing at all. `condition` may be a
 function returning true/false or a simple boolean.
 
     var view = {condition: function() {
@@ -93,7 +113,7 @@ enumeration section.
 If a section key returns a function, it will be called and passed both the
 unrendered block of text and a renderer convenience function.
 
-Given this JS:
+Given this object:
 
     "name": "Tater",
     "bolder": function() {
@@ -115,39 +135,36 @@ to implement caching, filters (like syntax highlighting), etc.
 
 You can use `this.name` to access the attribute `name` from your view.
 
-### Dereferencing Section
+### Dereferencing Sections
+
+If your data has components that are logically grouped into nested objects,
+you may wish to dereference an object to access its values.
 
-If you have a nested object structure in your view, it can sometimes be easier
-to use sections like this:
+Given this object:
 
-    var objects = {
-      a_object: {
-        title: 'this is an object',
-        description: 'one of its attributes is a list',
-        a_list: [{label: 'listitem1'}, {label: 'listitem2'}]
+    {
+      "name": "Bill",
+      "address": {
+        "street": "801 Streetly street",
+        "city": "Boston",
+        "state": "MA",
+        "zip" "02101"
       }
-    };
+    }
 
-This is our template:
+And this template:
 
-    {{#a_object}}
-      <h1>{{title}}</h1>
-      <p>{{description}}</p>
-      <ul>
-        {{#a_list}}
-          <li>{{label}}</li>
-        {{/a_list}}
-      </ul>
-    {{/a_object}}
+    <h1>Contact: {{name}}</h1>
+    {{#address}}
+      <p>{{street}}</p>
+      <p>{{city}}, {{state}} {{zip}}</p>
+    {{/address}}
 
-Here is the result:
+We'll get this output:
 
-    <h1>this is an object</h1>
-      <p>one of its attributes is a list</p>
-      <ul>
-        <li>listitem1</li>
-        <li>listitem2</li>
-      </ul>
+    <h1>Contact: Bill</h1>
+      <p>801 Streetly street</p>
+      <p>Boston, MA 02101</p>
 
 ### Inverted Sections
 
@@ -199,12 +216,11 @@ 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 tripple mustaches like `{{{unescaped_variable}}}`.
+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`.
 
@@ -256,31 +272,48 @@ own iteration marker:
       {{bob}}
     {{/foo}}
 
-## FaQ
-
-### Why doesn’t Mustache allow dot notation like `{{variable.member}}`?
-
-The reason is given in the [mustache.rb
-bugtracker](http://github.com/defunkt/mustache/issues/issue/6).
-
-Mustache implementations strive to be template-compatible.
-
-
-## More Examples and Documentation
-
-See `examples/` for more goodies and read the [original mustache docs][m]
-
-## Command Line
-
-See `mustache(1)` man page or
-<http://defunkt.github.com/mustache/mustache.1.html>
-for command line docs.
-
-Or just install it as a RubyGem:
-
-    $ gem install mustache
-    $ mustache -h
-
-[m]: http://github.com/defunkt/mustache/#readme
-[node.js]: http://nodejs.org
-[couchdb]: http://couchdb.apache.org
+## Plugins for JavaScript Libraries
+
+mustache.js may be built specifically for several different client libraries
+and platforms, including the following:
+
+  - [node](http://nodejs.org/) (or other CommonJS platforms)
+  - [jQuery](http://jquery.com/)
+  - [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 commonjs
+    $ rake jquery
+    $ 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 4629c97..16bc724 100644
--- a/Rakefile
+++ b/Rakefile
@@ -1,42 +1,48 @@
 require 'rake'
-require 'spec/rake/spectask'
+require 'rake/clean'
 
 task :default => :spec
 
-Spec::Rake::SpecTask.new(:spec) do |t|
-  #t.spec_opts = ['--options', "\"#{File.dirname(__FILE__)}/spec/spec.opts\""]
-  t.spec_files = FileList['test/*_spec.rb']
+desc "Run all specs"
+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 = 'spec/*_spec.rb'
+  end
 end
 
-desc "Run all specs"
-task :spec
+def version
+  File.read("mustache.js").match('version: "([^\"]+)",$')[1]
+end
 
+# Creates 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.
 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]
+
+    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}"
+      #{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
-
-   puts "Done, see #{opts[:location] || '.'}/#{target_js}"
+        > #{opts[:location]}/#{opts[:extra]}"
+    end
 
+    puts "Done, see #{opts[:location] || '.'}/#{target_js}"
   end
 end
 
@@ -44,13 +50,5 @@ templated_build "CommonJS", :location => "lib", :extra => "package.json"
 templated_build "jQuery"
 templated_build "Dojo", :location => "dojox/string"
 templated_build "YUI3", :location => "yui3/mustache"
-
-def version
-  File.read("mustache.js").match('version: "([^\"]+)",$')[1]
-end
-
-
-desc "Remove temporary files."
-task :clean do
-  sh "git clean -fdx"
-end
+templated_build "RequireJS"
+templated_build "qooxdoo"
diff --git a/TESTING.md b/TESTING.md
new file mode 100644
index 0000000..6abec93
--- /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 `somename.mustache`
+  * Create a javascript file with data and functions `somename.js`
+  * Create a file the expected result `somename.txt`
diff --git a/THANKS.md b/THANKS.md
deleted file mode 100644
index 6dac939..0000000
--- a/THANKS.md
+++ /dev/null
@@ -1,20 +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
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/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 f659e2f..0000000
--- a/mustache-dojo/mustache.js.tpl.post
+++ /dev/null
@@ -1,5 +0,0 @@
-
-  d.mustache = function(template, view, partials) {
-    return Mustache.to_html(template, view, partials);
-  };
-})(dojo);
\ No newline at end of file
diff --git a/mustache.js b/mustache.js
index 0996227..91ec828 100644
--- a/mustache.js
+++ b/mustache.js
@@ -4,8 +4,53 @@
   See http://mustache.github.com/ for more info.
 */
 
-var Mustache = function() {
-  var Renderer = function() {};
+var Mustache = function () {
+  var _toString = Object.prototype.toString;
+
+  Array.isArray = Array.isArray || function (obj) {
+    return _toString.call(obj) == "[object Array]";
+  }
+
+  var _trim = String.prototype.trim, trim;
+
+  if (_trim) {
+    trim = function (text) {
+      return text == null ? "" : _trim.call(text);
+    }
+  } else {
+    var trimLeft, trimRight;
+
+    // IE doesn't match non-breaking spaces with \s.
+    if ((/\S/).test("\xA0")) {
+      trimLeft = /^[\s\xA0]+/;
+      trimRight = /[\s\xA0]+$/;
+    } else {
+      trimLeft = /^\s+/;
+      trimRight = /\s+$/;
+    }
+
+    trim = function (text) {
+      return text == null ? "" :
+        text.toString().replace(trimLeft, "").replace(trimRight, "");
+    }
+  }
+
+  var escapeMap = {
+    "&": "&",
+    "<": "<",
+    ">": ">",
+    '"': '"',
+    "'": '''
+  };
+
+  function escapeHTML(string) {
+    return String(string).replace(/&(?!\w+;)|[<>"']/g, function (s) {
+      return escapeMap[s] || s;
+    });
+  }
+
+  var regexCache = {};
+  var Renderer = function () {};
 
   Renderer.prototype = {
     otag: "{{",
@@ -17,16 +62,16 @@ var Mustache = function() {
     },
     context: {},
 
-    render: function(template, context, partials, in_recursion) {
+    render: function (template, context, partials, in_recursion) {
       // reset buffer & set context
-      if(!in_recursion) {
+      if (!in_recursion) {
         this.context = context;
         this.buffer = []; // TODO: make this non-lazy
       }
 
       // fail fast
-      if(!this.includes("", template)) {
-        if(in_recursion) {
+      if (!this.includes("", template)) {
+        if (in_recursion) {
           return template;
         } else {
           this.send(template);
@@ -34,44 +79,64 @@ var Mustache = function() {
         }
       }
 
+      // get the pragmas together
       template = this.render_pragmas(template);
+
+      // render the template
       var html = this.render_section(template, context, partials);
-      if(in_recursion) {
-        return this.render_tags(html, context, partials, in_recursion);
+
+      // 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);
       }
 
-      this.render_tags(html, context, partials, in_recursion);
+      if (in_recursion) {
+        return html;
+      } else {
+        this.sendLines(html);
+      }
     },
 
     /*
       Sends parsed lines
     */
-    send: function(line) {
-      if(line != "") {
+    send: function (line) {
+      if (line !== "") {
         this.buffer.push(line);
       }
     },
 
+    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) {
+    render_pragmas: function (template) {
       // no pragmas
-      if(!this.includes("%", template)) {
+      if (!this.includes("%", template)) {
         return template;
       }
 
       var that = this;
-      var regex = new RegExp(this.otag + "%([\\w-]+) ?([\\w]+=[\\w]+)?" +
-            this.ctag);
-      return template.replace(regex, function(match, pragma, options) {
-        if(!that.pragmas_implemented[pragma]) {
-          throw({message: 
+      var regex = this.getCachedRegex("render_pragmas", function (otag, ctag) {
+        return new RegExp(otag + "%([\\w-]+) ?([\\w]+=[\\w]+)?" + ctag, "g");
+      });
+
+      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) {
+        if (options) {
           var opts = options.split("=");
           that.pragmas[pragma][opts[0]] = opts[1];
         }
@@ -83,12 +148,12 @@ var Mustache = function() {
     /*
       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) {
+    render_partial: function (name, context, partials) {
+      name = trim(name);
+      if (!partials || partials[name] === undefined) {
         throw({message: "unknown_partial '" + name + "'"});
       }
-      if(typeof(context[name]) != "object") {
+      if (!context || typeof context[name] != "object") {
         return this.render(partials[name], context, partials, true);
       }
       return this.render(partials[name], context[name], partials, true);
@@ -97,64 +162,94 @@ var Mustache = function() {
     /*
       Renders inverted (^) and normal (#) sections
     */
-    render_section: function(template, context, partials) {
-      if(!this.includes("#", template) && !this.includes("^", template)) {
-        return template;
+    render_section: function (template, context, partials) {
+      if (!this.includes("#", template) && !this.includes("^", template)) {
+        // did not render anything, there were no sections
+        return false;
       }
 
       var that = this;
-      // CSW - Added "+?" so it finds the tighest bound, not the widest
-      var regex = new RegExp(this.otag + "(\\^|\\#)\\s*(.+)\\s*" + this.ctag +
-              "\n*([\\s\\S]+?)" + this.otag + "\\/\\s*\\2\\s*" + this.ctag +
-              "\\s*", "mg");
+
+      var regex = this.getCachedRegex("render_section", function (otag, ctag) {
+        // This regex matches _the first_ section ({{#foo}}{{/foo}}), and captures the remainder
+        return new RegExp(
+          "^([\\s\\S]*?)" +         // all the crap at the beginning that is not {{*}} ($1)
+
+          otag +                    // {{
+          "(\\^|\\#)\\s*(.+)\\s*" + //  #foo (# == $2, foo == $3)
+          ctag +                    // }}
+
+          "\n*([\\s\\S]*?)" +       // between the tag ($2). leading newlines are dropped
+
+          otag +                    // {{
+          "\\/\\s*\\3\\s*" +        //  /foo (backreference to the opening tag).
+          ctag +                    // }}
+
+          "\\s*([\\s\\S]*)$",       // everything else in the string ($4). leading whitespace is dropped.
+
+        "g");
+      });
+
 
       // for each {{#foo}}{{/foo}} section do...
-      return template.replace(regex, function(match, type, name, content) {
-        var value = that.find(name, context);
-        if(type == "^") { // inverted section
-          if(!value || that.is_array(value) && value.length === 0) {
+      return template.replace(regex, function (match, before, type, name, content, after) {
+        // before contains only tags, no sections
+        var renderedBefore = before ? that.render_tags(before, context, partials, true) : "",
+
+        // after may contain both sections and tags, so use full rendering function
+            renderedAfter = after ? that.render(after, context, partials, true) : "",
+
+        // will be computed below
+            renderedContent,
+
+            value = that.find(name, context);
+
+        if (type === "^") { // inverted section
+          if (!value || Array.isArray(value) && value.length === 0) {
             // false or empty list, render it
-            return that.render(content, context, partials, true);
+            renderedContent = that.render(content, context, partials, true);
           } else {
-            return "";
+            renderedContent = "";
           }
-        } else if(type == "#") { // normal section
-          if(that.is_array(value)) { // Enumerable, Let's loop!
-            return that.map(value, function(row) {
-              return that.render(content, that.create_context(row),
-                partials, true);
+        } else if (type === "#") { // normal section
+          if (Array.isArray(value)) { // Enumerable, Let's loop!
+            renderedContent = that.map(value, function (row) {
+              return that.render(content, that.create_context(row), partials, true);
             }).join("");
-          } else if(that.is_object(value)) { // Object, Use it as subcontext!
-            return that.render(content, that.create_context(value),
+          } else if (that.is_object(value)) { // Object, Use it as subcontext!
+            renderedContent = that.render(content, that.create_context(value),
               partials, true);
-          } else if(typeof value === "function") {
+          } else if (typeof value == "function") {
             // higher order section
-            return value.call(context, content, function(text) {
+            renderedContent = value.call(context, content, function (text) {
               return that.render(text, context, partials, true);
             });
-          } else if(value) { // boolean section
-            return that.render(content, context, partials, true);
+          } else if (value) { // boolean section
+            renderedContent = that.render(content, context, partials, true);
           } else {
-            return "";
+            renderedContent = "";
           }
         }
+
+        return renderedBefore + renderedContent + renderedAfter;
       });
     },
 
     /*
       Replace {{foo}} and friends with values from our view
     */
-    render_tags: function(template, context, partials, in_recursion) {
+    render_tags: function (template, context, partials, in_recursion) {
       // tit for tat
       var that = this;
 
-      var new_regex = function() {
-        return new RegExp(that.otag + "(=|!|>|\\{|%)?([^\\/#\\^]+?)\\1?" +
-          that.ctag + "+", "g");
+      var new_regex = function () {
+        return that.getCachedRegex("render_tags", function (otag, ctag) {
+          return new RegExp(otag + "(=|!|>|&|\\{|%)?([^#\\^]+?)\\1?" + ctag + "+", "g");
+        });
       };
 
       var regex = new_regex();
-      var tag_replace_callback = function(match, operator, name) {
+      var tag_replace_callback = function (match, operator, name) {
         switch(operator) {
         case "!": // ignore comments
           return "";
@@ -165,33 +260,34 @@ var Mustache = function() {
         case ">": // render partial
           return that.render_partial(name, context, partials);
         case "{": // the triple mustache is unescaped
+        case "&": // & operator is an alternative unescape method
           return that.find(name, context);
         default: // escape the value
-          return that.escape(that.find(name, context));
+          return escapeHTML(that.find(name, context));
         }
       };
       var lines = template.split("\n");
       for(var i = 0; i < lines.length; i++) {
         lines[i] = lines[i].replace(regex, tag_replace_callback, this);
-        if(!in_recursion) {
+        if (!in_recursion) {
           this.send(lines[i]);
         }
       }
 
-      if(in_recursion) {
+      if (in_recursion) {
         return lines.join("\n");
       }
     },
 
-    set_delimiters: function(delimiters) {
+    set_delimiters: function (delimiters) {
       var dels = delimiters.split(" ");
       this.otag = this.escape_regex(dels[0]);
       this.ctag = this.escape_regex(dels[1]);
     },
 
-    escape_regex: function(text) {
+    escape_regex: function (text) {
       // thank you Simon Willison
-      if(!arguments.callee.sRE) {
+      if (!arguments.callee.sRE) {
         var specials = [
           '/', '.', '*', '+', '?', '|',
           '(', ')', '[', ']', '{', '}', '\\'
@@ -207,8 +303,8 @@ var Mustache = function() {
       find `name` in current `context`. That is find me a value
       from the view object
     */
-    find: function(name, context) {
-      name = this.trim(name);
+    find: function (name, context) {
+      name = trim(name);
 
       // Checks whether a value is thruthy or false or 0
       function is_kinda_truthy(bool) {
@@ -216,53 +312,61 @@ var Mustache = function() {
       }
 
       var value;
-      if(is_kinda_truthy(context[name])) {
-        value = context[name];
-      } else if(is_kinda_truthy(this.context[name])) {
-        value = this.context[name];
+
+      // check for dot notation eg. foo.bar
+      if (name.match(/([a-z_]+)\./ig)) {
+        var childValue = this.walk_context(name, context);
+        if (is_kinda_truthy(childValue)) {
+          value = childValue;
+        }
+      } else {
+        if (is_kinda_truthy(context[name])) {
+          value = context[name];
+        } else if (is_kinda_truthy(this.context[name])) {
+          value = this.context[name];
+        }
       }
 
-      if(typeof value === "function") {
+      if (typeof value == "function") {
         return value.apply(context);
       }
-      if(value !== undefined) {
+      if (value !== undefined) {
         return value;
       }
       // silently ignore unkown variables
       return "";
     },
 
+    walk_context: function (name, context) {
+      var path = name.split('.');
+      // if the var doesn't exist in current context, check the top level context
+      var value_context = (context[path[0]] != undefined) ? context : this.context;
+      var value = value_context[path.shift()];
+      while (value != undefined && path.length > 0) {
+        value_context = value;
+        value = value[path.shift()];
+      }
+      // if the value is a function, call it, binding the correct context
+      if (typeof value == "function") {
+        return value.apply(value_context);
+      }
+      return value;
+    },
+
     // Utility methods
 
     /* includes tag */
-    includes: function(needle, haystack) {
+    includes: function (needle, haystack) {
       return haystack.indexOf(this.otag + needle) != -1;
     },
 
-    /*
-      Does away with nasty characters
-    */
-    escape: function(s) {
-      s = String(s === null ? "" : s);
-      return s.replace(/&(?!\w+;)|["<>\\]/g, function(s) {
-        switch(s) {
-        case "&": return "&";
-        case "\\": return "\\\\";
-        case '"': return '\"';
-        case "<": return "<";
-        case ">": return ">";
-        default: return s;
-        }
-      });
-    },
-
     // by @langalex, support for arrays of strings
-    create_context: function(_context) {
-      if(this.is_object(_context)) {
+    create_context: function (_context) {
+      if (this.is_object(_context)) {
         return _context;
       } else {
         var iterator = ".";
-        if(this.pragmas["IMPLICIT-ITERATOR"]) {
+        if (this.pragmas["IMPLICIT-ITERATOR"]) {
           iterator = this.pragmas["IMPLICIT-ITERATOR"].iterator;
         }
         var ctx = {};
@@ -271,25 +375,14 @@ var Mustache = function() {
       }
     },
 
-    is_object: function(a) {
+    is_object: function (a) {
       return a && typeof a == "object";
     },
 
-    is_array: function(a) {
-      return Object.prototype.toString.call(a) === '[object Array]';
-    },
-
-    /*
-      Gets rid of leading and trailing whitespace
-    */
-    trim: function(s) {
-      return s.replace(/^\s*|\s*$/g, "");
-    },
-
     /*
       Why, why, why? Because IE. Cry, cry cry.
     */
-    map: function(array, fn) {
+    map: function (array, fn) {
       if (typeof array.map == "function") {
         return array.map(fn);
       } else {
@@ -300,23 +393,42 @@ var Mustache = function() {
         }
         return r;
       }
+    },
+
+    getCachedRegex: function (name, generator) {
+      var byOtag = regexCache[this.otag];
+      if (!byOtag) {
+        byOtag = regexCache[this.otag] = {};
+      }
+
+      var byCtag = byOtag[this.ctag];
+      if (!byCtag) {
+        byCtag = byOtag[this.ctag] = {};
+      }
+
+      var regex = byCtag[name];
+      if (!regex) {
+        regex = byCtag[name] = generator(this.otag, this.ctag);
+      }
+
+      return regex;
     }
   };
 
   return({
     name: "mustache.js",
-    version: "0.3.0",
+    version: "0.4.0",
 
     /*
       Turns a template and view into HTML
     */
-    to_html: function(template, view, partials, send_fun) {
+    to_html: function (template, view, partials, send_fun) {
       var renderer = new Renderer();
-      if(send_fun) {
+      if (send_fun) {
         renderer.send = send_fun;
       }
-      renderer.render(template, view, partials);
-      if(!send_fun) {
+      renderer.render(template, view || {}, partials);
+      if (!send_fun) {
         return renderer.buffer.join("\n");
       }
     }
diff --git a/spec/_files/ampersand_escape.js b/spec/_files/ampersand_escape.js
new file mode 100644
index 0000000..645efe1
--- /dev/null
+++ b/spec/_files/ampersand_escape.js
@@ -0,0 +1,3 @@
+var ampersand_escape = {
+  message: "Some <code>"
+};
diff --git a/spec/_files/ampersand_escape.mustache b/spec/_files/ampersand_escape.mustache
new file mode 100644
index 0000000..6501a48
--- /dev/null
+++ b/spec/_files/ampersand_escape.mustache
@@ -0,0 +1 @@
+{{&message}}
diff --git a/spec/_files/ampersand_escape.txt b/spec/_files/ampersand_escape.txt
new file mode 100644
index 0000000..2ed3fd3
--- /dev/null
+++ b/spec/_files/ampersand_escape.txt
@@ -0,0 +1 @@
+Some <code>
diff --git a/spec/_files/apostrophe.js b/spec/_files/apostrophe.js
new file mode 100644
index 0000000..df69cd2
--- /dev/null
+++ b/spec/_files/apostrophe.js
@@ -0,0 +1 @@
+var apostrophe = {'apos': "'", 'control':'X'};
diff --git a/spec/_files/apostrophe.mustache b/spec/_files/apostrophe.mustache
new file mode 100644
index 0000000..e8687aa
--- /dev/null
+++ b/spec/_files/apostrophe.mustache
@@ -0,0 +1 @@
+{{apos}}{{control}}
diff --git a/spec/_files/apostrophe.txt b/spec/_files/apostrophe.txt
new file mode 100644
index 0000000..4427c30
--- /dev/null
+++ b/spec/_files/apostrophe.txt
@@ -0,0 +1 @@
+'X
diff --git a/examples/array_of_partials_implicit_partial.2.html b/spec/_files/array_of_partials_implicit_partial.2.mustache
similarity index 100%
rename from examples/array_of_partials_implicit_partial.2.html
rename to spec/_files/array_of_partials_implicit_partial.2.mustache
diff --git a/examples/array_of_partials_implicit_partial.js b/spec/_files/array_of_partials_implicit_partial.js
similarity index 100%
rename from examples/array_of_partials_implicit_partial.js
rename to spec/_files/array_of_partials_implicit_partial.js
diff --git a/examples/array_of_partials_implicit_partial.html b/spec/_files/array_of_partials_implicit_partial.mustache
similarity index 100%
rename from examples/array_of_partials_implicit_partial.html
rename to spec/_files/array_of_partials_implicit_partial.mustache
diff --git a/examples/array_of_partials_implicit_partial.txt b/spec/_files/array_of_partials_implicit_partial.txt
similarity index 100%
rename from examples/array_of_partials_implicit_partial.txt
rename to spec/_files/array_of_partials_implicit_partial.txt
diff --git a/examples/array_of_partials_partial.2.html b/spec/_files/array_of_partials_partial.2.mustache
similarity index 100%
rename from examples/array_of_partials_partial.2.html
rename to spec/_files/array_of_partials_partial.2.mustache
diff --git a/examples/array_of_partials_partial.js b/spec/_files/array_of_partials_partial.js
similarity index 100%
rename from examples/array_of_partials_partial.js
rename to spec/_files/array_of_partials_partial.js
diff --git a/examples/array_of_partials_partial.html b/spec/_files/array_of_partials_partial.mustache
similarity index 100%
rename from examples/array_of_partials_partial.html
rename to spec/_files/array_of_partials_partial.mustache
diff --git a/examples/array_of_partials_partial.txt b/spec/_files/array_of_partials_partial.txt
similarity index 100%
rename from examples/array_of_partials_partial.txt
rename to spec/_files/array_of_partials_partial.txt
diff --git a/examples/array_of_strings.js b/spec/_files/array_of_strings.js
similarity index 100%
rename from examples/array_of_strings.js
rename to spec/_files/array_of_strings.js
diff --git a/examples/array_of_strings.html b/spec/_files/array_of_strings.mustache
similarity index 100%
rename from examples/array_of_strings.html
rename to spec/_files/array_of_strings.mustache
diff --git a/examples/array_of_strings.txt b/spec/_files/array_of_strings.txt
similarity index 100%
rename from examples/array_of_strings.txt
rename to spec/_files/array_of_strings.txt
diff --git a/examples/array_of_strings_options.js b/spec/_files/array_of_strings_options.js
similarity index 100%
rename from examples/array_of_strings_options.js
rename to spec/_files/array_of_strings_options.js
diff --git a/examples/array_of_strings_options.html b/spec/_files/array_of_strings_options.mustache
similarity index 100%
rename from examples/array_of_strings_options.html
rename to spec/_files/array_of_strings_options.mustache
diff --git a/examples/array_of_strings_options.txt b/spec/_files/array_of_strings_options.txt
similarity index 100%
rename from examples/array_of_strings_options.txt
rename to spec/_files/array_of_strings_options.txt
diff --git a/examples/array_partial.2.html b/spec/_files/array_partial.2.mustache
similarity index 100%
rename from examples/array_partial.2.html
rename to spec/_files/array_partial.2.mustache
diff --git a/examples/array_partial.js b/spec/_files/array_partial.js
similarity index 100%
rename from examples/array_partial.js
rename to spec/_files/array_partial.js
diff --git a/examples/array_partial.html b/spec/_files/array_partial.mustache
similarity index 100%
rename from examples/array_partial.html
rename to spec/_files/array_partial.mustache
diff --git a/examples/array_partial.txt b/spec/_files/array_partial.txt
similarity index 100%
rename from examples/array_partial.txt
rename to spec/_files/array_partial.txt
diff --git a/examples/bug_11_eating_whitespace.js b/spec/_files/bug_11_eating_whitespace.js
similarity index 100%
rename from examples/bug_11_eating_whitespace.js
rename to spec/_files/bug_11_eating_whitespace.js
diff --git a/examples/bug_11_eating_whitespace.html b/spec/_files/bug_11_eating_whitespace.mustache
similarity index 100%
rename from examples/bug_11_eating_whitespace.html
rename to spec/_files/bug_11_eating_whitespace.mustache
diff --git a/examples/bug_11_eating_whitespace.txt b/spec/_files/bug_11_eating_whitespace.txt
similarity index 100%
rename from examples/bug_11_eating_whitespace.txt
rename to spec/_files/bug_11_eating_whitespace.txt
diff --git a/examples/comments.js b/spec/_files/comments.js
similarity index 100%
rename from examples/comments.js
rename to spec/_files/comments.js
diff --git a/examples/comments.html b/spec/_files/comments.mustache
similarity index 100%
rename from examples/comments.html
rename to spec/_files/comments.mustache
diff --git a/examples/comments.txt b/spec/_files/comments.txt
similarity index 100%
rename from examples/comments.txt
rename to spec/_files/comments.txt
diff --git a/examples/complex.js b/spec/_files/complex.js
similarity index 100%
rename from examples/complex.js
rename to spec/_files/complex.js
diff --git a/examples/complex.html b/spec/_files/complex.mustache
similarity index 100%
rename from examples/complex.html
rename to spec/_files/complex.mustache
diff --git a/examples/complex.txt b/spec/_files/complex.txt
similarity index 100%
rename from examples/complex.txt
rename to spec/_files/complex.txt
diff --git a/examples/delimiters.js b/spec/_files/delimiters.js
similarity index 100%
rename from examples/delimiters.js
rename to spec/_files/delimiters.js
diff --git a/examples/delimiters.html b/spec/_files/delimiters.mustache
similarity index 100%
rename from examples/delimiters.html
rename to spec/_files/delimiters.mustache
diff --git a/examples/delimiters.txt b/spec/_files/delimiters.txt
similarity index 100%
rename from examples/delimiters.txt
rename to spec/_files/delimiters.txt
diff --git a/spec/_files/dot_notation.js b/spec/_files/dot_notation.js
new file mode 100644
index 0000000..c1295f5
--- /dev/null
+++ b/spec/_files/dot_notation.js
@@ -0,0 +1,23 @@
+var dot_notation = {
+  name: "A Book",
+  authors: ["John Power", "Jamie Walsh"],
+  price:{
+    value: 200,
+    vat: function() {
+      return this.value * 0.2;
+    },
+    currency: {
+      symbol: '€',
+      name: 'Euro'
+    }
+  },
+  availability:{
+    status: true,
+    text: "In Stock"
+  },
+  // And now, some truthy false values
+  truthy: {
+    zero: 0,
+    notTrue: false
+  }
+};
diff --git a/spec/_files/dot_notation.mustache b/spec/_files/dot_notation.mustache
new file mode 100644
index 0000000..da1bad7
--- /dev/null
+++ b/spec/_files/dot_notation.mustache
@@ -0,0 +1,9 @@
+<!-- exciting part -->
+<h1>{{name}}</h1>
+<p>Authors: <ul>{{#authors}}<li>{{.}}</li>{{/authors}}</ul></p>
+<p>Price: {{price.currency.symbol}}{{price.value}} {{#price.currency}}{{name}} <b>{{availability.text}}</b>{{/price.currency}}</p>
+<p>VAT: {{price.currency.symbol}}{{price.vat}}</p>
+<!-- boring part -->
+<h2>Test truthy false values:</h2>
+<p>Zero: {{truthy.zero}}</p>
+<p>False: {{truthy.notTrue}}</p>
diff --git a/spec/_files/dot_notation.txt b/spec/_files/dot_notation.txt
new file mode 100644
index 0000000..d0e4707
--- /dev/null
+++ b/spec/_files/dot_notation.txt
@@ -0,0 +1,9 @@
+<!-- exciting part -->
+<h1>A Book</h1>
+<p>Authors: <ul><li>John Power</li><li>Jamie Walsh</li></ul></p>
+<p>Price: €200 Euro <b>In Stock</b></p>
+<p>VAT: €40</p>
+<!-- boring part -->
+<h2>Test truthy false values:</h2>
+<p>Zero: 0</p>
+<p>False: false</p>
diff --git a/spec/_files/double_render.js b/spec/_files/double_render.js
new file mode 100644
index 0000000..24125dc
--- /dev/null
+++ b/spec/_files/double_render.js
@@ -0,0 +1,5 @@
+var double_render = {
+  foo: true,
+  bar: "{{win}}",
+  win: "FAIL"
+};
\ No newline at end of file
diff --git a/spec/_files/double_render.mustache b/spec/_files/double_render.mustache
new file mode 100644
index 0000000..4500fd7
--- /dev/null
+++ b/spec/_files/double_render.mustache
@@ -0,0 +1 @@
+{{#foo}}{{bar}}{{/foo}}
diff --git a/spec/_files/double_render.txt b/spec/_files/double_render.txt
new file mode 100644
index 0000000..b6e652d
--- /dev/null
+++ b/spec/_files/double_render.txt
@@ -0,0 +1 @@
+{{win}}
diff --git a/examples/empty_partial.2.html b/spec/_files/empty_partial.2.mustache
similarity index 100%
rename from examples/empty_partial.2.html
rename to spec/_files/empty_partial.2.mustache
diff --git a/examples/empty_partial.js b/spec/_files/empty_partial.js
similarity index 100%
rename from examples/empty_partial.js
rename to spec/_files/empty_partial.js
diff --git a/examples/empty_partial.html b/spec/_files/empty_partial.mustache
similarity index 100%
rename from examples/empty_partial.html
rename to spec/_files/empty_partial.mustache
diff --git a/examples/empty_partial.txt b/spec/_files/empty_partial.txt
similarity index 100%
rename from examples/empty_partial.txt
rename to spec/_files/empty_partial.txt
diff --git a/spec/_files/empty_sections.js b/spec/_files/empty_sections.js
new file mode 100644
index 0000000..6e50514
--- /dev/null
+++ b/spec/_files/empty_sections.js
@@ -0,0 +1 @@
+var empty_sections = {};
diff --git a/spec/_files/empty_sections.mustache b/spec/_files/empty_sections.mustache
new file mode 100644
index 0000000..b6065db
--- /dev/null
+++ b/spec/_files/empty_sections.mustache
@@ -0,0 +1 @@
+{{#foo}}{{/foo}}foo{{#bar}}{{/bar}}
diff --git a/spec/_files/empty_sections.txt b/spec/_files/empty_sections.txt
new file mode 100644
index 0000000..257cc56
--- /dev/null
+++ b/spec/_files/empty_sections.txt
@@ -0,0 +1 @@
+foo
diff --git a/examples/empty_template.js b/spec/_files/empty_template.js
similarity index 100%
rename from examples/empty_template.js
rename to spec/_files/empty_template.js
diff --git a/examples/empty_template.html b/spec/_files/empty_template.mustache
similarity index 100%
rename from examples/empty_template.html
rename to spec/_files/empty_template.mustache
diff --git a/examples/empty_template.txt b/spec/_files/empty_template.txt
similarity index 100%
rename from examples/empty_template.txt
rename to spec/_files/empty_template.txt
diff --git a/examples/error_not_found.js b/spec/_files/error_not_found.js
similarity index 100%
rename from examples/error_not_found.js
rename to spec/_files/error_not_found.js
diff --git a/examples/error_not_found.html b/spec/_files/error_not_found.mustache
similarity index 100%
rename from examples/error_not_found.html
rename to spec/_files/error_not_found.mustache
diff --git a/examples/error_not_found.txt b/spec/_files/error_not_found.txt
similarity index 100%
copy from examples/error_not_found.txt
copy to spec/_files/error_not_found.txt
diff --git a/examples/escaped.js b/spec/_files/escaped.js
similarity index 100%
rename from examples/escaped.js
rename to spec/_files/escaped.js
diff --git a/examples/escaped.html b/spec/_files/escaped.mustache
similarity index 100%
rename from examples/escaped.html
rename to spec/_files/escaped.mustache
diff --git a/examples/escaped.txt b/spec/_files/escaped.txt
similarity index 100%
rename from examples/escaped.txt
rename to spec/_files/escaped.txt
diff --git a/examples/higher_order_sections.js b/spec/_files/higher_order_sections.js
similarity index 100%
rename from examples/higher_order_sections.js
rename to spec/_files/higher_order_sections.js
diff --git a/examples/higher_order_sections.html b/spec/_files/higher_order_sections.mustache
similarity index 100%
rename from examples/higher_order_sections.html
rename to spec/_files/higher_order_sections.mustache
diff --git a/examples/higher_order_sections.txt b/spec/_files/higher_order_sections.txt
similarity index 100%
rename from examples/higher_order_sections.txt
rename to spec/_files/higher_order_sections.txt
diff --git a/examples/inverted_section.js b/spec/_files/inverted_section.js
similarity index 100%
rename from examples/inverted_section.js
rename to spec/_files/inverted_section.js
diff --git a/examples/inverted_section.html b/spec/_files/inverted_section.mustache
similarity index 100%
rename from examples/inverted_section.html
rename to spec/_files/inverted_section.mustache
diff --git a/examples/inverted_section.txt b/spec/_files/inverted_section.txt
similarity index 100%
rename from examples/inverted_section.txt
rename to spec/_files/inverted_section.txt
diff --git a/spec/_files/keys_with_questionmarks.js b/spec/_files/keys_with_questionmarks.js
new file mode 100644
index 0000000..55a220d
--- /dev/null
+++ b/spec/_files/keys_with_questionmarks.js
@@ -0,0 +1,5 @@
+var keys_with_questionmarks = {
+  "person?": {
+    name: "Jon"
+  }
+}
diff --git a/spec/_files/keys_with_questionmarks.mustache b/spec/_files/keys_with_questionmarks.mustache
new file mode 100644
index 0000000..417f17f
--- /dev/null
+++ b/spec/_files/keys_with_questionmarks.mustache
@@ -0,0 +1,3 @@
+{{#person?}}
+  Hi {{name}}!
+{{/person?}}
diff --git a/spec/_files/keys_with_questionmarks.txt b/spec/_files/keys_with_questionmarks.txt
new file mode 100644
index 0000000..0f69b94
--- /dev/null
+++ b/spec/_files/keys_with_questionmarks.txt
@@ -0,0 +1 @@
+  Hi Jon!
diff --git a/spec/_files/nesting.js b/spec/_files/nesting.js
new file mode 100644
index 0000000..1f76cd0
--- /dev/null
+++ b/spec/_files/nesting.js
@@ -0,0 +1,7 @@
+var nesting = {
+  foo: [
+    {a: {b: 1}},
+    {a: {b: 2}},
+    {a: {b: 3}}
+  ]
+};
diff --git a/spec/_files/nesting.mustache b/spec/_files/nesting.mustache
new file mode 100644
index 0000000..551366d
--- /dev/null
+++ b/spec/_files/nesting.mustache
@@ -0,0 +1,5 @@
+{{#foo}}
+  {{#a}}
+    {{b}}
+  {{/a}}
+{{/foo}}
diff --git a/spec/_files/nesting.txt b/spec/_files/nesting.txt
new file mode 100644
index 0000000..caf5afd
--- /dev/null
+++ b/spec/_files/nesting.txt
@@ -0,0 +1,4 @@
+      1
+        2
+        3
+  
diff --git a/examples/null_string.js b/spec/_files/null_string.js
similarity index 100%
rename from examples/null_string.js
rename to spec/_files/null_string.js
diff --git a/examples/null_string.html b/spec/_files/null_string.mustache
similarity index 100%
rename from examples/null_string.html
rename to spec/_files/null_string.mustache
diff --git a/examples/null_string.txt b/spec/_files/null_string.txt
similarity index 100%
rename from examples/null_string.txt
rename to spec/_files/null_string.txt
diff --git a/examples/partial_recursion.2.html b/spec/_files/partial_recursion.2.mustache
similarity index 100%
rename from examples/partial_recursion.2.html
rename to spec/_files/partial_recursion.2.mustache
diff --git a/examples/partial_recursion.js b/spec/_files/partial_recursion.js
similarity index 100%
rename from examples/partial_recursion.js
rename to spec/_files/partial_recursion.js
diff --git a/examples/partial_recursion.html b/spec/_files/partial_recursion.mustache
similarity index 100%
rename from examples/partial_recursion.html
rename to spec/_files/partial_recursion.mustache
diff --git a/examples/partial_recursion.txt b/spec/_files/partial_recursion.txt
similarity index 100%
rename from examples/partial_recursion.txt
rename to spec/_files/partial_recursion.txt
diff --git a/examples/recursion_with_same_names.js b/spec/_files/recursion_with_same_names.js
similarity index 100%
rename from examples/recursion_with_same_names.js
rename to spec/_files/recursion_with_same_names.js
diff --git a/examples/recursion_with_same_names.html b/spec/_files/recursion_with_same_names.mustache
similarity index 100%
rename from examples/recursion_with_same_names.html
rename to spec/_files/recursion_with_same_names.mustache
diff --git a/examples/recursion_with_same_names.txt b/spec/_files/recursion_with_same_names.txt
similarity index 100%
rename from examples/recursion_with_same_names.txt
rename to spec/_files/recursion_with_same_names.txt
diff --git a/examples/reuse_of_enumerables.js b/spec/_files/reuse_of_enumerables.js
similarity index 100%
rename from examples/reuse_of_enumerables.js
rename to spec/_files/reuse_of_enumerables.js
diff --git a/examples/reuse_of_enumerables.html b/spec/_files/reuse_of_enumerables.mustache
similarity index 100%
rename from examples/reuse_of_enumerables.html
rename to spec/_files/reuse_of_enumerables.mustache
diff --git a/examples/reuse_of_enumerables.txt b/spec/_files/reuse_of_enumerables.txt
similarity index 100%
rename from examples/reuse_of_enumerables.txt
rename to spec/_files/reuse_of_enumerables.txt
diff --git a/examples/section_as_context.js b/spec/_files/section_as_context.js
similarity index 100%
rename from examples/section_as_context.js
rename to spec/_files/section_as_context.js
diff --git a/examples/section_as_context.html b/spec/_files/section_as_context.mustache
similarity index 100%
rename from examples/section_as_context.html
rename to spec/_files/section_as_context.mustache
diff --git a/examples/section_as_context.txt b/spec/_files/section_as_context.txt
similarity index 100%
rename from examples/section_as_context.txt
rename to spec/_files/section_as_context.txt
diff --git a/examples/simple.js b/spec/_files/simple.js
similarity index 100%
rename from examples/simple.js
rename to spec/_files/simple.js
diff --git a/examples/simple.html b/spec/_files/simple.mustache
similarity index 100%
rename from examples/simple.html
rename to spec/_files/simple.mustache
diff --git a/examples/simple.txt b/spec/_files/simple.txt
similarity index 100%
rename from examples/simple.txt
rename to spec/_files/simple.txt
diff --git a/examples/template_partial.2.html b/spec/_files/template_partial.2.mustache
similarity index 100%
rename from examples/template_partial.2.html
rename to spec/_files/template_partial.2.mustache
diff --git a/examples/template_partial.js b/spec/_files/template_partial.js
similarity index 100%
rename from examples/template_partial.js
rename to spec/_files/template_partial.js
diff --git a/examples/template_partial.html b/spec/_files/template_partial.mustache
similarity index 100%
rename from examples/template_partial.html
rename to spec/_files/template_partial.mustache
diff --git a/examples/template_partial.txt b/spec/_files/template_partial.txt
similarity index 100%
rename from examples/template_partial.txt
rename to spec/_files/template_partial.txt
diff --git a/examples/two_in_a_row.js b/spec/_files/two_in_a_row.js
similarity index 100%
rename from examples/two_in_a_row.js
rename to spec/_files/two_in_a_row.js
diff --git a/examples/two_in_a_row.html b/spec/_files/two_in_a_row.mustache
similarity index 100%
rename from examples/two_in_a_row.html
rename to spec/_files/two_in_a_row.mustache
diff --git a/examples/two_in_a_row.txt b/spec/_files/two_in_a_row.txt
similarity index 100%
rename from examples/two_in_a_row.txt
rename to spec/_files/two_in_a_row.txt
diff --git a/spec/_files/two_sections.js b/spec/_files/two_sections.js
new file mode 100644
index 0000000..8546f64
--- /dev/null
+++ b/spec/_files/two_sections.js
@@ -0,0 +1 @@
+var two_sections = {};
\ No newline at end of file
diff --git a/spec/_files/two_sections.mustache b/spec/_files/two_sections.mustache
new file mode 100644
index 0000000..a4b9f2a
--- /dev/null
+++ b/spec/_files/two_sections.mustache
@@ -0,0 +1,4 @@
+{{#foo}}
+{{/foo}}
+{{#bar}}
+{{/bar}}
diff --git a/examples/error_not_found.txt b/spec/_files/two_sections.txt
similarity index 100%
rename from examples/error_not_found.txt
rename to spec/_files/two_sections.txt
diff --git a/examples/unescaped.js b/spec/_files/unescaped.js
similarity index 100%
rename from examples/unescaped.js
rename to spec/_files/unescaped.js
diff --git a/examples/unescaped.html b/spec/_files/unescaped.mustache
similarity index 100%
rename from examples/unescaped.html
rename to spec/_files/unescaped.mustache
diff --git a/examples/unescaped.txt b/spec/_files/unescaped.txt
similarity index 100%
rename from examples/unescaped.txt
rename to spec/_files/unescaped.txt
diff --git a/examples/unknown_pragma.js b/spec/_files/unknown_pragma.js
similarity index 100%
rename from examples/unknown_pragma.js
rename to spec/_files/unknown_pragma.js
diff --git a/examples/unknown_pragma.html b/spec/_files/unknown_pragma.mustache
similarity index 100%
rename from examples/unknown_pragma.html
rename to spec/_files/unknown_pragma.mustache
diff --git a/examples/unknown_pragma.txt b/spec/_files/unknown_pragma.txt
similarity index 100%
rename from examples/unknown_pragma.txt
rename to spec/_files/unknown_pragma.txt
diff --git a/examples/view_partial.2.html b/spec/_files/view_partial.2.mustache
similarity index 100%
rename from examples/view_partial.2.html
rename to spec/_files/view_partial.2.mustache
diff --git a/examples/view_partial.js b/spec/_files/view_partial.js
similarity index 100%
rename from examples/view_partial.js
rename to spec/_files/view_partial.js
diff --git a/examples/view_partial.html b/spec/_files/view_partial.mustache
similarity index 100%
rename from examples/view_partial.html
rename to spec/_files/view_partial.mustache
diff --git a/examples/view_partial.txt b/spec/_files/view_partial.txt
similarity index 100%
rename from examples/view_partial.txt
rename to spec/_files/view_partial.txt
diff --git a/examples/whitespace_partial.2.html b/spec/_files/whitespace_partial.2.mustache
similarity index 100%
rename from examples/whitespace_partial.2.html
rename to spec/_files/whitespace_partial.2.mustache
diff --git a/examples/whitespace_partial.js b/spec/_files/whitespace_partial.js
similarity index 100%
rename from examples/whitespace_partial.js
rename to spec/_files/whitespace_partial.js
diff --git a/examples/whitespace_partial.html b/spec/_files/whitespace_partial.mustache
similarity index 100%
rename from examples/whitespace_partial.html
rename to spec/_files/whitespace_partial.mustache
diff --git a/examples/whitespace_partial.txt b/spec/_files/whitespace_partial.txt
similarity index 100%
rename from examples/whitespace_partial.txt
rename to spec/_files/whitespace_partial.txt
diff --git a/spec/mustache_spec.rb b/spec/mustache_spec.rb
new file mode 100644
index 0000000..e04bec1
--- /dev/null
+++ b/spec/mustache_spec.rb
@@ -0,0 +1,276 @@
+require 'rubygems'
+require 'json'
+
+ROOT = File.expand_path('../..', __FILE__)
+SPEC = File.join(ROOT, 'spec')
+FILES = File.join(SPEC, '_files')
+
+MUSTACHE = File.read(File.join(ROOT, "mustache.js"))
+
+TESTS = Dir.glob(File.join(FILES, '*.js')).map do |name|
+  File.basename name, '.js'
+end
+
+PARTIALS = TESTS.select {|t| t.include? "partial" }
+NON_PARTIALS = TESTS.select {|t| not t.include? "partial" }
+
+NODE_PATH = `which node`.strip
+JS_PATH = `which js`.strip
+JSC_PATH = "/System/Library/Frameworks/JavaScriptCore.framework/Versions/A/Resources/jsc"
+RHINO_JAR = "org.mozilla.javascript.tools.shell.Main"
+
+def load_test(name, is_partial=false)
+  view = File.read(File.join(FILES, "#{name}.js"))
+  template = File.read(File.join(FILES, "#{name}.mustache")).to_json
+  expect = File.read(File.join(FILES, "#{name}.txt"))
+
+  test = [view, template, expect]
+
+  if is_partial
+    test << File.read(File.join(FILES, "#{name}.2.mustache")).to_json
+  end
+
+  test
+end
+
+def run_js(runner, js)
+  cmd = case runner
+    when :spidermonkey
+      JS_PATH
+    when :jsc
+      JSC_PATH
+    when :rhino
+      "java #{RHINO_JAR}"
+    when :node
+      NODE_PATH
+    end
+
+  runner_file = "runner.js"
+  File.open(runner_file, 'w') {|file| file.write(js) }
+  `#{cmd} #{runner_file}`
+ensure
+  FileUtils.rm_r(runner_file)
+end
+
+$engines_run = 0
+
+describe "mustache" do
+  shared_examples_for "mustache rendering" do
+    before(:all) do
+      $engines_run += 1
+    end
+
+    it "should return the same when invoked multiple times" do
+      js = <<-JS
+        #{@boilerplate}
+        Mustache.to_html("x")
+        print(Mustache.to_html("x"));
+      JS
+
+      run_js(@runner, js).should == "x\n"
+    end
+
+    it "should clear the context after each run" do
+      js = <<-JS
+        #{@boilerplate}
+        Mustache.to_html("{{#list}}{{x}}{{/list}}", {list: [{x: 1}]})
+        try {
+          print(Mustache.to_html("{{#list}}{{x}}{{/list}}", {list: [{}]}));
+        } catch(e) {
+          print('ERROR: ' + e.message);
+        }
+      JS
+
+      run_js(@runner, js).should == "\n"
+    end
+
+    NON_PARTIALS.each do |test|
+      describe test do
+        it "should generate the correct html" do
+          view, template, expect = load_test(test)
+
+          js = <<-JS
+            try {
+              #{@boilerplate}
+              #{view}
+              var template = #{template};
+              var result = Mustache.to_html(template, #{test});
+              print(result);
+            } catch(e) {
+              print('ERROR: ' + e.message);
+            }
+          JS
+
+          run_js(@runner, js).should == expect
+        end
+
+        it "should sendFun the correct html" do
+          view, template, expect = load_test(test)
+
+          js = <<-JS
+            try {
+              #{@boilerplate}
+              #{view}
+              var chunks = [];
+              var sendFun = function(chunk) {
+                if (chunk != "") {
+                  chunks.push(chunk);
+                }
+              }
+              var template = #{template};
+              Mustache.to_html(template, #{test}, null, sendFun);
+              print(chunks.join("\\n"));
+            } catch(e) {
+              print('ERROR: ' + e.message);
+            }
+          JS
+
+          run_js(@runner, js).strip.should == expect.strip
+        end
+      end
+    end
+
+    PARTIALS.each do |test|
+      describe test do
+        it "should generate the correct html" do
+          view, template, expect, partial = load_test(test, true)
+
+          js = <<-JS
+            try {
+              #{@boilerplate}
+              #{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, js).should == expect
+        end
+
+        it "should sendFun the correct html" do
+          view, template, expect, partial = load_test(test, true)
+
+          js = <<-JS
+            try {
+              #{@boilerplate}
+              #{view};
+              var template = #{template};
+              var partials = {"partial": #{partial}};
+              var chunks = [];
+              var sendFun = function(chunk) {
+                if (chunk != "") {
+                  chunks.push(chunk);
+                }
+              }
+              Mustache.to_html(template, partial_context, partials, sendFun);
+              print(chunks.join("\\n"));
+            } catch(e) {
+              print('ERROR: ' + e.message);
+            }
+          JS
+
+          run_js(@runner, js).strip.should == expect.strip
+        end
+      end
+    end
+  end
+
+  context "running in node" do
+    if File.exist?(NODE_PATH)
+      before(:all) do
+        $stdout.write "Testing in node "
+        @runner = :node
+        @boilerplate = MUSTACHE.dup
+        @boilerplate << <<-JS
+        function print(message) {
+          console.log(message);
+        }
+        JS
+      end
+
+      after(:all) do
+        puts " Done!"
+      end
+
+      it_should_behave_like "mustache rendering"
+    else
+      puts "Skipping tests in node (node not found)"
+    end
+  end
+
+  context "running in SpiderMonkey (Mozilla, Firefox)" do
+    if File.exist?(JS_PATH)
+      before(:all) do
+        $stdout.write "Testing in SpiderMonkey "
+        @runner = :spidermonkey
+        @boilerplate = MUSTACHE.dup
+      end
+
+      after(:all) do
+        puts " Done!"
+      end
+
+      it_should_behave_like "mustache rendering"
+    else
+      puts "Skipping tests in SpiderMonkey (js not found)"
+    end
+  end
+
+  context "running in JavaScriptCore (WebKit, Safari)" do
+    if File.exist?(JSC_PATH)
+      before(:all) do
+        $stdout.write "Testing in JavaScriptCore "
+        @runner = :jsc
+        @boilerplate = MUSTACHE.dup
+      end
+
+      after(:all) do
+        puts " Done!"
+      end
+
+      it_should_behave_like "mustache rendering"
+    else
+      puts "Skipping tests in JavaScriptCore (jsc not found)"
+    end
+  end
+
+  context "running in Rhino (Mozilla, Java)" do
+    if `java #{RHINO_JAR} 'foo' 2>&1` !~ /ClassNotFoundException/
+      before(:all) do
+        $stdout.write "Testing in Rhino "
+        @runner = :rhino
+        @boilerplate = MUSTACHE.dup
+      end
+
+      after(:all) do
+        puts " Done!"
+      end
+
+      it_should_behave_like "mustache rendering"
+    else
+      puts "Skipping tests in Rhino (JAR #{RHINO_JAR} was not found)"
+    end
+  end
+
+  context "suite" do
+    before(:each) do
+      $stdout.write "Verifying that we ran at the tests in at least one engine ... "
+    end
+
+    after(:each) do
+      if @exception.nil?
+        puts "OK"
+      else
+        puts "ERROR!"
+      end
+    end
+
+    it "should have run at least one time" do
+      $engines_run.should > 0
+    end
+  end
+end
diff --git a/test/mustache_spec.rb b/test/mustache_spec.rb
deleted file mode 100644
index 9ede6c8..0000000
--- a/test/mustache_spec.rb
+++ /dev/null
@@ -1,156 +0,0 @@
-require 'rubygems'
-require 'json'
-
-__DIR__ = File.dirname(__FILE__)
-
-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")
-  end
-
-  it "should return the same when invoked multiple times" do
-    js = <<-JS
-      #{@mustache}
-      Mustache.to_html("x")
-      print(Mustache.to_html("x"));
-    JS
-    run_js(js).should == "x\n"
-
-  end
-
-  it "should clear the context after each run" do
-    js = <<-JS
-      #{@mustache}
-      Mustache.to_html("{{#list}}{{x}}{{/list}}", {list: [{x: 1}]})
-      try {
-        print(Mustache.to_html("{{#list}}{{x}}{{/list}}", {list: [{}]}));
-      } catch(e) {
-        print('ERROR: ' + e.message);
-      }
-    JS
-    run_js(js).should == "\n"
-  end
-  
-  non_partials.each do |testname|
-    describe testname do 
-      it "should generate the correct html" do
-
-        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);
-          }
-        JS
-
-        run_js(runner).should == expect
-      end
-      it "should sendFun the correct html" do
-
-        view, template, expect = load_test(__DIR__, testname)
-
-        runner = <<-JS
-          try {
-            #{@mustache}
-            #{view}
-            var chunks = [];
-            var sendFun = function(chunk) {
-              if (chunk != "") {
-                chunks.push(chunk);
-              }
-            }
-            var template = #{template};
-            Mustache.to_html(template, #{testname}, null, sendFun);
-            print(chunks.join("\\n"));
-          } catch(e) {
-            print('ERROR: ' + e.message);
-          }
-        JS
-
-        run_js(runner).strip.should == expect.strip
-      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
-      it "should sendFun 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 chunks = [];
-            var sendFun = function(chunk) {
-              if (chunk != "") {
-                chunks.push(chunk);
-              }
-            }
-            Mustache.to_html(template, partial_context, partials, sendFun);
-            print(chunks.join("\\n"));
-          } catch(e) {
-            print('ERROR: ' + e.message);
-          }
-        JS
-
-        run_js(runner).strip.should == expect.strip
-      end
-    end
-  end
-
-  def run_js(js)
-    File.open("runner.js", 'w') {|f| f << js}
-    `js runner.js`
-  end
-end
-
diff --git a/wrappers/commonjs/mustache.js.tpl.post b/wrappers/commonjs/mustache.js.tpl.post
new file mode 100644
index 0000000..f566443
--- /dev/null
+++ b/wrappers/commonjs/mustache.js.tpl.post
@@ -0,0 +1,8 @@
+if (typeof module !== 'undefined' && module.exports) {
+    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/wrappers/commonjs/mustache.js.tpl.pre
similarity index 100%
rename from mustache-commonjs/mustache.js.tpl.pre
rename to wrappers/commonjs/mustache.js.tpl.pre
diff --git a/wrappers/commonjs/package.json b/wrappers/commonjs/package.json
new file mode 100644
index 0000000..8e13993
--- /dev/null
+++ b/wrappers/commonjs/package.json
@@ -0,0 +1,8 @@
+{
+    "name": "mustache",
+    "author": "http://mustache.github.com/",
+    "description": "Logic-less {{mustache}} templates with JavaScript",
+    "keywords": ["template", "templates", "mustache"],
+    "version": "{{version}}",
+    "main": "./mustache"
+}
diff --git a/wrappers/dojo/mustache.js.tpl.post b/wrappers/dojo/mustache.js.tpl.post
new file mode 100644
index 0000000..d64667f
--- /dev/null
+++ b/wrappers/dojo/mustache.js.tpl.post
@@ -0,0 +1,4 @@
+
+  dojox.mustache = dojo.hitch(Mustache, "to_html");
+
+})();
\ No newline at end of file
diff --git a/mustache-dojo/mustache.js.tpl.pre b/wrappers/dojo/mustache.js.tpl.pre
similarity index 71%
rename from mustache-dojo/mustache.js.tpl.pre
rename to wrappers/dojo/mustache.js.tpl.pre
index ac9a308..f87f3cd 100644
--- a/mustache-dojo/mustache.js.tpl.pre
+++ b/wrappers/dojo/mustache.js.tpl.pre
@@ -5,5 +5,5 @@ Shameless port of a shameless port
 See http://github.com/defunkt/mustache for more info.
 */
 
-dojo.provide("dojox.string.mustache");
-;(function(d) {
+dojo.provide("dojox.mustache._base");
+(function(){
diff --git a/mustache-jquery/jquery.mustache.js.tpl.post b/wrappers/jquery/jquery.mustache.js.tpl.post
similarity index 100%
rename from mustache-jquery/jquery.mustache.js.tpl.post
rename to wrappers/jquery/jquery.mustache.js.tpl.post
diff --git a/mustache-jquery/jquery.mustache.js.tpl.pre b/wrappers/jquery/jquery.mustache.js.tpl.pre
similarity index 100%
rename from mustache-jquery/jquery.mustache.js.tpl.pre
rename to wrappers/jquery/jquery.mustache.js.tpl.pre
diff --git a/wrappers/qooxdoo/qooxdoo.mustache.js.tpl.post b/wrappers/qooxdoo/qooxdoo.mustache.js.tpl.post
new file mode 100644
index 0000000..e66f9e6
--- /dev/null
+++ b/wrappers/qooxdoo/qooxdoo.mustache.js.tpl.post
@@ -0,0 +1,9 @@
+/**
+ * Above is the original mustache code.
+ */
+
+// EXPOSE qooxdoo variant
+qx.bom.Template.version = Mustache.version;
+qx.bom.Template.toHtml = Mustache.to_html;
+
+})();
diff --git a/wrappers/qooxdoo/qooxdoo.mustache.js.tpl.pre b/wrappers/qooxdoo/qooxdoo.mustache.js.tpl.pre
new file mode 100644
index 0000000..a70d0a4
--- /dev/null
+++ b/wrappers/qooxdoo/qooxdoo.mustache.js.tpl.pre
@@ -0,0 +1,127 @@
+/* ************************************************************************
+
+   qooxdoo - the new era of web development
+
+   http://qooxdoo.org
+
+   Copyright:
+     2004-2011 1&1 Internet AG, Germany, http://www.1und1.de
+
+   License:
+     LGPL: http://www.gnu.org/licenses/lgpl.html
+     EPL: http://www.eclipse.org/org/documents/epl-v10.php
+     See the LICENSE file in the project's top-level directory for details.
+
+   Authors:
+     * Martin Wittemann (martinwittemann)
+
+   ======================================================================
+
+   This class contains code based on the following work:
+
+   * Mustache.js version 0.4.0-dev
+
+     Code:
+       https://github.com/janl/mustache.js
+
+     Copyright:
+       (c) 2009 Chris Wanstrath (Ruby)
+       (c) 2010 Jan Lehnardt (JavaScript)
+
+     License:
+       MIT: http://www.opensource.org/licenses/mit-license.php
+
+   ----------------------------------------------------------------------
+
+   Copyright (c) 2009 Chris Wanstrath (Ruby)
+   Copyright (c) 2010 Jan Lehnardt (JavaScript)
+
+   Permission is hereby granted, free of charge, to any person obtaining
+   a copy of this software and associated documentation files (the
+   "Software"), to deal in the Software without restriction, including
+   without limitation the rights to use, copy, modify, merge, publish,
+   distribute, sublicense, and/or sell copies of the Software, and to
+   permit persons to whom the Software is furnished to do so, subject to
+   the following conditions:
+
+   The above copyright notice and this permission notice shall be
+   included in all copies or substantial portions of the Software.
+
+   THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+   EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+   MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+   NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+   LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+   OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+   WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+************************************************************************ */
+
+/**
+ * The is a template class which can be used for HTML templating. In fact, 
+ * this is a wrapper for mustache.js which is a "framework-agnostic way to 
+ * render logic-free views".
+ * 
+ * Here is a basic example how to use it:
+ * Template:
+ * <pre>
+ * var template = "Hi, my name is {{name}}!";
+ * var view = {name: "qooxdoo"};
+ * qx.bom.Template.toHtml(template, view);
+ * // return "Hi, my name is qooxdoo!"
+ * </pre>
+ * 
+ * For further details, please visit the mustache.js documentation here:
+ *   https://github.com/janl/mustache.js/blob/master/README.md
+ */
+qx.Class.define("qx.bom.Template", {
+  statics : {
+    /** Contains the mustache.js version. */
+    version: null,
+
+    /**
+     * Original and only template method of mustache.js. For further
+     * documentation, please visit https://github.com/janl/mustache.js
+     *
+     * @signature function(template, view, partials, send_fun)
+     * @param template {String} The String containing the template.
+     * @param view {Object} The object holding the data to render.
+     * @param partials {Object} Object holding parts of a template.
+     * @param send_fun {Function?} Callback function for streaming.
+     * @return {String} The parsed template.
+     */
+    toHtml: null,
+
+
+    /**
+     * Helper method which provides you with a direct access to templates 
+     * stored as HTML in the DOM. The DOM node with the given ID will be reated 
+     * as a template, parsed and a new DOM node will be returned containing the 
+     * parsed data.
+     *
+     * @param id {String} The id of the HTML template in the DOM.
+     * @param view {Object} The object holding the data to render.
+     * @param partials {Object} Object holding parts of a template.
+     * @return {DomNode} A DOM element holding the parsed template data.
+     */
+    get : function(id, view, partials) {
+      var template = document.getElementById(id);
+      var inner = template.innerHTML;
+
+      inner = this.toHtml(inner, view, partials);
+
+      var helper = qx.bom.Element.create("div");
+      helper.innerHTML = inner;
+      
+      return helper.children[0];
+    }
+  }
+});
+
+(function() {
+
+/**
+ * Below is the original mustache.js code. Snapshot date is mentioned in 
+ * the head of this file.
+ */
+ 
\ No newline at end of file
diff --git a/wrappers/requirejs/requirejs.mustache.js.tpl.post b/wrappers/requirejs/requirejs.mustache.js.tpl.post
new file mode 100644
index 0000000..632fd2a
--- /dev/null
+++ b/wrappers/requirejs/requirejs.mustache.js.tpl.post
@@ -0,0 +1,3 @@
+
+return Mustache;
+});
\ No newline at end of file
diff --git a/wrappers/requirejs/requirejs.mustache.js.tpl.pre b/wrappers/requirejs/requirejs.mustache.js.tpl.pre
new file mode 100644
index 0000000..160286d
--- /dev/null
+++ b/wrappers/requirejs/requirejs.mustache.js.tpl.pre
@@ -0,0 +1,6 @@
+/*
+Shameless port of a shameless port ^ 2
+ at defunkt => @janl => @aq => @voodootikigod => @timruffles
+ 
+*/
+define(function(){
diff --git a/mustache-yui3/mustache.js.tpl.post b/wrappers/yui3/mustache.js.tpl.post
similarity index 100%
rename from mustache-yui3/mustache.js.tpl.post
rename to wrappers/yui3/mustache.js.tpl.post
diff --git a/mustache-yui3/mustache.js.tpl.pre b/wrappers/yui3/mustache.js.tpl.pre
similarity index 100%
rename from mustache-yui3/mustache.js.tpl.pre
rename to wrappers/yui3/mustache.js.tpl.pre

-- 
Alioth's /usr/local/bin/git-commit-notice on /srv/git.debian.org/git/pkg-javascript/mustache.js.git



More information about the Pkg-javascript-commits mailing list