| @@ -0,0 +1,23 @@ | |||||
| The MIT License | |||||
| 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. | |||||
| @@ -0,0 +1,418 @@ | |||||
| # mustache.js - Logic-less {{mustache}} templates with JavaScript | |||||
| > What could be more logical awesome than no logic at all? | |||||
| [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. | |||||
| 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. | |||||
| 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? | |||||
| 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. | |||||
| ## 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 | |||||
| Below is quick example how to use mustache.js: | |||||
| var view = { | |||||
| title: "Joe", | |||||
| calc: function () { | |||||
| return 2 + 4; | |||||
| } | |||||
| }; | |||||
| var output = Mustache.render("{{title}} spends {{calc}}", 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. | |||||
| ### 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: | |||||
| var Mustache = require("mustache"); | |||||
| ## Templates | |||||
| 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. | |||||
| There are several types of tags available in mustache.js. | |||||
| ### Variables | |||||
| 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. | |||||
| 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: | |||||
| * {{name}} | |||||
| * {{age}} | |||||
| * {{company}} | |||||
| * {{{company}}} | |||||
| * {{&company}} | |||||
| View: | |||||
| { | |||||
| "name": "Chris", | |||||
| "company": "<b>GitHub</b>" | |||||
| } | |||||
| Output: | |||||
| * Chris | |||||
| * | |||||
| * <b>GitHub</b> | |||||
| * <b>GitHub</b> | |||||
| * <b>GitHub</b> | |||||
| JavaScript's dot notation may be used to access keys that are properties of | |||||
| objects in a view. | |||||
| Template: | |||||
| * {{name.first}} {{name.last}} | |||||
| * {{age}} | |||||
| View: | |||||
| { | |||||
| "name": { | |||||
| "first": "Michael", | |||||
| "last": "Jackson" | |||||
| }, | |||||
| "age": "RIP" | |||||
| } | |||||
| Output: | |||||
| * Michael Jackson | |||||
| * RIP | |||||
| ### Sections | |||||
| Sections render blocks of text one or more times, depending on the value of the | |||||
| key in the current context. | |||||
| 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. | |||||
| #### False Values or Empty Lists | |||||
| 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. | |||||
| Template: | |||||
| Shown. | |||||
| {{#nothin}} | |||||
| Never shown! | |||||
| {{/nothin}} | |||||
| View: | |||||
| { | |||||
| "person": true | |||||
| } | |||||
| Output: | |||||
| Shown. | |||||
| #### Non-Empty Lists | |||||
| 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. | |||||
| 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. | |||||
| Template: | |||||
| {{#stooges}} | |||||
| <b>{{name}}</b> | |||||
| {{/stooges}} | |||||
| View: | |||||
| { | |||||
| "stooges": [ | |||||
| { "name": "Moe" }, | |||||
| { "name": "Larry" }, | |||||
| { "name": "Curly" } | |||||
| ] | |||||
| } | |||||
| Output: | |||||
| <b>Moe</b> | |||||
| <b>Larry</b> | |||||
| <b>Curly</b> | |||||
| When looping over an array of strings, a `.` can be used to refer to the current | |||||
| item in the list. | |||||
| Template: | |||||
| {{#musketeers}} | |||||
| * {{.}} | |||||
| {{/musketeers}} | |||||
| View: | |||||
| { | |||||
| "musketeers": ["Athos", "Aramis", "Porthos", "D'Artagnan"] | |||||
| } | |||||
| Output: | |||||
| * Athos | |||||
| * Aramis | |||||
| * Porthos | |||||
| * D'Artagnan | |||||
| 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: | |||||
| {{#beatles}} | |||||
| * {{name}} | |||||
| {{/beatles}} | |||||
| View: | |||||
| { | |||||
| "beatles": [ | |||||
| { "firstName": "John", "lastName": "Lennon" }, | |||||
| { "firstName": "Paul", "lastName": "McCartney" }, | |||||
| { "firstName": "George", "lastName": "Harrison" }, | |||||
| { "firstName": "Ringo", "lastName": "Starr" } | |||||
| ], | |||||
| "name": function () { | |||||
| return this.firstName + " " + this.lastName; | |||||
| } | |||||
| } | |||||
| Output: | |||||
| * John Lennon | |||||
| * Paul McCartney | |||||
| * George Harrison | |||||
| * Ringo Starr | |||||
| #### Functions | |||||
| 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: | |||||
| {{#bold}}Hi {{name}}.{{/bold}} | |||||
| View: | |||||
| { | |||||
| "name": "Tater", | |||||
| "bold": function () { | |||||
| return function (text, render) { | |||||
| return "<b>" + render(text) + "</b>"; | |||||
| } | |||||
| } | |||||
| } | |||||
| Output: | |||||
| <b>Hi Tater.</b> | |||||
| ### Inverted Sections | |||||
| 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. | |||||
| Template: | |||||
| {{#repos}}<b>{{name}}</b>{{/repos}} | |||||
| {{^repos}}No repos :({{/repos}} | |||||
| View: | |||||
| { | |||||
| "repos": [] | |||||
| } | |||||
| Output: | |||||
| No repos :( | |||||
| ### Comments | |||||
| Comments begin with a bang and are ignored. The following template: | |||||
| <h1>Today{{! ignore me }}.</h1> | |||||
| Will render as follows: | |||||
| <h1>Today.</h1> | |||||
| Comments may contain newlines. | |||||
| ### Partials | |||||
| Partials begin with a greater than sign, like {{> box}}. | |||||
| Partials are rendered at runtime (as opposed to compile time), so recursive | |||||
| partials are possible. Just avoid infinite loops. | |||||
| They also inherit the calling context. Whereas in ERB you may have this: | |||||
| <%= partial :next_more, :start => start, :size => size %> | |||||
| Mustache requires only this: | |||||
| {{> next_more}} | |||||
| 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. | |||||
| For example, this template and partial: | |||||
| base.mustache: | |||||
| <h2>Names</h2> | |||||
| {{#names}} | |||||
| {{> user}} | |||||
| {{/names}} | |||||
| user.mustache: | |||||
| <strong>{{name}}</strong> | |||||
| Can be thought of as a single, expanded template: | |||||
| <h2>Names</h2> | |||||
| {{#names}} | |||||
| <strong>{{name}}</strong> | |||||
| {{/names}} | |||||
| 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. | |||||
| ### Set Delimiter | |||||
| Set Delimiter tags start with an equals sign and change the tag delimiters from | |||||
| `{{` and `}}` to custom strings. | |||||
| Consider the following contrived example: | |||||
| * {{ default_tags }} | |||||
| {{=<% %>=}} | |||||
| * <% erb_style_tags %> | |||||
| <%={{ }}=%> | |||||
| * {{ default_tags_again }} | |||||
| 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. | |||||
| 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." | |||||
| Custom delimiters may not contain whitespace or the equals sign. | |||||
| ## Streaming | |||||
| To stream template results out of mustache.js, you can pass an optional callback | |||||
| to the call to `Mustache.render`: | |||||
| Mustache.render(template, view, partials, function (chunk) { | |||||
| print(chunk); | |||||
| }); | |||||
| 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 | |||||
| @@ -0,0 +1,57 @@ | |||||
| <!--I am generated, don't touch me--> | |||||
| <!DOCTYPE html> | |||||
| <meta charset="utf-8"> | |||||
| <link rel="stylesheet" href="../io.css"> | |||||
| <title>Mustache.js 0.5.48</title> | |||||
| <h1>Mustache.js 0.5.48 </h1> | |||||
| <p> Mustache.js 0.5.48 was released on Fri Feb 24 13:25:13 CET 2012. | |||||
| <ul class="index"> | |||||
| <li><a href="README.md">README.md</a></li> | |||||
| <li><a href="mustache.js">mustache.js</a></li> | |||||
| <li><a href="mustache.min.js">mustache.min.js</a></li> | |||||
| <li><a href="package.json">package.json</a></li> | |||||
| <li><a href="LICENSE">LICENSE</a></li> | |||||
| </ul> | |||||
| <h2> Distributions </h2> | |||||
| <ul class="index"> | |||||
| <li> | |||||
| <a href="mustache-dojo/dojo.mustache.js">dojo.mustache.js</a>, | |||||
| <a href="mustache-dojo/dojo.mustache.min.js">.min</a>, | |||||
| <a href="mustache-dojo/mustache-dojo.tar.gz">[.tar.gz]</a> | |||||
| </li> | |||||
| <li> | |||||
| <a href="mustache-jquery/jquery.mustache.js">jquery.mustache.js</a>, | |||||
| <a href="mustache-dojo/jquery.mustache.min.js">.min</a>, | |||||
| <a href="mustache-jquery/mustache-jquery.tar.gz"> [.tar.gz]</a> | |||||
| </li> | |||||
| <li> | |||||
| <a href="mustache-mootools/mootools.mustache.js">mootools.mustache.js</a>, | |||||
| <a href="mustache-dojo/mootools.mustache.min.js">.min</a>, | |||||
| <a href="mustache-mootools/mustache-mootools.tar.gz">[.tar.gz]</a> | |||||
| </li> | |||||
| <li> | |||||
| <a href="mustache-qooxdoo/qooxdoo.mustache.js">qooxdoo.mustache.js</a>, | |||||
| <a href="mustache-dojo/qooxdoo.mustache.min.js">.min</a>, | |||||
| <a href="mustache-qooxdoo/mustache-qooxdoo.tar.gz">[.tar.gz]</a> | |||||
| </li> | |||||
| <li> | |||||
| <a href="mustache-requirejs/requirejs.mustache.js">requirejs.mustache.js</a>, | |||||
| <a href="mustache-dojo/requirejs.mustache.min.js">.min</a>, | |||||
| <a href="mustache-requirejs/mustache-requirejs.tar.gz">[.tar.gz]</a> | |||||
| </li> | |||||
| <li> | |||||
| <a href="mustache-yui3/yui3.mustache.js">yui3.mustache.js</a>, | |||||
| <a href="mustache-dojo/yui3.mustache.min.js">.min</a>, | |||||
| <a href="mustache-yui3/mustache-yui3.tar.gz">[.tar.gz]</a> | |||||
| </li> | |||||
| </ul> | |||||
| <h2> Thanks! </h2> | |||||
| <p> Thanks to <a href="https://github.com/janl/mustache.js/contributors">all contributors</a>! — Love, Jan, Michael, Nathan. | |||||
| <p> <a href="../">}</a> | |||||
| @@ -0,0 +1,23 @@ | |||||
| The MIT License | |||||
| 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. | |||||
| @@ -0,0 +1,418 @@ | |||||
| # mustache.js - Logic-less {{mustache}} templates with JavaScript | |||||
| > What could be more logical awesome than no logic at all? | |||||
| [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. | |||||
| 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. | |||||
| 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? | |||||
| 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. | |||||
| ## 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 | |||||
| Below is quick example how to use mustache.js: | |||||
| var view = { | |||||
| title: "Joe", | |||||
| calc: function () { | |||||
| return 2 + 4; | |||||
| } | |||||
| }; | |||||
| var output = Mustache.render("{{title}} spends {{calc}}", 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. | |||||
| ### 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: | |||||
| var Mustache = require("mustache"); | |||||
| ## Templates | |||||
| 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. | |||||
| There are several types of tags available in mustache.js. | |||||
| ### Variables | |||||
| 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. | |||||
| 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: | |||||
| * {{name}} | |||||
| * {{age}} | |||||
| * {{company}} | |||||
| * {{{company}}} | |||||
| * {{&company}} | |||||
| View: | |||||
| { | |||||
| "name": "Chris", | |||||
| "company": "<b>GitHub</b>" | |||||
| } | |||||
| Output: | |||||
| * Chris | |||||
| * | |||||
| * <b>GitHub</b> | |||||
| * <b>GitHub</b> | |||||
| * <b>GitHub</b> | |||||
| JavaScript's dot notation may be used to access keys that are properties of | |||||
| objects in a view. | |||||
| Template: | |||||
| * {{name.first}} {{name.last}} | |||||
| * {{age}} | |||||
| View: | |||||
| { | |||||
| "name": { | |||||
| "first": "Michael", | |||||
| "last": "Jackson" | |||||
| }, | |||||
| "age": "RIP" | |||||
| } | |||||
| Output: | |||||
| * Michael Jackson | |||||
| * RIP | |||||
| ### Sections | |||||
| Sections render blocks of text one or more times, depending on the value of the | |||||
| key in the current context. | |||||
| 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. | |||||
| #### False Values or Empty Lists | |||||
| 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. | |||||
| Template: | |||||
| Shown. | |||||
| {{#nothin}} | |||||
| Never shown! | |||||
| {{/nothin}} | |||||
| View: | |||||
| { | |||||
| "person": true | |||||
| } | |||||
| Output: | |||||
| Shown. | |||||
| #### Non-Empty Lists | |||||
| 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. | |||||
| 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. | |||||
| Template: | |||||
| {{#stooges}} | |||||
| <b>{{name}}</b> | |||||
| {{/stooges}} | |||||
| View: | |||||
| { | |||||
| "stooges": [ | |||||
| { "name": "Moe" }, | |||||
| { "name": "Larry" }, | |||||
| { "name": "Curly" } | |||||
| ] | |||||
| } | |||||
| Output: | |||||
| <b>Moe</b> | |||||
| <b>Larry</b> | |||||
| <b>Curly</b> | |||||
| When looping over an array of strings, a `.` can be used to refer to the current | |||||
| item in the list. | |||||
| Template: | |||||
| {{#musketeers}} | |||||
| * {{.}} | |||||
| {{/musketeers}} | |||||
| View: | |||||
| { | |||||
| "musketeers": ["Athos", "Aramis", "Porthos", "D'Artagnan"] | |||||
| } | |||||
| Output: | |||||
| * Athos | |||||
| * Aramis | |||||
| * Porthos | |||||
| * D'Artagnan | |||||
| 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: | |||||
| {{#beatles}} | |||||
| * {{name}} | |||||
| {{/beatles}} | |||||
| View: | |||||
| { | |||||
| "beatles": [ | |||||
| { "firstName": "John", "lastName": "Lennon" }, | |||||
| { "firstName": "Paul", "lastName": "McCartney" }, | |||||
| { "firstName": "George", "lastName": "Harrison" }, | |||||
| { "firstName": "Ringo", "lastName": "Starr" } | |||||
| ], | |||||
| "name": function () { | |||||
| return this.firstName + " " + this.lastName; | |||||
| } | |||||
| } | |||||
| Output: | |||||
| * John Lennon | |||||
| * Paul McCartney | |||||
| * George Harrison | |||||
| * Ringo Starr | |||||
| #### Functions | |||||
| 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: | |||||
| {{#bold}}Hi {{name}}.{{/bold}} | |||||
| View: | |||||
| { | |||||
| "name": "Tater", | |||||
| "bold": function () { | |||||
| return function (text, render) { | |||||
| return "<b>" + render(text) + "</b>"; | |||||
| } | |||||
| } | |||||
| } | |||||
| Output: | |||||
| <b>Hi Tater.</b> | |||||
| ### Inverted Sections | |||||
| 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. | |||||
| Template: | |||||
| {{#repos}}<b>{{name}}</b>{{/repos}} | |||||
| {{^repos}}No repos :({{/repos}} | |||||
| View: | |||||
| { | |||||
| "repos": [] | |||||
| } | |||||
| Output: | |||||
| No repos :( | |||||
| ### Comments | |||||
| Comments begin with a bang and are ignored. The following template: | |||||
| <h1>Today{{! ignore me }}.</h1> | |||||
| Will render as follows: | |||||
| <h1>Today.</h1> | |||||
| Comments may contain newlines. | |||||
| ### Partials | |||||
| Partials begin with a greater than sign, like {{> box}}. | |||||
| Partials are rendered at runtime (as opposed to compile time), so recursive | |||||
| partials are possible. Just avoid infinite loops. | |||||
| They also inherit the calling context. Whereas in ERB you may have this: | |||||
| <%= partial :next_more, :start => start, :size => size %> | |||||
| Mustache requires only this: | |||||
| {{> next_more}} | |||||
| 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. | |||||
| For example, this template and partial: | |||||
| base.mustache: | |||||
| <h2>Names</h2> | |||||
| {{#names}} | |||||
| {{> user}} | |||||
| {{/names}} | |||||
| user.mustache: | |||||
| <strong>{{name}}</strong> | |||||
| Can be thought of as a single, expanded template: | |||||
| <h2>Names</h2> | |||||
| {{#names}} | |||||
| <strong>{{name}}</strong> | |||||
| {{/names}} | |||||
| 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. | |||||
| ### Set Delimiter | |||||
| Set Delimiter tags start with an equals sign and change the tag delimiters from | |||||
| `{{` and `}}` to custom strings. | |||||
| Consider the following contrived example: | |||||
| * {{ default_tags }} | |||||
| {{=<% %>=}} | |||||
| * <% erb_style_tags %> | |||||
| <%={{ }}=%> | |||||
| * {{ default_tags_again }} | |||||
| 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. | |||||
| 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." | |||||
| Custom delimiters may not contain whitespace or the equals sign. | |||||
| ## Streaming | |||||
| To stream template results out of mustache.js, you can pass an optional callback | |||||
| to the call to `Mustache.render`: | |||||
| Mustache.render(template, view, partials, function (chunk) { | |||||
| print(chunk); | |||||
| }); | |||||
| 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 | |||||
| @@ -0,0 +1,536 @@ | |||||
| /*! | |||||
| * 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.14"; | |||||
| 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; | |||||
| } | |||||
| }; | |||||
| 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]+$/; | |||||
| } | |||||
| trim = function (string) { | |||||
| return string == null ? "" : | |||||
| String(string).replace(trimLeft, "").replace(trimRight, ""); | |||||
| }; | |||||
| } | |||||
| var escapeMap = { | |||||
| "&": "&", | |||||
| "<": "<", | |||||
| ">": ">", | |||||
| '"': '"', | |||||
| "'": ''' | |||||
| }; | |||||
| 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 || "<template>"; | |||||
| var lines = template.split("\n"), | |||||
| start = Math.max(line - 3, 0), | |||||
| end = Math.min(lines.length, line + 3), | |||||
| context = lines.slice(start, end); | |||||
| var c; | |||||
| for (var i = 0, len = context.length; i < len; ++i) { | |||||
| c = i + start + 1; | |||||
| context[i] = (c === line ? " >> " : " ") + context[i]; | |||||
| } | |||||
| e.template = template; | |||||
| e.line = line; | |||||
| e.file = file; | |||||
| e.message = [file + ":" + line, context.join("\n"), "", e.message].join("\n"); | |||||
| return e; | |||||
| } | |||||
| /** | |||||
| * Looks up the value of the given `name` in the given context `stack`. | |||||
| */ | |||||
| function lookup(name, stack, defaultValue) { | |||||
| if (name === ".") { | |||||
| return stack[stack.length - 1]; | |||||
| } | |||||
| var names = name.split("."); | |||||
| var lastIndex = names.length - 1; | |||||
| var target = names[lastIndex]; | |||||
| var value, context, i = stack.length, j, localStack; | |||||
| while (i) { | |||||
| localStack = stack.slice(0); | |||||
| context = stack[--i]; | |||||
| j = 0; | |||||
| while (j < lastIndex) { | |||||
| context = context[names[j++]]; | |||||
| if (context == null) { | |||||
| break; | |||||
| } | |||||
| localStack.push(context); | |||||
| } | |||||
| if (context && typeof context === "object" && target in context) { | |||||
| value = context[target]; | |||||
| break; | |||||
| } | |||||
| } | |||||
| // If the value is a function, call it in the current context. | |||||
| if (typeof value === "function") { | |||||
| value = value.call(localStack[localStack.length - 1]); | |||||
| } | |||||
| if (value == null) { | |||||
| return defaultValue; | |||||
| } | |||||
| return value; | |||||
| } | |||||
| function renderSection(name, stack, callback, inverted) { | |||||
| var buffer = ""; | |||||
| var value = lookup(name, stack); | |||||
| if (inverted) { | |||||
| // From the spec: inverted sections may render text once based on the | |||||
| // inverse value of the key. That is, they will be rendered if the key | |||||
| // doesn't exist, is false, or is an empty list. | |||||
| if (value == null || value === false || (isArray(value) && value.length === 0)) { | |||||
| buffer += callback(); | |||||
| } | |||||
| } else if (isArray(value)) { | |||||
| forEach(value, function (value) { | |||||
| stack.push(value); | |||||
| buffer += callback(); | |||||
| stack.pop(); | |||||
| }); | |||||
| } else if (typeof value === "object") { | |||||
| stack.push(value); | |||||
| buffer += callback(); | |||||
| stack.pop(); | |||||
| } else if (typeof value === "function") { | |||||
| var scope = stack[stack.length - 1]; | |||||
| var scopedRender = function (template) { | |||||
| return render(template, scope); | |||||
| }; | |||||
| buffer += value.call(scope, callback(), scopedRender) || ""; | |||||
| } else if (value) { | |||||
| buffer += callback(); | |||||
| } | |||||
| return buffer; | |||||
| } | |||||
| /** | |||||
| * Parses the given `template` and returns the source of a function that, | |||||
| * with the proper arguments, will render the template. Recognized options | |||||
| * include the following: | |||||
| * | |||||
| * - file The name of the file the template comes from (displayed in | |||||
| * error messages) | |||||
| * - tags An array of open and close tags the `template` uses. Defaults | |||||
| * to the value of Mustache.tags | |||||
| * - debug Set `true` to log the body of the generated function to the | |||||
| * console | |||||
| * - space Set `true` to preserve whitespace from lines that otherwise | |||||
| * contain only a {{tag}}. Defaults to `false` | |||||
| */ | |||||
| function parse(template, options) { | |||||
| options = options || {}; | |||||
| var tags = options.tags || exports.tags, | |||||
| openTag = tags[0], | |||||
| closeTag = tags[tags.length - 1]; | |||||
| var code = [ | |||||
| 'var buffer = "";', // output buffer | |||||
| "\nvar line = 1;", // keep track of source line number | |||||
| "\ntry {", | |||||
| '\nbuffer += "' | |||||
| ]; | |||||
| var spaces = [], // indices of whitespace in code on the current line | |||||
| hasTag = false, // is there a {{tag}} on the current line? | |||||
| nonSpace = false; // is there a non-space char on the current line? | |||||
| // Strips all space characters from the code array for the current line | |||||
| // if there was a {{tag}} on it and otherwise only spaces. | |||||
| var stripSpace = function () { | |||||
| if (hasTag && !nonSpace && !options.space) { | |||||
| while (spaces.length) { | |||||
| code.splice(spaces.pop(), 1); | |||||
| } | |||||
| } else { | |||||
| spaces = []; | |||||
| } | |||||
| hasTag = false; | |||||
| nonSpace = false; | |||||
| }; | |||||
| var sectionStack = [], updateLine, nextOpenTag, nextCloseTag; | |||||
| var setTags = function (source) { | |||||
| tags = trim(source).split(/\s+/); | |||||
| nextOpenTag = tags[0]; | |||||
| nextCloseTag = tags[tags.length - 1]; | |||||
| }; | |||||
| var includePartial = function (source) { | |||||
| code.push( | |||||
| '";', | |||||
| updateLine, | |||||
| '\nvar partial = partials["' + trim(source) + '"];', | |||||
| '\nif (partial) {', | |||||
| '\n buffer += render(partial,stack[stack.length - 1],partials);', | |||||
| '\n}', | |||||
| '\nbuffer += "' | |||||
| ); | |||||
| }; | |||||
| var openSection = function (source, inverted) { | |||||
| var name = trim(source); | |||||
| if (name === "") { | |||||
| throw debug(new Error("Section name may not be empty"), template, line, options.file); | |||||
| } | |||||
| sectionStack.push({name: name, inverted: inverted}); | |||||
| code.push( | |||||
| '";', | |||||
| updateLine, | |||||
| '\nvar name = "' + name + '";', | |||||
| '\nvar callback = (function () {', | |||||
| '\n return function () {', | |||||
| '\n var buffer = "";', | |||||
| '\nbuffer += "' | |||||
| ); | |||||
| }; | |||||
| var openInvertedSection = function (source) { | |||||
| openSection(source, true); | |||||
| }; | |||||
| var closeSection = function (source) { | |||||
| var name = trim(source); | |||||
| var openName = sectionStack.length != 0 && sectionStack[sectionStack.length - 1].name; | |||||
| if (!openName || name != openName) { | |||||
| throw debug(new Error('Section named "' + name + '" was never opened'), template, line, options.file); | |||||
| } | |||||
| var section = sectionStack.pop(); | |||||
| code.push( | |||||
| '";', | |||||
| '\n return buffer;', | |||||
| '\n };', | |||||
| '\n})();' | |||||
| ); | |||||
| if (section.inverted) { | |||||
| code.push("\nbuffer += renderSection(name,stack,callback,true);"); | |||||
| } else { | |||||
| code.push("\nbuffer += renderSection(name,stack,callback);"); | |||||
| } | |||||
| code.push('\nbuffer += "'); | |||||
| }; | |||||
| var sendPlain = function (source) { | |||||
| code.push( | |||||
| '";', | |||||
| updateLine, | |||||
| '\nbuffer += lookup("' + trim(source) + '",stack,"");', | |||||
| '\nbuffer += "' | |||||
| ); | |||||
| }; | |||||
| var sendEscaped = function (source) { | |||||
| code.push( | |||||
| '";', | |||||
| updateLine, | |||||
| '\nbuffer += escapeHTML(lookup("' + trim(source) + '",stack,""));', | |||||
| '\nbuffer += "' | |||||
| ); | |||||
| }; | |||||
| var line = 1, c, callback; | |||||
| for (var i = 0, len = template.length; i < len; ++i) { | |||||
| if (template.slice(i, i + openTag.length) === openTag) { | |||||
| i += openTag.length; | |||||
| c = template.substr(i, 1); | |||||
| updateLine = '\nline = ' + line + ';'; | |||||
| nextOpenTag = openTag; | |||||
| nextCloseTag = closeTag; | |||||
| hasTag = true; | |||||
| switch (c) { | |||||
| case "!": // comment | |||||
| i++; | |||||
| callback = null; | |||||
| break; | |||||
| case "=": // change open/close tags, e.g. {{=<% %>=}} | |||||
| i++; | |||||
| closeTag = "=" + closeTag; | |||||
| callback = setTags; | |||||
| break; | |||||
| case ">": // include partial | |||||
| i++; | |||||
| callback = includePartial; | |||||
| break; | |||||
| case "#": // start section | |||||
| i++; | |||||
| callback = openSection; | |||||
| break; | |||||
| case "^": // start inverted section | |||||
| i++; | |||||
| callback = openInvertedSection; | |||||
| break; | |||||
| case "/": // end section | |||||
| i++; | |||||
| callback = closeSection; | |||||
| break; | |||||
| case "{": // plain variable | |||||
| closeTag = "}" + closeTag; | |||||
| // fall through | |||||
| case "&": // plain variable | |||||
| i++; | |||||
| nonSpace = true; | |||||
| callback = sendPlain; | |||||
| break; | |||||
| default: // escaped variable | |||||
| nonSpace = true; | |||||
| callback = sendEscaped; | |||||
| } | |||||
| var end = template.indexOf(closeTag, i); | |||||
| if (end === -1) { | |||||
| throw debug(new Error('Tag "' + openTag + '" was not closed properly'), template, line, options.file); | |||||
| } | |||||
| var source = template.substring(i, end); | |||||
| if (callback) { | |||||
| callback(source); | |||||
| } | |||||
| // Maintain line count for \n in source. | |||||
| var n = 0; | |||||
| while (~(n = source.indexOf("\n", n))) { | |||||
| line++; | |||||
| n++; | |||||
| } | |||||
| i = end + closeTag.length - 1; | |||||
| openTag = nextOpenTag; | |||||
| closeTag = nextCloseTag; | |||||
| } else { | |||||
| c = template.substr(i, 1); | |||||
| switch (c) { | |||||
| case '"': | |||||
| case "\\": | |||||
| nonSpace = true; | |||||
| code.push("\\" + c); | |||||
| break; | |||||
| case "\r": | |||||
| // Ignore carriage returns. | |||||
| break; | |||||
| case "\n": | |||||
| spaces.push(code.length); | |||||
| code.push("\\n"); | |||||
| stripSpace(); // Check for whitespace on the current line. | |||||
| line++; | |||||
| break; | |||||
| default: | |||||
| if (isWhitespace(c)) { | |||||
| spaces.push(code.length); | |||||
| } else { | |||||
| nonSpace = true; | |||||
| } | |||||
| code.push(c); | |||||
| } | |||||
| } | |||||
| } | |||||
| if (sectionStack.length != 0) { | |||||
| throw debug(new Error('Section "' + sectionStack[sectionStack.length - 1].name + '" was not closed properly'), template, line, options.file); | |||||
| } | |||||
| // Clean up any whitespace from a closing {{tag}} that was at the end | |||||
| // of the template without a trailing \n. | |||||
| stripSpace(); | |||||
| code.push( | |||||
| '";', | |||||
| "\nreturn buffer;", | |||||
| "\n} catch (e) { throw {error: e, line: line}; }" | |||||
| ); | |||||
| // Ignore `buffer += "";` statements. | |||||
| var body = code.join("").replace(/buffer \+= "";\n/g, ""); | |||||
| if (options.debug) { | |||||
| if (typeof console != "undefined" && console.log) { | |||||
| console.log(body); | |||||
| } else if (typeof print === "function") { | |||||
| print(body); | |||||
| } | |||||
| } | |||||
| return body; | |||||
| } | |||||
| /** | |||||
| * Used by `compile` to generate a reusable function for the given `template`. | |||||
| */ | |||||
| function _compile(template, options) { | |||||
| var args = "view,partials,stack,lookup,escapeHTML,renderSection,render"; | |||||
| var body = parse(template, options); | |||||
| var fn = new Function(args, body); | |||||
| // This anonymous function wraps the generated function so we can do | |||||
| // argument coercion, setup some variables, and handle any errors | |||||
| // encountered while executing it. | |||||
| return function (view, partials) { | |||||
| partials = partials || {}; | |||||
| var stack = [view]; // context stack | |||||
| try { | |||||
| return fn(view, partials, stack, lookup, escapeHTML, renderSection, render); | |||||
| } catch (e) { | |||||
| throw debug(e.error, template, e.line, options.file); | |||||
| } | |||||
| }; | |||||
| } | |||||
| // Cache of pre-compiled templates. | |||||
| var _cache = {}; | |||||
| /** | |||||
| * Clear the cache of compiled templates. | |||||
| */ | |||||
| function clearCache() { | |||||
| _cache = {}; | |||||
| } | |||||
| /** | |||||
| * Compiles the given `template` into a reusable function using the given | |||||
| * `options`. In addition to the options accepted by Mustache.parse, | |||||
| * recognized options include the following: | |||||
| * | |||||
| * - cache Set `false` to bypass any pre-compiled version of the given | |||||
| * template. Otherwise, a given `template` string will be cached | |||||
| * the first time it is parsed | |||||
| */ | |||||
| function compile(template, options) { | |||||
| options = options || {}; | |||||
| // Use a pre-compiled version from the cache if we have one. | |||||
| if (options.cache !== false) { | |||||
| if (!_cache[template]) { | |||||
| _cache[template] = _compile(template, options); | |||||
| } | |||||
| return _cache[template]; | |||||
| } | |||||
| return _compile(template, options); | |||||
| } | |||||
| /** | |||||
| * High-level function that renders the given `template` using the given | |||||
| * `view` and `partials`. If you need to use any of the template options (see | |||||
| * `compile` above), you must compile in a separate step, and then call that | |||||
| * compiled function. | |||||
| */ | |||||
| function render(template, view, partials) { | |||||
| return compile(template)(view, partials); | |||||
| } | |||||
| })(Mustache); | |||||
| @@ -0,0 +1,5 @@ | |||||
| /*! Version: 0.5.48 */ | |||||
| /*! | |||||
| * mustache.js - Logic-less {{mustache}} templates with JavaScript | |||||
| * http://github.com/janl/mustache.js | |||||
| */var Mustache=typeof module!="undefined"&&module.exports||{};(function(a){function i(a){return h.test(a)}function n(a){return String(a).replace(/&(?!\w+;)|[<>"']/g,function(a){return m[a]||a})}function o(a,b,c,d){d=d||"<template>";var e=b.split("\n"),f=Math.max(c-3,0),g=Math.min(e.length,c+3),h=e.slice(f,g),i;for(var j=0,k=h.length;j<k;++j)i=j+f+1,h[j]=(i===c?" >> ":" ")+h[j];return a.template=b,a.line=c,a.file=d,a.message=[d+":"+c,h.join("\n"),"",a.message].join("\n"),a}function p(a,b,c){if(a===".")return b[b.length-1];var d=a.split("."),e=d.length-1,f=d[e],g,h,i=b.length,j,k;while(i){k=b.slice(0),h=b[--i],j=0;while(j<e){h=h[d[j++]];if(h==null)break;k.push(h)}if(h&&typeof h=="object"&&f in h){g=h[f];break}}return typeof g=="function"&&(g=g.call(k[k.length-1])),g==null?c:g}function q(a,b,c,d){var e="",h=p(a,b);if(d){if(h==null||h===!1||f(h)&&h.length===0)e+=c()}else if(f(h))g(h,function(a){b.push(a),e+=c(),b.pop()});else if(typeof h=="object")b.push(h),e+=c(),b.pop();else if(typeof h=="function"){var i=b[b.length-1],j=function(a){return w(a,i)};e+=h.call(i,c(),j)||""}else h&&(e+=c());return e}function r(b,c){c=c||{};var d=c.tags||a.tags,e=d[0],f=d[d.length-1],g=['var buffer = "";',"\nvar line = 1;","\ntry {",'\nbuffer += "'],h=[],k=!1,l=!1,m=function(){if(k&&!l&&!c.space)while(h.length)g.splice(h.pop(),1);else h=[];k=!1,l=!1},n=[],p,q,r,s=function(a){d=j(a).split(/\s+/),q=d[0],r=d[d.length-1]},t=function(a){g.push('";',p,'\nvar partial = partials["'+j(a)+'"];',"\nif (partial) {","\n buffer += render(partial,stack[stack.length - 1],partials);","\n}",'\nbuffer += "')},u=function(a,d){var e=j(a);if(e==="")throw o(new Error("Section name may not be empty"),b,z,c.file);n.push({name:e,inverted:d}),g.push('";',p,'\nvar name = "'+e+'";',"\nvar callback = (function () {","\n return function () {",'\n var buffer = "";','\nbuffer += "')},v=function(a){u(a,!0)},w=function(a){var d=j(a),e=n.length!=0&&n[n.length-1].name;if(!e||d!=e)throw o(new Error('Section named "'+d+'" was never opened'),b,z,c.file);var f=n.pop();g.push('";',"\n return buffer;","\n };","\n})();"),f.inverted?g.push("\nbuffer += renderSection(name,stack,callback,true);"):g.push("\nbuffer += renderSection(name,stack,callback);"),g.push('\nbuffer += "')},x=function(a){g.push('";',p,'\nbuffer += lookup("'+j(a)+'",stack,"");','\nbuffer += "')},y=function(a){g.push('";',p,'\nbuffer += escapeHTML(lookup("'+j(a)+'",stack,""));','\nbuffer += "')},z=1,A,B;for(var C=0,D=b.length;C<D;++C)if(b.slice(C,C+e.length)===e){C+=e.length,A=b.substr(C,1),p="\nline = "+z+";",q=e,r=f,k=!0;switch(A){case"!":C++,B=null;break;case"=":C++,f="="+f,B=s;break;case">":C++,B=t;break;case"#":C++,B=u;break;case"^":C++,B=v;break;case"/":C++,B=w;break;case"{":f="}"+f;case"&":C++,l=!0,B=x;break;default:l=!0,B=y}var E=b.indexOf(f,C);if(E===-1)throw o(new Error('Tag "'+e+'" was not closed properly'),b,z,c.file);var F=b.substring(C,E);B&&B(F);var G=0;while(~(G=F.indexOf("\n",G)))z++,G++;C=E+f.length-1,e=q,f=r}else{A=b.substr(C,1);switch(A){case'"':case"\\":l=!0,g.push("\\"+A);break;case"\r":break;case"\n":h.push(g.length),g.push("\\n"),m(),z++;break;default:i(A)?h.push(g.length):l=!0,g.push(A)}}if(n.length!=0)throw o(new Error('Section "'+n[n.length-1].name+'" was not closed properly'),b,z,c.file);m(),g.push('";',"\nreturn buffer;","\n} catch (e) { throw {error: e, line: line}; }");var H=g.join("").replace(/buffer \+= "";\n/g,"");return c.debug&&(typeof console!="undefined"&&console.log?console.log(H):typeof print=="function"&&print(H)),H}function s(a,b){var c="view,partials,stack,lookup,escapeHTML,renderSection,render",d=r(a,b),e=new Function(c,d);return function(c,d){d=d||{};var f=[c];try{return e(c,d,f,p,n,q,w)}catch(g){throw o(g.error,a,g.line,b.file)}}}function u(){t={}}function v(a,b){return b=b||{},b.cache!==!1?(t[a]||(t[a]=s(a,b)),t[a]):s(a,b)}function w(a,b,c){return v(a)(b,c)}a.name="mustache.js",a.version="0.5.14",a.tags=["{{","}}"],a.parse=r,a.compile=v,a.render=w,a.clearCache=u,a.to_html=function(a,b,c,d){var e=w(a,b,c);if(typeof d!="function")return e;d(e)};var b=Object.prototype.toString,c=Array.isArray,d=Array.prototype.forEach,e=String.prototype.trim,f;c?f=c:f=function(a){return b.call(a)==="[object Array]"};var g;d?g=function(a,b,c){return d.call(a,b,c)}:g=function(a,b,c){for(var d=0,e=a.length;d<e;++d)b.call(c,a[d],d,a)};var h=/^\s*$/,j;if(e)j=function(a){return a==null?"":e.call(a)};else{var k,l;i(" ")?(k=/^\s+/,l=/\s+$/):(k=/^[\s\xA0]+/,l=/[\s\xA0]+$/),j=function(a){return a==null?"":String(a).replace(k,"").replace(l,"")}}var m={"&":"&","<":"<",">":">",'"':""","'":"'"},t={}})(Mustache); | |||||
| @@ -0,0 +1,8 @@ | |||||
| { | |||||
| "name": "mustache", | |||||
| "version": "0.5.48", | |||||
| "description": "Logic-less {{mustache}} templates with JavaScript", | |||||
| "author": "mustache.js Authors <http://github.com/janl/mustache.js>", | |||||
| "keywords": ["mustache", "template", "templates", "ejs"], | |||||
| "main": "./mustache" | |||||
| } | |||||
| @@ -0,0 +1,23 @@ | |||||
| The MIT License | |||||
| 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. | |||||
| @@ -0,0 +1,418 @@ | |||||
| # mustache.js - Logic-less {{mustache}} templates with JavaScript | |||||
| > What could be more logical awesome than no logic at all? | |||||
| [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. | |||||
| 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. | |||||
| 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? | |||||
| 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. | |||||
| ## 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 | |||||
| Below is quick example how to use mustache.js: | |||||
| var view = { | |||||
| title: "Joe", | |||||
| calc: function () { | |||||
| return 2 + 4; | |||||
| } | |||||
| }; | |||||
| var output = Mustache.render("{{title}} spends {{calc}}", 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. | |||||
| ### 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: | |||||
| var Mustache = require("mustache"); | |||||
| ## Templates | |||||
| 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. | |||||
| There are several types of tags available in mustache.js. | |||||
| ### Variables | |||||
| 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. | |||||
| 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: | |||||
| * {{name}} | |||||
| * {{age}} | |||||
| * {{company}} | |||||
| * {{{company}}} | |||||
| * {{&company}} | |||||
| View: | |||||
| { | |||||
| "name": "Chris", | |||||
| "company": "<b>GitHub</b>" | |||||
| } | |||||
| Output: | |||||
| * Chris | |||||
| * | |||||
| * <b>GitHub</b> | |||||
| * <b>GitHub</b> | |||||
| * <b>GitHub</b> | |||||
| JavaScript's dot notation may be used to access keys that are properties of | |||||
| objects in a view. | |||||
| Template: | |||||
| * {{name.first}} {{name.last}} | |||||
| * {{age}} | |||||
| View: | |||||
| { | |||||
| "name": { | |||||
| "first": "Michael", | |||||
| "last": "Jackson" | |||||
| }, | |||||
| "age": "RIP" | |||||
| } | |||||
| Output: | |||||
| * Michael Jackson | |||||
| * RIP | |||||
| ### Sections | |||||
| Sections render blocks of text one or more times, depending on the value of the | |||||
| key in the current context. | |||||
| 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. | |||||
| #### False Values or Empty Lists | |||||
| 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. | |||||
| Template: | |||||
| Shown. | |||||
| {{#nothin}} | |||||
| Never shown! | |||||
| {{/nothin}} | |||||
| View: | |||||
| { | |||||
| "person": true | |||||
| } | |||||
| Output: | |||||
| Shown. | |||||
| #### Non-Empty Lists | |||||
| 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. | |||||
| 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. | |||||
| Template: | |||||
| {{#stooges}} | |||||
| <b>{{name}}</b> | |||||
| {{/stooges}} | |||||
| View: | |||||
| { | |||||
| "stooges": [ | |||||
| { "name": "Moe" }, | |||||
| { "name": "Larry" }, | |||||
| { "name": "Curly" } | |||||
| ] | |||||
| } | |||||
| Output: | |||||
| <b>Moe</b> | |||||
| <b>Larry</b> | |||||
| <b>Curly</b> | |||||
| When looping over an array of strings, a `.` can be used to refer to the current | |||||
| item in the list. | |||||
| Template: | |||||
| {{#musketeers}} | |||||
| * {{.}} | |||||
| {{/musketeers}} | |||||
| View: | |||||
| { | |||||
| "musketeers": ["Athos", "Aramis", "Porthos", "D'Artagnan"] | |||||
| } | |||||
| Output: | |||||
| * Athos | |||||
| * Aramis | |||||
| * Porthos | |||||
| * D'Artagnan | |||||
| 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: | |||||
| {{#beatles}} | |||||
| * {{name}} | |||||
| {{/beatles}} | |||||
| View: | |||||
| { | |||||
| "beatles": [ | |||||
| { "firstName": "John", "lastName": "Lennon" }, | |||||
| { "firstName": "Paul", "lastName": "McCartney" }, | |||||
| { "firstName": "George", "lastName": "Harrison" }, | |||||
| { "firstName": "Ringo", "lastName": "Starr" } | |||||
| ], | |||||
| "name": function () { | |||||
| return this.firstName + " " + this.lastName; | |||||
| } | |||||
| } | |||||
| Output: | |||||
| * John Lennon | |||||
| * Paul McCartney | |||||
| * George Harrison | |||||
| * Ringo Starr | |||||
| #### Functions | |||||
| 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: | |||||
| {{#bold}}Hi {{name}}.{{/bold}} | |||||
| View: | |||||
| { | |||||
| "name": "Tater", | |||||
| "bold": function () { | |||||
| return function (text, render) { | |||||
| return "<b>" + render(text) + "</b>"; | |||||
| } | |||||
| } | |||||
| } | |||||
| Output: | |||||
| <b>Hi Tater.</b> | |||||
| ### Inverted Sections | |||||
| 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. | |||||
| Template: | |||||
| {{#repos}}<b>{{name}}</b>{{/repos}} | |||||
| {{^repos}}No repos :({{/repos}} | |||||
| View: | |||||
| { | |||||
| "repos": [] | |||||
| } | |||||
| Output: | |||||
| No repos :( | |||||
| ### Comments | |||||
| Comments begin with a bang and are ignored. The following template: | |||||
| <h1>Today{{! ignore me }}.</h1> | |||||
| Will render as follows: | |||||
| <h1>Today.</h1> | |||||
| Comments may contain newlines. | |||||
| ### Partials | |||||
| Partials begin with a greater than sign, like {{> box}}. | |||||
| Partials are rendered at runtime (as opposed to compile time), so recursive | |||||
| partials are possible. Just avoid infinite loops. | |||||
| They also inherit the calling context. Whereas in ERB you may have this: | |||||
| <%= partial :next_more, :start => start, :size => size %> | |||||
| Mustache requires only this: | |||||
| {{> next_more}} | |||||
| 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. | |||||
| For example, this template and partial: | |||||
| base.mustache: | |||||
| <h2>Names</h2> | |||||
| {{#names}} | |||||
| {{> user}} | |||||
| {{/names}} | |||||
| user.mustache: | |||||
| <strong>{{name}}</strong> | |||||
| Can be thought of as a single, expanded template: | |||||
| <h2>Names</h2> | |||||
| {{#names}} | |||||
| <strong>{{name}}</strong> | |||||
| {{/names}} | |||||
| 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. | |||||
| ### Set Delimiter | |||||
| Set Delimiter tags start with an equals sign and change the tag delimiters from | |||||
| `{{` and `}}` to custom strings. | |||||
| Consider the following contrived example: | |||||
| * {{ default_tags }} | |||||
| {{=<% %>=}} | |||||
| * <% erb_style_tags %> | |||||
| <%={{ }}=%> | |||||
| * {{ default_tags_again }} | |||||
| 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. | |||||
| 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." | |||||
| Custom delimiters may not contain whitespace or the equals sign. | |||||
| ## Streaming | |||||
| To stream template results out of mustache.js, you can pass an optional callback | |||||
| to the call to `Mustache.render`: | |||||
| Mustache.render(template, view, partials, function (chunk) { | |||||
| print(chunk); | |||||
| }); | |||||
| 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 | |||||
| @@ -0,0 +1,549 @@ | |||||
| /* | |||||
| Shameless port of a shameless port | |||||
| @defunkt => @janl => @aq => @voodootikigod | |||||
| See http://github.com/defunkt/mustache for more info. | |||||
| */ | |||||
| dojo.provide("dojox.mustache._base"); | |||||
| (function(){ | |||||
| /*! | |||||
| * 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.14"; | |||||
| 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; | |||||
| } | |||||
| }; | |||||
| 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]+$/; | |||||
| } | |||||
| trim = function (string) { | |||||
| return string == null ? "" : | |||||
| String(string).replace(trimLeft, "").replace(trimRight, ""); | |||||
| }; | |||||
| } | |||||
| var escapeMap = { | |||||
| "&": "&", | |||||
| "<": "<", | |||||
| ">": ">", | |||||
| '"': '"', | |||||
| "'": ''' | |||||
| }; | |||||
| 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 || "<template>"; | |||||
| var lines = template.split("\n"), | |||||
| start = Math.max(line - 3, 0), | |||||
| end = Math.min(lines.length, line + 3), | |||||
| context = lines.slice(start, end); | |||||
| var c; | |||||
| for (var i = 0, len = context.length; i < len; ++i) { | |||||
| c = i + start + 1; | |||||
| context[i] = (c === line ? " >> " : " ") + context[i]; | |||||
| } | |||||
| e.template = template; | |||||
| e.line = line; | |||||
| e.file = file; | |||||
| e.message = [file + ":" + line, context.join("\n"), "", e.message].join("\n"); | |||||
| return e; | |||||
| } | |||||
| /** | |||||
| * Looks up the value of the given `name` in the given context `stack`. | |||||
| */ | |||||
| function lookup(name, stack, defaultValue) { | |||||
| if (name === ".") { | |||||
| return stack[stack.length - 1]; | |||||
| } | |||||
| var names = name.split("."); | |||||
| var lastIndex = names.length - 1; | |||||
| var target = names[lastIndex]; | |||||
| var value, context, i = stack.length, j, localStack; | |||||
| while (i) { | |||||
| localStack = stack.slice(0); | |||||
| context = stack[--i]; | |||||
| j = 0; | |||||
| while (j < lastIndex) { | |||||
| context = context[names[j++]]; | |||||
| if (context == null) { | |||||
| break; | |||||
| } | |||||
| localStack.push(context); | |||||
| } | |||||
| if (context && typeof context === "object" && target in context) { | |||||
| value = context[target]; | |||||
| break; | |||||
| } | |||||
| } | |||||
| // If the value is a function, call it in the current context. | |||||
| if (typeof value === "function") { | |||||
| value = value.call(localStack[localStack.length - 1]); | |||||
| } | |||||
| if (value == null) { | |||||
| return defaultValue; | |||||
| } | |||||
| return value; | |||||
| } | |||||
| function renderSection(name, stack, callback, inverted) { | |||||
| var buffer = ""; | |||||
| var value = lookup(name, stack); | |||||
| if (inverted) { | |||||
| // From the spec: inverted sections may render text once based on the | |||||
| // inverse value of the key. That is, they will be rendered if the key | |||||
| // doesn't exist, is false, or is an empty list. | |||||
| if (value == null || value === false || (isArray(value) && value.length === 0)) { | |||||
| buffer += callback(); | |||||
| } | |||||
| } else if (isArray(value)) { | |||||
| forEach(value, function (value) { | |||||
| stack.push(value); | |||||
| buffer += callback(); | |||||
| stack.pop(); | |||||
| }); | |||||
| } else if (typeof value === "object") { | |||||
| stack.push(value); | |||||
| buffer += callback(); | |||||
| stack.pop(); | |||||
| } else if (typeof value === "function") { | |||||
| var scope = stack[stack.length - 1]; | |||||
| var scopedRender = function (template) { | |||||
| return render(template, scope); | |||||
| }; | |||||
| buffer += value.call(scope, callback(), scopedRender) || ""; | |||||
| } else if (value) { | |||||
| buffer += callback(); | |||||
| } | |||||
| return buffer; | |||||
| } | |||||
| /** | |||||
| * Parses the given `template` and returns the source of a function that, | |||||
| * with the proper arguments, will render the template. Recognized options | |||||
| * include the following: | |||||
| * | |||||
| * - file The name of the file the template comes from (displayed in | |||||
| * error messages) | |||||
| * - tags An array of open and close tags the `template` uses. Defaults | |||||
| * to the value of Mustache.tags | |||||
| * - debug Set `true` to log the body of the generated function to the | |||||
| * console | |||||
| * - space Set `true` to preserve whitespace from lines that otherwise | |||||
| * contain only a {{tag}}. Defaults to `false` | |||||
| */ | |||||
| function parse(template, options) { | |||||
| options = options || {}; | |||||
| var tags = options.tags || exports.tags, | |||||
| openTag = tags[0], | |||||
| closeTag = tags[tags.length - 1]; | |||||
| var code = [ | |||||
| 'var buffer = "";', // output buffer | |||||
| "\nvar line = 1;", // keep track of source line number | |||||
| "\ntry {", | |||||
| '\nbuffer += "' | |||||
| ]; | |||||
| var spaces = [], // indices of whitespace in code on the current line | |||||
| hasTag = false, // is there a {{tag}} on the current line? | |||||
| nonSpace = false; // is there a non-space char on the current line? | |||||
| // Strips all space characters from the code array for the current line | |||||
| // if there was a {{tag}} on it and otherwise only spaces. | |||||
| var stripSpace = function () { | |||||
| if (hasTag && !nonSpace && !options.space) { | |||||
| while (spaces.length) { | |||||
| code.splice(spaces.pop(), 1); | |||||
| } | |||||
| } else { | |||||
| spaces = []; | |||||
| } | |||||
| hasTag = false; | |||||
| nonSpace = false; | |||||
| }; | |||||
| var sectionStack = [], updateLine, nextOpenTag, nextCloseTag; | |||||
| var setTags = function (source) { | |||||
| tags = trim(source).split(/\s+/); | |||||
| nextOpenTag = tags[0]; | |||||
| nextCloseTag = tags[tags.length - 1]; | |||||
| }; | |||||
| var includePartial = function (source) { | |||||
| code.push( | |||||
| '";', | |||||
| updateLine, | |||||
| '\nvar partial = partials["' + trim(source) + '"];', | |||||
| '\nif (partial) {', | |||||
| '\n buffer += render(partial,stack[stack.length - 1],partials);', | |||||
| '\n}', | |||||
| '\nbuffer += "' | |||||
| ); | |||||
| }; | |||||
| var openSection = function (source, inverted) { | |||||
| var name = trim(source); | |||||
| if (name === "") { | |||||
| throw debug(new Error("Section name may not be empty"), template, line, options.file); | |||||
| } | |||||
| sectionStack.push({name: name, inverted: inverted}); | |||||
| code.push( | |||||
| '";', | |||||
| updateLine, | |||||
| '\nvar name = "' + name + '";', | |||||
| '\nvar callback = (function () {', | |||||
| '\n return function () {', | |||||
| '\n var buffer = "";', | |||||
| '\nbuffer += "' | |||||
| ); | |||||
| }; | |||||
| var openInvertedSection = function (source) { | |||||
| openSection(source, true); | |||||
| }; | |||||
| var closeSection = function (source) { | |||||
| var name = trim(source); | |||||
| var openName = sectionStack.length != 0 && sectionStack[sectionStack.length - 1].name; | |||||
| if (!openName || name != openName) { | |||||
| throw debug(new Error('Section named "' + name + '" was never opened'), template, line, options.file); | |||||
| } | |||||
| var section = sectionStack.pop(); | |||||
| code.push( | |||||
| '";', | |||||
| '\n return buffer;', | |||||
| '\n };', | |||||
| '\n})();' | |||||
| ); | |||||
| if (section.inverted) { | |||||
| code.push("\nbuffer += renderSection(name,stack,callback,true);"); | |||||
| } else { | |||||
| code.push("\nbuffer += renderSection(name,stack,callback);"); | |||||
| } | |||||
| code.push('\nbuffer += "'); | |||||
| }; | |||||
| var sendPlain = function (source) { | |||||
| code.push( | |||||
| '";', | |||||
| updateLine, | |||||
| '\nbuffer += lookup("' + trim(source) + '",stack,"");', | |||||
| '\nbuffer += "' | |||||
| ); | |||||
| }; | |||||
| var sendEscaped = function (source) { | |||||
| code.push( | |||||
| '";', | |||||
| updateLine, | |||||
| '\nbuffer += escapeHTML(lookup("' + trim(source) + '",stack,""));', | |||||
| '\nbuffer += "' | |||||
| ); | |||||
| }; | |||||
| var line = 1, c, callback; | |||||
| for (var i = 0, len = template.length; i < len; ++i) { | |||||
| if (template.slice(i, i + openTag.length) === openTag) { | |||||
| i += openTag.length; | |||||
| c = template.substr(i, 1); | |||||
| updateLine = '\nline = ' + line + ';'; | |||||
| nextOpenTag = openTag; | |||||
| nextCloseTag = closeTag; | |||||
| hasTag = true; | |||||
| switch (c) { | |||||
| case "!": // comment | |||||
| i++; | |||||
| callback = null; | |||||
| break; | |||||
| case "=": // change open/close tags, e.g. {{=<% %>=}} | |||||
| i++; | |||||
| closeTag = "=" + closeTag; | |||||
| callback = setTags; | |||||
| break; | |||||
| case ">": // include partial | |||||
| i++; | |||||
| callback = includePartial; | |||||
| break; | |||||
| case "#": // start section | |||||
| i++; | |||||
| callback = openSection; | |||||
| break; | |||||
| case "^": // start inverted section | |||||
| i++; | |||||
| callback = openInvertedSection; | |||||
| break; | |||||
| case "/": // end section | |||||
| i++; | |||||
| callback = closeSection; | |||||
| break; | |||||
| case "{": // plain variable | |||||
| closeTag = "}" + closeTag; | |||||
| // fall through | |||||
| case "&": // plain variable | |||||
| i++; | |||||
| nonSpace = true; | |||||
| callback = sendPlain; | |||||
| break; | |||||
| default: // escaped variable | |||||
| nonSpace = true; | |||||
| callback = sendEscaped; | |||||
| } | |||||
| var end = template.indexOf(closeTag, i); | |||||
| if (end === -1) { | |||||
| throw debug(new Error('Tag "' + openTag + '" was not closed properly'), template, line, options.file); | |||||
| } | |||||
| var source = template.substring(i, end); | |||||
| if (callback) { | |||||
| callback(source); | |||||
| } | |||||
| // Maintain line count for \n in source. | |||||
| var n = 0; | |||||
| while (~(n = source.indexOf("\n", n))) { | |||||
| line++; | |||||
| n++; | |||||
| } | |||||
| i = end + closeTag.length - 1; | |||||
| openTag = nextOpenTag; | |||||
| closeTag = nextCloseTag; | |||||
| } else { | |||||
| c = template.substr(i, 1); | |||||
| switch (c) { | |||||
| case '"': | |||||
| case "\\": | |||||
| nonSpace = true; | |||||
| code.push("\\" + c); | |||||
| break; | |||||
| case "\r": | |||||
| // Ignore carriage returns. | |||||
| break; | |||||
| case "\n": | |||||
| spaces.push(code.length); | |||||
| code.push("\\n"); | |||||
| stripSpace(); // Check for whitespace on the current line. | |||||
| line++; | |||||
| break; | |||||
| default: | |||||
| if (isWhitespace(c)) { | |||||
| spaces.push(code.length); | |||||
| } else { | |||||
| nonSpace = true; | |||||
| } | |||||
| code.push(c); | |||||
| } | |||||
| } | |||||
| } | |||||
| if (sectionStack.length != 0) { | |||||
| throw debug(new Error('Section "' + sectionStack[sectionStack.length - 1].name + '" was not closed properly'), template, line, options.file); | |||||
| } | |||||
| // Clean up any whitespace from a closing {{tag}} that was at the end | |||||
| // of the template without a trailing \n. | |||||
| stripSpace(); | |||||
| code.push( | |||||
| '";', | |||||
| "\nreturn buffer;", | |||||
| "\n} catch (e) { throw {error: e, line: line}; }" | |||||
| ); | |||||
| // Ignore `buffer += "";` statements. | |||||
| var body = code.join("").replace(/buffer \+= "";\n/g, ""); | |||||
| if (options.debug) { | |||||
| if (typeof console != "undefined" && console.log) { | |||||
| console.log(body); | |||||
| } else if (typeof print === "function") { | |||||
| print(body); | |||||
| } | |||||
| } | |||||
| return body; | |||||
| } | |||||
| /** | |||||
| * Used by `compile` to generate a reusable function for the given `template`. | |||||
| */ | |||||
| function _compile(template, options) { | |||||
| var args = "view,partials,stack,lookup,escapeHTML,renderSection,render"; | |||||
| var body = parse(template, options); | |||||
| var fn = new Function(args, body); | |||||
| // This anonymous function wraps the generated function so we can do | |||||
| // argument coercion, setup some variables, and handle any errors | |||||
| // encountered while executing it. | |||||
| return function (view, partials) { | |||||
| partials = partials || {}; | |||||
| var stack = [view]; // context stack | |||||
| try { | |||||
| return fn(view, partials, stack, lookup, escapeHTML, renderSection, render); | |||||
| } catch (e) { | |||||
| throw debug(e.error, template, e.line, options.file); | |||||
| } | |||||
| }; | |||||
| } | |||||
| // Cache of pre-compiled templates. | |||||
| var _cache = {}; | |||||
| /** | |||||
| * Clear the cache of compiled templates. | |||||
| */ | |||||
| function clearCache() { | |||||
| _cache = {}; | |||||
| } | |||||
| /** | |||||
| * Compiles the given `template` into a reusable function using the given | |||||
| * `options`. In addition to the options accepted by Mustache.parse, | |||||
| * recognized options include the following: | |||||
| * | |||||
| * - cache Set `false` to bypass any pre-compiled version of the given | |||||
| * template. Otherwise, a given `template` string will be cached | |||||
| * the first time it is parsed | |||||
| */ | |||||
| function compile(template, options) { | |||||
| options = options || {}; | |||||
| // Use a pre-compiled version from the cache if we have one. | |||||
| if (options.cache !== false) { | |||||
| if (!_cache[template]) { | |||||
| _cache[template] = _compile(template, options); | |||||
| } | |||||
| return _cache[template]; | |||||
| } | |||||
| return _compile(template, options); | |||||
| } | |||||
| /** | |||||
| * High-level function that renders the given `template` using the given | |||||
| * `view` and `partials`. If you need to use any of the template options (see | |||||
| * `compile` above), you must compile in a separate step, and then call that | |||||
| * compiled function. | |||||
| */ | |||||
| function render(template, view, partials) { | |||||
| return compile(template)(view, partials); | |||||
| } | |||||
| })(Mustache); | |||||
| dojox.mustache = dojo.hitch(Mustache, "render"); | |||||
| })(); | |||||
| @@ -0,0 +1,6 @@ | |||||
| /* | |||||
| Shameless port of a shameless port | |||||
| @defunkt => @janl => @aq => @voodootikigod | |||||
| See http://github.com/defunkt/mustache for more info. | |||||
| */dojo.provide("dojox.mustache._base"),function(){var a=typeof module!="undefined"&&module.exports||{};(function(a){function i(a){return h.test(a)}function n(a){return String(a).replace(/&(?!\w+;)|[<>"']/g,function(a){return m[a]||a})}function o(a,b,c,d){d=d||"<template>";var e=b.split("\n"),f=Math.max(c-3,0),g=Math.min(e.length,c+3),h=e.slice(f,g),i;for(var j=0,k=h.length;j<k;++j)i=j+f+1,h[j]=(i===c?" >> ":" ")+h[j];return a.template=b,a.line=c,a.file=d,a.message=[d+":"+c,h.join("\n"),"",a.message].join("\n"),a}function p(a,b,c){if(a===".")return b[b.length-1];var d=a.split("."),e=d.length-1,f=d[e],g,h,i=b.length,j,k;while(i){k=b.slice(0),h=b[--i],j=0;while(j<e){h=h[d[j++]];if(h==null)break;k.push(h)}if(h&&typeof h=="object"&&f in h){g=h[f];break}}return typeof g=="function"&&(g=g.call(k[k.length-1])),g==null?c:g}function q(a,b,c,d){var e="",h=p(a,b);if(d){if(h==null||h===!1||f(h)&&h.length===0)e+=c()}else if(f(h))g(h,function(a){b.push(a),e+=c(),b.pop()});else if(typeof h=="object")b.push(h),e+=c(),b.pop();else if(typeof h=="function"){var i=b[b.length-1],j=function(a){return w(a,i)};e+=h.call(i,c(),j)||""}else h&&(e+=c());return e}function r(b,c){c=c||{};var d=c.tags||a.tags,e=d[0],f=d[d.length-1],g=['var buffer = "";',"\nvar line = 1;","\ntry {",'\nbuffer += "'],h=[],k=!1,l=!1,m=function(){if(k&&!l&&!c.space)while(h.length)g.splice(h.pop(),1);else h=[];k=!1,l=!1},n=[],p,q,r,s=function(a){d=j(a).split(/\s+/),q=d[0],r=d[d.length-1]},t=function(a){g.push('";',p,'\nvar partial = partials["'+j(a)+'"];',"\nif (partial) {","\n buffer += render(partial,stack[stack.length - 1],partials);","\n}",'\nbuffer += "')},u=function(a,d){var e=j(a);if(e==="")throw o(new Error("Section name may not be empty"),b,z,c.file);n.push({name:e,inverted:d}),g.push('";',p,'\nvar name = "'+e+'";',"\nvar callback = (function () {","\n return function () {",'\n var buffer = "";','\nbuffer += "')},v=function(a){u(a,!0)},w=function(a){var d=j(a),e=n.length!=0&&n[n.length-1].name;if(!e||d!=e)throw o(new Error('Section named "'+d+'" was never opened'),b,z,c.file);var f=n.pop();g.push('";',"\n return buffer;","\n };","\n})();"),f.inverted?g.push("\nbuffer += renderSection(name,stack,callback,true);"):g.push("\nbuffer += renderSection(name,stack,callback);"),g.push('\nbuffer += "')},x=function(a){g.push('";',p,'\nbuffer += lookup("'+j(a)+'",stack,"");','\nbuffer += "')},y=function(a){g.push('";',p,'\nbuffer += escapeHTML(lookup("'+j(a)+'",stack,""));','\nbuffer += "')},z=1,A,B;for(var C=0,D=b.length;C<D;++C)if(b.slice(C,C+e.length)===e){C+=e.length,A=b.substr(C,1),p="\nline = "+z+";",q=e,r=f,k=!0;switch(A){case"!":C++,B=null;break;case"=":C++,f="="+f,B=s;break;case">":C++,B=t;break;case"#":C++,B=u;break;case"^":C++,B=v;break;case"/":C++,B=w;break;case"{":f="}"+f;case"&":C++,l=!0,B=x;break;default:l=!0,B=y}var E=b.indexOf(f,C);if(E===-1)throw o(new Error('Tag "'+e+'" was not closed properly'),b,z,c.file);var F=b.substring(C,E);B&&B(F);var G=0;while(~(G=F.indexOf("\n",G)))z++,G++;C=E+f.length-1,e=q,f=r}else{A=b.substr(C,1);switch(A){case'"':case"\\":l=!0,g.push("\\"+A);break;case"\r":break;case"\n":h.push(g.length),g.push("\\n"),m(),z++;break;default:i(A)?h.push(g.length):l=!0,g.push(A)}}if(n.length!=0)throw o(new Error('Section "'+n[n.length-1].name+'" was not closed properly'),b,z,c.file);m(),g.push('";',"\nreturn buffer;","\n} catch (e) { throw {error: e, line: line}; }");var H=g.join("").replace(/buffer \+= "";\n/g,"");return c.debug&&(typeof console!="undefined"&&console.log?console.log(H):typeof print=="function"&&print(H)),H}function s(a,b){var c="view,partials,stack,lookup,escapeHTML,renderSection,render",d=r(a,b),e=new Function(c,d);return function(c,d){d=d||{};var f=[c];try{return e(c,d,f,p,n,q,w)}catch(g){throw o(g.error,a,g.line,b.file)}}}function u(){t={}}function v(a,b){return b=b||{},b.cache!==!1?(t[a]||(t[a]=s(a,b)),t[a]):s(a,b)}function w(a,b,c){return v(a)(b,c)}a.name="mustache.js",a.version="0.5.14",a.tags=["{{","}}"],a.parse=r,a.compile=v,a.render=w,a.clearCache=u,a.to_html=function(a,b,c,d){var e=w(a,b,c);if(typeof d!="function")return e;d(e)};var b=Object.prototype.toString,c=Array.isArray,d=Array.prototype.forEach,e=String.prototype.trim,f;c?f=c:f=function(a){return b.call(a)==="[object Array]"};var g;d?g=function(a,b,c){return d.call(a,b,c)}:g=function(a,b,c){for(var d=0,e=a.length;d<e;++d)b.call(c,a[d],d,a)};var h=/^\s*$/,j;if(e)j=function(a){return a==null?"":e.call(a)};else{var k,l;i(" ")?(k=/^\s+/,l=/\s+$/):(k=/^[\s\xA0]+/,l=/[\s\xA0]+$/),j=function(a){return a==null?"":String(a).replace(k,"").replace(l,"")}}var m={"&":"&","<":"<",">":">",'"':""","'":"'"},t={}})(a),dojox.mustache=dojo.hitch(a,"render")}(); | |||||
| @@ -0,0 +1,23 @@ | |||||
| The MIT License | |||||
| 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. | |||||
| @@ -0,0 +1,418 @@ | |||||
| # mustache.js - Logic-less {{mustache}} templates with JavaScript | |||||
| > What could be more logical awesome than no logic at all? | |||||
| [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. | |||||
| 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. | |||||
| 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? | |||||
| 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. | |||||
| ## 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 | |||||
| Below is quick example how to use mustache.js: | |||||
| var view = { | |||||
| title: "Joe", | |||||
| calc: function () { | |||||
| return 2 + 4; | |||||
| } | |||||
| }; | |||||
| var output = Mustache.render("{{title}} spends {{calc}}", 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. | |||||
| ### 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: | |||||
| var Mustache = require("mustache"); | |||||
| ## Templates | |||||
| 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. | |||||
| There are several types of tags available in mustache.js. | |||||
| ### Variables | |||||
| 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. | |||||
| 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: | |||||
| * {{name}} | |||||
| * {{age}} | |||||
| * {{company}} | |||||
| * {{{company}}} | |||||
| * {{&company}} | |||||
| View: | |||||
| { | |||||
| "name": "Chris", | |||||
| "company": "<b>GitHub</b>" | |||||
| } | |||||
| Output: | |||||
| * Chris | |||||
| * | |||||
| * <b>GitHub</b> | |||||
| * <b>GitHub</b> | |||||
| * <b>GitHub</b> | |||||
| JavaScript's dot notation may be used to access keys that are properties of | |||||
| objects in a view. | |||||
| Template: | |||||
| * {{name.first}} {{name.last}} | |||||
| * {{age}} | |||||
| View: | |||||
| { | |||||
| "name": { | |||||
| "first": "Michael", | |||||
| "last": "Jackson" | |||||
| }, | |||||
| "age": "RIP" | |||||
| } | |||||
| Output: | |||||
| * Michael Jackson | |||||
| * RIP | |||||
| ### Sections | |||||
| Sections render blocks of text one or more times, depending on the value of the | |||||
| key in the current context. | |||||
| 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. | |||||
| #### False Values or Empty Lists | |||||
| 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. | |||||
| Template: | |||||
| Shown. | |||||
| {{#nothin}} | |||||
| Never shown! | |||||
| {{/nothin}} | |||||
| View: | |||||
| { | |||||
| "person": true | |||||
| } | |||||
| Output: | |||||
| Shown. | |||||
| #### Non-Empty Lists | |||||
| 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. | |||||
| 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. | |||||
| Template: | |||||
| {{#stooges}} | |||||
| <b>{{name}}</b> | |||||
| {{/stooges}} | |||||
| View: | |||||
| { | |||||
| "stooges": [ | |||||
| { "name": "Moe" }, | |||||
| { "name": "Larry" }, | |||||
| { "name": "Curly" } | |||||
| ] | |||||
| } | |||||
| Output: | |||||
| <b>Moe</b> | |||||
| <b>Larry</b> | |||||
| <b>Curly</b> | |||||
| When looping over an array of strings, a `.` can be used to refer to the current | |||||
| item in the list. | |||||
| Template: | |||||
| {{#musketeers}} | |||||
| * {{.}} | |||||
| {{/musketeers}} | |||||
| View: | |||||
| { | |||||
| "musketeers": ["Athos", "Aramis", "Porthos", "D'Artagnan"] | |||||
| } | |||||
| Output: | |||||
| * Athos | |||||
| * Aramis | |||||
| * Porthos | |||||
| * D'Artagnan | |||||
| 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: | |||||
| {{#beatles}} | |||||
| * {{name}} | |||||
| {{/beatles}} | |||||
| View: | |||||
| { | |||||
| "beatles": [ | |||||
| { "firstName": "John", "lastName": "Lennon" }, | |||||
| { "firstName": "Paul", "lastName": "McCartney" }, | |||||
| { "firstName": "George", "lastName": "Harrison" }, | |||||
| { "firstName": "Ringo", "lastName": "Starr" } | |||||
| ], | |||||
| "name": function () { | |||||
| return this.firstName + " " + this.lastName; | |||||
| } | |||||
| } | |||||
| Output: | |||||
| * John Lennon | |||||
| * Paul McCartney | |||||
| * George Harrison | |||||
| * Ringo Starr | |||||
| #### Functions | |||||
| 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: | |||||
| {{#bold}}Hi {{name}}.{{/bold}} | |||||
| View: | |||||
| { | |||||
| "name": "Tater", | |||||
| "bold": function () { | |||||
| return function (text, render) { | |||||
| return "<b>" + render(text) + "</b>"; | |||||
| } | |||||
| } | |||||
| } | |||||
| Output: | |||||
| <b>Hi Tater.</b> | |||||
| ### Inverted Sections | |||||
| 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. | |||||
| Template: | |||||
| {{#repos}}<b>{{name}}</b>{{/repos}} | |||||
| {{^repos}}No repos :({{/repos}} | |||||
| View: | |||||
| { | |||||
| "repos": [] | |||||
| } | |||||
| Output: | |||||
| No repos :( | |||||
| ### Comments | |||||
| Comments begin with a bang and are ignored. The following template: | |||||
| <h1>Today{{! ignore me }}.</h1> | |||||
| Will render as follows: | |||||
| <h1>Today.</h1> | |||||
| Comments may contain newlines. | |||||
| ### Partials | |||||
| Partials begin with a greater than sign, like {{> box}}. | |||||
| Partials are rendered at runtime (as opposed to compile time), so recursive | |||||
| partials are possible. Just avoid infinite loops. | |||||
| They also inherit the calling context. Whereas in ERB you may have this: | |||||
| <%= partial :next_more, :start => start, :size => size %> | |||||
| Mustache requires only this: | |||||
| {{> next_more}} | |||||
| 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. | |||||
| For example, this template and partial: | |||||
| base.mustache: | |||||
| <h2>Names</h2> | |||||
| {{#names}} | |||||
| {{> user}} | |||||
| {{/names}} | |||||
| user.mustache: | |||||
| <strong>{{name}}</strong> | |||||
| Can be thought of as a single, expanded template: | |||||
| <h2>Names</h2> | |||||
| {{#names}} | |||||
| <strong>{{name}}</strong> | |||||
| {{/names}} | |||||
| 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. | |||||
| ### Set Delimiter | |||||
| Set Delimiter tags start with an equals sign and change the tag delimiters from | |||||
| `{{` and `}}` to custom strings. | |||||
| Consider the following contrived example: | |||||
| * {{ default_tags }} | |||||
| {{=<% %>=}} | |||||
| * <% erb_style_tags %> | |||||
| <%={{ }}=%> | |||||
| * {{ default_tags_again }} | |||||
| 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. | |||||
| 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." | |||||
| Custom delimiters may not contain whitespace or the equals sign. | |||||
| ## Streaming | |||||
| To stream template results out of mustache.js, you can pass an optional callback | |||||
| to the call to `Mustache.render`: | |||||
| Mustache.render(template, view, partials, function (chunk) { | |||||
| print(chunk); | |||||
| }); | |||||
| 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 | |||||
| @@ -0,0 +1,559 @@ | |||||
| /* | |||||
| Shameless port of a shameless port | |||||
| @defunkt => @janl => @aq | |||||
| See http://github.com/defunkt/mustache for more info. | |||||
| */ | |||||
| ;(function($) { | |||||
| /*! | |||||
| * 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.14"; | |||||
| 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; | |||||
| } | |||||
| }; | |||||
| 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]+$/; | |||||
| } | |||||
| trim = function (string) { | |||||
| return string == null ? "" : | |||||
| String(string).replace(trimLeft, "").replace(trimRight, ""); | |||||
| }; | |||||
| } | |||||
| var escapeMap = { | |||||
| "&": "&", | |||||
| "<": "<", | |||||
| ">": ">", | |||||
| '"': '"', | |||||
| "'": ''' | |||||
| }; | |||||
| 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 || "<template>"; | |||||
| var lines = template.split("\n"), | |||||
| start = Math.max(line - 3, 0), | |||||
| end = Math.min(lines.length, line + 3), | |||||
| context = lines.slice(start, end); | |||||
| var c; | |||||
| for (var i = 0, len = context.length; i < len; ++i) { | |||||
| c = i + start + 1; | |||||
| context[i] = (c === line ? " >> " : " ") + context[i]; | |||||
| } | |||||
| e.template = template; | |||||
| e.line = line; | |||||
| e.file = file; | |||||
| e.message = [file + ":" + line, context.join("\n"), "", e.message].join("\n"); | |||||
| return e; | |||||
| } | |||||
| /** | |||||
| * Looks up the value of the given `name` in the given context `stack`. | |||||
| */ | |||||
| function lookup(name, stack, defaultValue) { | |||||
| if (name === ".") { | |||||
| return stack[stack.length - 1]; | |||||
| } | |||||
| var names = name.split("."); | |||||
| var lastIndex = names.length - 1; | |||||
| var target = names[lastIndex]; | |||||
| var value, context, i = stack.length, j, localStack; | |||||
| while (i) { | |||||
| localStack = stack.slice(0); | |||||
| context = stack[--i]; | |||||
| j = 0; | |||||
| while (j < lastIndex) { | |||||
| context = context[names[j++]]; | |||||
| if (context == null) { | |||||
| break; | |||||
| } | |||||
| localStack.push(context); | |||||
| } | |||||
| if (context && typeof context === "object" && target in context) { | |||||
| value = context[target]; | |||||
| break; | |||||
| } | |||||
| } | |||||
| // If the value is a function, call it in the current context. | |||||
| if (typeof value === "function") { | |||||
| value = value.call(localStack[localStack.length - 1]); | |||||
| } | |||||
| if (value == null) { | |||||
| return defaultValue; | |||||
| } | |||||
| return value; | |||||
| } | |||||
| function renderSection(name, stack, callback, inverted) { | |||||
| var buffer = ""; | |||||
| var value = lookup(name, stack); | |||||
| if (inverted) { | |||||
| // From the spec: inverted sections may render text once based on the | |||||
| // inverse value of the key. That is, they will be rendered if the key | |||||
| // doesn't exist, is false, or is an empty list. | |||||
| if (value == null || value === false || (isArray(value) && value.length === 0)) { | |||||
| buffer += callback(); | |||||
| } | |||||
| } else if (isArray(value)) { | |||||
| forEach(value, function (value) { | |||||
| stack.push(value); | |||||
| buffer += callback(); | |||||
| stack.pop(); | |||||
| }); | |||||
| } else if (typeof value === "object") { | |||||
| stack.push(value); | |||||
| buffer += callback(); | |||||
| stack.pop(); | |||||
| } else if (typeof value === "function") { | |||||
| var scope = stack[stack.length - 1]; | |||||
| var scopedRender = function (template) { | |||||
| return render(template, scope); | |||||
| }; | |||||
| buffer += value.call(scope, callback(), scopedRender) || ""; | |||||
| } else if (value) { | |||||
| buffer += callback(); | |||||
| } | |||||
| return buffer; | |||||
| } | |||||
| /** | |||||
| * Parses the given `template` and returns the source of a function that, | |||||
| * with the proper arguments, will render the template. Recognized options | |||||
| * include the following: | |||||
| * | |||||
| * - file The name of the file the template comes from (displayed in | |||||
| * error messages) | |||||
| * - tags An array of open and close tags the `template` uses. Defaults | |||||
| * to the value of Mustache.tags | |||||
| * - debug Set `true` to log the body of the generated function to the | |||||
| * console | |||||
| * - space Set `true` to preserve whitespace from lines that otherwise | |||||
| * contain only a {{tag}}. Defaults to `false` | |||||
| */ | |||||
| function parse(template, options) { | |||||
| options = options || {}; | |||||
| var tags = options.tags || exports.tags, | |||||
| openTag = tags[0], | |||||
| closeTag = tags[tags.length - 1]; | |||||
| var code = [ | |||||
| 'var buffer = "";', // output buffer | |||||
| "\nvar line = 1;", // keep track of source line number | |||||
| "\ntry {", | |||||
| '\nbuffer += "' | |||||
| ]; | |||||
| var spaces = [], // indices of whitespace in code on the current line | |||||
| hasTag = false, // is there a {{tag}} on the current line? | |||||
| nonSpace = false; // is there a non-space char on the current line? | |||||
| // Strips all space characters from the code array for the current line | |||||
| // if there was a {{tag}} on it and otherwise only spaces. | |||||
| var stripSpace = function () { | |||||
| if (hasTag && !nonSpace && !options.space) { | |||||
| while (spaces.length) { | |||||
| code.splice(spaces.pop(), 1); | |||||
| } | |||||
| } else { | |||||
| spaces = []; | |||||
| } | |||||
| hasTag = false; | |||||
| nonSpace = false; | |||||
| }; | |||||
| var sectionStack = [], updateLine, nextOpenTag, nextCloseTag; | |||||
| var setTags = function (source) { | |||||
| tags = trim(source).split(/\s+/); | |||||
| nextOpenTag = tags[0]; | |||||
| nextCloseTag = tags[tags.length - 1]; | |||||
| }; | |||||
| var includePartial = function (source) { | |||||
| code.push( | |||||
| '";', | |||||
| updateLine, | |||||
| '\nvar partial = partials["' + trim(source) + '"];', | |||||
| '\nif (partial) {', | |||||
| '\n buffer += render(partial,stack[stack.length - 1],partials);', | |||||
| '\n}', | |||||
| '\nbuffer += "' | |||||
| ); | |||||
| }; | |||||
| var openSection = function (source, inverted) { | |||||
| var name = trim(source); | |||||
| if (name === "") { | |||||
| throw debug(new Error("Section name may not be empty"), template, line, options.file); | |||||
| } | |||||
| sectionStack.push({name: name, inverted: inverted}); | |||||
| code.push( | |||||
| '";', | |||||
| updateLine, | |||||
| '\nvar name = "' + name + '";', | |||||
| '\nvar callback = (function () {', | |||||
| '\n return function () {', | |||||
| '\n var buffer = "";', | |||||
| '\nbuffer += "' | |||||
| ); | |||||
| }; | |||||
| var openInvertedSection = function (source) { | |||||
| openSection(source, true); | |||||
| }; | |||||
| var closeSection = function (source) { | |||||
| var name = trim(source); | |||||
| var openName = sectionStack.length != 0 && sectionStack[sectionStack.length - 1].name; | |||||
| if (!openName || name != openName) { | |||||
| throw debug(new Error('Section named "' + name + '" was never opened'), template, line, options.file); | |||||
| } | |||||
| var section = sectionStack.pop(); | |||||
| code.push( | |||||
| '";', | |||||
| '\n return buffer;', | |||||
| '\n };', | |||||
| '\n})();' | |||||
| ); | |||||
| if (section.inverted) { | |||||
| code.push("\nbuffer += renderSection(name,stack,callback,true);"); | |||||
| } else { | |||||
| code.push("\nbuffer += renderSection(name,stack,callback);"); | |||||
| } | |||||
| code.push('\nbuffer += "'); | |||||
| }; | |||||
| var sendPlain = function (source) { | |||||
| code.push( | |||||
| '";', | |||||
| updateLine, | |||||
| '\nbuffer += lookup("' + trim(source) + '",stack,"");', | |||||
| '\nbuffer += "' | |||||
| ); | |||||
| }; | |||||
| var sendEscaped = function (source) { | |||||
| code.push( | |||||
| '";', | |||||
| updateLine, | |||||
| '\nbuffer += escapeHTML(lookup("' + trim(source) + '",stack,""));', | |||||
| '\nbuffer += "' | |||||
| ); | |||||
| }; | |||||
| var line = 1, c, callback; | |||||
| for (var i = 0, len = template.length; i < len; ++i) { | |||||
| if (template.slice(i, i + openTag.length) === openTag) { | |||||
| i += openTag.length; | |||||
| c = template.substr(i, 1); | |||||
| updateLine = '\nline = ' + line + ';'; | |||||
| nextOpenTag = openTag; | |||||
| nextCloseTag = closeTag; | |||||
| hasTag = true; | |||||
| switch (c) { | |||||
| case "!": // comment | |||||
| i++; | |||||
| callback = null; | |||||
| break; | |||||
| case "=": // change open/close tags, e.g. {{=<% %>=}} | |||||
| i++; | |||||
| closeTag = "=" + closeTag; | |||||
| callback = setTags; | |||||
| break; | |||||
| case ">": // include partial | |||||
| i++; | |||||
| callback = includePartial; | |||||
| break; | |||||
| case "#": // start section | |||||
| i++; | |||||
| callback = openSection; | |||||
| break; | |||||
| case "^": // start inverted section | |||||
| i++; | |||||
| callback = openInvertedSection; | |||||
| break; | |||||
| case "/": // end section | |||||
| i++; | |||||
| callback = closeSection; | |||||
| break; | |||||
| case "{": // plain variable | |||||
| closeTag = "}" + closeTag; | |||||
| // fall through | |||||
| case "&": // plain variable | |||||
| i++; | |||||
| nonSpace = true; | |||||
| callback = sendPlain; | |||||
| break; | |||||
| default: // escaped variable | |||||
| nonSpace = true; | |||||
| callback = sendEscaped; | |||||
| } | |||||
| var end = template.indexOf(closeTag, i); | |||||
| if (end === -1) { | |||||
| throw debug(new Error('Tag "' + openTag + '" was not closed properly'), template, line, options.file); | |||||
| } | |||||
| var source = template.substring(i, end); | |||||
| if (callback) { | |||||
| callback(source); | |||||
| } | |||||
| // Maintain line count for \n in source. | |||||
| var n = 0; | |||||
| while (~(n = source.indexOf("\n", n))) { | |||||
| line++; | |||||
| n++; | |||||
| } | |||||
| i = end + closeTag.length - 1; | |||||
| openTag = nextOpenTag; | |||||
| closeTag = nextCloseTag; | |||||
| } else { | |||||
| c = template.substr(i, 1); | |||||
| switch (c) { | |||||
| case '"': | |||||
| case "\\": | |||||
| nonSpace = true; | |||||
| code.push("\\" + c); | |||||
| break; | |||||
| case "\r": | |||||
| // Ignore carriage returns. | |||||
| break; | |||||
| case "\n": | |||||
| spaces.push(code.length); | |||||
| code.push("\\n"); | |||||
| stripSpace(); // Check for whitespace on the current line. | |||||
| line++; | |||||
| break; | |||||
| default: | |||||
| if (isWhitespace(c)) { | |||||
| spaces.push(code.length); | |||||
| } else { | |||||
| nonSpace = true; | |||||
| } | |||||
| code.push(c); | |||||
| } | |||||
| } | |||||
| } | |||||
| if (sectionStack.length != 0) { | |||||
| throw debug(new Error('Section "' + sectionStack[sectionStack.length - 1].name + '" was not closed properly'), template, line, options.file); | |||||
| } | |||||
| // Clean up any whitespace from a closing {{tag}} that was at the end | |||||
| // of the template without a trailing \n. | |||||
| stripSpace(); | |||||
| code.push( | |||||
| '";', | |||||
| "\nreturn buffer;", | |||||
| "\n} catch (e) { throw {error: e, line: line}; }" | |||||
| ); | |||||
| // Ignore `buffer += "";` statements. | |||||
| var body = code.join("").replace(/buffer \+= "";\n/g, ""); | |||||
| if (options.debug) { | |||||
| if (typeof console != "undefined" && console.log) { | |||||
| console.log(body); | |||||
| } else if (typeof print === "function") { | |||||
| print(body); | |||||
| } | |||||
| } | |||||
| return body; | |||||
| } | |||||
| /** | |||||
| * Used by `compile` to generate a reusable function for the given `template`. | |||||
| */ | |||||
| function _compile(template, options) { | |||||
| var args = "view,partials,stack,lookup,escapeHTML,renderSection,render"; | |||||
| var body = parse(template, options); | |||||
| var fn = new Function(args, body); | |||||
| // This anonymous function wraps the generated function so we can do | |||||
| // argument coercion, setup some variables, and handle any errors | |||||
| // encountered while executing it. | |||||
| return function (view, partials) { | |||||
| partials = partials || {}; | |||||
| var stack = [view]; // context stack | |||||
| try { | |||||
| return fn(view, partials, stack, lookup, escapeHTML, renderSection, render); | |||||
| } catch (e) { | |||||
| throw debug(e.error, template, e.line, options.file); | |||||
| } | |||||
| }; | |||||
| } | |||||
| // Cache of pre-compiled templates. | |||||
| var _cache = {}; | |||||
| /** | |||||
| * Clear the cache of compiled templates. | |||||
| */ | |||||
| function clearCache() { | |||||
| _cache = {}; | |||||
| } | |||||
| /** | |||||
| * Compiles the given `template` into a reusable function using the given | |||||
| * `options`. In addition to the options accepted by Mustache.parse, | |||||
| * recognized options include the following: | |||||
| * | |||||
| * - cache Set `false` to bypass any pre-compiled version of the given | |||||
| * template. Otherwise, a given `template` string will be cached | |||||
| * the first time it is parsed | |||||
| */ | |||||
| function compile(template, options) { | |||||
| options = options || {}; | |||||
| // Use a pre-compiled version from the cache if we have one. | |||||
| if (options.cache !== false) { | |||||
| if (!_cache[template]) { | |||||
| _cache[template] = _compile(template, options); | |||||
| } | |||||
| return _cache[template]; | |||||
| } | |||||
| return _compile(template, options); | |||||
| } | |||||
| /** | |||||
| * High-level function that renders the given `template` using the given | |||||
| * `view` and `partials`. If you need to use any of the template options (see | |||||
| * `compile` above), you must compile in a separate step, and then call that | |||||
| * compiled function. | |||||
| */ | |||||
| function render(template, view, partials) { | |||||
| return compile(template)(view, partials); | |||||
| } | |||||
| })(Mustache); | |||||
| $.mustache = function (template, view, partials) { | |||||
| return Mustache.render(template, view, partials); | |||||
| }; | |||||
| $.fn.mustache = function (view, partials) { | |||||
| return $(this).map(function (i, elm) { | |||||
| var template = $(elm).html().trim(); | |||||
| var output = $.mustache(template, view, partials); | |||||
| return $(output).get(); | |||||
| }); | |||||
| }; | |||||
| })(jQuery); | |||||
| @@ -0,0 +1,6 @@ | |||||
| /* | |||||
| Shameless port of a shameless port | |||||
| @defunkt => @janl => @aq | |||||
| See http://github.com/defunkt/mustache for more info. | |||||
| */(function(a){var b=typeof module!="undefined"&&module.exports||{};(function(a){function i(a){return h.test(a)}function n(a){return String(a).replace(/&(?!\w+;)|[<>"']/g,function(a){return m[a]||a})}function o(a,b,c,d){d=d||"<template>";var e=b.split("\n"),f=Math.max(c-3,0),g=Math.min(e.length,c+3),h=e.slice(f,g),i;for(var j=0,k=h.length;j<k;++j)i=j+f+1,h[j]=(i===c?" >> ":" ")+h[j];return a.template=b,a.line=c,a.file=d,a.message=[d+":"+c,h.join("\n"),"",a.message].join("\n"),a}function p(a,b,c){if(a===".")return b[b.length-1];var d=a.split("."),e=d.length-1,f=d[e],g,h,i=b.length,j,k;while(i){k=b.slice(0),h=b[--i],j=0;while(j<e){h=h[d[j++]];if(h==null)break;k.push(h)}if(h&&typeof h=="object"&&f in h){g=h[f];break}}return typeof g=="function"&&(g=g.call(k[k.length-1])),g==null?c:g}function q(a,b,c,d){var e="",h=p(a,b);if(d){if(h==null||h===!1||f(h)&&h.length===0)e+=c()}else if(f(h))g(h,function(a){b.push(a),e+=c(),b.pop()});else if(typeof h=="object")b.push(h),e+=c(),b.pop();else if(typeof h=="function"){var i=b[b.length-1],j=function(a){return w(a,i)};e+=h.call(i,c(),j)||""}else h&&(e+=c());return e}function r(b,c){c=c||{};var d=c.tags||a.tags,e=d[0],f=d[d.length-1],g=['var buffer = "";',"\nvar line = 1;","\ntry {",'\nbuffer += "'],h=[],k=!1,l=!1,m=function(){if(k&&!l&&!c.space)while(h.length)g.splice(h.pop(),1);else h=[];k=!1,l=!1},n=[],p,q,r,s=function(a){d=j(a).split(/\s+/),q=d[0],r=d[d.length-1]},t=function(a){g.push('";',p,'\nvar partial = partials["'+j(a)+'"];',"\nif (partial) {","\n buffer += render(partial,stack[stack.length - 1],partials);","\n}",'\nbuffer += "')},u=function(a,d){var e=j(a);if(e==="")throw o(new Error("Section name may not be empty"),b,z,c.file);n.push({name:e,inverted:d}),g.push('";',p,'\nvar name = "'+e+'";',"\nvar callback = (function () {","\n return function () {",'\n var buffer = "";','\nbuffer += "')},v=function(a){u(a,!0)},w=function(a){var d=j(a),e=n.length!=0&&n[n.length-1].name;if(!e||d!=e)throw o(new Error('Section named "'+d+'" was never opened'),b,z,c.file);var f=n.pop();g.push('";',"\n return buffer;","\n };","\n})();"),f.inverted?g.push("\nbuffer += renderSection(name,stack,callback,true);"):g.push("\nbuffer += renderSection(name,stack,callback);"),g.push('\nbuffer += "')},x=function(a){g.push('";',p,'\nbuffer += lookup("'+j(a)+'",stack,"");','\nbuffer += "')},y=function(a){g.push('";',p,'\nbuffer += escapeHTML(lookup("'+j(a)+'",stack,""));','\nbuffer += "')},z=1,A,B;for(var C=0,D=b.length;C<D;++C)if(b.slice(C,C+e.length)===e){C+=e.length,A=b.substr(C,1),p="\nline = "+z+";",q=e,r=f,k=!0;switch(A){case"!":C++,B=null;break;case"=":C++,f="="+f,B=s;break;case">":C++,B=t;break;case"#":C++,B=u;break;case"^":C++,B=v;break;case"/":C++,B=w;break;case"{":f="}"+f;case"&":C++,l=!0,B=x;break;default:l=!0,B=y}var E=b.indexOf(f,C);if(E===-1)throw o(new Error('Tag "'+e+'" was not closed properly'),b,z,c.file);var F=b.substring(C,E);B&&B(F);var G=0;while(~(G=F.indexOf("\n",G)))z++,G++;C=E+f.length-1,e=q,f=r}else{A=b.substr(C,1);switch(A){case'"':case"\\":l=!0,g.push("\\"+A);break;case"\r":break;case"\n":h.push(g.length),g.push("\\n"),m(),z++;break;default:i(A)?h.push(g.length):l=!0,g.push(A)}}if(n.length!=0)throw o(new Error('Section "'+n[n.length-1].name+'" was not closed properly'),b,z,c.file);m(),g.push('";',"\nreturn buffer;","\n} catch (e) { throw {error: e, line: line}; }");var H=g.join("").replace(/buffer \+= "";\n/g,"");return c.debug&&(typeof console!="undefined"&&console.log?console.log(H):typeof print=="function"&&print(H)),H}function s(a,b){var c="view,partials,stack,lookup,escapeHTML,renderSection,render",d=r(a,b),e=new Function(c,d);return function(c,d){d=d||{};var f=[c];try{return e(c,d,f,p,n,q,w)}catch(g){throw o(g.error,a,g.line,b.file)}}}function u(){t={}}function v(a,b){return b=b||{},b.cache!==!1?(t[a]||(t[a]=s(a,b)),t[a]):s(a,b)}function w(a,b,c){return v(a)(b,c)}a.name="mustache.js",a.version="0.5.14",a.tags=["{{","}}"],a.parse=r,a.compile=v,a.render=w,a.clearCache=u,a.to_html=function(a,b,c,d){var e=w(a,b,c);if(typeof d!="function")return e;d(e)};var b=Object.prototype.toString,c=Array.isArray,d=Array.prototype.forEach,e=String.prototype.trim,f;c?f=c:f=function(a){return b.call(a)==="[object Array]"};var g;d?g=function(a,b,c){return d.call(a,b,c)}:g=function(a,b,c){for(var d=0,e=a.length;d<e;++d)b.call(c,a[d],d,a)};var h=/^\s*$/,j;if(e)j=function(a){return a==null?"":e.call(a)};else{var k,l;i(" ")?(k=/^\s+/,l=/\s+$/):(k=/^[\s\xA0]+/,l=/[\s\xA0]+$/),j=function(a){return a==null?"":String(a).replace(k,"").replace(l,"")}}var m={"&":"&","<":"<",">":">",'"':""","'":"'"},t={}})(b),a.mustache=function(a,c,d){return b.render(a,c,d)},a.fn.mustache=function(b,c){return a(this).map(function(d,e){var f=a(e).html().trim(),g=a.mustache(f,b,c);return a(g).get()})}})(jQuery); | |||||
| @@ -0,0 +1,23 @@ | |||||
| The MIT License | |||||
| 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. | |||||
| @@ -0,0 +1,418 @@ | |||||
| # mustache.js - Logic-less {{mustache}} templates with JavaScript | |||||
| > What could be more logical awesome than no logic at all? | |||||
| [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. | |||||
| 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. | |||||
| 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? | |||||
| 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. | |||||
| ## 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 | |||||
| Below is quick example how to use mustache.js: | |||||
| var view = { | |||||
| title: "Joe", | |||||
| calc: function () { | |||||
| return 2 + 4; | |||||
| } | |||||
| }; | |||||
| var output = Mustache.render("{{title}} spends {{calc}}", 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. | |||||
| ### 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: | |||||
| var Mustache = require("mustache"); | |||||
| ## Templates | |||||
| 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. | |||||
| There are several types of tags available in mustache.js. | |||||
| ### Variables | |||||
| 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. | |||||
| 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: | |||||
| * {{name}} | |||||
| * {{age}} | |||||
| * {{company}} | |||||
| * {{{company}}} | |||||
| * {{&company}} | |||||
| View: | |||||
| { | |||||
| "name": "Chris", | |||||
| "company": "<b>GitHub</b>" | |||||
| } | |||||
| Output: | |||||
| * Chris | |||||
| * | |||||
| * <b>GitHub</b> | |||||
| * <b>GitHub</b> | |||||
| * <b>GitHub</b> | |||||
| JavaScript's dot notation may be used to access keys that are properties of | |||||
| objects in a view. | |||||
| Template: | |||||
| * {{name.first}} {{name.last}} | |||||
| * {{age}} | |||||
| View: | |||||
| { | |||||
| "name": { | |||||
| "first": "Michael", | |||||
| "last": "Jackson" | |||||
| }, | |||||
| "age": "RIP" | |||||
| } | |||||
| Output: | |||||
| * Michael Jackson | |||||
| * RIP | |||||
| ### Sections | |||||
| Sections render blocks of text one or more times, depending on the value of the | |||||
| key in the current context. | |||||
| 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. | |||||
| #### False Values or Empty Lists | |||||
| 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. | |||||
| Template: | |||||
| Shown. | |||||
| {{#nothin}} | |||||
| Never shown! | |||||
| {{/nothin}} | |||||
| View: | |||||
| { | |||||
| "person": true | |||||
| } | |||||
| Output: | |||||
| Shown. | |||||
| #### Non-Empty Lists | |||||
| 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. | |||||
| 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. | |||||
| Template: | |||||
| {{#stooges}} | |||||
| <b>{{name}}</b> | |||||
| {{/stooges}} | |||||
| View: | |||||
| { | |||||
| "stooges": [ | |||||
| { "name": "Moe" }, | |||||
| { "name": "Larry" }, | |||||
| { "name": "Curly" } | |||||
| ] | |||||
| } | |||||
| Output: | |||||
| <b>Moe</b> | |||||
| <b>Larry</b> | |||||
| <b>Curly</b> | |||||
| When looping over an array of strings, a `.` can be used to refer to the current | |||||
| item in the list. | |||||
| Template: | |||||
| {{#musketeers}} | |||||
| * {{.}} | |||||
| {{/musketeers}} | |||||
| View: | |||||
| { | |||||
| "musketeers": ["Athos", "Aramis", "Porthos", "D'Artagnan"] | |||||
| } | |||||
| Output: | |||||
| * Athos | |||||
| * Aramis | |||||
| * Porthos | |||||
| * D'Artagnan | |||||
| 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: | |||||
| {{#beatles}} | |||||
| * {{name}} | |||||
| {{/beatles}} | |||||
| View: | |||||
| { | |||||
| "beatles": [ | |||||
| { "firstName": "John", "lastName": "Lennon" }, | |||||
| { "firstName": "Paul", "lastName": "McCartney" }, | |||||
| { "firstName": "George", "lastName": "Harrison" }, | |||||
| { "firstName": "Ringo", "lastName": "Starr" } | |||||
| ], | |||||
| "name": function () { | |||||
| return this.firstName + " " + this.lastName; | |||||
| } | |||||
| } | |||||
| Output: | |||||
| * John Lennon | |||||
| * Paul McCartney | |||||
| * George Harrison | |||||
| * Ringo Starr | |||||
| #### Functions | |||||
| 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: | |||||
| {{#bold}}Hi {{name}}.{{/bold}} | |||||
| View: | |||||
| { | |||||
| "name": "Tater", | |||||
| "bold": function () { | |||||
| return function (text, render) { | |||||
| return "<b>" + render(text) + "</b>"; | |||||
| } | |||||
| } | |||||
| } | |||||
| Output: | |||||
| <b>Hi Tater.</b> | |||||
| ### Inverted Sections | |||||
| 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. | |||||
| Template: | |||||
| {{#repos}}<b>{{name}}</b>{{/repos}} | |||||
| {{^repos}}No repos :({{/repos}} | |||||
| View: | |||||
| { | |||||
| "repos": [] | |||||
| } | |||||
| Output: | |||||
| No repos :( | |||||
| ### Comments | |||||
| Comments begin with a bang and are ignored. The following template: | |||||
| <h1>Today{{! ignore me }}.</h1> | |||||
| Will render as follows: | |||||
| <h1>Today.</h1> | |||||
| Comments may contain newlines. | |||||
| ### Partials | |||||
| Partials begin with a greater than sign, like {{> box}}. | |||||
| Partials are rendered at runtime (as opposed to compile time), so recursive | |||||
| partials are possible. Just avoid infinite loops. | |||||
| They also inherit the calling context. Whereas in ERB you may have this: | |||||
| <%= partial :next_more, :start => start, :size => size %> | |||||
| Mustache requires only this: | |||||
| {{> next_more}} | |||||
| 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. | |||||
| For example, this template and partial: | |||||
| base.mustache: | |||||
| <h2>Names</h2> | |||||
| {{#names}} | |||||
| {{> user}} | |||||
| {{/names}} | |||||
| user.mustache: | |||||
| <strong>{{name}}</strong> | |||||
| Can be thought of as a single, expanded template: | |||||
| <h2>Names</h2> | |||||
| {{#names}} | |||||
| <strong>{{name}}</strong> | |||||
| {{/names}} | |||||
| 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. | |||||
| ### Set Delimiter | |||||
| Set Delimiter tags start with an equals sign and change the tag delimiters from | |||||
| `{{` and `}}` to custom strings. | |||||
| Consider the following contrived example: | |||||
| * {{ default_tags }} | |||||
| {{=<% %>=}} | |||||
| * <% erb_style_tags %> | |||||
| <%={{ }}=%> | |||||
| * {{ default_tags_again }} | |||||
| 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. | |||||
| 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." | |||||
| Custom delimiters may not contain whitespace or the equals sign. | |||||
| ## Streaming | |||||
| To stream template results out of mustache.js, you can pass an optional callback | |||||
| to the call to `Mustache.render`: | |||||
| Mustache.render(template, view, partials, function (chunk) { | |||||
| print(chunk); | |||||
| }); | |||||
| 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 | |||||
| @@ -0,0 +1,543 @@ | |||||
| (function(){ | |||||
| /*! | |||||
| * 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.14"; | |||||
| 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; | |||||
| } | |||||
| }; | |||||
| 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]+$/; | |||||
| } | |||||
| trim = function (string) { | |||||
| return string == null ? "" : | |||||
| String(string).replace(trimLeft, "").replace(trimRight, ""); | |||||
| }; | |||||
| } | |||||
| var escapeMap = { | |||||
| "&": "&", | |||||
| "<": "<", | |||||
| ">": ">", | |||||
| '"': '"', | |||||
| "'": ''' | |||||
| }; | |||||
| 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 || "<template>"; | |||||
| var lines = template.split("\n"), | |||||
| start = Math.max(line - 3, 0), | |||||
| end = Math.min(lines.length, line + 3), | |||||
| context = lines.slice(start, end); | |||||
| var c; | |||||
| for (var i = 0, len = context.length; i < len; ++i) { | |||||
| c = i + start + 1; | |||||
| context[i] = (c === line ? " >> " : " ") + context[i]; | |||||
| } | |||||
| e.template = template; | |||||
| e.line = line; | |||||
| e.file = file; | |||||
| e.message = [file + ":" + line, context.join("\n"), "", e.message].join("\n"); | |||||
| return e; | |||||
| } | |||||
| /** | |||||
| * Looks up the value of the given `name` in the given context `stack`. | |||||
| */ | |||||
| function lookup(name, stack, defaultValue) { | |||||
| if (name === ".") { | |||||
| return stack[stack.length - 1]; | |||||
| } | |||||
| var names = name.split("."); | |||||
| var lastIndex = names.length - 1; | |||||
| var target = names[lastIndex]; | |||||
| var value, context, i = stack.length, j, localStack; | |||||
| while (i) { | |||||
| localStack = stack.slice(0); | |||||
| context = stack[--i]; | |||||
| j = 0; | |||||
| while (j < lastIndex) { | |||||
| context = context[names[j++]]; | |||||
| if (context == null) { | |||||
| break; | |||||
| } | |||||
| localStack.push(context); | |||||
| } | |||||
| if (context && typeof context === "object" && target in context) { | |||||
| value = context[target]; | |||||
| break; | |||||
| } | |||||
| } | |||||
| // If the value is a function, call it in the current context. | |||||
| if (typeof value === "function") { | |||||
| value = value.call(localStack[localStack.length - 1]); | |||||
| } | |||||
| if (value == null) { | |||||
| return defaultValue; | |||||
| } | |||||
| return value; | |||||
| } | |||||
| function renderSection(name, stack, callback, inverted) { | |||||
| var buffer = ""; | |||||
| var value = lookup(name, stack); | |||||
| if (inverted) { | |||||
| // From the spec: inverted sections may render text once based on the | |||||
| // inverse value of the key. That is, they will be rendered if the key | |||||
| // doesn't exist, is false, or is an empty list. | |||||
| if (value == null || value === false || (isArray(value) && value.length === 0)) { | |||||
| buffer += callback(); | |||||
| } | |||||
| } else if (isArray(value)) { | |||||
| forEach(value, function (value) { | |||||
| stack.push(value); | |||||
| buffer += callback(); | |||||
| stack.pop(); | |||||
| }); | |||||
| } else if (typeof value === "object") { | |||||
| stack.push(value); | |||||
| buffer += callback(); | |||||
| stack.pop(); | |||||
| } else if (typeof value === "function") { | |||||
| var scope = stack[stack.length - 1]; | |||||
| var scopedRender = function (template) { | |||||
| return render(template, scope); | |||||
| }; | |||||
| buffer += value.call(scope, callback(), scopedRender) || ""; | |||||
| } else if (value) { | |||||
| buffer += callback(); | |||||
| } | |||||
| return buffer; | |||||
| } | |||||
| /** | |||||
| * Parses the given `template` and returns the source of a function that, | |||||
| * with the proper arguments, will render the template. Recognized options | |||||
| * include the following: | |||||
| * | |||||
| * - file The name of the file the template comes from (displayed in | |||||
| * error messages) | |||||
| * - tags An array of open and close tags the `template` uses. Defaults | |||||
| * to the value of Mustache.tags | |||||
| * - debug Set `true` to log the body of the generated function to the | |||||
| * console | |||||
| * - space Set `true` to preserve whitespace from lines that otherwise | |||||
| * contain only a {{tag}}. Defaults to `false` | |||||
| */ | |||||
| function parse(template, options) { | |||||
| options = options || {}; | |||||
| var tags = options.tags || exports.tags, | |||||
| openTag = tags[0], | |||||
| closeTag = tags[tags.length - 1]; | |||||
| var code = [ | |||||
| 'var buffer = "";', // output buffer | |||||
| "\nvar line = 1;", // keep track of source line number | |||||
| "\ntry {", | |||||
| '\nbuffer += "' | |||||
| ]; | |||||
| var spaces = [], // indices of whitespace in code on the current line | |||||
| hasTag = false, // is there a {{tag}} on the current line? | |||||
| nonSpace = false; // is there a non-space char on the current line? | |||||
| // Strips all space characters from the code array for the current line | |||||
| // if there was a {{tag}} on it and otherwise only spaces. | |||||
| var stripSpace = function () { | |||||
| if (hasTag && !nonSpace && !options.space) { | |||||
| while (spaces.length) { | |||||
| code.splice(spaces.pop(), 1); | |||||
| } | |||||
| } else { | |||||
| spaces = []; | |||||
| } | |||||
| hasTag = false; | |||||
| nonSpace = false; | |||||
| }; | |||||
| var sectionStack = [], updateLine, nextOpenTag, nextCloseTag; | |||||
| var setTags = function (source) { | |||||
| tags = trim(source).split(/\s+/); | |||||
| nextOpenTag = tags[0]; | |||||
| nextCloseTag = tags[tags.length - 1]; | |||||
| }; | |||||
| var includePartial = function (source) { | |||||
| code.push( | |||||
| '";', | |||||
| updateLine, | |||||
| '\nvar partial = partials["' + trim(source) + '"];', | |||||
| '\nif (partial) {', | |||||
| '\n buffer += render(partial,stack[stack.length - 1],partials);', | |||||
| '\n}', | |||||
| '\nbuffer += "' | |||||
| ); | |||||
| }; | |||||
| var openSection = function (source, inverted) { | |||||
| var name = trim(source); | |||||
| if (name === "") { | |||||
| throw debug(new Error("Section name may not be empty"), template, line, options.file); | |||||
| } | |||||
| sectionStack.push({name: name, inverted: inverted}); | |||||
| code.push( | |||||
| '";', | |||||
| updateLine, | |||||
| '\nvar name = "' + name + '";', | |||||
| '\nvar callback = (function () {', | |||||
| '\n return function () {', | |||||
| '\n var buffer = "";', | |||||
| '\nbuffer += "' | |||||
| ); | |||||
| }; | |||||
| var openInvertedSection = function (source) { | |||||
| openSection(source, true); | |||||
| }; | |||||
| var closeSection = function (source) { | |||||
| var name = trim(source); | |||||
| var openName = sectionStack.length != 0 && sectionStack[sectionStack.length - 1].name; | |||||
| if (!openName || name != openName) { | |||||
| throw debug(new Error('Section named "' + name + '" was never opened'), template, line, options.file); | |||||
| } | |||||
| var section = sectionStack.pop(); | |||||
| code.push( | |||||
| '";', | |||||
| '\n return buffer;', | |||||
| '\n };', | |||||
| '\n})();' | |||||
| ); | |||||
| if (section.inverted) { | |||||
| code.push("\nbuffer += renderSection(name,stack,callback,true);"); | |||||
| } else { | |||||
| code.push("\nbuffer += renderSection(name,stack,callback);"); | |||||
| } | |||||
| code.push('\nbuffer += "'); | |||||
| }; | |||||
| var sendPlain = function (source) { | |||||
| code.push( | |||||
| '";', | |||||
| updateLine, | |||||
| '\nbuffer += lookup("' + trim(source) + '",stack,"");', | |||||
| '\nbuffer += "' | |||||
| ); | |||||
| }; | |||||
| var sendEscaped = function (source) { | |||||
| code.push( | |||||
| '";', | |||||
| updateLine, | |||||
| '\nbuffer += escapeHTML(lookup("' + trim(source) + '",stack,""));', | |||||
| '\nbuffer += "' | |||||
| ); | |||||
| }; | |||||
| var line = 1, c, callback; | |||||
| for (var i = 0, len = template.length; i < len; ++i) { | |||||
| if (template.slice(i, i + openTag.length) === openTag) { | |||||
| i += openTag.length; | |||||
| c = template.substr(i, 1); | |||||
| updateLine = '\nline = ' + line + ';'; | |||||
| nextOpenTag = openTag; | |||||
| nextCloseTag = closeTag; | |||||
| hasTag = true; | |||||
| switch (c) { | |||||
| case "!": // comment | |||||
| i++; | |||||
| callback = null; | |||||
| break; | |||||
| case "=": // change open/close tags, e.g. {{=<% %>=}} | |||||
| i++; | |||||
| closeTag = "=" + closeTag; | |||||
| callback = setTags; | |||||
| break; | |||||
| case ">": // include partial | |||||
| i++; | |||||
| callback = includePartial; | |||||
| break; | |||||
| case "#": // start section | |||||
| i++; | |||||
| callback = openSection; | |||||
| break; | |||||
| case "^": // start inverted section | |||||
| i++; | |||||
| callback = openInvertedSection; | |||||
| break; | |||||
| case "/": // end section | |||||
| i++; | |||||
| callback = closeSection; | |||||
| break; | |||||
| case "{": // plain variable | |||||
| closeTag = "}" + closeTag; | |||||
| // fall through | |||||
| case "&": // plain variable | |||||
| i++; | |||||
| nonSpace = true; | |||||
| callback = sendPlain; | |||||
| break; | |||||
| default: // escaped variable | |||||
| nonSpace = true; | |||||
| callback = sendEscaped; | |||||
| } | |||||
| var end = template.indexOf(closeTag, i); | |||||
| if (end === -1) { | |||||
| throw debug(new Error('Tag "' + openTag + '" was not closed properly'), template, line, options.file); | |||||
| } | |||||
| var source = template.substring(i, end); | |||||
| if (callback) { | |||||
| callback(source); | |||||
| } | |||||
| // Maintain line count for \n in source. | |||||
| var n = 0; | |||||
| while (~(n = source.indexOf("\n", n))) { | |||||
| line++; | |||||
| n++; | |||||
| } | |||||
| i = end + closeTag.length - 1; | |||||
| openTag = nextOpenTag; | |||||
| closeTag = nextCloseTag; | |||||
| } else { | |||||
| c = template.substr(i, 1); | |||||
| switch (c) { | |||||
| case '"': | |||||
| case "\\": | |||||
| nonSpace = true; | |||||
| code.push("\\" + c); | |||||
| break; | |||||
| case "\r": | |||||
| // Ignore carriage returns. | |||||
| break; | |||||
| case "\n": | |||||
| spaces.push(code.length); | |||||
| code.push("\\n"); | |||||
| stripSpace(); // Check for whitespace on the current line. | |||||
| line++; | |||||
| break; | |||||
| default: | |||||
| if (isWhitespace(c)) { | |||||
| spaces.push(code.length); | |||||
| } else { | |||||
| nonSpace = true; | |||||
| } | |||||
| code.push(c); | |||||
| } | |||||
| } | |||||
| } | |||||
| if (sectionStack.length != 0) { | |||||
| throw debug(new Error('Section "' + sectionStack[sectionStack.length - 1].name + '" was not closed properly'), template, line, options.file); | |||||
| } | |||||
| // Clean up any whitespace from a closing {{tag}} that was at the end | |||||
| // of the template without a trailing \n. | |||||
| stripSpace(); | |||||
| code.push( | |||||
| '";', | |||||
| "\nreturn buffer;", | |||||
| "\n} catch (e) { throw {error: e, line: line}; }" | |||||
| ); | |||||
| // Ignore `buffer += "";` statements. | |||||
| var body = code.join("").replace(/buffer \+= "";\n/g, ""); | |||||
| if (options.debug) { | |||||
| if (typeof console != "undefined" && console.log) { | |||||
| console.log(body); | |||||
| } else if (typeof print === "function") { | |||||
| print(body); | |||||
| } | |||||
| } | |||||
| return body; | |||||
| } | |||||
| /** | |||||
| * Used by `compile` to generate a reusable function for the given `template`. | |||||
| */ | |||||
| function _compile(template, options) { | |||||
| var args = "view,partials,stack,lookup,escapeHTML,renderSection,render"; | |||||
| var body = parse(template, options); | |||||
| var fn = new Function(args, body); | |||||
| // This anonymous function wraps the generated function so we can do | |||||
| // argument coercion, setup some variables, and handle any errors | |||||
| // encountered while executing it. | |||||
| return function (view, partials) { | |||||
| partials = partials || {}; | |||||
| var stack = [view]; // context stack | |||||
| try { | |||||
| return fn(view, partials, stack, lookup, escapeHTML, renderSection, render); | |||||
| } catch (e) { | |||||
| throw debug(e.error, template, e.line, options.file); | |||||
| } | |||||
| }; | |||||
| } | |||||
| // Cache of pre-compiled templates. | |||||
| var _cache = {}; | |||||
| /** | |||||
| * Clear the cache of compiled templates. | |||||
| */ | |||||
| function clearCache() { | |||||
| _cache = {}; | |||||
| } | |||||
| /** | |||||
| * Compiles the given `template` into a reusable function using the given | |||||
| * `options`. In addition to the options accepted by Mustache.parse, | |||||
| * recognized options include the following: | |||||
| * | |||||
| * - cache Set `false` to bypass any pre-compiled version of the given | |||||
| * template. Otherwise, a given `template` string will be cached | |||||
| * the first time it is parsed | |||||
| */ | |||||
| function compile(template, options) { | |||||
| options = options || {}; | |||||
| // Use a pre-compiled version from the cache if we have one. | |||||
| if (options.cache !== false) { | |||||
| if (!_cache[template]) { | |||||
| _cache[template] = _compile(template, options); | |||||
| } | |||||
| return _cache[template]; | |||||
| } | |||||
| return _compile(template, options); | |||||
| } | |||||
| /** | |||||
| * High-level function that renders the given `template` using the given | |||||
| * `view` and `partials`. If you need to use any of the template options (see | |||||
| * `compile` above), you must compile in a separate step, and then call that | |||||
| * compiled function. | |||||
| */ | |||||
| function render(template, view, partials) { | |||||
| return compile(template)(view, partials); | |||||
| } | |||||
| })(Mustache); | |||||
| Object.implement('mustache', function(view, partials){ | |||||
| return Mustache.render(view, this, partials); | |||||
| }); | |||||
| })(); | |||||
| @@ -0,0 +1 @@ | |||||
| (function(){var a=typeof module!="undefined"&&module.exports||{};(function(a){function i(a){return h.test(a)}function n(a){return String(a).replace(/&(?!\w+;)|[<>"']/g,function(a){return m[a]||a})}function o(a,b,c,d){d=d||"<template>";var e=b.split("\n"),f=Math.max(c-3,0),g=Math.min(e.length,c+3),h=e.slice(f,g),i;for(var j=0,k=h.length;j<k;++j)i=j+f+1,h[j]=(i===c?" >> ":" ")+h[j];return a.template=b,a.line=c,a.file=d,a.message=[d+":"+c,h.join("\n"),"",a.message].join("\n"),a}function p(a,b,c){if(a===".")return b[b.length-1];var d=a.split("."),e=d.length-1,f=d[e],g,h,i=b.length,j,k;while(i){k=b.slice(0),h=b[--i],j=0;while(j<e){h=h[d[j++]];if(h==null)break;k.push(h)}if(h&&typeof h=="object"&&f in h){g=h[f];break}}return typeof g=="function"&&(g=g.call(k[k.length-1])),g==null?c:g}function q(a,b,c,d){var e="",h=p(a,b);if(d){if(h==null||h===!1||f(h)&&h.length===0)e+=c()}else if(f(h))g(h,function(a){b.push(a),e+=c(),b.pop()});else if(typeof h=="object")b.push(h),e+=c(),b.pop();else if(typeof h=="function"){var i=b[b.length-1],j=function(a){return w(a,i)};e+=h.call(i,c(),j)||""}else h&&(e+=c());return e}function r(b,c){c=c||{};var d=c.tags||a.tags,e=d[0],f=d[d.length-1],g=['var buffer = "";',"\nvar line = 1;","\ntry {",'\nbuffer += "'],h=[],k=!1,l=!1,m=function(){if(k&&!l&&!c.space)while(h.length)g.splice(h.pop(),1);else h=[];k=!1,l=!1},n=[],p,q,r,s=function(a){d=j(a).split(/\s+/),q=d[0],r=d[d.length-1]},t=function(a){g.push('";',p,'\nvar partial = partials["'+j(a)+'"];',"\nif (partial) {","\n buffer += render(partial,stack[stack.length - 1],partials);","\n}",'\nbuffer += "')},u=function(a,d){var e=j(a);if(e==="")throw o(new Error("Section name may not be empty"),b,z,c.file);n.push({name:e,inverted:d}),g.push('";',p,'\nvar name = "'+e+'";',"\nvar callback = (function () {","\n return function () {",'\n var buffer = "";','\nbuffer += "')},v=function(a){u(a,!0)},w=function(a){var d=j(a),e=n.length!=0&&n[n.length-1].name;if(!e||d!=e)throw o(new Error('Section named "'+d+'" was never opened'),b,z,c.file);var f=n.pop();g.push('";',"\n return buffer;","\n };","\n})();"),f.inverted?g.push("\nbuffer += renderSection(name,stack,callback,true);"):g.push("\nbuffer += renderSection(name,stack,callback);"),g.push('\nbuffer += "')},x=function(a){g.push('";',p,'\nbuffer += lookup("'+j(a)+'",stack,"");','\nbuffer += "')},y=function(a){g.push('";',p,'\nbuffer += escapeHTML(lookup("'+j(a)+'",stack,""));','\nbuffer += "')},z=1,A,B;for(var C=0,D=b.length;C<D;++C)if(b.slice(C,C+e.length)===e){C+=e.length,A=b.substr(C,1),p="\nline = "+z+";",q=e,r=f,k=!0;switch(A){case"!":C++,B=null;break;case"=":C++,f="="+f,B=s;break;case">":C++,B=t;break;case"#":C++,B=u;break;case"^":C++,B=v;break;case"/":C++,B=w;break;case"{":f="}"+f;case"&":C++,l=!0,B=x;break;default:l=!0,B=y}var E=b.indexOf(f,C);if(E===-1)throw o(new Error('Tag "'+e+'" was not closed properly'),b,z,c.file);var F=b.substring(C,E);B&&B(F);var G=0;while(~(G=F.indexOf("\n",G)))z++,G++;C=E+f.length-1,e=q,f=r}else{A=b.substr(C,1);switch(A){case'"':case"\\":l=!0,g.push("\\"+A);break;case"\r":break;case"\n":h.push(g.length),g.push("\\n"),m(),z++;break;default:i(A)?h.push(g.length):l=!0,g.push(A)}}if(n.length!=0)throw o(new Error('Section "'+n[n.length-1].name+'" was not closed properly'),b,z,c.file);m(),g.push('";',"\nreturn buffer;","\n} catch (e) { throw {error: e, line: line}; }");var H=g.join("").replace(/buffer \+= "";\n/g,"");return c.debug&&(typeof console!="undefined"&&console.log?console.log(H):typeof print=="function"&&print(H)),H}function s(a,b){var c="view,partials,stack,lookup,escapeHTML,renderSection,render",d=r(a,b),e=new Function(c,d);return function(c,d){d=d||{};var f=[c];try{return e(c,d,f,p,n,q,w)}catch(g){throw o(g.error,a,g.line,b.file)}}}function u(){t={}}function v(a,b){return b=b||{},b.cache!==!1?(t[a]||(t[a]=s(a,b)),t[a]):s(a,b)}function w(a,b,c){return v(a)(b,c)}a.name="mustache.js",a.version="0.5.14",a.tags=["{{","}}"],a.parse=r,a.compile=v,a.render=w,a.clearCache=u,a.to_html=function(a,b,c,d){var e=w(a,b,c);if(typeof d!="function")return e;d(e)};var b=Object.prototype.toString,c=Array.isArray,d=Array.prototype.forEach,e=String.prototype.trim,f;c?f=c:f=function(a){return b.call(a)==="[object Array]"};var g;d?g=function(a,b,c){return d.call(a,b,c)}:g=function(a,b,c){for(var d=0,e=a.length;d<e;++d)b.call(c,a[d],d,a)};var h=/^\s*$/,j;if(e)j=function(a){return a==null?"":e.call(a)};else{var k,l;i(" ")?(k=/^\s+/,l=/\s+$/):(k=/^[\s\xA0]+/,l=/[\s\xA0]+$/),j=function(a){return a==null?"":String(a).replace(k,"").replace(l,"")}}var m={"&":"&","<":"<",">":">",'"':""","'":"'"},t={}})(a),Object.implement("mustache",function(b,c){return a.render(b,this,c)})})(); | |||||
| @@ -0,0 +1,23 @@ | |||||
| The MIT License | |||||
| 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. | |||||
| @@ -0,0 +1,418 @@ | |||||
| # mustache.js - Logic-less {{mustache}} templates with JavaScript | |||||
| > What could be more logical awesome than no logic at all? | |||||
| [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. | |||||
| 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. | |||||
| 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? | |||||
| 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. | |||||
| ## 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 | |||||
| Below is quick example how to use mustache.js: | |||||
| var view = { | |||||
| title: "Joe", | |||||
| calc: function () { | |||||
| return 2 + 4; | |||||
| } | |||||
| }; | |||||
| var output = Mustache.render("{{title}} spends {{calc}}", 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. | |||||
| ### 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: | |||||
| var Mustache = require("mustache"); | |||||
| ## Templates | |||||
| 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. | |||||
| There are several types of tags available in mustache.js. | |||||
| ### Variables | |||||
| 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. | |||||
| 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: | |||||
| * {{name}} | |||||
| * {{age}} | |||||
| * {{company}} | |||||
| * {{{company}}} | |||||
| * {{&company}} | |||||
| View: | |||||
| { | |||||
| "name": "Chris", | |||||
| "company": "<b>GitHub</b>" | |||||
| } | |||||
| Output: | |||||
| * Chris | |||||
| * | |||||
| * <b>GitHub</b> | |||||
| * <b>GitHub</b> | |||||
| * <b>GitHub</b> | |||||
| JavaScript's dot notation may be used to access keys that are properties of | |||||
| objects in a view. | |||||
| Template: | |||||
| * {{name.first}} {{name.last}} | |||||
| * {{age}} | |||||
| View: | |||||
| { | |||||
| "name": { | |||||
| "first": "Michael", | |||||
| "last": "Jackson" | |||||
| }, | |||||
| "age": "RIP" | |||||
| } | |||||
| Output: | |||||
| * Michael Jackson | |||||
| * RIP | |||||
| ### Sections | |||||
| Sections render blocks of text one or more times, depending on the value of the | |||||
| key in the current context. | |||||
| 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. | |||||
| #### False Values or Empty Lists | |||||
| 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. | |||||
| Template: | |||||
| Shown. | |||||
| {{#nothin}} | |||||
| Never shown! | |||||
| {{/nothin}} | |||||
| View: | |||||
| { | |||||
| "person": true | |||||
| } | |||||
| Output: | |||||
| Shown. | |||||
| #### Non-Empty Lists | |||||
| 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. | |||||
| 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. | |||||
| Template: | |||||
| {{#stooges}} | |||||
| <b>{{name}}</b> | |||||
| {{/stooges}} | |||||
| View: | |||||
| { | |||||
| "stooges": [ | |||||
| { "name": "Moe" }, | |||||
| { "name": "Larry" }, | |||||
| { "name": "Curly" } | |||||
| ] | |||||
| } | |||||
| Output: | |||||
| <b>Moe</b> | |||||
| <b>Larry</b> | |||||
| <b>Curly</b> | |||||
| When looping over an array of strings, a `.` can be used to refer to the current | |||||
| item in the list. | |||||
| Template: | |||||
| {{#musketeers}} | |||||
| * {{.}} | |||||
| {{/musketeers}} | |||||
| View: | |||||
| { | |||||
| "musketeers": ["Athos", "Aramis", "Porthos", "D'Artagnan"] | |||||
| } | |||||
| Output: | |||||
| * Athos | |||||
| * Aramis | |||||
| * Porthos | |||||
| * D'Artagnan | |||||
| 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: | |||||
| {{#beatles}} | |||||
| * {{name}} | |||||
| {{/beatles}} | |||||
| View: | |||||
| { | |||||
| "beatles": [ | |||||
| { "firstName": "John", "lastName": "Lennon" }, | |||||
| { "firstName": "Paul", "lastName": "McCartney" }, | |||||
| { "firstName": "George", "lastName": "Harrison" }, | |||||
| { "firstName": "Ringo", "lastName": "Starr" } | |||||
| ], | |||||
| "name": function () { | |||||
| return this.firstName + " " + this.lastName; | |||||
| } | |||||
| } | |||||
| Output: | |||||
| * John Lennon | |||||
| * Paul McCartney | |||||
| * George Harrison | |||||
| * Ringo Starr | |||||
| #### Functions | |||||
| 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: | |||||
| {{#bold}}Hi {{name}}.{{/bold}} | |||||
| View: | |||||
| { | |||||
| "name": "Tater", | |||||
| "bold": function () { | |||||
| return function (text, render) { | |||||
| return "<b>" + render(text) + "</b>"; | |||||
| } | |||||
| } | |||||
| } | |||||
| Output: | |||||
| <b>Hi Tater.</b> | |||||
| ### Inverted Sections | |||||
| 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. | |||||
| Template: | |||||
| {{#repos}}<b>{{name}}</b>{{/repos}} | |||||
| {{^repos}}No repos :({{/repos}} | |||||
| View: | |||||
| { | |||||
| "repos": [] | |||||
| } | |||||
| Output: | |||||
| No repos :( | |||||
| ### Comments | |||||
| Comments begin with a bang and are ignored. The following template: | |||||
| <h1>Today{{! ignore me }}.</h1> | |||||
| Will render as follows: | |||||
| <h1>Today.</h1> | |||||
| Comments may contain newlines. | |||||
| ### Partials | |||||
| Partials begin with a greater than sign, like {{> box}}. | |||||
| Partials are rendered at runtime (as opposed to compile time), so recursive | |||||
| partials are possible. Just avoid infinite loops. | |||||
| They also inherit the calling context. Whereas in ERB you may have this: | |||||
| <%= partial :next_more, :start => start, :size => size %> | |||||
| Mustache requires only this: | |||||
| {{> next_more}} | |||||
| 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. | |||||
| For example, this template and partial: | |||||
| base.mustache: | |||||
| <h2>Names</h2> | |||||
| {{#names}} | |||||
| {{> user}} | |||||
| {{/names}} | |||||
| user.mustache: | |||||
| <strong>{{name}}</strong> | |||||
| Can be thought of as a single, expanded template: | |||||
| <h2>Names</h2> | |||||
| {{#names}} | |||||
| <strong>{{name}}</strong> | |||||
| {{/names}} | |||||
| 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. | |||||
| ### Set Delimiter | |||||
| Set Delimiter tags start with an equals sign and change the tag delimiters from | |||||
| `{{` and `}}` to custom strings. | |||||
| Consider the following contrived example: | |||||
| * {{ default_tags }} | |||||
| {{=<% %>=}} | |||||
| * <% erb_style_tags %> | |||||
| <%={{ }}=%> | |||||
| * {{ default_tags_again }} | |||||
| 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. | |||||
| 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." | |||||
| Custom delimiters may not contain whitespace or the equals sign. | |||||
| ## Streaming | |||||
| To stream template results out of mustache.js, you can pass an optional callback | |||||
| to the call to `Mustache.render`: | |||||
| Mustache.render(template, view, partials, function (chunk) { | |||||
| print(chunk); | |||||
| }); | |||||
| 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 | |||||
| @@ -0,0 +1,671 @@ | |||||
| /* ************************************************************************ | |||||
| 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.5.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. | |||||
| */ | |||||
| /*! | |||||
| * 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.14"; | |||||
| 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; | |||||
| } | |||||
| }; | |||||
| 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]+$/; | |||||
| } | |||||
| trim = function (string) { | |||||
| return string == null ? "" : | |||||
| String(string).replace(trimLeft, "").replace(trimRight, ""); | |||||
| }; | |||||
| } | |||||
| var escapeMap = { | |||||
| "&": "&", | |||||
| "<": "<", | |||||
| ">": ">", | |||||
| '"': '"', | |||||
| "'": ''' | |||||
| }; | |||||
| 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 || "<template>"; | |||||
| var lines = template.split("\n"), | |||||
| start = Math.max(line - 3, 0), | |||||
| end = Math.min(lines.length, line + 3), | |||||
| context = lines.slice(start, end); | |||||
| var c; | |||||
| for (var i = 0, len = context.length; i < len; ++i) { | |||||
| c = i + start + 1; | |||||
| context[i] = (c === line ? " >> " : " ") + context[i]; | |||||
| } | |||||
| e.template = template; | |||||
| e.line = line; | |||||
| e.file = file; | |||||
| e.message = [file + ":" + line, context.join("\n"), "", e.message].join("\n"); | |||||
| return e; | |||||
| } | |||||
| /** | |||||
| * Looks up the value of the given `name` in the given context `stack`. | |||||
| */ | |||||
| function lookup(name, stack, defaultValue) { | |||||
| if (name === ".") { | |||||
| return stack[stack.length - 1]; | |||||
| } | |||||
| var names = name.split("."); | |||||
| var lastIndex = names.length - 1; | |||||
| var target = names[lastIndex]; | |||||
| var value, context, i = stack.length, j, localStack; | |||||
| while (i) { | |||||
| localStack = stack.slice(0); | |||||
| context = stack[--i]; | |||||
| j = 0; | |||||
| while (j < lastIndex) { | |||||
| context = context[names[j++]]; | |||||
| if (context == null) { | |||||
| break; | |||||
| } | |||||
| localStack.push(context); | |||||
| } | |||||
| if (context && typeof context === "object" && target in context) { | |||||
| value = context[target]; | |||||
| break; | |||||
| } | |||||
| } | |||||
| // If the value is a function, call it in the current context. | |||||
| if (typeof value === "function") { | |||||
| value = value.call(localStack[localStack.length - 1]); | |||||
| } | |||||
| if (value == null) { | |||||
| return defaultValue; | |||||
| } | |||||
| return value; | |||||
| } | |||||
| function renderSection(name, stack, callback, inverted) { | |||||
| var buffer = ""; | |||||
| var value = lookup(name, stack); | |||||
| if (inverted) { | |||||
| // From the spec: inverted sections may render text once based on the | |||||
| // inverse value of the key. That is, they will be rendered if the key | |||||
| // doesn't exist, is false, or is an empty list. | |||||
| if (value == null || value === false || (isArray(value) && value.length === 0)) { | |||||
| buffer += callback(); | |||||
| } | |||||
| } else if (isArray(value)) { | |||||
| forEach(value, function (value) { | |||||
| stack.push(value); | |||||
| buffer += callback(); | |||||
| stack.pop(); | |||||
| }); | |||||
| } else if (typeof value === "object") { | |||||
| stack.push(value); | |||||
| buffer += callback(); | |||||
| stack.pop(); | |||||
| } else if (typeof value === "function") { | |||||
| var scope = stack[stack.length - 1]; | |||||
| var scopedRender = function (template) { | |||||
| return render(template, scope); | |||||
| }; | |||||
| buffer += value.call(scope, callback(), scopedRender) || ""; | |||||
| } else if (value) { | |||||
| buffer += callback(); | |||||
| } | |||||
| return buffer; | |||||
| } | |||||
| /** | |||||
| * Parses the given `template` and returns the source of a function that, | |||||
| * with the proper arguments, will render the template. Recognized options | |||||
| * include the following: | |||||
| * | |||||
| * - file The name of the file the template comes from (displayed in | |||||
| * error messages) | |||||
| * - tags An array of open and close tags the `template` uses. Defaults | |||||
| * to the value of Mustache.tags | |||||
| * - debug Set `true` to log the body of the generated function to the | |||||
| * console | |||||
| * - space Set `true` to preserve whitespace from lines that otherwise | |||||
| * contain only a {{tag}}. Defaults to `false` | |||||
| */ | |||||
| function parse(template, options) { | |||||
| options = options || {}; | |||||
| var tags = options.tags || exports.tags, | |||||
| openTag = tags[0], | |||||
| closeTag = tags[tags.length - 1]; | |||||
| var code = [ | |||||
| 'var buffer = "";', // output buffer | |||||
| "\nvar line = 1;", // keep track of source line number | |||||
| "\ntry {", | |||||
| '\nbuffer += "' | |||||
| ]; | |||||
| var spaces = [], // indices of whitespace in code on the current line | |||||
| hasTag = false, // is there a {{tag}} on the current line? | |||||
| nonSpace = false; // is there a non-space char on the current line? | |||||
| // Strips all space characters from the code array for the current line | |||||
| // if there was a {{tag}} on it and otherwise only spaces. | |||||
| var stripSpace = function () { | |||||
| if (hasTag && !nonSpace && !options.space) { | |||||
| while (spaces.length) { | |||||
| code.splice(spaces.pop(), 1); | |||||
| } | |||||
| } else { | |||||
| spaces = []; | |||||
| } | |||||
| hasTag = false; | |||||
| nonSpace = false; | |||||
| }; | |||||
| var sectionStack = [], updateLine, nextOpenTag, nextCloseTag; | |||||
| var setTags = function (source) { | |||||
| tags = trim(source).split(/\s+/); | |||||
| nextOpenTag = tags[0]; | |||||
| nextCloseTag = tags[tags.length - 1]; | |||||
| }; | |||||
| var includePartial = function (source) { | |||||
| code.push( | |||||
| '";', | |||||
| updateLine, | |||||
| '\nvar partial = partials["' + trim(source) + '"];', | |||||
| '\nif (partial) {', | |||||
| '\n buffer += render(partial,stack[stack.length - 1],partials);', | |||||
| '\n}', | |||||
| '\nbuffer += "' | |||||
| ); | |||||
| }; | |||||
| var openSection = function (source, inverted) { | |||||
| var name = trim(source); | |||||
| if (name === "") { | |||||
| throw debug(new Error("Section name may not be empty"), template, line, options.file); | |||||
| } | |||||
| sectionStack.push({name: name, inverted: inverted}); | |||||
| code.push( | |||||
| '";', | |||||
| updateLine, | |||||
| '\nvar name = "' + name + '";', | |||||
| '\nvar callback = (function () {', | |||||
| '\n return function () {', | |||||
| '\n var buffer = "";', | |||||
| '\nbuffer += "' | |||||
| ); | |||||
| }; | |||||
| var openInvertedSection = function (source) { | |||||
| openSection(source, true); | |||||
| }; | |||||
| var closeSection = function (source) { | |||||
| var name = trim(source); | |||||
| var openName = sectionStack.length != 0 && sectionStack[sectionStack.length - 1].name; | |||||
| if (!openName || name != openName) { | |||||
| throw debug(new Error('Section named "' + name + '" was never opened'), template, line, options.file); | |||||
| } | |||||
| var section = sectionStack.pop(); | |||||
| code.push( | |||||
| '";', | |||||
| '\n return buffer;', | |||||
| '\n };', | |||||
| '\n})();' | |||||
| ); | |||||
| if (section.inverted) { | |||||
| code.push("\nbuffer += renderSection(name,stack,callback,true);"); | |||||
| } else { | |||||
| code.push("\nbuffer += renderSection(name,stack,callback);"); | |||||
| } | |||||
| code.push('\nbuffer += "'); | |||||
| }; | |||||
| var sendPlain = function (source) { | |||||
| code.push( | |||||
| '";', | |||||
| updateLine, | |||||
| '\nbuffer += lookup("' + trim(source) + '",stack,"");', | |||||
| '\nbuffer += "' | |||||
| ); | |||||
| }; | |||||
| var sendEscaped = function (source) { | |||||
| code.push( | |||||
| '";', | |||||
| updateLine, | |||||
| '\nbuffer += escapeHTML(lookup("' + trim(source) + '",stack,""));', | |||||
| '\nbuffer += "' | |||||
| ); | |||||
| }; | |||||
| var line = 1, c, callback; | |||||
| for (var i = 0, len = template.length; i < len; ++i) { | |||||
| if (template.slice(i, i + openTag.length) === openTag) { | |||||
| i += openTag.length; | |||||
| c = template.substr(i, 1); | |||||
| updateLine = '\nline = ' + line + ';'; | |||||
| nextOpenTag = openTag; | |||||
| nextCloseTag = closeTag; | |||||
| hasTag = true; | |||||
| switch (c) { | |||||
| case "!": // comment | |||||
| i++; | |||||
| callback = null; | |||||
| break; | |||||
| case "=": // change open/close tags, e.g. {{=<% %>=}} | |||||
| i++; | |||||
| closeTag = "=" + closeTag; | |||||
| callback = setTags; | |||||
| break; | |||||
| case ">": // include partial | |||||
| i++; | |||||
| callback = includePartial; | |||||
| break; | |||||
| case "#": // start section | |||||
| i++; | |||||
| callback = openSection; | |||||
| break; | |||||
| case "^": // start inverted section | |||||
| i++; | |||||
| callback = openInvertedSection; | |||||
| break; | |||||
| case "/": // end section | |||||
| i++; | |||||
| callback = closeSection; | |||||
| break; | |||||
| case "{": // plain variable | |||||
| closeTag = "}" + closeTag; | |||||
| // fall through | |||||
| case "&": // plain variable | |||||
| i++; | |||||
| nonSpace = true; | |||||
| callback = sendPlain; | |||||
| break; | |||||
| default: // escaped variable | |||||
| nonSpace = true; | |||||
| callback = sendEscaped; | |||||
| } | |||||
| var end = template.indexOf(closeTag, i); | |||||
| if (end === -1) { | |||||
| throw debug(new Error('Tag "' + openTag + '" was not closed properly'), template, line, options.file); | |||||
| } | |||||
| var source = template.substring(i, end); | |||||
| if (callback) { | |||||
| callback(source); | |||||
| } | |||||
| // Maintain line count for \n in source. | |||||
| var n = 0; | |||||
| while (~(n = source.indexOf("\n", n))) { | |||||
| line++; | |||||
| n++; | |||||
| } | |||||
| i = end + closeTag.length - 1; | |||||
| openTag = nextOpenTag; | |||||
| closeTag = nextCloseTag; | |||||
| } else { | |||||
| c = template.substr(i, 1); | |||||
| switch (c) { | |||||
| case '"': | |||||
| case "\\": | |||||
| nonSpace = true; | |||||
| code.push("\\" + c); | |||||
| break; | |||||
| case "\r": | |||||
| // Ignore carriage returns. | |||||
| break; | |||||
| case "\n": | |||||
| spaces.push(code.length); | |||||
| code.push("\\n"); | |||||
| stripSpace(); // Check for whitespace on the current line. | |||||
| line++; | |||||
| break; | |||||
| default: | |||||
| if (isWhitespace(c)) { | |||||
| spaces.push(code.length); | |||||
| } else { | |||||
| nonSpace = true; | |||||
| } | |||||
| code.push(c); | |||||
| } | |||||
| } | |||||
| } | |||||
| if (sectionStack.length != 0) { | |||||
| throw debug(new Error('Section "' + sectionStack[sectionStack.length - 1].name + '" was not closed properly'), template, line, options.file); | |||||
| } | |||||
| // Clean up any whitespace from a closing {{tag}} that was at the end | |||||
| // of the template without a trailing \n. | |||||
| stripSpace(); | |||||
| code.push( | |||||
| '";', | |||||
| "\nreturn buffer;", | |||||
| "\n} catch (e) { throw {error: e, line: line}; }" | |||||
| ); | |||||
| // Ignore `buffer += "";` statements. | |||||
| var body = code.join("").replace(/buffer \+= "";\n/g, ""); | |||||
| if (options.debug) { | |||||
| if (typeof console != "undefined" && console.log) { | |||||
| console.log(body); | |||||
| } else if (typeof print === "function") { | |||||
| print(body); | |||||
| } | |||||
| } | |||||
| return body; | |||||
| } | |||||
| /** | |||||
| * Used by `compile` to generate a reusable function for the given `template`. | |||||
| */ | |||||
| function _compile(template, options) { | |||||
| var args = "view,partials,stack,lookup,escapeHTML,renderSection,render"; | |||||
| var body = parse(template, options); | |||||
| var fn = new Function(args, body); | |||||
| // This anonymous function wraps the generated function so we can do | |||||
| // argument coercion, setup some variables, and handle any errors | |||||
| // encountered while executing it. | |||||
| return function (view, partials) { | |||||
| partials = partials || {}; | |||||
| var stack = [view]; // context stack | |||||
| try { | |||||
| return fn(view, partials, stack, lookup, escapeHTML, renderSection, render); | |||||
| } catch (e) { | |||||
| throw debug(e.error, template, e.line, options.file); | |||||
| } | |||||
| }; | |||||
| } | |||||
| // Cache of pre-compiled templates. | |||||
| var _cache = {}; | |||||
| /** | |||||
| * Clear the cache of compiled templates. | |||||
| */ | |||||
| function clearCache() { | |||||
| _cache = {}; | |||||
| } | |||||
| /** | |||||
| * Compiles the given `template` into a reusable function using the given | |||||
| * `options`. In addition to the options accepted by Mustache.parse, | |||||
| * recognized options include the following: | |||||
| * | |||||
| * - cache Set `false` to bypass any pre-compiled version of the given | |||||
| * template. Otherwise, a given `template` string will be cached | |||||
| * the first time it is parsed | |||||
| */ | |||||
| function compile(template, options) { | |||||
| options = options || {}; | |||||
| // Use a pre-compiled version from the cache if we have one. | |||||
| if (options.cache !== false) { | |||||
| if (!_cache[template]) { | |||||
| _cache[template] = _compile(template, options); | |||||
| } | |||||
| return _cache[template]; | |||||
| } | |||||
| return _compile(template, options); | |||||
| } | |||||
| /** | |||||
| * High-level function that renders the given `template` using the given | |||||
| * `view` and `partials`. If you need to use any of the template options (see | |||||
| * `compile` above), you must compile in a separate step, and then call that | |||||
| * compiled function. | |||||
| */ | |||||
| function render(template, view, partials) { | |||||
| return compile(template)(view, partials); | |||||
| } | |||||
| })(Mustache); | |||||
| /** | |||||
| * Above is the original mustache code. | |||||
| */ | |||||
| // EXPOSE qooxdoo variant | |||||
| qx.bom.Template.version = Mustache.version; | |||||
| qx.bom.Template.toHtml = Mustache.render; | |||||
| })(); | |||||
| @@ -0,0 +1,74 @@ | |||||
| /* ************************************************************************ | |||||
| 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.5.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:{version:null,toHtml:null,get:function(a,b,c){var d=document.getElementById(a),e=d.innerHTML;e=this.toHtml(e,b,c);var f=qx.bom.Element.create("div");return f.innerHTML=e,f.children[0]}}}),function(){var a=typeof module!="undefined"&&module.exports||{};(function(a){function i(a){return h.test(a)}function n(a){return String(a).replace(/&(?!\w+;)|[<>"']/g,function(a){return m[a]||a})}function o(a,b,c,d){d=d||"<template>";var e=b.split("\n"),f=Math.max(c-3,0),g=Math.min(e.length,c+3),h=e.slice(f,g),i;for(var j=0,k=h.length;j<k;++j)i=j+f+1,h[j]=(i===c?" >> ":" ")+h[j];return a.template=b,a.line=c,a.file=d,a.message=[d+":"+c,h.join("\n"),"",a.message].join("\n"),a}function p(a,b,c){if(a===".")return b[b.length-1];var d=a.split("."),e=d.length-1,f=d[e],g,h,i=b.length,j,k;while(i){k=b.slice(0),h=b[--i],j=0;while(j<e){h=h[d[j++]];if(h==null)break;k.push(h)}if(h&&typeof h=="object"&&f in h){g=h[f];break}}return typeof g=="function"&&(g=g.call(k[k.length-1])),g==null?c:g}function q(a,b,c,d){var e="",h=p(a,b);if(d){if(h==null||h===!1||f(h)&&h.length===0)e+=c()}else if(f(h))g(h,function(a){b.push(a),e+=c(),b.pop()});else if(typeof h=="object")b.push(h),e+=c(),b.pop();else if(typeof h=="function"){var i=b[b.length-1],j=function(a){return w(a,i)};e+=h.call(i,c(),j)||""}else h&&(e+=c());return e}function r(b,c){c=c||{};var d=c.tags||a.tags,e=d[0],f=d[d.length-1],g=['var buffer = "";',"\nvar line = 1;","\ntry {",'\nbuffer += "'],h=[],k=!1,l=!1,m=function(){if(k&&!l&&!c.space)while(h.length)g.splice(h.pop(),1);else h=[];k=!1,l=!1},n=[],p,q,r,s=function(a){d=j(a).split(/\s+/),q=d[0],r=d[d.length-1]},t=function(a){g.push('";',p,'\nvar partial = partials["'+j(a)+'"];',"\nif (partial) {","\n buffer += render(partial,stack[stack.length - 1],partials);","\n}",'\nbuffer += "')},u=function(a,d){var e=j(a);if(e==="")throw o(new Error("Section name may not be empty"),b,z,c.file);n.push({name:e,inverted:d}),g.push('";',p,'\nvar name = "'+e+'";',"\nvar callback = (function () {","\n return function () {",'\n var buffer = "";','\nbuffer += "')},v=function(a){u(a,!0)},w=function(a){var d=j(a),e=n.length!=0&&n[n.length-1].name;if(!e||d!=e)throw o(new Error('Section named "'+d+'" was never opened'),b,z,c.file);var f=n.pop();g.push('";',"\n return buffer;","\n };","\n})();"),f.inverted?g.push("\nbuffer += renderSection(name,stack,callback,true);"):g.push("\nbuffer += renderSection(name,stack,callback);"),g.push('\nbuffer += "')},x=function(a){g.push('";',p,'\nbuffer += lookup("'+j(a)+'",stack,"");','\nbuffer += "')},y=function(a){g.push('";',p,'\nbuffer += escapeHTML(lookup("'+j(a)+'",stack,""));','\nbuffer += "')},z=1,A,B;for(var C=0,D=b.length;C<D;++C)if(b.slice(C,C+e.length)===e){C+=e.length,A=b.substr(C,1),p="\nline = "+z+";",q=e,r=f,k=!0;switch(A){case"!":C++,B=null;break;case"=":C++,f="="+f,B=s;break;case">":C++,B=t;break;case"#":C++,B=u;break;case"^":C++,B=v;break;case"/":C++,B=w;break;case"{":f="}"+f;case"&":C++,l=!0,B=x;break;default:l=!0,B=y}var E=b.indexOf(f,C);if(E===-1)throw o(new Error('Tag "'+e+'" was not closed properly'),b,z,c.file);var F=b.substring(C,E);B&&B(F);var G=0;while(~(G=F.indexOf("\n",G)))z++,G++;C=E+f.length-1,e=q,f=r}else{A=b.substr(C,1);switch(A){case'"':case"\\":l=!0,g.push("\\"+A);break;case"\r":break;case"\n":h.push(g.length),g.push("\\n"),m(),z++;break;default:i(A)?h.push(g.length):l=!0,g.push(A)}}if(n.length!=0)throw o(new Error('Section "'+n[n.length-1].name+'" was not closed properly'),b,z,c.file);m(),g.push('";',"\nreturn buffer;","\n} catch (e) { throw {error: e, line: line}; }");var H=g.join("").replace(/buffer \+= "";\n/g,"");return c.debug&&(typeof console!="undefined"&&console.log?console.log(H):typeof print=="function"&&print(H)),H}function s(a,b){var c="view,partials,stack,lookup,escapeHTML,renderSection,render",d=r(a,b),e=new Function(c,d);return function(c,d){d=d||{};var f=[c];try{return e(c,d,f,p,n,q,w)}catch(g){throw o(g.error,a,g.line,b.file)}}}function u(){t={}}function v(a,b){return b=b||{},b.cache!==!1?(t[a]||(t[a]=s(a,b)),t[a]):s(a,b)}function w(a,b,c){return v(a)(b,c)}a.name="mustache.js",a.version="0.5.14",a.tags=["{{","}}"],a.parse=r,a.compile=v,a.render=w,a.clearCache=u,a.to_html=function(a,b,c,d){var e=w(a,b,c);if(typeof d!="function")return e;d(e)};var b=Object.prototype.toString,c=Array.isArray,d=Array.prototype.forEach,e=String.prototype.trim,f;c?f=c:f=function(a){return b.call(a)==="[object Array]"};var g;d?g=function(a,b,c){return d.call(a,b,c)}:g=function(a,b,c){for(var d=0,e=a.length;d<e;++d)b.call(c,a[d],d,a)};var h=/^\s*$/,j;if(e)j=function(a){return a==null?"":e.call(a)};else{var k,l;i(" ")?(k=/^\s+/,l=/\s+$/):(k=/^[\s\xA0]+/,l=/[\s\xA0]+$/),j=function(a){return a==null?"":String(a).replace(k,"").replace(l,"")}}var m={"&":"&","<":"<",">":">",'"':""","'":"'"},t={}})(a),qx.bom.Template.version=a.version,qx.bom.Template.toHtml=a.render}(); | |||||
| @@ -0,0 +1,23 @@ | |||||
| The MIT License | |||||
| 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. | |||||
| @@ -0,0 +1,418 @@ | |||||
| # mustache.js - Logic-less {{mustache}} templates with JavaScript | |||||
| > What could be more logical awesome than no logic at all? | |||||
| [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. | |||||
| 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. | |||||
| 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? | |||||
| 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. | |||||
| ## 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 | |||||
| Below is quick example how to use mustache.js: | |||||
| var view = { | |||||
| title: "Joe", | |||||
| calc: function () { | |||||
| return 2 + 4; | |||||
| } | |||||
| }; | |||||
| var output = Mustache.render("{{title}} spends {{calc}}", 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. | |||||
| ### 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: | |||||
| var Mustache = require("mustache"); | |||||
| ## Templates | |||||
| 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. | |||||
| There are several types of tags available in mustache.js. | |||||
| ### Variables | |||||
| 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. | |||||
| 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: | |||||
| * {{name}} | |||||
| * {{age}} | |||||
| * {{company}} | |||||
| * {{{company}}} | |||||
| * {{&company}} | |||||
| View: | |||||
| { | |||||
| "name": "Chris", | |||||
| "company": "<b>GitHub</b>" | |||||
| } | |||||
| Output: | |||||
| * Chris | |||||
| * | |||||
| * <b>GitHub</b> | |||||
| * <b>GitHub</b> | |||||
| * <b>GitHub</b> | |||||
| JavaScript's dot notation may be used to access keys that are properties of | |||||
| objects in a view. | |||||
| Template: | |||||
| * {{name.first}} {{name.last}} | |||||
| * {{age}} | |||||
| View: | |||||
| { | |||||
| "name": { | |||||
| "first": "Michael", | |||||
| "last": "Jackson" | |||||
| }, | |||||
| "age": "RIP" | |||||
| } | |||||
| Output: | |||||
| * Michael Jackson | |||||
| * RIP | |||||
| ### Sections | |||||
| Sections render blocks of text one or more times, depending on the value of the | |||||
| key in the current context. | |||||
| 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. | |||||
| #### False Values or Empty Lists | |||||
| 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. | |||||
| Template: | |||||
| Shown. | |||||
| {{#nothin}} | |||||
| Never shown! | |||||
| {{/nothin}} | |||||
| View: | |||||
| { | |||||
| "person": true | |||||
| } | |||||
| Output: | |||||
| Shown. | |||||
| #### Non-Empty Lists | |||||
| 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. | |||||
| 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. | |||||
| Template: | |||||
| {{#stooges}} | |||||
| <b>{{name}}</b> | |||||
| {{/stooges}} | |||||
| View: | |||||
| { | |||||
| "stooges": [ | |||||
| { "name": "Moe" }, | |||||
| { "name": "Larry" }, | |||||
| { "name": "Curly" } | |||||
| ] | |||||
| } | |||||
| Output: | |||||
| <b>Moe</b> | |||||
| <b>Larry</b> | |||||
| <b>Curly</b> | |||||
| When looping over an array of strings, a `.` can be used to refer to the current | |||||
| item in the list. | |||||
| Template: | |||||
| {{#musketeers}} | |||||
| * {{.}} | |||||
| {{/musketeers}} | |||||
| View: | |||||
| { | |||||
| "musketeers": ["Athos", "Aramis", "Porthos", "D'Artagnan"] | |||||
| } | |||||
| Output: | |||||
| * Athos | |||||
| * Aramis | |||||
| * Porthos | |||||
| * D'Artagnan | |||||
| 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: | |||||
| {{#beatles}} | |||||
| * {{name}} | |||||
| {{/beatles}} | |||||
| View: | |||||
| { | |||||
| "beatles": [ | |||||
| { "firstName": "John", "lastName": "Lennon" }, | |||||
| { "firstName": "Paul", "lastName": "McCartney" }, | |||||
| { "firstName": "George", "lastName": "Harrison" }, | |||||
| { "firstName": "Ringo", "lastName": "Starr" } | |||||
| ], | |||||
| "name": function () { | |||||
| return this.firstName + " " + this.lastName; | |||||
| } | |||||
| } | |||||
| Output: | |||||
| * John Lennon | |||||
| * Paul McCartney | |||||
| * George Harrison | |||||
| * Ringo Starr | |||||
| #### Functions | |||||
| 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: | |||||
| {{#bold}}Hi {{name}}.{{/bold}} | |||||
| View: | |||||
| { | |||||
| "name": "Tater", | |||||
| "bold": function () { | |||||
| return function (text, render) { | |||||
| return "<b>" + render(text) + "</b>"; | |||||
| } | |||||
| } | |||||
| } | |||||
| Output: | |||||
| <b>Hi Tater.</b> | |||||
| ### Inverted Sections | |||||
| 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. | |||||
| Template: | |||||
| {{#repos}}<b>{{name}}</b>{{/repos}} | |||||
| {{^repos}}No repos :({{/repos}} | |||||
| View: | |||||
| { | |||||
| "repos": [] | |||||
| } | |||||
| Output: | |||||
| No repos :( | |||||
| ### Comments | |||||
| Comments begin with a bang and are ignored. The following template: | |||||
| <h1>Today{{! ignore me }}.</h1> | |||||
| Will render as follows: | |||||
| <h1>Today.</h1> | |||||
| Comments may contain newlines. | |||||
| ### Partials | |||||
| Partials begin with a greater than sign, like {{> box}}. | |||||
| Partials are rendered at runtime (as opposed to compile time), so recursive | |||||
| partials are possible. Just avoid infinite loops. | |||||
| They also inherit the calling context. Whereas in ERB you may have this: | |||||
| <%= partial :next_more, :start => start, :size => size %> | |||||
| Mustache requires only this: | |||||
| {{> next_more}} | |||||
| 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. | |||||
| For example, this template and partial: | |||||
| base.mustache: | |||||
| <h2>Names</h2> | |||||
| {{#names}} | |||||
| {{> user}} | |||||
| {{/names}} | |||||
| user.mustache: | |||||
| <strong>{{name}}</strong> | |||||
| Can be thought of as a single, expanded template: | |||||
| <h2>Names</h2> | |||||
| {{#names}} | |||||
| <strong>{{name}}</strong> | |||||
| {{/names}} | |||||
| 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. | |||||
| ### Set Delimiter | |||||
| Set Delimiter tags start with an equals sign and change the tag delimiters from | |||||
| `{{` and `}}` to custom strings. | |||||
| Consider the following contrived example: | |||||
| * {{ default_tags }} | |||||
| {{=<% %>=}} | |||||
| * <% erb_style_tags %> | |||||
| <%={{ }}=%> | |||||
| * {{ default_tags_again }} | |||||
| 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. | |||||
| 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." | |||||
| Custom delimiters may not contain whitespace or the equals sign. | |||||
| ## Streaming | |||||
| To stream template results out of mustache.js, you can pass an optional callback | |||||
| to the call to `Mustache.render`: | |||||
| Mustache.render(template, view, partials, function (chunk) { | |||||
| print(chunk); | |||||
| }); | |||||
| 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 | |||||
| @@ -0,0 +1,545 @@ | |||||
| /* | |||||
| Shameless port of a shameless port ^ 2 | |||||
| @defunkt => @janl => @aq => @voodootikigod => @timruffles | |||||
| */ | |||||
| define(function(){ | |||||
| /*! | |||||
| * 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.14"; | |||||
| 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; | |||||
| } | |||||
| }; | |||||
| 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]+$/; | |||||
| } | |||||
| trim = function (string) { | |||||
| return string == null ? "" : | |||||
| String(string).replace(trimLeft, "").replace(trimRight, ""); | |||||
| }; | |||||
| } | |||||
| var escapeMap = { | |||||
| "&": "&", | |||||
| "<": "<", | |||||
| ">": ">", | |||||
| '"': '"', | |||||
| "'": ''' | |||||
| }; | |||||
| 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 || "<template>"; | |||||
| var lines = template.split("\n"), | |||||
| start = Math.max(line - 3, 0), | |||||
| end = Math.min(lines.length, line + 3), | |||||
| context = lines.slice(start, end); | |||||
| var c; | |||||
| for (var i = 0, len = context.length; i < len; ++i) { | |||||
| c = i + start + 1; | |||||
| context[i] = (c === line ? " >> " : " ") + context[i]; | |||||
| } | |||||
| e.template = template; | |||||
| e.line = line; | |||||
| e.file = file; | |||||
| e.message = [file + ":" + line, context.join("\n"), "", e.message].join("\n"); | |||||
| return e; | |||||
| } | |||||
| /** | |||||
| * Looks up the value of the given `name` in the given context `stack`. | |||||
| */ | |||||
| function lookup(name, stack, defaultValue) { | |||||
| if (name === ".") { | |||||
| return stack[stack.length - 1]; | |||||
| } | |||||
| var names = name.split("."); | |||||
| var lastIndex = names.length - 1; | |||||
| var target = names[lastIndex]; | |||||
| var value, context, i = stack.length, j, localStack; | |||||
| while (i) { | |||||
| localStack = stack.slice(0); | |||||
| context = stack[--i]; | |||||
| j = 0; | |||||
| while (j < lastIndex) { | |||||
| context = context[names[j++]]; | |||||
| if (context == null) { | |||||
| break; | |||||
| } | |||||
| localStack.push(context); | |||||
| } | |||||
| if (context && typeof context === "object" && target in context) { | |||||
| value = context[target]; | |||||
| break; | |||||
| } | |||||
| } | |||||
| // If the value is a function, call it in the current context. | |||||
| if (typeof value === "function") { | |||||
| value = value.call(localStack[localStack.length - 1]); | |||||
| } | |||||
| if (value == null) { | |||||
| return defaultValue; | |||||
| } | |||||
| return value; | |||||
| } | |||||
| function renderSection(name, stack, callback, inverted) { | |||||
| var buffer = ""; | |||||
| var value = lookup(name, stack); | |||||
| if (inverted) { | |||||
| // From the spec: inverted sections may render text once based on the | |||||
| // inverse value of the key. That is, they will be rendered if the key | |||||
| // doesn't exist, is false, or is an empty list. | |||||
| if (value == null || value === false || (isArray(value) && value.length === 0)) { | |||||
| buffer += callback(); | |||||
| } | |||||
| } else if (isArray(value)) { | |||||
| forEach(value, function (value) { | |||||
| stack.push(value); | |||||
| buffer += callback(); | |||||
| stack.pop(); | |||||
| }); | |||||
| } else if (typeof value === "object") { | |||||
| stack.push(value); | |||||
| buffer += callback(); | |||||
| stack.pop(); | |||||
| } else if (typeof value === "function") { | |||||
| var scope = stack[stack.length - 1]; | |||||
| var scopedRender = function (template) { | |||||
| return render(template, scope); | |||||
| }; | |||||
| buffer += value.call(scope, callback(), scopedRender) || ""; | |||||
| } else if (value) { | |||||
| buffer += callback(); | |||||
| } | |||||
| return buffer; | |||||
| } | |||||
| /** | |||||
| * Parses the given `template` and returns the source of a function that, | |||||
| * with the proper arguments, will render the template. Recognized options | |||||
| * include the following: | |||||
| * | |||||
| * - file The name of the file the template comes from (displayed in | |||||
| * error messages) | |||||
| * - tags An array of open and close tags the `template` uses. Defaults | |||||
| * to the value of Mustache.tags | |||||
| * - debug Set `true` to log the body of the generated function to the | |||||
| * console | |||||
| * - space Set `true` to preserve whitespace from lines that otherwise | |||||
| * contain only a {{tag}}. Defaults to `false` | |||||
| */ | |||||
| function parse(template, options) { | |||||
| options = options || {}; | |||||
| var tags = options.tags || exports.tags, | |||||
| openTag = tags[0], | |||||
| closeTag = tags[tags.length - 1]; | |||||
| var code = [ | |||||
| 'var buffer = "";', // output buffer | |||||
| "\nvar line = 1;", // keep track of source line number | |||||
| "\ntry {", | |||||
| '\nbuffer += "' | |||||
| ]; | |||||
| var spaces = [], // indices of whitespace in code on the current line | |||||
| hasTag = false, // is there a {{tag}} on the current line? | |||||
| nonSpace = false; // is there a non-space char on the current line? | |||||
| // Strips all space characters from the code array for the current line | |||||
| // if there was a {{tag}} on it and otherwise only spaces. | |||||
| var stripSpace = function () { | |||||
| if (hasTag && !nonSpace && !options.space) { | |||||
| while (spaces.length) { | |||||
| code.splice(spaces.pop(), 1); | |||||
| } | |||||
| } else { | |||||
| spaces = []; | |||||
| } | |||||
| hasTag = false; | |||||
| nonSpace = false; | |||||
| }; | |||||
| var sectionStack = [], updateLine, nextOpenTag, nextCloseTag; | |||||
| var setTags = function (source) { | |||||
| tags = trim(source).split(/\s+/); | |||||
| nextOpenTag = tags[0]; | |||||
| nextCloseTag = tags[tags.length - 1]; | |||||
| }; | |||||
| var includePartial = function (source) { | |||||
| code.push( | |||||
| '";', | |||||
| updateLine, | |||||
| '\nvar partial = partials["' + trim(source) + '"];', | |||||
| '\nif (partial) {', | |||||
| '\n buffer += render(partial,stack[stack.length - 1],partials);', | |||||
| '\n}', | |||||
| '\nbuffer += "' | |||||
| ); | |||||
| }; | |||||
| var openSection = function (source, inverted) { | |||||
| var name = trim(source); | |||||
| if (name === "") { | |||||
| throw debug(new Error("Section name may not be empty"), template, line, options.file); | |||||
| } | |||||
| sectionStack.push({name: name, inverted: inverted}); | |||||
| code.push( | |||||
| '";', | |||||
| updateLine, | |||||
| '\nvar name = "' + name + '";', | |||||
| '\nvar callback = (function () {', | |||||
| '\n return function () {', | |||||
| '\n var buffer = "";', | |||||
| '\nbuffer += "' | |||||
| ); | |||||
| }; | |||||
| var openInvertedSection = function (source) { | |||||
| openSection(source, true); | |||||
| }; | |||||
| var closeSection = function (source) { | |||||
| var name = trim(source); | |||||
| var openName = sectionStack.length != 0 && sectionStack[sectionStack.length - 1].name; | |||||
| if (!openName || name != openName) { | |||||
| throw debug(new Error('Section named "' + name + '" was never opened'), template, line, options.file); | |||||
| } | |||||
| var section = sectionStack.pop(); | |||||
| code.push( | |||||
| '";', | |||||
| '\n return buffer;', | |||||
| '\n };', | |||||
| '\n})();' | |||||
| ); | |||||
| if (section.inverted) { | |||||
| code.push("\nbuffer += renderSection(name,stack,callback,true);"); | |||||
| } else { | |||||
| code.push("\nbuffer += renderSection(name,stack,callback);"); | |||||
| } | |||||
| code.push('\nbuffer += "'); | |||||
| }; | |||||
| var sendPlain = function (source) { | |||||
| code.push( | |||||
| '";', | |||||
| updateLine, | |||||
| '\nbuffer += lookup("' + trim(source) + '",stack,"");', | |||||
| '\nbuffer += "' | |||||
| ); | |||||
| }; | |||||
| var sendEscaped = function (source) { | |||||
| code.push( | |||||
| '";', | |||||
| updateLine, | |||||
| '\nbuffer += escapeHTML(lookup("' + trim(source) + '",stack,""));', | |||||
| '\nbuffer += "' | |||||
| ); | |||||
| }; | |||||
| var line = 1, c, callback; | |||||
| for (var i = 0, len = template.length; i < len; ++i) { | |||||
| if (template.slice(i, i + openTag.length) === openTag) { | |||||
| i += openTag.length; | |||||
| c = template.substr(i, 1); | |||||
| updateLine = '\nline = ' + line + ';'; | |||||
| nextOpenTag = openTag; | |||||
| nextCloseTag = closeTag; | |||||
| hasTag = true; | |||||
| switch (c) { | |||||
| case "!": // comment | |||||
| i++; | |||||
| callback = null; | |||||
| break; | |||||
| case "=": // change open/close tags, e.g. {{=<% %>=}} | |||||
| i++; | |||||
| closeTag = "=" + closeTag; | |||||
| callback = setTags; | |||||
| break; | |||||
| case ">": // include partial | |||||
| i++; | |||||
| callback = includePartial; | |||||
| break; | |||||
| case "#": // start section | |||||
| i++; | |||||
| callback = openSection; | |||||
| break; | |||||
| case "^": // start inverted section | |||||
| i++; | |||||
| callback = openInvertedSection; | |||||
| break; | |||||
| case "/": // end section | |||||
| i++; | |||||
| callback = closeSection; | |||||
| break; | |||||
| case "{": // plain variable | |||||
| closeTag = "}" + closeTag; | |||||
| // fall through | |||||
| case "&": // plain variable | |||||
| i++; | |||||
| nonSpace = true; | |||||
| callback = sendPlain; | |||||
| break; | |||||
| default: // escaped variable | |||||
| nonSpace = true; | |||||
| callback = sendEscaped; | |||||
| } | |||||
| var end = template.indexOf(closeTag, i); | |||||
| if (end === -1) { | |||||
| throw debug(new Error('Tag "' + openTag + '" was not closed properly'), template, line, options.file); | |||||
| } | |||||
| var source = template.substring(i, end); | |||||
| if (callback) { | |||||
| callback(source); | |||||
| } | |||||
| // Maintain line count for \n in source. | |||||
| var n = 0; | |||||
| while (~(n = source.indexOf("\n", n))) { | |||||
| line++; | |||||
| n++; | |||||
| } | |||||
| i = end + closeTag.length - 1; | |||||
| openTag = nextOpenTag; | |||||
| closeTag = nextCloseTag; | |||||
| } else { | |||||
| c = template.substr(i, 1); | |||||
| switch (c) { | |||||
| case '"': | |||||
| case "\\": | |||||
| nonSpace = true; | |||||
| code.push("\\" + c); | |||||
| break; | |||||
| case "\r": | |||||
| // Ignore carriage returns. | |||||
| break; | |||||
| case "\n": | |||||
| spaces.push(code.length); | |||||
| code.push("\\n"); | |||||
| stripSpace(); // Check for whitespace on the current line. | |||||
| line++; | |||||
| break; | |||||
| default: | |||||
| if (isWhitespace(c)) { | |||||
| spaces.push(code.length); | |||||
| } else { | |||||
| nonSpace = true; | |||||
| } | |||||
| code.push(c); | |||||
| } | |||||
| } | |||||
| } | |||||
| if (sectionStack.length != 0) { | |||||
| throw debug(new Error('Section "' + sectionStack[sectionStack.length - 1].name + '" was not closed properly'), template, line, options.file); | |||||
| } | |||||
| // Clean up any whitespace from a closing {{tag}} that was at the end | |||||
| // of the template without a trailing \n. | |||||
| stripSpace(); | |||||
| code.push( | |||||
| '";', | |||||
| "\nreturn buffer;", | |||||
| "\n} catch (e) { throw {error: e, line: line}; }" | |||||
| ); | |||||
| // Ignore `buffer += "";` statements. | |||||
| var body = code.join("").replace(/buffer \+= "";\n/g, ""); | |||||
| if (options.debug) { | |||||
| if (typeof console != "undefined" && console.log) { | |||||
| console.log(body); | |||||
| } else if (typeof print === "function") { | |||||
| print(body); | |||||
| } | |||||
| } | |||||
| return body; | |||||
| } | |||||
| /** | |||||
| * Used by `compile` to generate a reusable function for the given `template`. | |||||
| */ | |||||
| function _compile(template, options) { | |||||
| var args = "view,partials,stack,lookup,escapeHTML,renderSection,render"; | |||||
| var body = parse(template, options); | |||||
| var fn = new Function(args, body); | |||||
| // This anonymous function wraps the generated function so we can do | |||||
| // argument coercion, setup some variables, and handle any errors | |||||
| // encountered while executing it. | |||||
| return function (view, partials) { | |||||
| partials = partials || {}; | |||||
| var stack = [view]; // context stack | |||||
| try { | |||||
| return fn(view, partials, stack, lookup, escapeHTML, renderSection, render); | |||||
| } catch (e) { | |||||
| throw debug(e.error, template, e.line, options.file); | |||||
| } | |||||
| }; | |||||
| } | |||||
| // Cache of pre-compiled templates. | |||||
| var _cache = {}; | |||||
| /** | |||||
| * Clear the cache of compiled templates. | |||||
| */ | |||||
| function clearCache() { | |||||
| _cache = {}; | |||||
| } | |||||
| /** | |||||
| * Compiles the given `template` into a reusable function using the given | |||||
| * `options`. In addition to the options accepted by Mustache.parse, | |||||
| * recognized options include the following: | |||||
| * | |||||
| * - cache Set `false` to bypass any pre-compiled version of the given | |||||
| * template. Otherwise, a given `template` string will be cached | |||||
| * the first time it is parsed | |||||
| */ | |||||
| function compile(template, options) { | |||||
| options = options || {}; | |||||
| // Use a pre-compiled version from the cache if we have one. | |||||
| if (options.cache !== false) { | |||||
| if (!_cache[template]) { | |||||
| _cache[template] = _compile(template, options); | |||||
| } | |||||
| return _cache[template]; | |||||
| } | |||||
| return _compile(template, options); | |||||
| } | |||||
| /** | |||||
| * High-level function that renders the given `template` using the given | |||||
| * `view` and `partials`. If you need to use any of the template options (see | |||||
| * `compile` above), you must compile in a separate step, and then call that | |||||
| * compiled function. | |||||
| */ | |||||
| function render(template, view, partials) { | |||||
| return compile(template)(view, partials); | |||||
| } | |||||
| })(Mustache); | |||||
| return Mustache; | |||||
| }); | |||||
| @@ -0,0 +1,5 @@ | |||||
| /* | |||||
| Shameless port of a shameless port ^ 2 | |||||
| @defunkt => @janl => @aq => @voodootikigod => @timruffles | |||||
| */define(function(){var a=typeof module!="undefined"&&module.exports||{};return function(a){function i(a){return h.test(a)}function n(a){return String(a).replace(/&(?!\w+;)|[<>"']/g,function(a){return m[a]||a})}function o(a,b,c,d){d=d||"<template>";var e=b.split("\n"),f=Math.max(c-3,0),g=Math.min(e.length,c+3),h=e.slice(f,g),i;for(var j=0,k=h.length;j<k;++j)i=j+f+1,h[j]=(i===c?" >> ":" ")+h[j];return a.template=b,a.line=c,a.file=d,a.message=[d+":"+c,h.join("\n"),"",a.message].join("\n"),a}function p(a,b,c){if(a===".")return b[b.length-1];var d=a.split("."),e=d.length-1,f=d[e],g,h,i=b.length,j,k;while(i){k=b.slice(0),h=b[--i],j=0;while(j<e){h=h[d[j++]];if(h==null)break;k.push(h)}if(h&&typeof h=="object"&&f in h){g=h[f];break}}return typeof g=="function"&&(g=g.call(k[k.length-1])),g==null?c:g}function q(a,b,c,d){var e="",h=p(a,b);if(d){if(h==null||h===!1||f(h)&&h.length===0)e+=c()}else if(f(h))g(h,function(a){b.push(a),e+=c(),b.pop()});else if(typeof h=="object")b.push(h),e+=c(),b.pop();else if(typeof h=="function"){var i=b[b.length-1],j=function(a){return w(a,i)};e+=h.call(i,c(),j)||""}else h&&(e+=c());return e}function r(b,c){c=c||{};var d=c.tags||a.tags,e=d[0],f=d[d.length-1],g=['var buffer = "";',"\nvar line = 1;","\ntry {",'\nbuffer += "'],h=[],k=!1,l=!1,m=function(){if(k&&!l&&!c.space)while(h.length)g.splice(h.pop(),1);else h=[];k=!1,l=!1},n=[],p,q,r,s=function(a){d=j(a).split(/\s+/),q=d[0],r=d[d.length-1]},t=function(a){g.push('";',p,'\nvar partial = partials["'+j(a)+'"];',"\nif (partial) {","\n buffer += render(partial,stack[stack.length - 1],partials);","\n}",'\nbuffer += "')},u=function(a,d){var e=j(a);if(e==="")throw o(new Error("Section name may not be empty"),b,z,c.file);n.push({name:e,inverted:d}),g.push('";',p,'\nvar name = "'+e+'";',"\nvar callback = (function () {","\n return function () {",'\n var buffer = "";','\nbuffer += "')},v=function(a){u(a,!0)},w=function(a){var d=j(a),e=n.length!=0&&n[n.length-1].name;if(!e||d!=e)throw o(new Error('Section named "'+d+'" was never opened'),b,z,c.file);var f=n.pop();g.push('";',"\n return buffer;","\n };","\n})();"),f.inverted?g.push("\nbuffer += renderSection(name,stack,callback,true);"):g.push("\nbuffer += renderSection(name,stack,callback);"),g.push('\nbuffer += "')},x=function(a){g.push('";',p,'\nbuffer += lookup("'+j(a)+'",stack,"");','\nbuffer += "')},y=function(a){g.push('";',p,'\nbuffer += escapeHTML(lookup("'+j(a)+'",stack,""));','\nbuffer += "')},z=1,A,B;for(var C=0,D=b.length;C<D;++C)if(b.slice(C,C+e.length)===e){C+=e.length,A=b.substr(C,1),p="\nline = "+z+";",q=e,r=f,k=!0;switch(A){case"!":C++,B=null;break;case"=":C++,f="="+f,B=s;break;case">":C++,B=t;break;case"#":C++,B=u;break;case"^":C++,B=v;break;case"/":C++,B=w;break;case"{":f="}"+f;case"&":C++,l=!0,B=x;break;default:l=!0,B=y}var E=b.indexOf(f,C);if(E===-1)throw o(new Error('Tag "'+e+'" was not closed properly'),b,z,c.file);var F=b.substring(C,E);B&&B(F);var G=0;while(~(G=F.indexOf("\n",G)))z++,G++;C=E+f.length-1,e=q,f=r}else{A=b.substr(C,1);switch(A){case'"':case"\\":l=!0,g.push("\\"+A);break;case"\r":break;case"\n":h.push(g.length),g.push("\\n"),m(),z++;break;default:i(A)?h.push(g.length):l=!0,g.push(A)}}if(n.length!=0)throw o(new Error('Section "'+n[n.length-1].name+'" was not closed properly'),b,z,c.file);m(),g.push('";',"\nreturn buffer;","\n} catch (e) { throw {error: e, line: line}; }");var H=g.join("").replace(/buffer \+= "";\n/g,"");return c.debug&&(typeof console!="undefined"&&console.log?console.log(H):typeof print=="function"&&print(H)),H}function s(a,b){var c="view,partials,stack,lookup,escapeHTML,renderSection,render",d=r(a,b),e=new Function(c,d);return function(c,d){d=d||{};var f=[c];try{return e(c,d,f,p,n,q,w)}catch(g){throw o(g.error,a,g.line,b.file)}}}function u(){t={}}function v(a,b){return b=b||{},b.cache!==!1?(t[a]||(t[a]=s(a,b)),t[a]):s(a,b)}function w(a,b,c){return v(a)(b,c)}a.name="mustache.js",a.version="0.5.14",a.tags=["{{","}}"],a.parse=r,a.compile=v,a.render=w,a.clearCache=u,a.to_html=function(a,b,c,d){var e=w(a,b,c);if(typeof d!="function")return e;d(e)};var b=Object.prototype.toString,c=Array.isArray,d=Array.prototype.forEach,e=String.prototype.trim,f;c?f=c:f=function(a){return b.call(a)==="[object Array]"};var g;d?g=function(a,b,c){return d.call(a,b,c)}:g=function(a,b,c){for(var d=0,e=a.length;d<e;++d)b.call(c,a[d],d,a)};var h=/^\s*$/,j;if(e)j=function(a){return a==null?"":e.call(a)};else{var k,l;i(" ")?(k=/^\s+/,l=/\s+$/):(k=/^[\s\xA0]+/,l=/[\s\xA0]+$/),j=function(a){return a==null?"":String(a).replace(k,"").replace(l,"")}}var m={"&":"&","<":"<",">":">",'"':""","'":"'"},t={}}(a),a}); | |||||
| @@ -0,0 +1,23 @@ | |||||
| The MIT License | |||||
| 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. | |||||
| @@ -0,0 +1,418 @@ | |||||
| # mustache.js - Logic-less {{mustache}} templates with JavaScript | |||||
| > What could be more logical awesome than no logic at all? | |||||
| [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. | |||||
| 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. | |||||
| 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? | |||||
| 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. | |||||
| ## 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 | |||||
| Below is quick example how to use mustache.js: | |||||
| var view = { | |||||
| title: "Joe", | |||||
| calc: function () { | |||||
| return 2 + 4; | |||||
| } | |||||
| }; | |||||
| var output = Mustache.render("{{title}} spends {{calc}}", 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. | |||||
| ### 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: | |||||
| var Mustache = require("mustache"); | |||||
| ## Templates | |||||
| 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. | |||||
| There are several types of tags available in mustache.js. | |||||
| ### Variables | |||||
| 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. | |||||
| 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: | |||||
| * {{name}} | |||||
| * {{age}} | |||||
| * {{company}} | |||||
| * {{{company}}} | |||||
| * {{&company}} | |||||
| View: | |||||
| { | |||||
| "name": "Chris", | |||||
| "company": "<b>GitHub</b>" | |||||
| } | |||||
| Output: | |||||
| * Chris | |||||
| * | |||||
| * <b>GitHub</b> | |||||
| * <b>GitHub</b> | |||||
| * <b>GitHub</b> | |||||
| JavaScript's dot notation may be used to access keys that are properties of | |||||
| objects in a view. | |||||
| Template: | |||||
| * {{name.first}} {{name.last}} | |||||
| * {{age}} | |||||
| View: | |||||
| { | |||||
| "name": { | |||||
| "first": "Michael", | |||||
| "last": "Jackson" | |||||
| }, | |||||
| "age": "RIP" | |||||
| } | |||||
| Output: | |||||
| * Michael Jackson | |||||
| * RIP | |||||
| ### Sections | |||||
| Sections render blocks of text one or more times, depending on the value of the | |||||
| key in the current context. | |||||
| 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. | |||||
| #### False Values or Empty Lists | |||||
| 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. | |||||
| Template: | |||||
| Shown. | |||||
| {{#nothin}} | |||||
| Never shown! | |||||
| {{/nothin}} | |||||
| View: | |||||
| { | |||||
| "person": true | |||||
| } | |||||
| Output: | |||||
| Shown. | |||||
| #### Non-Empty Lists | |||||
| 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. | |||||
| 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. | |||||
| Template: | |||||
| {{#stooges}} | |||||
| <b>{{name}}</b> | |||||
| {{/stooges}} | |||||
| View: | |||||
| { | |||||
| "stooges": [ | |||||
| { "name": "Moe" }, | |||||
| { "name": "Larry" }, | |||||
| { "name": "Curly" } | |||||
| ] | |||||
| } | |||||
| Output: | |||||
| <b>Moe</b> | |||||
| <b>Larry</b> | |||||
| <b>Curly</b> | |||||
| When looping over an array of strings, a `.` can be used to refer to the current | |||||
| item in the list. | |||||
| Template: | |||||
| {{#musketeers}} | |||||
| * {{.}} | |||||
| {{/musketeers}} | |||||
| View: | |||||
| { | |||||
| "musketeers": ["Athos", "Aramis", "Porthos", "D'Artagnan"] | |||||
| } | |||||
| Output: | |||||
| * Athos | |||||
| * Aramis | |||||
| * Porthos | |||||
| * D'Artagnan | |||||
| 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: | |||||
| {{#beatles}} | |||||
| * {{name}} | |||||
| {{/beatles}} | |||||
| View: | |||||
| { | |||||
| "beatles": [ | |||||
| { "firstName": "John", "lastName": "Lennon" }, | |||||
| { "firstName": "Paul", "lastName": "McCartney" }, | |||||
| { "firstName": "George", "lastName": "Harrison" }, | |||||
| { "firstName": "Ringo", "lastName": "Starr" } | |||||
| ], | |||||
| "name": function () { | |||||
| return this.firstName + " " + this.lastName; | |||||
| } | |||||
| } | |||||
| Output: | |||||
| * John Lennon | |||||
| * Paul McCartney | |||||
| * George Harrison | |||||
| * Ringo Starr | |||||
| #### Functions | |||||
| 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: | |||||
| {{#bold}}Hi {{name}}.{{/bold}} | |||||
| View: | |||||
| { | |||||
| "name": "Tater", | |||||
| "bold": function () { | |||||
| return function (text, render) { | |||||
| return "<b>" + render(text) + "</b>"; | |||||
| } | |||||
| } | |||||
| } | |||||
| Output: | |||||
| <b>Hi Tater.</b> | |||||
| ### Inverted Sections | |||||
| 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. | |||||
| Template: | |||||
| {{#repos}}<b>{{name}}</b>{{/repos}} | |||||
| {{^repos}}No repos :({{/repos}} | |||||
| View: | |||||
| { | |||||
| "repos": [] | |||||
| } | |||||
| Output: | |||||
| No repos :( | |||||
| ### Comments | |||||
| Comments begin with a bang and are ignored. The following template: | |||||
| <h1>Today{{! ignore me }}.</h1> | |||||
| Will render as follows: | |||||
| <h1>Today.</h1> | |||||
| Comments may contain newlines. | |||||
| ### Partials | |||||
| Partials begin with a greater than sign, like {{> box}}. | |||||
| Partials are rendered at runtime (as opposed to compile time), so recursive | |||||
| partials are possible. Just avoid infinite loops. | |||||
| They also inherit the calling context. Whereas in ERB you may have this: | |||||
| <%= partial :next_more, :start => start, :size => size %> | |||||
| Mustache requires only this: | |||||
| {{> next_more}} | |||||
| 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. | |||||
| For example, this template and partial: | |||||
| base.mustache: | |||||
| <h2>Names</h2> | |||||
| {{#names}} | |||||
| {{> user}} | |||||
| {{/names}} | |||||
| user.mustache: | |||||
| <strong>{{name}}</strong> | |||||
| Can be thought of as a single, expanded template: | |||||
| <h2>Names</h2> | |||||
| {{#names}} | |||||
| <strong>{{name}}</strong> | |||||
| {{/names}} | |||||
| 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. | |||||
| ### Set Delimiter | |||||
| Set Delimiter tags start with an equals sign and change the tag delimiters from | |||||
| `{{` and `}}` to custom strings. | |||||
| Consider the following contrived example: | |||||
| * {{ default_tags }} | |||||
| {{=<% %>=}} | |||||
| * <% erb_style_tags %> | |||||
| <%={{ }}=%> | |||||
| * {{ default_tags_again }} | |||||
| 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. | |||||
| 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." | |||||
| Custom delimiters may not contain whitespace or the equals sign. | |||||
| ## Streaming | |||||
| To stream template results out of mustache.js, you can pass an optional callback | |||||
| to the call to `Mustache.render`: | |||||
| Mustache.render(template, view, partials, function (chunk) { | |||||
| print(chunk); | |||||
| }); | |||||
| 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 | |||||
| @@ -0,0 +1,541 @@ | |||||
| YUI.add("mustache", function(Y) { | |||||
| /*! | |||||
| * 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.14"; | |||||
| 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; | |||||
| } | |||||
| }; | |||||
| 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]+$/; | |||||
| } | |||||
| trim = function (string) { | |||||
| return string == null ? "" : | |||||
| String(string).replace(trimLeft, "").replace(trimRight, ""); | |||||
| }; | |||||
| } | |||||
| var escapeMap = { | |||||
| "&": "&", | |||||
| "<": "<", | |||||
| ">": ">", | |||||
| '"': '"', | |||||
| "'": ''' | |||||
| }; | |||||
| 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 || "<template>"; | |||||
| var lines = template.split("\n"), | |||||
| start = Math.max(line - 3, 0), | |||||
| end = Math.min(lines.length, line + 3), | |||||
| context = lines.slice(start, end); | |||||
| var c; | |||||
| for (var i = 0, len = context.length; i < len; ++i) { | |||||
| c = i + start + 1; | |||||
| context[i] = (c === line ? " >> " : " ") + context[i]; | |||||
| } | |||||
| e.template = template; | |||||
| e.line = line; | |||||
| e.file = file; | |||||
| e.message = [file + ":" + line, context.join("\n"), "", e.message].join("\n"); | |||||
| return e; | |||||
| } | |||||
| /** | |||||
| * Looks up the value of the given `name` in the given context `stack`. | |||||
| */ | |||||
| function lookup(name, stack, defaultValue) { | |||||
| if (name === ".") { | |||||
| return stack[stack.length - 1]; | |||||
| } | |||||
| var names = name.split("."); | |||||
| var lastIndex = names.length - 1; | |||||
| var target = names[lastIndex]; | |||||
| var value, context, i = stack.length, j, localStack; | |||||
| while (i) { | |||||
| localStack = stack.slice(0); | |||||
| context = stack[--i]; | |||||
| j = 0; | |||||
| while (j < lastIndex) { | |||||
| context = context[names[j++]]; | |||||
| if (context == null) { | |||||
| break; | |||||
| } | |||||
| localStack.push(context); | |||||
| } | |||||
| if (context && typeof context === "object" && target in context) { | |||||
| value = context[target]; | |||||
| break; | |||||
| } | |||||
| } | |||||
| // If the value is a function, call it in the current context. | |||||
| if (typeof value === "function") { | |||||
| value = value.call(localStack[localStack.length - 1]); | |||||
| } | |||||
| if (value == null) { | |||||
| return defaultValue; | |||||
| } | |||||
| return value; | |||||
| } | |||||
| function renderSection(name, stack, callback, inverted) { | |||||
| var buffer = ""; | |||||
| var value = lookup(name, stack); | |||||
| if (inverted) { | |||||
| // From the spec: inverted sections may render text once based on the | |||||
| // inverse value of the key. That is, they will be rendered if the key | |||||
| // doesn't exist, is false, or is an empty list. | |||||
| if (value == null || value === false || (isArray(value) && value.length === 0)) { | |||||
| buffer += callback(); | |||||
| } | |||||
| } else if (isArray(value)) { | |||||
| forEach(value, function (value) { | |||||
| stack.push(value); | |||||
| buffer += callback(); | |||||
| stack.pop(); | |||||
| }); | |||||
| } else if (typeof value === "object") { | |||||
| stack.push(value); | |||||
| buffer += callback(); | |||||
| stack.pop(); | |||||
| } else if (typeof value === "function") { | |||||
| var scope = stack[stack.length - 1]; | |||||
| var scopedRender = function (template) { | |||||
| return render(template, scope); | |||||
| }; | |||||
| buffer += value.call(scope, callback(), scopedRender) || ""; | |||||
| } else if (value) { | |||||
| buffer += callback(); | |||||
| } | |||||
| return buffer; | |||||
| } | |||||
| /** | |||||
| * Parses the given `template` and returns the source of a function that, | |||||
| * with the proper arguments, will render the template. Recognized options | |||||
| * include the following: | |||||
| * | |||||
| * - file The name of the file the template comes from (displayed in | |||||
| * error messages) | |||||
| * - tags An array of open and close tags the `template` uses. Defaults | |||||
| * to the value of Mustache.tags | |||||
| * - debug Set `true` to log the body of the generated function to the | |||||
| * console | |||||
| * - space Set `true` to preserve whitespace from lines that otherwise | |||||
| * contain only a {{tag}}. Defaults to `false` | |||||
| */ | |||||
| function parse(template, options) { | |||||
| options = options || {}; | |||||
| var tags = options.tags || exports.tags, | |||||
| openTag = tags[0], | |||||
| closeTag = tags[tags.length - 1]; | |||||
| var code = [ | |||||
| 'var buffer = "";', // output buffer | |||||
| "\nvar line = 1;", // keep track of source line number | |||||
| "\ntry {", | |||||
| '\nbuffer += "' | |||||
| ]; | |||||
| var spaces = [], // indices of whitespace in code on the current line | |||||
| hasTag = false, // is there a {{tag}} on the current line? | |||||
| nonSpace = false; // is there a non-space char on the current line? | |||||
| // Strips all space characters from the code array for the current line | |||||
| // if there was a {{tag}} on it and otherwise only spaces. | |||||
| var stripSpace = function () { | |||||
| if (hasTag && !nonSpace && !options.space) { | |||||
| while (spaces.length) { | |||||
| code.splice(spaces.pop(), 1); | |||||
| } | |||||
| } else { | |||||
| spaces = []; | |||||
| } | |||||
| hasTag = false; | |||||
| nonSpace = false; | |||||
| }; | |||||
| var sectionStack = [], updateLine, nextOpenTag, nextCloseTag; | |||||
| var setTags = function (source) { | |||||
| tags = trim(source).split(/\s+/); | |||||
| nextOpenTag = tags[0]; | |||||
| nextCloseTag = tags[tags.length - 1]; | |||||
| }; | |||||
| var includePartial = function (source) { | |||||
| code.push( | |||||
| '";', | |||||
| updateLine, | |||||
| '\nvar partial = partials["' + trim(source) + '"];', | |||||
| '\nif (partial) {', | |||||
| '\n buffer += render(partial,stack[stack.length - 1],partials);', | |||||
| '\n}', | |||||
| '\nbuffer += "' | |||||
| ); | |||||
| }; | |||||
| var openSection = function (source, inverted) { | |||||
| var name = trim(source); | |||||
| if (name === "") { | |||||
| throw debug(new Error("Section name may not be empty"), template, line, options.file); | |||||
| } | |||||
| sectionStack.push({name: name, inverted: inverted}); | |||||
| code.push( | |||||
| '";', | |||||
| updateLine, | |||||
| '\nvar name = "' + name + '";', | |||||
| '\nvar callback = (function () {', | |||||
| '\n return function () {', | |||||
| '\n var buffer = "";', | |||||
| '\nbuffer += "' | |||||
| ); | |||||
| }; | |||||
| var openInvertedSection = function (source) { | |||||
| openSection(source, true); | |||||
| }; | |||||
| var closeSection = function (source) { | |||||
| var name = trim(source); | |||||
| var openName = sectionStack.length != 0 && sectionStack[sectionStack.length - 1].name; | |||||
| if (!openName || name != openName) { | |||||
| throw debug(new Error('Section named "' + name + '" was never opened'), template, line, options.file); | |||||
| } | |||||
| var section = sectionStack.pop(); | |||||
| code.push( | |||||
| '";', | |||||
| '\n return buffer;', | |||||
| '\n };', | |||||
| '\n})();' | |||||
| ); | |||||
| if (section.inverted) { | |||||
| code.push("\nbuffer += renderSection(name,stack,callback,true);"); | |||||
| } else { | |||||
| code.push("\nbuffer += renderSection(name,stack,callback);"); | |||||
| } | |||||
| code.push('\nbuffer += "'); | |||||
| }; | |||||
| var sendPlain = function (source) { | |||||
| code.push( | |||||
| '";', | |||||
| updateLine, | |||||
| '\nbuffer += lookup("' + trim(source) + '",stack,"");', | |||||
| '\nbuffer += "' | |||||
| ); | |||||
| }; | |||||
| var sendEscaped = function (source) { | |||||
| code.push( | |||||
| '";', | |||||
| updateLine, | |||||
| '\nbuffer += escapeHTML(lookup("' + trim(source) + '",stack,""));', | |||||
| '\nbuffer += "' | |||||
| ); | |||||
| }; | |||||
| var line = 1, c, callback; | |||||
| for (var i = 0, len = template.length; i < len; ++i) { | |||||
| if (template.slice(i, i + openTag.length) === openTag) { | |||||
| i += openTag.length; | |||||
| c = template.substr(i, 1); | |||||
| updateLine = '\nline = ' + line + ';'; | |||||
| nextOpenTag = openTag; | |||||
| nextCloseTag = closeTag; | |||||
| hasTag = true; | |||||
| switch (c) { | |||||
| case "!": // comment | |||||
| i++; | |||||
| callback = null; | |||||
| break; | |||||
| case "=": // change open/close tags, e.g. {{=<% %>=}} | |||||
| i++; | |||||
| closeTag = "=" + closeTag; | |||||
| callback = setTags; | |||||
| break; | |||||
| case ">": // include partial | |||||
| i++; | |||||
| callback = includePartial; | |||||
| break; | |||||
| case "#": // start section | |||||
| i++; | |||||
| callback = openSection; | |||||
| break; | |||||
| case "^": // start inverted section | |||||
| i++; | |||||
| callback = openInvertedSection; | |||||
| break; | |||||
| case "/": // end section | |||||
| i++; | |||||
| callback = closeSection; | |||||
| break; | |||||
| case "{": // plain variable | |||||
| closeTag = "}" + closeTag; | |||||
| // fall through | |||||
| case "&": // plain variable | |||||
| i++; | |||||
| nonSpace = true; | |||||
| callback = sendPlain; | |||||
| break; | |||||
| default: // escaped variable | |||||
| nonSpace = true; | |||||
| callback = sendEscaped; | |||||
| } | |||||
| var end = template.indexOf(closeTag, i); | |||||
| if (end === -1) { | |||||
| throw debug(new Error('Tag "' + openTag + '" was not closed properly'), template, line, options.file); | |||||
| } | |||||
| var source = template.substring(i, end); | |||||
| if (callback) { | |||||
| callback(source); | |||||
| } | |||||
| // Maintain line count for \n in source. | |||||
| var n = 0; | |||||
| while (~(n = source.indexOf("\n", n))) { | |||||
| line++; | |||||
| n++; | |||||
| } | |||||
| i = end + closeTag.length - 1; | |||||
| openTag = nextOpenTag; | |||||
| closeTag = nextCloseTag; | |||||
| } else { | |||||
| c = template.substr(i, 1); | |||||
| switch (c) { | |||||
| case '"': | |||||
| case "\\": | |||||
| nonSpace = true; | |||||
| code.push("\\" + c); | |||||
| break; | |||||
| case "\r": | |||||
| // Ignore carriage returns. | |||||
| break; | |||||
| case "\n": | |||||
| spaces.push(code.length); | |||||
| code.push("\\n"); | |||||
| stripSpace(); // Check for whitespace on the current line. | |||||
| line++; | |||||
| break; | |||||
| default: | |||||
| if (isWhitespace(c)) { | |||||
| spaces.push(code.length); | |||||
| } else { | |||||
| nonSpace = true; | |||||
| } | |||||
| code.push(c); | |||||
| } | |||||
| } | |||||
| } | |||||
| if (sectionStack.length != 0) { | |||||
| throw debug(new Error('Section "' + sectionStack[sectionStack.length - 1].name + '" was not closed properly'), template, line, options.file); | |||||
| } | |||||
| // Clean up any whitespace from a closing {{tag}} that was at the end | |||||
| // of the template without a trailing \n. | |||||
| stripSpace(); | |||||
| code.push( | |||||
| '";', | |||||
| "\nreturn buffer;", | |||||
| "\n} catch (e) { throw {error: e, line: line}; }" | |||||
| ); | |||||
| // Ignore `buffer += "";` statements. | |||||
| var body = code.join("").replace(/buffer \+= "";\n/g, ""); | |||||
| if (options.debug) { | |||||
| if (typeof console != "undefined" && console.log) { | |||||
| console.log(body); | |||||
| } else if (typeof print === "function") { | |||||
| print(body); | |||||
| } | |||||
| } | |||||
| return body; | |||||
| } | |||||
| /** | |||||
| * Used by `compile` to generate a reusable function for the given `template`. | |||||
| */ | |||||
| function _compile(template, options) { | |||||
| var args = "view,partials,stack,lookup,escapeHTML,renderSection,render"; | |||||
| var body = parse(template, options); | |||||
| var fn = new Function(args, body); | |||||
| // This anonymous function wraps the generated function so we can do | |||||
| // argument coercion, setup some variables, and handle any errors | |||||
| // encountered while executing it. | |||||
| return function (view, partials) { | |||||
| partials = partials || {}; | |||||
| var stack = [view]; // context stack | |||||
| try { | |||||
| return fn(view, partials, stack, lookup, escapeHTML, renderSection, render); | |||||
| } catch (e) { | |||||
| throw debug(e.error, template, e.line, options.file); | |||||
| } | |||||
| }; | |||||
| } | |||||
| // Cache of pre-compiled templates. | |||||
| var _cache = {}; | |||||
| /** | |||||
| * Clear the cache of compiled templates. | |||||
| */ | |||||
| function clearCache() { | |||||
| _cache = {}; | |||||
| } | |||||
| /** | |||||
| * Compiles the given `template` into a reusable function using the given | |||||
| * `options`. In addition to the options accepted by Mustache.parse, | |||||
| * recognized options include the following: | |||||
| * | |||||
| * - cache Set `false` to bypass any pre-compiled version of the given | |||||
| * template. Otherwise, a given `template` string will be cached | |||||
| * the first time it is parsed | |||||
| */ | |||||
| function compile(template, options) { | |||||
| options = options || {}; | |||||
| // Use a pre-compiled version from the cache if we have one. | |||||
| if (options.cache !== false) { | |||||
| if (!_cache[template]) { | |||||
| _cache[template] = _compile(template, options); | |||||
| } | |||||
| return _cache[template]; | |||||
| } | |||||
| return _compile(template, options); | |||||
| } | |||||
| /** | |||||
| * High-level function that renders the given `template` using the given | |||||
| * `view` and `partials`. If you need to use any of the template options (see | |||||
| * `compile` above), you must compile in a separate step, and then call that | |||||
| * compiled function. | |||||
| */ | |||||
| function render(template, view, partials) { | |||||
| return compile(template)(view, partials); | |||||
| } | |||||
| })(Mustache); | |||||
| Y.mustache = Mustache.render; | |||||
| }, "0"); | |||||
| @@ -0,0 +1 @@ | |||||
| YUI.add("mustache",function(a){var b=typeof module!="undefined"&&module.exports||{};(function(a){function i(a){return h.test(a)}function n(a){return String(a).replace(/&(?!\w+;)|[<>"']/g,function(a){return m[a]||a})}function o(a,b,c,d){d=d||"<template>";var e=b.split("\n"),f=Math.max(c-3,0),g=Math.min(e.length,c+3),h=e.slice(f,g),i;for(var j=0,k=h.length;j<k;++j)i=j+f+1,h[j]=(i===c?" >> ":" ")+h[j];return a.template=b,a.line=c,a.file=d,a.message=[d+":"+c,h.join("\n"),"",a.message].join("\n"),a}function p(a,b,c){if(a===".")return b[b.length-1];var d=a.split("."),e=d.length-1,f=d[e],g,h,i=b.length,j,k;while(i){k=b.slice(0),h=b[--i],j=0;while(j<e){h=h[d[j++]];if(h==null)break;k.push(h)}if(h&&typeof h=="object"&&f in h){g=h[f];break}}return typeof g=="function"&&(g=g.call(k[k.length-1])),g==null?c:g}function q(a,b,c,d){var e="",h=p(a,b);if(d){if(h==null||h===!1||f(h)&&h.length===0)e+=c()}else if(f(h))g(h,function(a){b.push(a),e+=c(),b.pop()});else if(typeof h=="object")b.push(h),e+=c(),b.pop();else if(typeof h=="function"){var i=b[b.length-1],j=function(a){return w(a,i)};e+=h.call(i,c(),j)||""}else h&&(e+=c());return e}function r(b,c){c=c||{};var d=c.tags||a.tags,e=d[0],f=d[d.length-1],g=['var buffer = "";',"\nvar line = 1;","\ntry {",'\nbuffer += "'],h=[],k=!1,l=!1,m=function(){if(k&&!l&&!c.space)while(h.length)g.splice(h.pop(),1);else h=[];k=!1,l=!1},n=[],p,q,r,s=function(a){d=j(a).split(/\s+/),q=d[0],r=d[d.length-1]},t=function(a){g.push('";',p,'\nvar partial = partials["'+j(a)+'"];',"\nif (partial) {","\n buffer += render(partial,stack[stack.length - 1],partials);","\n}",'\nbuffer += "')},u=function(a,d){var e=j(a);if(e==="")throw o(new Error("Section name may not be empty"),b,z,c.file);n.push({name:e,inverted:d}),g.push('";',p,'\nvar name = "'+e+'";',"\nvar callback = (function () {","\n return function () {",'\n var buffer = "";','\nbuffer += "')},v=function(a){u(a,!0)},w=function(a){var d=j(a),e=n.length!=0&&n[n.length-1].name;if(!e||d!=e)throw o(new Error('Section named "'+d+'" was never opened'),b,z,c.file);var f=n.pop();g.push('";',"\n return buffer;","\n };","\n})();"),f.inverted?g.push("\nbuffer += renderSection(name,stack,callback,true);"):g.push("\nbuffer += renderSection(name,stack,callback);"),g.push('\nbuffer += "')},x=function(a){g.push('";',p,'\nbuffer += lookup("'+j(a)+'",stack,"");','\nbuffer += "')},y=function(a){g.push('";',p,'\nbuffer += escapeHTML(lookup("'+j(a)+'",stack,""));','\nbuffer += "')},z=1,A,B;for(var C=0,D=b.length;C<D;++C)if(b.slice(C,C+e.length)===e){C+=e.length,A=b.substr(C,1),p="\nline = "+z+";",q=e,r=f,k=!0;switch(A){case"!":C++,B=null;break;case"=":C++,f="="+f,B=s;break;case">":C++,B=t;break;case"#":C++,B=u;break;case"^":C++,B=v;break;case"/":C++,B=w;break;case"{":f="}"+f;case"&":C++,l=!0,B=x;break;default:l=!0,B=y}var E=b.indexOf(f,C);if(E===-1)throw o(new Error('Tag "'+e+'" was not closed properly'),b,z,c.file);var F=b.substring(C,E);B&&B(F);var G=0;while(~(G=F.indexOf("\n",G)))z++,G++;C=E+f.length-1,e=q,f=r}else{A=b.substr(C,1);switch(A){case'"':case"\\":l=!0,g.push("\\"+A);break;case"\r":break;case"\n":h.push(g.length),g.push("\\n"),m(),z++;break;default:i(A)?h.push(g.length):l=!0,g.push(A)}}if(n.length!=0)throw o(new Error('Section "'+n[n.length-1].name+'" was not closed properly'),b,z,c.file);m(),g.push('";',"\nreturn buffer;","\n} catch (e) { throw {error: e, line: line}; }");var H=g.join("").replace(/buffer \+= "";\n/g,"");return c.debug&&(typeof console!="undefined"&&console.log?console.log(H):typeof print=="function"&&print(H)),H}function s(a,b){var c="view,partials,stack,lookup,escapeHTML,renderSection,render",d=r(a,b),e=new Function(c,d);return function(c,d){d=d||{};var f=[c];try{return e(c,d,f,p,n,q,w)}catch(g){throw o(g.error,a,g.line,b.file)}}}function u(){t={}}function v(a,b){return b=b||{},b.cache!==!1?(t[a]||(t[a]=s(a,b)),t[a]):s(a,b)}function w(a,b,c){return v(a)(b,c)}a.name="mustache.js",a.version="0.5.14",a.tags=["{{","}}"],a.parse=r,a.compile=v,a.render=w,a.clearCache=u,a.to_html=function(a,b,c,d){var e=w(a,b,c);if(typeof d!="function")return e;d(e)};var b=Object.prototype.toString,c=Array.isArray,d=Array.prototype.forEach,e=String.prototype.trim,f;c?f=c:f=function(a){return b.call(a)==="[object Array]"};var g;d?g=function(a,b,c){return d.call(a,b,c)}:g=function(a,b,c){for(var d=0,e=a.length;d<e;++d)b.call(c,a[d],d,a)};var h=/^\s*$/,j;if(e)j=function(a){return a==null?"":e.call(a)};else{var k,l;i(" ")?(k=/^\s+/,l=/\s+$/):(k=/^[\s\xA0]+/,l=/[\s\xA0]+$/),j=function(a){return a==null?"":String(a).replace(k,"").replace(l,"")}}var m={"&":"&","<":"<",">":">",'"':""","'":"'"},t={}})(b),a.mustache=b.render},"0"); | |||||
| @@ -0,0 +1,536 @@ | |||||
| /*! | |||||
| * 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.14"; | |||||
| 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; | |||||
| } | |||||
| }; | |||||
| 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]+$/; | |||||
| } | |||||
| trim = function (string) { | |||||
| return string == null ? "" : | |||||
| String(string).replace(trimLeft, "").replace(trimRight, ""); | |||||
| }; | |||||
| } | |||||
| var escapeMap = { | |||||
| "&": "&", | |||||
| "<": "<", | |||||
| ">": ">", | |||||
| '"': '"', | |||||
| "'": ''' | |||||
| }; | |||||
| 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 || "<template>"; | |||||
| var lines = template.split("\n"), | |||||
| start = Math.max(line - 3, 0), | |||||
| end = Math.min(lines.length, line + 3), | |||||
| context = lines.slice(start, end); | |||||
| var c; | |||||
| for (var i = 0, len = context.length; i < len; ++i) { | |||||
| c = i + start + 1; | |||||
| context[i] = (c === line ? " >> " : " ") + context[i]; | |||||
| } | |||||
| e.template = template; | |||||
| e.line = line; | |||||
| e.file = file; | |||||
| e.message = [file + ":" + line, context.join("\n"), "", e.message].join("\n"); | |||||
| return e; | |||||
| } | |||||
| /** | |||||
| * Looks up the value of the given `name` in the given context `stack`. | |||||
| */ | |||||
| function lookup(name, stack, defaultValue) { | |||||
| if (name === ".") { | |||||
| return stack[stack.length - 1]; | |||||
| } | |||||
| var names = name.split("."); | |||||
| var lastIndex = names.length - 1; | |||||
| var target = names[lastIndex]; | |||||
| var value, context, i = stack.length, j, localStack; | |||||
| while (i) { | |||||
| localStack = stack.slice(0); | |||||
| context = stack[--i]; | |||||
| j = 0; | |||||
| while (j < lastIndex) { | |||||
| context = context[names[j++]]; | |||||
| if (context == null) { | |||||
| break; | |||||
| } | |||||
| localStack.push(context); | |||||
| } | |||||
| if (context && typeof context === "object" && target in context) { | |||||
| value = context[target]; | |||||
| break; | |||||
| } | |||||
| } | |||||
| // If the value is a function, call it in the current context. | |||||
| if (typeof value === "function") { | |||||
| value = value.call(localStack[localStack.length - 1]); | |||||
| } | |||||
| if (value == null) { | |||||
| return defaultValue; | |||||
| } | |||||
| return value; | |||||
| } | |||||
| function renderSection(name, stack, callback, inverted) { | |||||
| var buffer = ""; | |||||
| var value = lookup(name, stack); | |||||
| if (inverted) { | |||||
| // From the spec: inverted sections may render text once based on the | |||||
| // inverse value of the key. That is, they will be rendered if the key | |||||
| // doesn't exist, is false, or is an empty list. | |||||
| if (value == null || value === false || (isArray(value) && value.length === 0)) { | |||||
| buffer += callback(); | |||||
| } | |||||
| } else if (isArray(value)) { | |||||
| forEach(value, function (value) { | |||||
| stack.push(value); | |||||
| buffer += callback(); | |||||
| stack.pop(); | |||||
| }); | |||||
| } else if (typeof value === "object") { | |||||
| stack.push(value); | |||||
| buffer += callback(); | |||||
| stack.pop(); | |||||
| } else if (typeof value === "function") { | |||||
| var scope = stack[stack.length - 1]; | |||||
| var scopedRender = function (template) { | |||||
| return render(template, scope); | |||||
| }; | |||||
| buffer += value.call(scope, callback(), scopedRender) || ""; | |||||
| } else if (value) { | |||||
| buffer += callback(); | |||||
| } | |||||
| return buffer; | |||||
| } | |||||
| /** | |||||
| * Parses the given `template` and returns the source of a function that, | |||||
| * with the proper arguments, will render the template. Recognized options | |||||
| * include the following: | |||||
| * | |||||
| * - file The name of the file the template comes from (displayed in | |||||
| * error messages) | |||||
| * - tags An array of open and close tags the `template` uses. Defaults | |||||
| * to the value of Mustache.tags | |||||
| * - debug Set `true` to log the body of the generated function to the | |||||
| * console | |||||
| * - space Set `true` to preserve whitespace from lines that otherwise | |||||
| * contain only a {{tag}}. Defaults to `false` | |||||
| */ | |||||
| function parse(template, options) { | |||||
| options = options || {}; | |||||
| var tags = options.tags || exports.tags, | |||||
| openTag = tags[0], | |||||
| closeTag = tags[tags.length - 1]; | |||||
| var code = [ | |||||
| 'var buffer = "";', // output buffer | |||||
| "\nvar line = 1;", // keep track of source line number | |||||
| "\ntry {", | |||||
| '\nbuffer += "' | |||||
| ]; | |||||
| var spaces = [], // indices of whitespace in code on the current line | |||||
| hasTag = false, // is there a {{tag}} on the current line? | |||||
| nonSpace = false; // is there a non-space char on the current line? | |||||
| // Strips all space characters from the code array for the current line | |||||
| // if there was a {{tag}} on it and otherwise only spaces. | |||||
| var stripSpace = function () { | |||||
| if (hasTag && !nonSpace && !options.space) { | |||||
| while (spaces.length) { | |||||
| code.splice(spaces.pop(), 1); | |||||
| } | |||||
| } else { | |||||
| spaces = []; | |||||
| } | |||||
| hasTag = false; | |||||
| nonSpace = false; | |||||
| }; | |||||
| var sectionStack = [], updateLine, nextOpenTag, nextCloseTag; | |||||
| var setTags = function (source) { | |||||
| tags = trim(source).split(/\s+/); | |||||
| nextOpenTag = tags[0]; | |||||
| nextCloseTag = tags[tags.length - 1]; | |||||
| }; | |||||
| var includePartial = function (source) { | |||||
| code.push( | |||||
| '";', | |||||
| updateLine, | |||||
| '\nvar partial = partials["' + trim(source) + '"];', | |||||
| '\nif (partial) {', | |||||
| '\n buffer += render(partial,stack[stack.length - 1],partials);', | |||||
| '\n}', | |||||
| '\nbuffer += "' | |||||
| ); | |||||
| }; | |||||
| var openSection = function (source, inverted) { | |||||
| var name = trim(source); | |||||
| if (name === "") { | |||||
| throw debug(new Error("Section name may not be empty"), template, line, options.file); | |||||
| } | |||||
| sectionStack.push({name: name, inverted: inverted}); | |||||
| code.push( | |||||
| '";', | |||||
| updateLine, | |||||
| '\nvar name = "' + name + '";', | |||||
| '\nvar callback = (function () {', | |||||
| '\n return function () {', | |||||
| '\n var buffer = "";', | |||||
| '\nbuffer += "' | |||||
| ); | |||||
| }; | |||||
| var openInvertedSection = function (source) { | |||||
| openSection(source, true); | |||||
| }; | |||||
| var closeSection = function (source) { | |||||
| var name = trim(source); | |||||
| var openName = sectionStack.length != 0 && sectionStack[sectionStack.length - 1].name; | |||||
| if (!openName || name != openName) { | |||||
| throw debug(new Error('Section named "' + name + '" was never opened'), template, line, options.file); | |||||
| } | |||||
| var section = sectionStack.pop(); | |||||
| code.push( | |||||
| '";', | |||||
| '\n return buffer;', | |||||
| '\n };', | |||||
| '\n})();' | |||||
| ); | |||||
| if (section.inverted) { | |||||
| code.push("\nbuffer += renderSection(name,stack,callback,true);"); | |||||
| } else { | |||||
| code.push("\nbuffer += renderSection(name,stack,callback);"); | |||||
| } | |||||
| code.push('\nbuffer += "'); | |||||
| }; | |||||
| var sendPlain = function (source) { | |||||
| code.push( | |||||
| '";', | |||||
| updateLine, | |||||
| '\nbuffer += lookup("' + trim(source) + '",stack,"");', | |||||
| '\nbuffer += "' | |||||
| ); | |||||
| }; | |||||
| var sendEscaped = function (source) { | |||||
| code.push( | |||||
| '";', | |||||
| updateLine, | |||||
| '\nbuffer += escapeHTML(lookup("' + trim(source) + '",stack,""));', | |||||
| '\nbuffer += "' | |||||
| ); | |||||
| }; | |||||
| var line = 1, c, callback; | |||||
| for (var i = 0, len = template.length; i < len; ++i) { | |||||
| if (template.slice(i, i + openTag.length) === openTag) { | |||||
| i += openTag.length; | |||||
| c = template.substr(i, 1); | |||||
| updateLine = '\nline = ' + line + ';'; | |||||
| nextOpenTag = openTag; | |||||
| nextCloseTag = closeTag; | |||||
| hasTag = true; | |||||
| switch (c) { | |||||
| case "!": // comment | |||||
| i++; | |||||
| callback = null; | |||||
| break; | |||||
| case "=": // change open/close tags, e.g. {{=<% %>=}} | |||||
| i++; | |||||
| closeTag = "=" + closeTag; | |||||
| callback = setTags; | |||||
| break; | |||||
| case ">": // include partial | |||||
| i++; | |||||
| callback = includePartial; | |||||
| break; | |||||
| case "#": // start section | |||||
| i++; | |||||
| callback = openSection; | |||||
| break; | |||||
| case "^": // start inverted section | |||||
| i++; | |||||
| callback = openInvertedSection; | |||||
| break; | |||||
| case "/": // end section | |||||
| i++; | |||||
| callback = closeSection; | |||||
| break; | |||||
| case "{": // plain variable | |||||
| closeTag = "}" + closeTag; | |||||
| // fall through | |||||
| case "&": // plain variable | |||||
| i++; | |||||
| nonSpace = true; | |||||
| callback = sendPlain; | |||||
| break; | |||||
| default: // escaped variable | |||||
| nonSpace = true; | |||||
| callback = sendEscaped; | |||||
| } | |||||
| var end = template.indexOf(closeTag, i); | |||||
| if (end === -1) { | |||||
| throw debug(new Error('Tag "' + openTag + '" was not closed properly'), template, line, options.file); | |||||
| } | |||||
| var source = template.substring(i, end); | |||||
| if (callback) { | |||||
| callback(source); | |||||
| } | |||||
| // Maintain line count for \n in source. | |||||
| var n = 0; | |||||
| while (~(n = source.indexOf("\n", n))) { | |||||
| line++; | |||||
| n++; | |||||
| } | |||||
| i = end + closeTag.length - 1; | |||||
| openTag = nextOpenTag; | |||||
| closeTag = nextCloseTag; | |||||
| } else { | |||||
| c = template.substr(i, 1); | |||||
| switch (c) { | |||||
| case '"': | |||||
| case "\\": | |||||
| nonSpace = true; | |||||
| code.push("\\" + c); | |||||
| break; | |||||
| case "\r": | |||||
| // Ignore carriage returns. | |||||
| break; | |||||
| case "\n": | |||||
| spaces.push(code.length); | |||||
| code.push("\\n"); | |||||
| stripSpace(); // Check for whitespace on the current line. | |||||
| line++; | |||||
| break; | |||||
| default: | |||||
| if (isWhitespace(c)) { | |||||
| spaces.push(code.length); | |||||
| } else { | |||||
| nonSpace = true; | |||||
| } | |||||
| code.push(c); | |||||
| } | |||||
| } | |||||
| } | |||||
| if (sectionStack.length != 0) { | |||||
| throw debug(new Error('Section "' + sectionStack[sectionStack.length - 1].name + '" was not closed properly'), template, line, options.file); | |||||
| } | |||||
| // Clean up any whitespace from a closing {{tag}} that was at the end | |||||
| // of the template without a trailing \n. | |||||
| stripSpace(); | |||||
| code.push( | |||||
| '";', | |||||
| "\nreturn buffer;", | |||||
| "\n} catch (e) { throw {error: e, line: line}; }" | |||||
| ); | |||||
| // Ignore `buffer += "";` statements. | |||||
| var body = code.join("").replace(/buffer \+= "";\n/g, ""); | |||||
| if (options.debug) { | |||||
| if (typeof console != "undefined" && console.log) { | |||||
| console.log(body); | |||||
| } else if (typeof print === "function") { | |||||
| print(body); | |||||
| } | |||||
| } | |||||
| return body; | |||||
| } | |||||
| /** | |||||
| * Used by `compile` to generate a reusable function for the given `template`. | |||||
| */ | |||||
| function _compile(template, options) { | |||||
| var args = "view,partials,stack,lookup,escapeHTML,renderSection,render"; | |||||
| var body = parse(template, options); | |||||
| var fn = new Function(args, body); | |||||
| // This anonymous function wraps the generated function so we can do | |||||
| // argument coercion, setup some variables, and handle any errors | |||||
| // encountered while executing it. | |||||
| return function (view, partials) { | |||||
| partials = partials || {}; | |||||
| var stack = [view]; // context stack | |||||
| try { | |||||
| return fn(view, partials, stack, lookup, escapeHTML, renderSection, render); | |||||
| } catch (e) { | |||||
| throw debug(e.error, template, e.line, options.file); | |||||
| } | |||||
| }; | |||||
| } | |||||
| // Cache of pre-compiled templates. | |||||
| var _cache = {}; | |||||
| /** | |||||
| * Clear the cache of compiled templates. | |||||
| */ | |||||
| function clearCache() { | |||||
| _cache = {}; | |||||
| } | |||||
| /** | |||||
| * Compiles the given `template` into a reusable function using the given | |||||
| * `options`. In addition to the options accepted by Mustache.parse, | |||||
| * recognized options include the following: | |||||
| * | |||||
| * - cache Set `false` to bypass any pre-compiled version of the given | |||||
| * template. Otherwise, a given `template` string will be cached | |||||
| * the first time it is parsed | |||||
| */ | |||||
| function compile(template, options) { | |||||
| options = options || {}; | |||||
| // Use a pre-compiled version from the cache if we have one. | |||||
| if (options.cache !== false) { | |||||
| if (!_cache[template]) { | |||||
| _cache[template] = _compile(template, options); | |||||
| } | |||||
| return _cache[template]; | |||||
| } | |||||
| return _compile(template, options); | |||||
| } | |||||
| /** | |||||
| * High-level function that renders the given `template` using the given | |||||
| * `view` and `partials`. If you need to use any of the template options (see | |||||
| * `compile` above), you must compile in a separate step, and then call that | |||||
| * compiled function. | |||||
| */ | |||||
| function render(template, view, partials) { | |||||
| return compile(template)(view, partials); | |||||
| } | |||||
| })(Mustache); | |||||
| @@ -0,0 +1,5 @@ | |||||
| /*! Version: 0.5.48 */ | |||||
| /*! | |||||
| * mustache.js - Logic-less {{mustache}} templates with JavaScript | |||||
| * http://github.com/janl/mustache.js | |||||
| */var Mustache=typeof module!="undefined"&&module.exports||{};(function(a){function i(a){return h.test(a)}function n(a){return String(a).replace(/&(?!\w+;)|[<>"']/g,function(a){return m[a]||a})}function o(a,b,c,d){d=d||"<template>";var e=b.split("\n"),f=Math.max(c-3,0),g=Math.min(e.length,c+3),h=e.slice(f,g),i;for(var j=0,k=h.length;j<k;++j)i=j+f+1,h[j]=(i===c?" >> ":" ")+h[j];return a.template=b,a.line=c,a.file=d,a.message=[d+":"+c,h.join("\n"),"",a.message].join("\n"),a}function p(a,b,c){if(a===".")return b[b.length-1];var d=a.split("."),e=d.length-1,f=d[e],g,h,i=b.length,j,k;while(i){k=b.slice(0),h=b[--i],j=0;while(j<e){h=h[d[j++]];if(h==null)break;k.push(h)}if(h&&typeof h=="object"&&f in h){g=h[f];break}}return typeof g=="function"&&(g=g.call(k[k.length-1])),g==null?c:g}function q(a,b,c,d){var e="",h=p(a,b);if(d){if(h==null||h===!1||f(h)&&h.length===0)e+=c()}else if(f(h))g(h,function(a){b.push(a),e+=c(),b.pop()});else if(typeof h=="object")b.push(h),e+=c(),b.pop();else if(typeof h=="function"){var i=b[b.length-1],j=function(a){return w(a,i)};e+=h.call(i,c(),j)||""}else h&&(e+=c());return e}function r(b,c){c=c||{};var d=c.tags||a.tags,e=d[0],f=d[d.length-1],g=['var buffer = "";',"\nvar line = 1;","\ntry {",'\nbuffer += "'],h=[],k=!1,l=!1,m=function(){if(k&&!l&&!c.space)while(h.length)g.splice(h.pop(),1);else h=[];k=!1,l=!1},n=[],p,q,r,s=function(a){d=j(a).split(/\s+/),q=d[0],r=d[d.length-1]},t=function(a){g.push('";',p,'\nvar partial = partials["'+j(a)+'"];',"\nif (partial) {","\n buffer += render(partial,stack[stack.length - 1],partials);","\n}",'\nbuffer += "')},u=function(a,d){var e=j(a);if(e==="")throw o(new Error("Section name may not be empty"),b,z,c.file);n.push({name:e,inverted:d}),g.push('";',p,'\nvar name = "'+e+'";',"\nvar callback = (function () {","\n return function () {",'\n var buffer = "";','\nbuffer += "')},v=function(a){u(a,!0)},w=function(a){var d=j(a),e=n.length!=0&&n[n.length-1].name;if(!e||d!=e)throw o(new Error('Section named "'+d+'" was never opened'),b,z,c.file);var f=n.pop();g.push('";',"\n return buffer;","\n };","\n})();"),f.inverted?g.push("\nbuffer += renderSection(name,stack,callback,true);"):g.push("\nbuffer += renderSection(name,stack,callback);"),g.push('\nbuffer += "')},x=function(a){g.push('";',p,'\nbuffer += lookup("'+j(a)+'",stack,"");','\nbuffer += "')},y=function(a){g.push('";',p,'\nbuffer += escapeHTML(lookup("'+j(a)+'",stack,""));','\nbuffer += "')},z=1,A,B;for(var C=0,D=b.length;C<D;++C)if(b.slice(C,C+e.length)===e){C+=e.length,A=b.substr(C,1),p="\nline = "+z+";",q=e,r=f,k=!0;switch(A){case"!":C++,B=null;break;case"=":C++,f="="+f,B=s;break;case">":C++,B=t;break;case"#":C++,B=u;break;case"^":C++,B=v;break;case"/":C++,B=w;break;case"{":f="}"+f;case"&":C++,l=!0,B=x;break;default:l=!0,B=y}var E=b.indexOf(f,C);if(E===-1)throw o(new Error('Tag "'+e+'" was not closed properly'),b,z,c.file);var F=b.substring(C,E);B&&B(F);var G=0;while(~(G=F.indexOf("\n",G)))z++,G++;C=E+f.length-1,e=q,f=r}else{A=b.substr(C,1);switch(A){case'"':case"\\":l=!0,g.push("\\"+A);break;case"\r":break;case"\n":h.push(g.length),g.push("\\n"),m(),z++;break;default:i(A)?h.push(g.length):l=!0,g.push(A)}}if(n.length!=0)throw o(new Error('Section "'+n[n.length-1].name+'" was not closed properly'),b,z,c.file);m(),g.push('";',"\nreturn buffer;","\n} catch (e) { throw {error: e, line: line}; }");var H=g.join("").replace(/buffer \+= "";\n/g,"");return c.debug&&(typeof console!="undefined"&&console.log?console.log(H):typeof print=="function"&&print(H)),H}function s(a,b){var c="view,partials,stack,lookup,escapeHTML,renderSection,render",d=r(a,b),e=new Function(c,d);return function(c,d){d=d||{};var f=[c];try{return e(c,d,f,p,n,q,w)}catch(g){throw o(g.error,a,g.line,b.file)}}}function u(){t={}}function v(a,b){return b=b||{},b.cache!==!1?(t[a]||(t[a]=s(a,b)),t[a]):s(a,b)}function w(a,b,c){return v(a)(b,c)}a.name="mustache.js",a.version="0.5.14",a.tags=["{{","}}"],a.parse=r,a.compile=v,a.render=w,a.clearCache=u,a.to_html=function(a,b,c,d){var e=w(a,b,c);if(typeof d!="function")return e;d(e)};var b=Object.prototype.toString,c=Array.isArray,d=Array.prototype.forEach,e=String.prototype.trim,f;c?f=c:f=function(a){return b.call(a)==="[object Array]"};var g;d?g=function(a,b,c){return d.call(a,b,c)}:g=function(a,b,c){for(var d=0,e=a.length;d<e;++d)b.call(c,a[d],d,a)};var h=/^\s*$/,j;if(e)j=function(a){return a==null?"":e.call(a)};else{var k,l;i(" ")?(k=/^\s+/,l=/\s+$/):(k=/^[\s\xA0]+/,l=/[\s\xA0]+$/),j=function(a){return a==null?"":String(a).replace(k,"").replace(l,"")}}var m={"&":"&","<":"<",">":">",'"':""","'":"'"},t={}})(Mustache); | |||||
| @@ -0,0 +1,8 @@ | |||||
| { | |||||
| "name": "mustache", | |||||
| "version": "0.5.48", | |||||
| "description": "Logic-less {{mustache}} templates with JavaScript", | |||||
| "author": "mustache.js Authors <http://github.com/janl/mustache.js>", | |||||
| "keywords": ["mustache", "template", "templates", "ejs"], | |||||
| "main": "./mustache" | |||||
| } | |||||
| @@ -11,6 +11,7 @@ | |||||
| <h2> Latest Releases </h2> | <h2> Latest Releases </h2> | ||||
| <ul class="index"> | <ul class="index"> | ||||
| <li><a href="0.5.48/">0.5.48</a></li> | |||||
| <li><a href="0.5.47/">0.5.47</a></li> | <li><a href="0.5.47/">0.5.47</a></li> | ||||
| <li><a href="0.5.45/">0.5.45</a></li> | <li><a href="0.5.45/">0.5.45</a></li> | ||||
| <li><a href="0.5.44/">0.5.44</a></li> | <li><a href="0.5.44/">0.5.44</a></li> | ||||