Преглед изворни кода

Merge remote branch 'janl/master'

tags/0.5.2
wittemann пре 14 година
родитељ
комит
acb6d1a3c9
100 измењених фајлова са 969 додато и 880 уклоњено
  1. +5
    -1
      .gitignore
  2. +1
    -1
      .travis.yml
  3. +0
    -49
      CHANGES.md
  4. +290
    -220
      README.md
  5. +29
    -29
      Rakefile
  6. +62
    -0
      TESTING.md
  7. +0
    -54
      TESTS_HELP.md
  8. +0
    -22
      THANKS.md
  9. +0
    -3
      examples/array_of_partials_implicit_partial.js
  10. +0
    -1
      examples/array_of_strings.html
  11. +0
    -2
      examples/array_of_strings_options.html
  12. +0
    -1
      examples/array_of_strings_options.js
  13. +0
    -1
      examples/array_of_strings_options.txt
  14. +0
    -5
      examples/array_partial.js
  15. +0
    -6
      examples/complex.txt
  16. +0
    -1
      examples/empty_partial.2.html
  17. +0
    -3
      examples/empty_partial.js
  18. +0
    -1
      examples/empty_template.txt
  19. +0
    -1
      examples/error_not_found.txt
  20. +0
    -2
      examples/inverted_section.html
  21. +0
    -4
      examples/nesting.txt
  22. +0
    -1
      examples/template_partial.2.html
  23. +0
    -8
      examples/template_partial.js
  24. +0
    -1
      examples/two_in_a_row.html
  25. +0
    -1
      examples/two_sections.txt
  26. +0
    -1
      examples/unescaped.html
  27. +0
    -1
      examples/unknown_pragma.html
  28. +0
    -1
      examples/unknown_pragma.js
  29. +0
    -1
      examples/unknown_pragma.txt
  30. +0
    -19
      examples/view_partial.js
  31. +0
    -19
      examples/whitespace_partial.js
  32. +0
    -8
      lib/package.json
  33. +0
    -7
      mustache-commonjs/mustache.js.tpl.post
  34. +0
    -6
      mustache-commonjs/mustache.js.tpl.pre
  35. +0
    -7
      mustache-commonjs/package.json
  36. +0
    -4
      mustache-dojo/mustache.js.tpl.post
  37. +0
    -6
      mustache-jquery/jquery.mustache.js.tpl.post
  38. +0
    -4
      mustache-yui3/mustache.js.tpl.post
  39. +490
    -376
      mustache.js
  40. +14
    -0
      mustache.js.nuspec
  41. +8
    -0
      package.json
  42. +3
    -0
      spec/_files/ampersand_escape.js
  43. +1
    -0
      spec/_files/ampersand_escape.mustache
  44. +1
    -0
      spec/_files/ampersand_escape.txt
  45. +0
    -0
      spec/_files/apostrophe.js
  46. +0
    -0
      spec/_files/apostrophe.mustache
  47. +0
    -0
      spec/_files/apostrophe.txt
  48. +0
    -0
      spec/_files/array_of_strings.js
  49. +1
    -0
      spec/_files/array_of_strings.mustache
  50. +0
    -0
      spec/_files/array_of_strings.txt
  51. +3
    -0
      spec/_files/backslashes.js
  52. +7
    -0
      spec/_files/backslashes.mustache
  53. +7
    -0
      spec/_files/backslashes.txt
  54. +0
    -0
      spec/_files/bug_11_eating_whitespace.js
  55. +0
    -0
      spec/_files/bug_11_eating_whitespace.mustache
  56. +0
    -0
      spec/_files/bug_11_eating_whitespace.txt
  57. +4
    -0
      spec/_files/changing_delimiters.js
  58. +1
    -0
      spec/_files/changing_delimiters.mustache
  59. +1
    -0
      spec/_files/changing_delimiters.txt
  60. +0
    -0
      spec/_files/comments.js
  61. +0
    -0
      spec/_files/comments.mustache
  62. +0
    -0
      spec/_files/comments.txt
  63. +0
    -0
      spec/_files/complex.js
  64. +1
    -1
      spec/_files/complex.mustache
  65. +6
    -0
      spec/_files/complex.txt
  66. +8
    -0
      spec/_files/context_lookup.js
  67. +1
    -0
      spec/_files/context_lookup.mustache
  68. +1
    -0
      spec/_files/context_lookup.txt
  69. +0
    -0
      spec/_files/delimiters.js
  70. +1
    -1
      spec/_files/delimiters.mustache
  71. +0
    -0
      spec/_files/delimiters.txt
  72. +4
    -0
      spec/_files/disappearing_whitespace.js
  73. +1
    -0
      spec/_files/disappearing_whitespace.mustache
  74. +1
    -0
      spec/_files/disappearing_whitespace.txt
  75. +0
    -0
      spec/_files/dot_notation.js
  76. +0
    -0
      spec/_files/dot_notation.mustache
  77. +0
    -0
      spec/_files/dot_notation.txt
  78. +0
    -0
      spec/_files/double_render.js
  79. +0
    -0
      spec/_files/double_render.mustache
  80. +0
    -0
      spec/_files/double_render.txt
  81. +3
    -0
      spec/_files/empty_list.js
  82. +4
    -0
      spec/_files/empty_list.mustache
  83. +1
    -0
      spec/_files/empty_list.txt
  84. +0
    -0
      spec/_files/empty_sections.js
  85. +0
    -0
      spec/_files/empty_sections.mustache
  86. +0
    -0
      spec/_files/empty_sections.txt
  87. +6
    -0
      spec/_files/empty_string.js
  88. +1
    -0
      spec/_files/empty_string.mustache
  89. +1
    -0
      spec/_files/empty_string.txt
  90. +0
    -0
      spec/_files/empty_template.js
  91. +0
    -0
      spec/_files/empty_template.mustache
  92. +1
    -0
      spec/_files/empty_template.txt
  93. +0
    -0
      spec/_files/error_not_found.js
  94. +0
    -0
      spec/_files/error_not_found.mustache
  95. +0
    -0
      spec/_files/error_not_found.txt
  96. +0
    -0
      spec/_files/escaped.js
  97. +0
    -0
      spec/_files/escaped.mustache
  98. +0
    -0
      spec/_files/escaped.txt
  99. +0
    -0
      spec/_files/higher_order_sections.js
  100. +0
    -0
      spec/_files/higher_order_sections.mustache

+ 5
- 1
.gitignore Прегледај датотеку

@@ -1,5 +1,9 @@
.DS_Store
.rvmrc
runner.js
jquery.mustache.js
qooxdoo.mustache.js
dojox
yui3
commonjs.mustache.js
requirejs.mustache.js


+ 1
- 1
.travis.yml Прегледај датотеку

@@ -1,5 +1,5 @@
rvm:
- 1.9.2
before_script:
- sudo apt-get install xulrunner-1.9
- sudo apt-get -y install xulrunner-2.0
- gem install rspec

+ 0
- 49
CHANGES.md Прегледај датотеку

@@ -1,49 +0,0 @@
# mustache.js Changes

## 0.3.1-dev-twitter-b (8/23/2011)

* Cached regexes for improved performance

## 0.3.1-dev-twitter (12/3/2010)

* fixed double-rendering bug
* added Rhino test-runner alongside JavaScriptCore

## 0.3.1 (??-??-????)

## 0.3.0 (21-07-2010)

* Improved whitespace handling.
* Make IMPLICIT ITERATORS a first class feature.
* Fix Rhino compat.
* CommonJS packaging is no longer a special case.
* DRY Rakefile.
* Allow whitespace around tag names.
* Fix partial scope.
* Fix Comments.
* Added inverted sections.
* Avoid double encoding of entities.
* Use sections to dereference subcontexts.
* Added higher order sections.


## 0.2.3 (28-03-2010)

* Better error message for missing partials.
* Added more robust type detection.
* Parse pragmas only once.
* Throw exception when encountering an unknown pragma.
* Ignore undefined partial contexts. Returns verbatim partials.
* Added yui3 packaging.


## 0.2.2 (11-02-2010)

* ctemplate compat: Partials are indicated by >, not <.
* Add support for {{%PRAGMA}} to enable features.
* Made array of strings an option. Enable with `{{%JSTACHE-ENABLE-STRING-ARRAYS}}`.
* mustache compat: Don't barf on unknown variables.
* Add `rake dojo` target to create a dojo package.
* Add streaming api.
* Rename JSTACHE-ENABLE-STRING-ARRAYS to IMPLICIT-ITERATOR.
* Add support for pragma options.

+ 290
- 220
README.md Прегледај датотеку

@@ -1,348 +1,418 @@
# mustache.js — Logic-less templates with JavaScript
# mustache.js - Logic-less {{mustache}} templates with JavaScript

> What could be more logical awesome than no logic at all?

For a list of implementations (other than JavaScript) and editor
plugins, see <http://mustache.github.com/>.
[mustache.js](http://github.com/janl/mustache.js) is an implementation of the
[Mustache](http://mustache.github.com/) template system in JavaScript.

[Mustache](http://mustache.github.com/) is a logic-less template syntax. It can
be used for HTML, config files, source code - anything. It works by expanding
tags in a template using values provided in a hash or object.

## Where to Use?
We call it "logic-less" because there are no if statements, else clauses, or for
loops. Instead there are only tags. Some tags are replaced with a value, some
nothing, and others a series of values.

You can use mustache.js rendering stuff in various scenarios. E.g. you can
render templates in your browser, or rendering server-side stuff with
[node.js][node.js], use it for rendering stuff in [CouchDB][couchdb]’s views.
For a language-agnostic overview of Mustache's template syntax, see the
`mustache(5)` [manpage](http://mustache.github.com/mustache.5.html).

## Where to use mustache.js?

## Who Uses Mustache?
You can use mustache.js to render templates in many various scenarios where you
can use JavaScript. For example, you can render templates in a browser,
server-side using [node](http://nodejs.org/), in [CouchDB](http://couchdb.apache.org/)
views, or in almost any other environment where you can use JavaScript.

An updated list is kept on the Github wiki. Add yourself, if you use
mustache.js: <http://wiki.github.com/janl/mustache.js/beard-competition>
## Who uses mustache.js?

An updated list of mustache.js users is kept [on the Github wiki](http://wiki.github.com/janl/mustache.js/beard-competition).
Add yourself or your company if you use mustache.js!

## Usage

A quick example how to use mustache.js:
Below is quick example how to use mustache.js:

var view = {
title: "Joe",
calc: function() {
calc: function () {
return 2 + 4;
}
}
};

var template = "{{title}} spends {{calc}}";
var output = Mustache.render("{{title}} spends {{calc}}", view);

var html = Mustache.to_html(template, view);
In this example, the `Mustache.render` function takes two parameters: 1) the
[mustache](http://mustache.github.com/) template and 2) a `view` object that
contains the data and code needed to render the template.

`template` is a simple string with mustache tags and `view` is a JavaScript
object containing the data and any code to render the template.
### CommonJS

mustache.js is usable without any modification in both browsers and [CommonJS](http://www.commonjs.org/)
environments like [node.js](http://nodejs.org/). To use it as a CommonJS module,
simply require the file, like this:

## Template Tag Types
var Mustache = require("mustache");

There are several types of tags currently implemented in mustache.js.
## Templates

For a language-agnostic overview of Mustache’s template syntax, see the
`mustache(5)` manpage or <http://mustache.github.com/mustache.5.html>.
A [mustache](http://mustache.github.com/) template is a string that contains
any number of mustache tags. Tags are indicated by the double mustaches that
surround them. `{{person}}` is a tag, as is `{{#person}}`. In both examples we
refer to `person` as the tag's key.

### Simple Tags
There are several types of tags available in mustache.js.

Tags are always surrounded by mustaches like this `{{foobar}}`.
### Variables

var view = {name: "Joe", say_hello: function(){ return "hello" }}
The most basic tag type is a simple variable. A `{{name}}` tag renders the value
of the `name` key in the current context. If there is no such key, nothing is
rendered.

template = "{{say_hello}}, {{name}}"
All variables are HTML-escaped by default. If you want to render unescaped HTML,
use the triple mustache: `{{{name}}}`. You can also use `&` to unescape a
variable.

Template:

#### Accessing values in nested objects (Dot Notation)
* {{name}}
* {{age}}
* {{company}}
* {{{company}}}
* {{&company}}

To access data logically grouped into nested objects, specify a '.' delimited
path to the value.
View:

var contact = {
name: {first: "Bill", last: "Bobitybob" },
age: 37
{
"name": "Chris",
"company": "<b>GitHub</b>"
}

template = "Hello, {{name.first}} {{name.last}}. You are {{age}} years old."
Output:

*NOTICE*: The dot notation feature was recently implemented for the 0.4
release, which is not out as of Nov 9 2011. You can find the feature in the
current master branch of mustachejs.
* Chris
*
* &lt;b&gt;GitHub&lt;/b&gt;
* <b>GitHub</b>
* <b>GitHub</b>

### Conditional Sections
JavaScript's dot notation may be used to access keys that are properties of
objects in a view.

Conditional sections begin with `{{#condition}}` and end with
`{{/condition}}`. When `condition` evaluates to true, the section is rendered,
otherwise the whole block will output nothing at all. `condition` may be a
function returning true/false or a simple boolean.
Template:

var view = {condition: function() {
// [...your code goes here...]
return true;
}}
* {{name.first}} {{name.last}}
* {{age}}

{{#condition}}
I will be visible if condition is true
{{/condition}}
View:

{
"name": {
"first": "Michael",
"last": "Jackson"
},
"age": "RIP"
}

### Enumerable Sections
Output:

Enumerable Sections use the same syntax as condition sections do.
`{{#shopping_items}}` and `{{/shopping_items}}`. Actually the view decides how
mustache.js renders the section. If the view returns an array, it will
iterator over the items. Use `{{.}}` to access the current item inside the
enumeration section.
* Michael Jackson
* RIP

var view = {name: "Joe's shopping card",
items: ["bananas", "apples"]}
### Sections

var template = "{{name}}: <ul> {{#items}}<li>{{.}}</li>{{/items}} </ul>"
Sections render blocks of text one or more times, depending on the value of the
key in the current context.

Outputs:
Joe's shopping card: <ul><li>bananas</li><li>apples</li></ul>
A section begins with a pound and ends with a slash. That is, `{{#person}}`
begins a `person` section, while `{{/person}}` ends it. The text between the two
tags is referred to as that section's "block".

The behavior of the section is determined by the value of the key.

### Higher Order Sections
#### False Values or Empty Lists

If a section key returns a function, it will be called and passed both the
unrendered block of text and a renderer convenience function.
If the `person` key exists and has a value of `null`, `undefined`, or `false`,
or is an empty list, the block will not be rendered.

Given this object:
Template:

"name": "Tater",
"bolder": function() {
return function(text, render) {
return "<b>" + render(text) + '</b>'
}
}
Shown.
{{#person}}
Never shown!
{{/person}}

And this template:
View:

{{#bolder}}Hi {{name}}.{{/bolder}}
{
"person": true
}

We'll get this output:
Output:

<b>Hi Tater.</b>
Shown.

#### Non-Empty Lists

As you can see, we’re pre-processing the text in the block. This can be used
to implement caching, filters (like syntax highlighting), etc.
If the `person` key exists and is not `null`, `undefined`, or `false`, and is
not an empty list the block will be rendered one or more times.

You can use `this.name` to access the attribute `name` from your view.
When the value is a list, the block is rendered once for each item in the list.
The context of the block is set to the current item in the list for each
iteration. In this way we can loop over collections.

### Dereferencing Sections
Template:

If your data has components that are logically grouped into nested objects,
you may wish to dereference an object to access its values.
{{#stooges}}
<b>{{name}}</b>
{{/stooges}}

Given this object:
View:

{
"name": "Bill",
"address": {
"street": "801 Streetly street",
"city": "Boston",
"state": "MA",
"zip" "02101"
}
"stooges": [
{ "name": "Moe" },
{ "name": "Larry" },
{ "name": "Curly" }
]
}

And this template:

<h1>Contact: {{name}}</h1>
{{#address}}
<p>{{street}}</p>
<p>{{city}}, {{state}} {{zip}}</p>
{{/address}}
Output:

We'll get this output:
<b>Moe</b>
<b>Larry</b>
<b>Curly</b>

<h1>Contact: Bill</h1>
<p>801 Streetly street</p>
<p>Boston, MA 02101</p>
When looping over an array of strings, a `.` can be used to refer to the current
item in the list.

### Inverted Sections
Template:

An inverted section opens with `{{^section}}` instead of `{{#section}}` and
uses a boolean negative to evaluate. Empty arrays are considered falsy.
{{#musketeers}}
* {{.}}
{{/musketeers}}

View:

var inverted_section = {
"repo": []
{
"musketeers": ["Athos", "Aramis", "Porthos", "D'Artagnan"]
}

Template:

{{#repo}}<b>{{name}}</b>{{/repo}}
{{^repo}}No repos :({{/repo}}
Output:

Result:
* Athos
* Aramis
* Porthos
* D'Artagnan

No repos :(
If the value of a section variable is a function, it will be called in the
context of the current item in the list on each iteration.

Template:

### View Partials
{{#beatles}}
* {{name}}
{{/beatles}}

mustache.js supports a quite powerful but yet simple view partial mechanism.
Use the following syntax for partials: `{{>partial_name}}`
View:

var view = {
name: "Joe",
winnings: {
value: 1000,
taxed_value: function() {
return this.value - (this.value * 0.4);
}
{
"beatles": [
{ "firstName": "John", "lastName": "Lennon" },
{ "firstName": "Paul", "lastName": "McCartney" },
{ "firstName": "George", "lastName": "Harrison" },
{ "firstName": "Ringo", "lastName": "Starr" }
],
"name": function () {
return this.firstName + " " + this.lastName;
}
};

var template = "Welcome, {{name}}! {{>winnings}}"
var partials = {
winnings: "You just won ${{value}} (which is ${{taxed_value}} after tax)"};

var output = Mustache.to_html(template, view, partials)

output will be:
Welcome, Joe! You just won $1000 (which is $600 after tax)

You invoke a partial with `{{>winnings}}`. Invoking the partial `winnings`
will tell mustache.js to look for a object in the context's property
`winnings`. It will then use that object as the context for the template found
in `partials` for `winnings`.

## Escaping

mustache.js does escape all values when using the standard double mustache
syntax. Characters which will be escaped: `& \ " ' < >`. To disable escaping,
simply use triple mustaches like `{{{unescaped_variable}}}`.

Example: Using `{{variable}}` inside a template for `5 > 2` will result in `5 &gt; 2`, where as the usage of `{{{variable}}}` will result in `5 > 2`.
}

Output:

## Streaming
* John Lennon
* Paul McCartney
* George Harrison
* Ringo Starr

To stream template results out of mustache.js, you can pass an optional
`send()` callback to the `to_html()` call:
#### Functions

Mustache.to_html(template, view, partials, function(line) {
print(line);
});
If the value of a section key is a function, it is called with the section's
literal block of text, un-rendered, as its first argument. The second argument
is a special rendering function that uses the current view as its view argument.
It is called in the context of the current view object.

Template:

## Pragmas
{{#bold}}Hi {{name}}.{{/bold}}

Pragma tags let you alter the behaviour of mustache.js. They have the format
of
View:

{{%PRAGMANAME}}
{
"name": "Tater",
"bold": function () {
return function (text, render) {
return "<b>" + render(text) + "</b>";
}
}
}

and they accept options:
Output:

{{%PRAGMANAME option=value}}
<b>Hi Tater.</b>

### Inverted Sections

### IMPLICIT-ITERATOR
An inverted section opens with `{{^section}}` instead of `{{#section}}`. The
block of an inverted section is rendered only if the value of that section's tag
is `null`, `undefined`, `false`, or an empty list.

When using a block to iterate over an enumerable (Array), mustache.js expects
an objects as enumerable items. The implicit iterator pragma enables optional
behaviour of allowing literals as enumerable items. Consider this view:
Template:

var view = {
foo: [1, 2, 3, 4, 5, "french"]
};
{{#repos}}<b>{{name}}</b>{{/repos}}
{{^repos}}No repos :({{/repos}}

The following template can iterate over the member `foo`:
View:

{{%IMPLICIT-ITERATOR}}
{{#foo}}
{{.}}
{{/foo}}
{
"repos": []
}

If you don't like the dot in there, the pragma accepts an option to set your
own iteration marker:
Output:

{{%IMPLICIT-ITERATOR iterator=bob}}
{{#foo}}
{{bob}}
{{/foo}}
No repos :(

## More Examples and Documentation
### Comments

See `examples/` for more goodies and read the [original mustache docs][m]
Comments begin with a bang and are ignored. The following template:

## Command Line
<h1>Today{{! ignore me }}.</h1>

See `mustache(1)` man page or
<http://defunkt.github.com/mustache/mustache.1.html>
for command line docs.
Will render as follows:

Or just install it as a RubyGem:
<h1>Today.</h1>

$ gem install mustache
$ mustache -h
Comments may contain newlines.

[m]: http://github.com/defunkt/mustache/#readme
[node.js]: http://nodejs.org
[couchdb]: http://couchdb.apache.org
### Partials

Partials begin with a greater than sign, like {{> box}}.

## Plugins for jQuery, Dojo, Yui, CommonJS, qooxdoo
Partials are rendered at runtime (as opposed to compile time), so recursive
partials are possible. Just avoid infinite loops.

This repository lets you build modules for [jQuery][], [Dojo][], [Yui][] and
[CommonJS][] / [Node.js][] with the help of `rake`.
They also inherit the calling context. Whereas in ERB you may have this:

NOTE: The default `rake` task is only used for testing and require rspec to be
installed (see below).
<%= partial :next_more, :start => start, :size => size %>

Run `rake jquery` to get a jQuery compatible plugin file in the
`mustache-jquery/` directory.
Mustache requires only this:

Run `rake dojo` to get a Dojo compatible plugin file in the `mustache-dojo/`
directory.
{{> next_more}}

Run `rake yui` to get a Yui compatible plugin file in the `mustache-yui/`
directory.
Why? Because the `next_more.mustache` file will inherit the `size` and `start`
variables from the calling context. In this way you may want to think of
partials as includes, or template expansion, even though it's not literally true.

Run `rake commonjs` to get a CommonJS compatible plugin file in the
`mustache-commonjs/` directory which you can also use with [Node.js][].
For example, this template and partial:

Run `rake qooxdoo` to get a qooxdoo compatible file named `qooxdoo.mustache.js`.
base.mustache:
<h2>Names</h2>
{{#names}}
{{> user}}
{{/names}}

## Testing
user.mustache:
<strong>{{name}}</strong>

NOTE: You will need to install rspec first by running `gem install rspec`.
Can be thought of as a single, expanded template:

To run the mustache.js test suite, run `rake spec`. All specs will be run first with JavaScriptCore (using `jsc`)
and again with Rhino, using `java org.mozilla.javascript.tools.shell.Main`.
<h2>Names</h2>
{{#names}}
<strong>{{name}}</strong>
{{/names}}

JavaScriptCore is used from the OSX default location:
In mustache.js an object of partials may be passed as the third argument to
`Mustache.render`. The object should be keyed by the name of the partial, and
its value should be the partial text.

/System/Library/Frameworks/JavaScriptCore.framework/Versions/A/Resources/jsc
### Set Delimiter

To install Rhino on OSX, follow [these instructions](Rhino Install).
Set Delimiter tags start with an equals sign and change the tag delimiters from
`{{` and `}}` to custom strings.

### Adding Tests
Consider the following contrived example:

Tests are located in the `examples/` directory. Adding a new test requires three files. Here's an example to add a test named "foo":
* {{ default_tags }}
{{=<% %>=}}
* <% erb_style_tags %>
<%={{ }}=%>
* {{ default_tags_again }}

`examples/foo.html` (the template):
Here we have a list with three items. The first item uses the default tag style,
the second uses ERB style as defined by the Set Delimiter tag, and the third
returns to the default style after yet another Set Delimiter declaration.

foo {{bar}}
According to [ctemplates](http://google-ctemplate.googlecode.com/svn/trunk/doc/howto.html),
this "is useful for languages like TeX, where double-braces may occur in the
text and are awkward to use for markup."

`examples/foo.js` (the view context):
Custom delimiters may not contain whitespace or the equals sign.

var foo = {
bar: "baz"
};
## Streaming

`examples/foo.txt` (the expected output):
To stream template results out of mustache.js, you can pass an optional callback
to the call to `Mustache.render`:

foo baz
Mustache.render(template, view, partials, function (chunk) {
print(chunk);
});

[jQuery]: http://jquery.com/
[Dojo]: http://www.dojotoolkit.org/
[Yui]: http://developer.yahoo.com/yui/
[CommonJS]: http://www.commonjs.org/
[Node.js]: http://nodejs.org/
[Rhino Install]: http://michaux.ca/articles/installing-rhino-on-os-x
When the template is finished rendering, the callback will be called with `null`
after which it won't be called anymore for that rendering.

## Plugins for JavaScript Libraries

By default mustache.js may be used in a browser or any [CommonJS](http://www.commonjs.org/)
environment, including [node](http://nodejs.org/). Additionally, mustache.js may
be built specifically for several different client libraries and platforms,
including the following:

- [jQuery](http://jquery.com/)
- [MooTools](http://mootools.net/)
- [Dojo](http://www.dojotoolkit.org/)
- [YUI](http://developer.yahoo.com/yui/)
- [RequireJS](http://requirejs.org/)
- [qooxdoo](http://qooxdoo.org/)

These may be built using [Rake](http://rake.rubyforge.org/) and one of the
following commands:

$ rake jquery
$ rake mootools
$ rake dojo
$ rake yui
$ rake requirejs
$ rake qooxdoo

## Thanks

Mustache.js wouldn't kick ass if it weren't for these fine souls:

* Chris Wanstrath / defunkt
* Alexander Lang / langalex
* Sebastian Cohnen / tisba
* J Chris Anderson / jchris
* Tom Robinson / tlrobinson
* Aaron Quint / quirkey
* Douglas Crockford
* Nikita Vasilyev / NV
* Elise Wood / glytch
* Damien Mathieu / dmathieu
* Jakub Kuźma / qoobaa
* Will Leinweber / will
* dpree
* Jason Smith / jhs
* Aaron Gibralter / agibralter
* Ross Boucher / boucher
* Matt Sanford / mzsanford
* Ben Cherry / bcherry
* Michael Jackson / mjijackson

+ 29
- 29
Rakefile Прегледај датотеку

@@ -1,4 +1,5 @@
require 'rake'
require 'rake/clean'

task :default => :spec

@@ -7,53 +8,52 @@ task :spec do
require 'rspec/core/rake_task'
RSpec::Core::RakeTask.new(:spec) do |t|
#t.spec_opts = ['--options', "\"#{File.dirname(__FILE__)}/spec/spec.opts\""]
t.pattern = 'test/*_spec.rb'
t.pattern = 'spec/*_spec.rb'
end
end

# Creates a task that uses the various template wrappers to make a wrapped
# output file. There is some extra complexity because Dojo and YUI use
# different final locations.
def templated_build(name, opts={})
# Create a rule that uses the .tmpl.{pre,post} stuff to make a final,
# wrapped, output file.
# There is some extra complexity because Dojo and YUI3 use different
# template files and final locations.
short = name.downcase
source = "mustache-#{short}"
source = File.join("wrappers", short)
dependencies = ["mustache.js"] + Dir.glob("#{source}/*.tpl.*")
target_js = opts[:location] ? "mustache.js" : "#{short}.mustache.js"

CLEAN.include(opts[:location] ? opts[:location] : target_js)

desc "Package for #{name}"
task short.to_sym => dependencies do
target_js = opts[:location] ? "mustache.js" : "#{short}.mustache.js"

puts "Packaging for #{name}"
sh "mkdir -p #{opts[:location]}" if opts[:location]
sh "cat #{source}/#{target_js}.tpl.pre mustache.js \
#{source}/#{target_js}.tpl.post > #{opts[:location] || '.'}/#{target_js}"

# extra
if opts[:extra]
sh "sed -e 's/{{version}}/#{version}/' #{source}/#{opts[:extra]} \
> #{opts[:location]}/#{opts[:extra]}"
end
mkdir_p opts[:location] if opts[:location]

files = [
"#{source}/mustache.js.pre",
'mustache.js',
"#{source}/mustache.js.post"
]

puts "Done, see #{opts[:location] || '.'}/#{target_js}"
open("#{opts[:location] || '.'}/#{target_js}", 'w') do |f|
files.each {|file| f << File.read(file) }
end

puts "Done, see #{opts[:location] || '.'}/#{target_js}"
end
end

templated_build "CommonJS", :location => "lib", :extra => "package.json"
templated_build "jQuery"
templated_build "qooxdoo"
templated_build "MooTools"
templated_build "Dojo", :location => "dojox/string"
templated_build "YUI3", :location => "yui3/mustache"
templated_build "requirejs"


def version
File.read("mustache.js").match('version: "([^\"]+)",$')[1]
end

templated_build "RequireJS"
templated_build "qooxdoo"

desc "Remove temporary files."
task :clean do
sh "git clean -fdx"
task :minify do
# npm install uglify-js
mmjs = "mustache.min.js"
`echo "/*! Version: 0.5.1-dev */" > #{mmjs}`
`uglifyjs mustache.js >> #{mmjs}`
puts "Created #{mmjs}"
end

+ 62
- 0
TESTING.md Прегледај датотеку

@@ -0,0 +1,62 @@
## Running the mustache.js test suite

The mustache.js test suite uses the [RSpec](http://rspec.info/) testing
framework. In order to run the tests you'll need to install [Ruby](http://ruby-lang.org/)
as well as the `rake`, `rspec` (>=2), and `json` [RubyGems](http://rubygems.org/).

### How to install Ruby and the required gems from source

Make sure you have the required tools to compile it:

$ apt-get install build-essential libssl-dev libreadline5-dev zlib1g-dev

Download and extract the Ruby source, and install it:

$ wget ftp://ftp.ruby-lang.org/pub/ruby/stable-snapshot.tar.gz
$ tar xvzf stable-snapshot.tar.gz
$ cd ruby
$ ./configure && make && make install

Download and extract RubyGems, and install it:

$ wget http://production.cf.rubygems.org/rubygems/rubygems-1.8.12.tgz
$ tar xzvf rubygems-1.8.12.tgz
$ cd rubygems-1.8.12
$ ruby setup.rb

If you want to update RubyGems:

$ gem update --system

Install the required gems:

$ gem install rake rspec json

That's it!

### How to run the tests

The mustache.js test suite currently uses 4 different JavaScript runtime engines
to maximize portability across platforms and browsers. They are:

* node
* SpiderMonkey (Mozilla, Firefox)
* JavaScriptCore (WebKit, Safari)
* Rhino (Mozilla, Java)

When the test suite runs it will automatically determine which platforms are
available on your machine and run on all of them. The suite must run on at least
one platform in order to succeed.

Once you have at least one JavaScript platform installed, you can run the test
suite with the following command:

$ rake

### How to create a test

All test files live in the spec/_files directory. To create a new test:

* Create a template file called `somename.mustache`
* Create a JavaScript file containing the view called `somename.js`
* Create a text file with the expected result called `somename.txt`

+ 0
- 54
TESTS_HELP.md Прегледај датотеку

@@ -1,54 +0,0 @@
## How to run the tests

To run the test, you need ruby and the following gems installed : rake, rspec (>=2), json

### How to install ruby and the required gems from source

Make sure you have the required tools to compile it

# apt-get install build-essential libssl-dev libreadline5-dev zlib1g-dev

Download ruby source and extract the source

$ cd ~/
$ wget ftp://ftp.ruby-lang.org/pub/ruby/stable-snapshot.tar.gz
$ tar xvzf stable-snapshot.tar.gz

Install it

$ ./configure && make
# make install

download the last version of RubyGems from here
http://rubyforge.org/frs/?group_id=126

Extract the source

$ tar xzvf rubygems-1.8.4.tgz

Install it

$ cd rubygems-1.8.4
# ruby setup.rb

If you want to update RubyGems

# gem update --system

Install the required gems

# gem install rake rspec json

That's it!

### How to run the tests

$ rake

### How to create a test

- Create a template file `somename.html`
- Create a javascript file with data and functions `somename.js`
- Create a file the expected result `somename.txt`

Done!

+ 0
- 22
THANKS.md Прегледај датотеку

@@ -1,22 +0,0 @@
# Thanks

Mustache.js wouldn't kick ass if it weren't for these fine souls:

* Chris Wanstrath / defunkt
* Alexander Lang / langalex
* Sebastian Cohnen / tisba
* J Chris Anderson / jchris
* Tom Robinson / tlrobinson
* Aaron Quint / quirkey
* Douglas Crockford
* Nikita Vasilyev / NV
* Elise Wood / glytch
* Damien Mathieu / dmathieu
* Jakub Kuźma / qoobaa
* Will Leinweber / will
* dpree
* Jason Smith / jhs
* Aaron Gibralter / agibralter
* Ross Boucher / boucher
* Matt Sanford / mzsanford
* Ben Cherry / bcherry

+ 0
- 3
examples/array_of_partials_implicit_partial.js Прегледај датотеку

@@ -1,3 +0,0 @@
var partial_context = {
numbers: ['1', '2', '3', '4']
};

+ 0
- 1
examples/array_of_strings.html Прегледај датотеку

@@ -1 +0,0 @@
{{#array_of_strings}}{{.}} {{/array_of_strings}}

+ 0
- 2
examples/array_of_strings_options.html Прегледај датотеку

@@ -1,2 +0,0 @@
{{%IMPLICIT-ITERATOR iterator=rob}}
{{#array_of_strings_options}}{{rob}} {{/array_of_strings_options}}

+ 0
- 1
examples/array_of_strings_options.js Прегледај датотеку

@@ -1 +0,0 @@
var array_of_strings_options = {array_of_strings_options: ['hello', 'world']};

+ 0
- 1
examples/array_of_strings_options.txt Прегледај датотеку

@@ -1 +0,0 @@
hello world

+ 0
- 5
examples/array_partial.js Прегледај датотеку

@@ -1,5 +0,0 @@
var partial_context = {
partial: {
array: ['1', '2', '3', '4']
}
};

+ 0
- 6
examples/complex.txt Прегледај датотеку

@@ -1,6 +0,0 @@
<h1>Colors</h1>
<ul>
<li><strong>red</strong></li>
<li><a href="#Green">green</a></li>
<li><a href="#Blue">blue</a></li>
</ul>

+ 0
- 1
examples/empty_partial.2.html Прегледај датотеку

@@ -1 +0,0 @@
yo

+ 0
- 3
examples/empty_partial.js Прегледај датотеку

@@ -1,3 +0,0 @@
var partial_context = {
foo: 1
};

+ 0
- 1
examples/empty_template.txt Прегледај датотеку

@@ -1 +0,0 @@
<html><head></head><body><h1>Test</h1></body></html>

+ 0
- 1
examples/error_not_found.txt Прегледај датотеку

@@ -1 +0,0 @@


+ 0
- 2
examples/inverted_section.html Прегледај датотеку

@@ -1,2 +0,0 @@
{{#repo}}<b>{{name}}</b>{{/repo}}
{{^repo}}No repos :({{/repo}}

+ 0
- 4
examples/nesting.txt Прегледај датотеку

@@ -1,4 +0,0 @@
1
2
3

+ 0
- 1
examples/template_partial.2.html Прегледај датотеку

@@ -1 +0,0 @@
Again, {{again}}!

+ 0
- 8
examples/template_partial.js Прегледај датотеку

@@ -1,8 +0,0 @@
var partial_context = {
title: function() {
return "Welcome";
},
partial: {
again: "Goodbye"
}
}

+ 0
- 1
examples/two_in_a_row.html Прегледај датотеку

@@ -1 +0,0 @@
{{greeting}}, {{name}}!

+ 0
- 1
examples/two_sections.txt Прегледај датотеку

@@ -1 +0,0 @@


+ 0
- 1
examples/unescaped.html Прегледај датотеку

@@ -1 +0,0 @@
<h1>{{{title}}}</h1>

+ 0
- 1
examples/unknown_pragma.html Прегледај датотеку

@@ -1 +0,0 @@
{{%I-HAVE-THE-GREATEST-MUSTACHE}}

+ 0
- 1
examples/unknown_pragma.js Прегледај датотеку

@@ -1 +0,0 @@
var unknown_pragma = {};

+ 0
- 1
examples/unknown_pragma.txt Прегледај датотеку

@@ -1 +0,0 @@
ERROR: This implementation of mustache doesn't understand the 'I-HAVE-THE-GREATEST-MUSTACHE' pragma

+ 0
- 19
examples/view_partial.js Прегледај датотеку

@@ -1,19 +0,0 @@
var partial_context = {
greeting: function() {
return "Welcome";
},

farewell: function() {
return "Fair enough, right?";
},

partial: {
name: "Chris",
value: 10000,
taxed_value: function() {
return this.value - (this.value * 0.4);
},
in_ca: true
}
};


+ 0
- 19
examples/whitespace_partial.js Прегледај датотеку

@@ -1,19 +0,0 @@
var partial_context = {
greeting: function() {
return "Welcome";
},

farewell: function() {
return "Fair enough, right?";
},

partial: {
name: "Chris",
value: 10000,
taxed_value: function() {
return this.value - (this.value * 0.4);
},
in_ca: true
}
};


+ 0
- 8
lib/package.json Прегледај датотеку

@@ -1,8 +0,0 @@
{
"name": "mustache",
"author": "http://mustache.github.com/",
"description": "{{ mustache }} in JavaScript — Logic-less templates.",
"keywords": ["template"],
"version": "0.3.1-dev-twitter",
"main": "./mustache"
}

+ 0
- 7
mustache-commonjs/mustache.js.tpl.post Прегледај датотеку

@@ -1,7 +0,0 @@

exports.name = Mustache.name;
exports.version = Mustache.version;

exports.to_html = function() {
return Mustache.to_html.apply(this, arguments);
};

+ 0
- 6
mustache-commonjs/mustache.js.tpl.pre Прегледај датотеку

@@ -1,6 +0,0 @@
/*
* CommonJS-compatible mustache.js module
*
* See http://github.com/janl/mustache.js for more info.
*/


+ 0
- 7
mustache-commonjs/package.json Прегледај датотеку

@@ -1,7 +0,0 @@
{
"name": "mustache",
"author": "http://mustache.github.com/",
"description": "{{ mustache }} in JavaScript — Logic-less templates.",
"keywords": ["template"],
"version": "{{version}}"
}

+ 0
- 4
mustache-dojo/mustache.js.tpl.post Прегледај датотеку

@@ -1,4 +0,0 @@

dojox.mustache = dojo.hitch(Mustache, "to_html");

})();

+ 0
- 6
mustache-jquery/jquery.mustache.js.tpl.post Прегледај датотеку

@@ -1,6 +0,0 @@

$.mustache = function(template, view, partials) {
return Mustache.to_html(template, view, partials);
};

})(jQuery);

+ 0
- 4
mustache-yui3/mustache.js.tpl.post Прегледај датотеку

@@ -1,4 +0,0 @@

Y.mustache = Mustache.to_html;

}, "0");

+ 490
- 376
mustache.js Прегледај датотеку

@@ -1,422 +1,536 @@
/*
mustache.js — Logic-less templates in JavaScript

See http://mustache.github.com/ for more info.
*/

var Mustache = function() {
var regexCache = {};
var Renderer = function() {};

Renderer.prototype = {
otag: "{{",
ctag: "}}",
pragmas: {},
buffer: [],
pragmas_implemented: {
"IMPLICIT-ITERATOR": true
},
context: {},

render: function(template, context, partials, in_recursion) {
// reset buffer & set context
if(!in_recursion) {
this.context = context;
this.buffer = []; // TODO: make this non-lazy
}

// fail fast
if(!this.includes("", template)) {
if(in_recursion) {
return template;
} else {
this.send(template);
return;
}
}

// get the pragmas together
template = this.render_pragmas(template);

// render the template
var html = this.render_section(template, context, partials);

// render_section did not find any sections, we still need to render the tags
if (html === false) {
html = this.render_tags(template, context, partials, in_recursion);
}

if (in_recursion) {
return html;
} else {
this.sendLines(html);
}
},

/*
Sends parsed lines
*/
send: function(line) {
if(line !== "") {
this.buffer.push(line);
}
},
/*!
* mustache.js - Logic-less {{mustache}} templates with JavaScript
* http://github.com/janl/mustache.js
*/
var Mustache = (typeof module !== "undefined" && module.exports) || {};

(function (exports) {

exports.name = "mustache.js";
exports.version = "0.5.0-dev";
exports.tags = ["{{", "}}"];
exports.parse = parse;
exports.compile = compile;
exports.render = render;
exports.clearCache = clearCache;

// This is here for backwards compatibility with 0.4.x.
exports.to_html = function (template, view, partials, send) {
var result = render(template, view, partials);

if (typeof send === "function") {
send(result);
} else {
return result;
}
};

sendLines: function(text) {
if (text) {
var lines = text.split("\n");
for (var i = 0; i < lines.length; i++) {
this.send(lines[i]);
}
}
},

/*
Looks for %PRAGMAS
*/
render_pragmas: function(template) {
// no pragmas
if(!this.includes("%", template)) {
return template;
var _toString = Object.prototype.toString;
var _isArray = Array.isArray;
var _forEach = Array.prototype.forEach;
var _trim = String.prototype.trim;

var isArray;
if (_isArray) {
isArray = _isArray;
} else {
isArray = function (obj) {
return _toString.call(obj) === "[object Array]";
};
}

var forEach;
if (_forEach) {
forEach = function (obj, callback, scope) {
return _forEach.call(obj, callback, scope);
};
} else {
forEach = function (obj, callback, scope) {
for (var i = 0, len = obj.length; i < len; ++i) {
callback.call(scope, obj[i], i, obj);
}
};
}

var spaceRe = /^\s*$/;

function isWhitespace(string) {
return spaceRe.test(string);
}

var trim;
if (_trim) {
trim = function (string) {
return string == null ? "" : _trim.call(string);
};
} else {
var trimLeft, trimRight;

if (isWhitespace("\xA0")) {
trimLeft = /^\s+/;
trimRight = /\s+$/;
} else {
// IE doesn't match non-breaking spaces with \s, thanks jQuery.
trimLeft = /^[\s\xA0]+/;
trimRight = /[\s\xA0]+$/;
}

var that = this;
var regex = this.getCachedRegex("render_pragmas", function(otag, ctag) {
return new RegExp(otag + "%([\\w-]+) ?([\\w]+=[\\w]+)?" + ctag, "g");
});
trim = function (string) {
return string == null ? "" :
String(string).replace(trimLeft, "").replace(trimRight, "");
};
}

var escapeMap = {
"&": "&amp;",
"<": "&lt;",
">": "&gt;",
'"': '&quot;',
"'": '&#39;'
};

return template.replace(regex, function(match, pragma, options) {
if(!that.pragmas_implemented[pragma]) {
throw({message:
"This implementation of mustache doesn't understand the '" +
pragma + "' pragma"});
}
that.pragmas[pragma] = {};
if(options) {
var opts = options.split("=");
that.pragmas[pragma][opts[0]] = opts[1];
}
return "";
// ignore unknown pragmas silently
});
},

/*
Tries to find a partial in the curent scope and render it
*/
render_partial: function(name, context, partials) {
name = this.trim(name);
if(!partials || partials[name] === undefined) {
throw({message: "unknown_partial '" + name + "'"});
}
if(typeof(context[name]) != "object") {
return this.render(partials[name], context, partials, true);
}
return this.render(partials[name], context[name], partials, true);
},

/*
Renders inverted (^) and normal (#) sections
*/
render_section: function(template, context, partials) {
if(!this.includes("#", template) && !this.includes("^", template)) {
// did not render anything, there were no sections
return false;
}
function escapeHTML(string) {
return String(string).replace(/&(?!\w+;)|[<>"']/g, function (s) {
return escapeMap[s] || s;
});
}

/**
* Adds the `template`, `line`, and `file` properties to the given error
* object and alters the message to provide more useful debugging information.
*/
function debug(e, template, line, file) {
file = file || "<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];
}

var that = this;
e.template = template;
e.line = line;
e.file = file;
e.message = [file + ":" + line, context.join("\n"), "", e.message].join("\n");

var regex = this.getCachedRegex("render_section", function(otag, ctag) {
// This regex matches _the first_ section ({{#foo}}{{/foo}}), and captures the remainder
return new RegExp(
"^([\\s\\S]*?)" + // all the crap at the beginning that is not {{*}} ($1)
return e;
}

otag + // {{
"(\\^|\\#)\\s*(.+)\\s*" + // #foo (# == $2, foo == $3)
ctag + // }}
/**
* 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];
}

"\n*([\\s\\S]*?)" + // between the tag ($2). leading newlines are dropped
var names = name.split(".");
var lastIndex = names.length - 1;
var target = names[lastIndex];

otag + // {{
"\\/\\s*\\3\\s*" + // /foo (backreference to the opening tag).
ctag + // }}
var value, context, i = stack.length, j, localStack;
while (i) {
localStack = stack.slice(0);
context = stack[--i];

"\\s*([\\s\\S]*)$", // everything else in the string ($4). leading whitespace is dropped.
j = 0;
while (j < lastIndex) {
context = context[names[j++]];

"g");
});
if (context == null) {
break;
}

localStack.push(context);
}

// for each {{#foo}}{{/foo}} section do...
return template.replace(regex, function(match, before, type, name, content, after) {
// before contains only tags, no sections
var renderedBefore = before ? that.render_tags(before, context, partials, true) : "",
if (context && typeof context === "object" && target in context) {
value = context[target];
break;
}
}

// after may contain both sections and tags, so use full rendering function
renderedAfter = after ? that.render(after, context, partials, true) : "",
// If the value is a function, call it in the current context.
if (typeof value === "function") {
value = value.call(localStack[localStack.length - 1]);
}

// will be computed below
renderedContent,
if (value == null) {
return defaultValue;
}

value = that.find(name, context);
return value;
}

if (type === "^") { // inverted section
if (!value || that.is_array(value) && value.length === 0) {
// false or empty list, render it
renderedContent = that.render(content, context, partials, true);
} else {
renderedContent = "";
}
} else if (type === "#") { // normal section
if (that.is_array(value)) { // Enumerable, Let's loop!
renderedContent = that.map(value, function(row) {
return that.render(content, that.create_context(row), partials, true);
}).join("");
} else if (that.is_object(value)) { // Object, Use it as subcontext!
renderedContent = that.render(content, that.create_context(value),
partials, true);
} else if (typeof value === "function") {
// higher order section
renderedContent = value.call(context, content, function(text) {
return that.render(text, context, partials, true);
});
} else if (value) { // boolean section
renderedContent = that.render(content, context, partials, true);
} else {
renderedContent = "";
}
}
function renderSection(name, stack, callback, inverted) {
var buffer = "";
var value = lookup(name, stack);

return renderedBefore + renderedContent + renderedAfter;
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();
});
},

/*
Replace {{foo}} and friends with values from our view
*/
render_tags: function(template, context, partials, in_recursion) {
// tit for tat
var that = this;



var new_regex = function() {
return that.getCachedRegex("render_tags", function(otag, ctag) {
return new RegExp(otag + "(=|!|>|\\{|%)?([^\\/#\\^]+?)\\1?" + ctag + "+", "g");
});
} 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();
}

var regex = new_regex();
var tag_replace_callback = function(match, operator, name) {
switch(operator) {
case "!": // ignore comments
return "";
case "=": // set new delimiters, rebuild the replace regexp
that.set_delimiters(name);
regex = new_regex();
return "";
case ">": // render partial
return that.render_partial(name, context, partials);
case "{": // the triple mustache is unescaped
return that.find(name, context);
default: // escape the value
return that.escape(that.find(name, context));
}
};
var lines = template.split("\n");
for(var i = 0; i < lines.length; i++) {
lines[i] = lines[i].replace(regex, tag_replace_callback, this);
if(!in_recursion) {
this.send(lines[i]);
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 = [];
}

if(in_recursion) {
return lines.join("\n");
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);
}
},

set_delimiters: function(delimiters) {
var dels = delimiters.split(" ");
this.otag = this.escape_regex(dels[0]);
this.ctag = this.escape_regex(dels[1]);
},

escape_regex: function(text) {
// thank you Simon Willison
if(!arguments.callee.sRE) {
var specials = [
'/', '.', '*', '+', '?', '|',
'(', ')', '[', ']', '{', '}', '\\'
];
arguments.callee.sRE = new RegExp(
'(\\' + specials.join('|\\') + ')', 'g'
);

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);
}
return text.replace(arguments.callee.sRE, '\\$1');
},

/*
find `name` in current `context`. That is find me a value
from the view object
*/
find: function(name, context) {
name = this.trim(name);

// Checks whether a value is thruthy or false or 0
function is_kinda_truthy(bool) {
return bool === false || bool === 0 || bool;

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);");
}

var value;
// check for dot notation eg. foo.bar
if(name.match(/([a-z_]+)\./ig)){
var childValue = this.walk_context(name, context);
if(is_kinda_truthy(childValue)) {
value = childValue;
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;
}
}
else{
if(is_kinda_truthy(context[name])) {
value = context[name];
} else if(is_kinda_truthy(this.context[name])) {
value = this.context[name];

var end = template.indexOf(closeTag, i);

if (end === -1) {
throw debug(new Error('Tag "' + openTag + '" was not closed properly'), template, line, options.file);
}
}

if(typeof value === "function") {
return value.apply(context);
}
if(value !== undefined) {
return value;
}
// silently ignore unkown variables
return "";
},

walk_context: function(name, context){
var path = name.split('.');
// if the var doesn't exist in current context, check the top level context
var value_context = (context[path[0]] != undefined) ? context : this.context;
var value = value_context[path.shift()];
while(value != undefined && path.length > 0){
value_context = value;
value = value[path.shift()];
}
// if the value is a function, call it, binding the correct context
if(typeof value === "function") {
return value.apply(value_context);
}
return value;
},

// Utility methods

/* includes tag */
includes: function(needle, haystack) {
return haystack.indexOf(this.otag + needle) != -1;
},

/*
Does away with nasty characters
*/
escape: function(s) {
s = String(s === null ? "" : s);
return s.replace(/&(?!\w+;)|["'<>\\]/g, function(s) {
switch(s) {
case "&": return "&amp;";
case '"': return '&quot;';
case "'": return '&#39;';
case "<": return "&lt;";
case ">": return "&gt;";
default: return s;
var source = template.substring(i, end);

if (callback) {
callback(source);
}
});
},

// by @langalex, support for arrays of strings
create_context: function(_context) {
if(this.is_object(_context)) {
return _context;
} else {
var iterator = ".";
if(this.pragmas["IMPLICIT-ITERATOR"]) {
iterator = this.pragmas["IMPLICIT-ITERATOR"].iterator;
// Maintain line count for \n in source.
var n = 0;
while (~(n = source.indexOf("\n", n))) {
line++;
n++;
}
var ctx = {};
ctx[iterator] = _context;
return ctx;
}
},

is_object: function(a) {
return a && typeof a == "object";
},

is_array: function(a) {
return Object.prototype.toString.call(a) === '[object Array]';
},

/*
Gets rid of leading and trailing whitespace
*/
trim: function(s) {
return s.replace(/^\s*|\s*$/g, "");
},

/*
Why, why, why? Because IE. Cry, cry cry.
*/
map: function(array, fn) {
if (typeof array.map == "function") {
return array.map(fn);

i = end + closeTag.length - 1;
openTag = nextOpenTag;
closeTag = nextCloseTag;
} else {
var r = [];
var l = array.length;
for(var i = 0; i < l; i++) {
r.push(fn(array[i]));
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);
}
return r;
}
},
}

getCachedRegex: function(name, generator) {
var byOtag = regexCache[this.otag];
if (!byOtag) {
byOtag = regexCache[this.otag] = {};
}
if (sectionStack.length != 0) {
throw debug(new Error('Section "' + sectionStack[sectionStack.length - 1].name + '" was not closed properly'), template, line, options.file);
}

var byCtag = byOtag[this.ctag];
if (!byCtag) {
byCtag = byOtag[this.ctag] = {};
}
// Clean up any whitespace from a closing {{tag}} that was at the end
// of the template without a trailing \n.
stripSpace();

var regex = byCtag[name];
if (!regex) {
regex = byCtag[name] = generator(this.otag, this.ctag);
}
code.push(
'";',
"\nreturn buffer;",
"\n} catch (e) { throw {error: e, line: line}; }"
);

return regex;
// 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({
name: "mustache.js",
version: "0.4.0-dev",

/*
Turns a template and view into HTML
*/
to_html: function(template, view, partials, send_fun) {
var renderer = new Renderer();
if(send_fun) {
renderer.send = send_fun;
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);
}
renderer.render(template, view || {}, partials);
if(!send_fun) {
return renderer.buffer.join("\n");
};
}

// 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);

+ 14
- 0
mustache.js.nuspec Прегледај датотеку

@@ -0,0 +1,14 @@
<?xml version="1.0"?>
<package >
<metadata>
<id>mustache.js</id>
<version>0.5.0-dev</version>
<authors>mustache.js Authors</authors>
<licenseUrl>https://github.com/janl/mustache.js/blob/master/LICENSE</licenseUrl>
<projectUrl>http://mustache.github.com/</projectUrl>
<requireLicenseAcceptance>false</requireLicenseAcceptance>
<description>Logic-less templates in JavaScript.</description>
<releaseNotes></releaseNotes>
<tags>mustache template templates javascript</tags>
</metadata>
</package>

+ 8
- 0
package.json Прегледај датотеку

@@ -0,0 +1,8 @@
{
"name": "mustache",
"version": "0.5.0-dev",
"description": "Logic-less {{mustache}} templates with JavaScript",
"author": "mustache.js Authors <http://github.com/janl/mustache.js>",
"keywords": ["mustache", "template", "templates", "ejs"],
"main": "./mustache"
}

+ 3
- 0
spec/_files/ampersand_escape.js Прегледај датотеку

@@ -0,0 +1,3 @@
var ampersand_escape = {
message: "Some <code>"
};

+ 1
- 0
spec/_files/ampersand_escape.mustache Прегледај датотеку

@@ -0,0 +1 @@
{{&message}}

+ 1
- 0
spec/_files/ampersand_escape.txt Прегледај датотеку

@@ -0,0 +1 @@
Some <code>

examples/apostrophe.js → spec/_files/apostrophe.js Прегледај датотеку


examples/apostrophe.html → spec/_files/apostrophe.mustache Прегледај датотеку


examples/apostrophe.txt → spec/_files/apostrophe.txt Прегледај датотеку


examples/array_of_strings.js → spec/_files/array_of_strings.js Прегледај датотеку


+ 1
- 0
spec/_files/array_of_strings.mustache Прегледај датотеку

@@ -0,0 +1 @@
{{#array_of_strings}}{{.}} {{/array_of_strings}}

examples/array_of_strings.txt → spec/_files/array_of_strings.txt Прегледај датотеку


+ 3
- 0
spec/_files/backslashes.js Прегледај датотеку

@@ -0,0 +1,3 @@
var backslashes = {
value: "\\abc"
};

+ 7
- 0
spec/_files/backslashes.mustache Прегледај датотеку

@@ -0,0 +1,7 @@
* {{value}}
* {{{value}}}
* {{&value}}
<script>
foo = { bar: 'abc\"xyz\"' };
foo = { bar: 'x\'y' };
</script>

+ 7
- 0
spec/_files/backslashes.txt Прегледај датотеку

@@ -0,0 +1,7 @@
* \abc
* \abc
* \abc
<script>
foo = { bar: 'abc\"xyz\"' };
foo = { bar: 'x\'y' };
</script>

examples/bug_11_eating_whitespace.js → spec/_files/bug_11_eating_whitespace.js Прегледај датотеку


examples/bug_11_eating_whitespace.html → spec/_files/bug_11_eating_whitespace.mustache Прегледај датотеку


examples/bug_11_eating_whitespace.txt → spec/_files/bug_11_eating_whitespace.txt Прегледај датотеку


+ 4
- 0
spec/_files/changing_delimiters.js Прегледај датотеку

@@ -0,0 +1,4 @@
var changing_delimiters = {
"foo": "foooooooooooooo",
"bar":"<b>bar!</b>"
};

+ 1
- 0
spec/_files/changing_delimiters.mustache Прегледај датотеку

@@ -0,0 +1 @@
{{=<% %>=}}<% foo %> {{foo}} <%{bar}%> {{{bar}}}

+ 1
- 0
spec/_files/changing_delimiters.txt Прегледај датотеку

@@ -0,0 +1 @@
foooooooooooooo {{foo}} <b>bar!</b> {{{bar}}}

examples/comments.js → spec/_files/comments.js Прегледај датотеку


examples/comments.html → spec/_files/comments.mustache Прегледај датотеку


examples/comments.txt → spec/_files/comments.txt Прегледај датотеку


examples/complex.js → spec/_files/complex.js Прегледај датотеку


examples/complex.html → spec/_files/complex.mustache Прегледај датотеку

@@ -13,4 +13,4 @@
{{/list}}
{{#empty}}
<p>The list is empty.</p>
{{/empty}}
{{/empty}}

+ 6
- 0
spec/_files/complex.txt Прегледај датотеку

@@ -0,0 +1,6 @@
<h1>Colors</h1>
<ul>
<li><strong>red</strong></li>
<li><a href="#Green">green</a></li>
<li><a href="#Blue">blue</a></li>
</ul>

+ 8
- 0
spec/_files/context_lookup.js Прегледај датотеку

@@ -0,0 +1,8 @@
var context_lookup = {
"outer": {
"id": 1,
"second": {
"nothing": 2
}
}
};

+ 1
- 0
spec/_files/context_lookup.mustache Прегледај датотеку

@@ -0,0 +1 @@
{{#outer}}{{#second}}{{id}}{{/second}}{{/outer}}

+ 1
- 0
spec/_files/context_lookup.txt Прегледај датотеку

@@ -0,0 +1 @@
1

examples/delimiters.js → spec/_files/delimiters.js Прегледај датотеку


examples/delimiters.html → spec/_files/delimiters.mustache Прегледај датотеку

@@ -4,4 +4,4 @@
<%=| |=%>
* | third |
|={{ }}=|
* {{ fourth }}
* {{ fourth }}

examples/delimiters.txt → spec/_files/delimiters.txt Прегледај датотеку


+ 4
- 0
spec/_files/disappearing_whitespace.js Прегледај датотеку

@@ -0,0 +1,4 @@
var disappearing_whitespace = {
bedrooms: true,
total: 1
};

+ 1
- 0
spec/_files/disappearing_whitespace.mustache Прегледај датотеку

@@ -0,0 +1 @@
{{#bedrooms}}{{total}}{{/bedrooms}} BED

+ 1
- 0
spec/_files/disappearing_whitespace.txt Прегледај датотеку

@@ -0,0 +1 @@
1 BED

examples/dot_notation.js → spec/_files/dot_notation.js Прегледај датотеку


examples/dot_notation.html → spec/_files/dot_notation.mustache Прегледај датотеку


examples/dot_notation.txt → spec/_files/dot_notation.txt Прегледај датотеку


examples/double_render.js → spec/_files/double_render.js Прегледај датотеку


examples/double_render.html → spec/_files/double_render.mustache Прегледај датотеку


examples/double_render.txt → spec/_files/double_render.txt Прегледај датотеку


+ 3
- 0
spec/_files/empty_list.js Прегледај датотеку

@@ -0,0 +1,3 @@
var empty_list = {
jobs: []
};

+ 4
- 0
spec/_files/empty_list.mustache Прегледај датотеку

@@ -0,0 +1,4 @@
These are the jobs:
{{#jobs}}
{{.}}
{{/jobs}}

+ 1
- 0
spec/_files/empty_list.txt Прегледај датотеку

@@ -0,0 +1 @@
These are the jobs:

examples/empty_sections.js → spec/_files/empty_sections.js Прегледај датотеку


examples/empty_sections.html → spec/_files/empty_sections.mustache Прегледај датотеку


examples/empty_sections.txt → spec/_files/empty_sections.txt Прегледај датотеку


+ 6
- 0
spec/_files/empty_string.js Прегледај датотеку

@@ -0,0 +1,6 @@
var empty_string = {
description: "That is all!",
child: {
description: ""
}
};

+ 1
- 0
spec/_files/empty_string.mustache Прегледај датотеку

@@ -0,0 +1 @@
{{description}}{{#child}}{{description}}{{/child}}

+ 1
- 0
spec/_files/empty_string.txt Прегледај датотеку

@@ -0,0 +1 @@
That is all!

examples/empty_template.js → spec/_files/empty_template.js Прегледај датотеку


examples/empty_template.html → spec/_files/empty_template.mustache Прегледај датотеку


+ 1
- 0
spec/_files/empty_template.txt Прегледај датотеку

@@ -0,0 +1 @@
<html><head></head><body><h1>Test</h1></body></html>

examples/error_not_found.js → spec/_files/error_not_found.js Прегледај датотеку


examples/error_not_found.html → spec/_files/error_not_found.mustache Прегледај датотеку


+ 0
- 0
spec/_files/error_not_found.txt Прегледај датотеку


examples/escaped.js → spec/_files/escaped.js Прегледај датотеку


examples/escaped.html → spec/_files/escaped.mustache Прегледај датотеку


examples/escaped.txt → spec/_files/escaped.txt Прегледај датотеку


examples/higher_order_sections.js → spec/_files/higher_order_sections.js Прегледај датотеку


examples/higher_order_sections.html → spec/_files/higher_order_sections.mustache Прегледај датотеку


Неке датотеке нису приказане због велике количине промена

Loading…
Откажи
Сачувај