Przeglądaj źródła

Parser rewrite

This commit is a complete rewrite of the core mustache.js file with two
main goals: 1) a major performance boost and 2) better compliance with
the mustache spec.

In order to improve performance templates are pre-compiled to JavaScript
functions. These compiled functions take a view, a partials object, and
an optional callback as arguments. They are cached to prevent
unnecessary re-compilation of an already compiled template. Both of
these enhancements facilitate a generous boost in performance.

A few other notes:

- The mustache.js file is now both browser and CommonJS ready without
  any modification.
- The API exposes two main methods: Mustache.compile and
  Mustache.render. The former is used to generate a function for a given
  template, while the latter is a higher-level function that is used to
  compile and render a template in one shot. Mustache.to_html is still
  available for backwards compatibility.
- The concept of pragmas is removed to conform more closely to the
  original mustache spec. The dot symbol still works to reference the
  current item in an array.
- The parser is much more strict about whitespace than it was before. The
  rule is simple: if a line contains only a non-variable tag (i.e. not
  {{tag}} or {{{tag}}}) and whitespace, that line is ignored in the
  output. Users may use the "space" option when compiling templates to
  preserve every whitespace character in the original template.
- The parser is able to provide detailed information about where errors
  occur when parsing and rendering templates, including the line number
  and surrounding code context.
tags/0.5.1
Michael Jackson 14 lat temu
rodzic
commit
eb01be0470
72 zmienionych plików z 596 dodań i 594 usunięć
  1. +0
    -1
      .gitignore
  2. +5
    -5
      README.md
  3. +1
    -8
      Rakefile
  4. +4
    -4
      TESTING.md
  5. +458
    -358
      mustache.js
  6. +8
    -0
      package.json
  7. +0
    -3
      spec/_files/array_of_partials_implicit_partial.js
  8. +1
    -1
      spec/_files/array_of_strings.mustache
  9. +0
    -1
      spec/_files/array_of_strings_options.js
  10. +0
    -2
      spec/_files/array_of_strings_options.mustache
  11. +0
    -1
      spec/_files/array_of_strings_options.txt
  12. +0
    -5
      spec/_files/array_partial.js
  13. +1
    -1
      spec/_files/complex.mustache
  14. +3
    -3
      spec/_files/complex.txt
  15. +1
    -1
      spec/_files/delimiters.mustache
  16. +0
    -1
      spec/_files/empty_partial.2.mustache
  17. +0
    -3
      spec/_files/empty_partial.js
  18. +1
    -1
      spec/_files/empty_template.txt
  19. +0
    -1
      spec/_files/error_not_found.txt
  20. +1
    -0
      spec/_files/inverted_section.txt
  21. +3
    -4
      spec/_files/nesting.txt
  22. +1
    -0
      spec/_files/null_string.js
  23. +2
    -1
      spec/_files/null_string.mustache
  24. +1
    -0
      spec/_files/null_string.txt
  25. +3
    -0
      spec/_files/partial_array.js
  26. +0
    -0
      spec/_files/partial_array.mustache
  27. +1
    -1
      spec/_files/partial_array.partial
  28. +0
    -1
      spec/_files/partial_array.txt
  29. +1
    -1
      spec/_files/partial_array_of_partials.js
  30. +0
    -0
      spec/_files/partial_array_of_partials.mustache
  31. +0
    -0
      spec/_files/partial_array_of_partials.partial
  32. +0
    -0
      spec/_files/partial_array_of_partials.txt
  33. +3
    -0
      spec/_files/partial_array_of_partials_implicit.js
  34. +0
    -0
      spec/_files/partial_array_of_partials_implicit.mustache
  35. +0
    -0
      spec/_files/partial_array_of_partials_implicit.partial
  36. +0
    -0
      spec/_files/partial_array_of_partials_implicit.txt
  37. +3
    -0
      spec/_files/partial_empty.js
  38. +0
    -0
      spec/_files/partial_empty.mustache
  39. +1
    -0
      spec/_files/partial_empty.partial
  40. +0
    -0
      spec/_files/partial_empty.txt
  41. +1
    -1
      spec/_files/partial_recursion.js
  42. +1
    -1
      spec/_files/partial_recursion.mustache
  43. +1
    -1
      spec/_files/partial_recursion.partial
  44. +6
    -0
      spec/_files/partial_template.js
  45. +1
    -1
      spec/_files/partial_template.mustache
  46. +1
    -0
      spec/_files/partial_template.partial
  47. +0
    -0
      spec/_files/partial_template.txt
  48. +16
    -0
      spec/_files/partial_view.js
  49. +1
    -1
      spec/_files/partial_view.mustache
  50. +0
    -0
      spec/_files/partial_view.partial
  51. +0
    -1
      spec/_files/partial_view.txt
  52. +17
    -0
      spec/_files/partial_whitespace.js
  53. +1
    -1
      spec/_files/partial_whitespace.mustache
  54. +0
    -0
      spec/_files/partial_whitespace.partial
  55. +0
    -1
      spec/_files/partial_whitespace.txt
  56. +1
    -1
      spec/_files/reuse_of_enumerables.js
  57. +3
    -3
      spec/_files/section_as_context.txt
  58. +1
    -1
      spec/_files/simple.mustache
  59. +0
    -1
      spec/_files/template_partial.2.mustache
  60. +0
    -8
      spec/_files/template_partial.js
  61. +1
    -1
      spec/_files/two_in_a_row.mustache
  62. +0
    -1
      spec/_files/two_sections.txt
  63. +1
    -1
      spec/_files/unescaped.mustache
  64. +0
    -1
      spec/_files/unknown_pragma.js
  65. +0
    -1
      spec/_files/unknown_pragma.mustache
  66. +0
    -1
      spec/_files/unknown_pragma.txt
  67. +0
    -19
      spec/_files/view_partial.js
  68. +0
    -19
      spec/_files/whitespace_partial.js
  69. +40
    -98
      spec/mustache_spec.rb
  70. +0
    -8
      wrappers/commonjs/mustache.js.tpl.post
  71. +0
    -6
      wrappers/commonjs/mustache.js.tpl.pre
  72. +0
    -8
      wrappers/commonjs/package.json

+ 0
- 1
.gitignore Wyświetl plik

@@ -1,7 +1,6 @@
.DS_Store .DS_Store
.rvmrc .rvmrc
runner.js runner.js
lib
jquery.mustache.js jquery.mustache.js
qooxdoo.mustache.js qooxdoo.mustache.js
dojox dojox


+ 5
- 5
README.md Wyświetl plik

@@ -3,7 +3,7 @@
> What could be more logical awesome than no logic at all? > 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.js](http://github.com/janl/mustache.js) is an implementation of the
[Mustache](http://mustache.github.com/) templating system in JavaScript.
[Mustache](http://mustache.github.com/) template system in JavaScript.


[Mustache](http://mustache.github.com/) is a logic-less template syntax. It can [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 be used for HTML, config files, source code - anything. It works by expanding
@@ -274,10 +274,11 @@ own iteration marker:


## Plugins for JavaScript Libraries ## Plugins for JavaScript Libraries


mustache.js may be built specifically for several different client libraries
and platforms, including the following:
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:


- [node](http://nodejs.org/) (or other CommonJS platforms)
- [jQuery](http://jquery.com/) - [jQuery](http://jquery.com/)
- [Dojo](http://www.dojotoolkit.org/) - [Dojo](http://www.dojotoolkit.org/)
- [YUI](http://developer.yahoo.com/yui/) - [YUI](http://developer.yahoo.com/yui/)
@@ -287,7 +288,6 @@ and platforms, including the following:
These may be built using [Rake](http://rake.rubyforge.org/) and one of the These may be built using [Rake](http://rake.rubyforge.org/) and one of the
following commands: following commands:


$ rake commonjs
$ rake jquery $ rake jquery
$ rake dojo $ rake dojo
$ rake yui $ rake yui


+ 1
- 8
Rakefile Wyświetl plik

@@ -13,7 +13,7 @@ task :spec do
end end


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


# Creates a rule that uses the .tmpl.{pre,post} stuff to make a final, # Creates a rule that uses the .tmpl.{pre,post} stuff to make a final,
@@ -36,17 +36,10 @@ def templated_build(name, opts={})
sh "cat #{source}/#{target_js}.tpl.pre mustache.js \ sh "cat #{source}/#{target_js}.tpl.pre mustache.js \
#{source}/#{target_js}.tpl.post > #{opts[:location] || '.'}/#{target_js}" #{source}/#{target_js}.tpl.post > #{opts[:location] || '.'}/#{target_js}"


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

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


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


+ 4
- 4
TESTING.md Wyświetl plik

@@ -1,4 +1,4 @@
## Running the mustache.js Test Suite
## Running the mustache.js test suite


The mustache.js test suite uses the [RSpec](http://rspec.info/) testing 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/) framework. In order to run the tests you'll need to install [Ruby](http://ruby-lang.org/)
@@ -57,6 +57,6 @@ suite with the following command:


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


* Create a template file `somename.mustache`
* Create a javascript file with data and functions `somename.js`
* Create a file the expected result `somename.txt`
* 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`

+ 458
- 358
mustache.js Wyświetl plik

@@ -1,38 +1,75 @@
/*
mustache.js — Logic-less templates in JavaScript
/*!
* mustache.js - Logic-less {{mustache}} templates with JavaScript
* http://github.com/janl/mustache.js
*/
var Mustache = (typeof module !== "undefined" && module.exports) || {};


See http://mustache.github.com/ for more info.
*/
(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;

exports.to_html = render; // keep backwards compatibility


var Mustache = function () {
var _toString = Object.prototype.toString; var _toString = Object.prototype.toString;
var _isArray = Array.isArray;
var _forEach = Array.prototype.forEach;
var _trim = String.prototype.trim;


Array.isArray = Array.isArray || function (obj) {
return _toString.call(obj) == "[object Array]";
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 _trim = String.prototype.trim, trim;
var spaceRe = /^\s*$/;


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

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


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


trim = function (text) {
return text == null ? "" :
text.toString().replace(trimLeft, "").replace(trimRight, "");
}
trim = function (string) {
return string == null ? "" :
String(string).replace(trimLeft, "").replace(trimRight, "");
};
} }


var escapeMap = { var escapeMap = {
@@ -49,388 +86,451 @@ 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
}
/**
* 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];
}


// fail fast
if (!this.includes("", template)) {
if (in_recursion) {
return template;
} else {
this.send(template);
return;
}
}
e.template = template;
e.line = line;
e.file = file;
e.message = [file + ":" + line, context.join("\n"), "", e.message].join("\n");


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


// render the template
var html = this.render_section(template, context, partials);
/**
* Looks up the value of the given `name` in the given context `stack`.
*/
function findName(name, stack) {
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;
}


// 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);
localStack.push(context);
} }


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

/*
Sends parsed lines
*/
send: function (line) {
if (line !== "") {
this.buffer.push(line);
if (context && target in context) {
value = context[target];
break;
} }
},
}


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;
}
// If the value is a function, call it in the current context.
if (typeof value === "function") {
value = value.call(localStack[localStack.length - 1]);
}

return value == null ? "" : value;
}


var that = this;
var regex = this.getCachedRegex("render_pragmas", function (otag, ctag) {
return new RegExp(otag + "%([\\w-]+) ?([\\w]+=[\\w]+)?" + ctag, "g");
function sendSection(send, value, callback, stack, inverted) {
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)) {
send(callback());
}
} else if (isArray(value)) {
forEach(value, function (value) {
stack.push(value);
send(callback());
stack.pop();
}); });
} else if (typeof value === "object") {
stack.push(value);
send(callback());
stack.pop();
} else if (typeof value === "function") {
var scope = stack[stack.length - 1];
var scopedRender = function (template) {
return render(template, scope);
};
send(value.call(scope, callback(), scopedRender) || "");
} else if (value) {
send(callback());
}
}


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];
/**
* 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 line = 1;", // keep track of source line number
"\ntry {",
'\nsend("'
];

var spaces = [], // indices of whitespace in code for 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 (!nonSpace && !options.space) {
while (spaces.length) {
code.splice(spaces.pop(), 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 = trim(name);
if (!partials || partials[name] === undefined) {
throw({message: "unknown_partial '" + name + "'"});
}
if (!context || 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;
} else {
spaces = [];
} }


var that = this;

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)
nonSpace = false;
};

// Returns a bit of code that can be used to find the given `name`.
var findFor = function (name) {
return name === "." ? "stack[stack.length - 1]" : 'find("' + name + '")';
};

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 send(render(partial, stack[stack.length - 1], partials));',
'\n}',
'\nsend("'
);
};

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


otag + // {{
"(\\^|\\#)\\s*(.+)\\s*" + // #foo (# == $2, foo == $3)
ctag + // }}
sectionStack.push({name: name, inverted: inverted});

code.push(
'");',
updateLine,
'\nvar value = ' + findFor(name) + ';',
'\nvar callback = (function () {',
'\n var buffer, send = function (chunk) { buffer.push(chunk); };',
'\n return function () {',
'\n buffer = [];',
'\nsend("'
);
};

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, file);
}


"\n*([\\s\\S]*?)" + // between the tag ($2). leading newlines are dropped
var section = sectionStack.pop();


otag + // {{
"\\/\\s*\\3\\s*" + // /foo (backreference to the opening tag).
ctag + // }}
code.push(
'");',
'\n return buffer.join("");',
'\n };',
'\n})();'
);


"\\s*([\\s\\S]*)$", // everything else in the string ($4). leading whitespace is dropped.
if (section.inverted) {
code.push("\nsendSection(send,value,callback,stack,true);");
} else {
code.push("\nsendSection(send,value,callback,stack);");
}


"g");
});
code.push('\nsend("');
};

var sendPlain = function (source) {
code.push(
'");',
updateLine,
'\nsend(' + findFor(trim(source)) + ');',
'\nsend("'
);
};

var sendEscaped = function (source) {
code.push(
'");',
updateLine,
'\nsend(escapeHTML(' + findFor(trim(source)) + '));',
'\nsend("'
);
};

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;

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


// 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 (end === -1) {
throw debug(new Error('Tag "' + openTag + '" was not closed properly'), template, line, options.file);
}


// after may contain both sections and tags, so use full rendering function
renderedAfter = after ? that.render(after, context, partials, true) : "",
var source = template.substring(i, end);


// will be computed below
renderedContent,
if (callback) {
callback(source);
}


value = that.find(name, context);
// Maintain line count for \n in source.
var n = 0;
while (~(n = source.indexOf("\n", n))) {
line++;
n++;
}


if (type === "^") { // inverted section
if (!value || Array.isArray(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 (Array.isArray(value)) { // Enumerable, Let's loop!
renderedContent = that.map(value, function (row) {
return that.render(content, that.create_context(row), partials, true);
}).join("");
} else if (that.is_object(value)) { // Object, Use it as subcontext!
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);
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 "\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 { } else {
renderedContent = "";
nonSpace = true;
} }
}


return renderedBefore + renderedContent + renderedAfter;
});
},

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

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
case "&": // & operator is an alternative unescape method
return that.find(name, context);
default: // escape the value
return escapeHTML(that.find(name, context));
}
};
var lines = template.split("\n");
for(var i = 0; i < lines.length; i++) {
lines[i] = lines[i].replace(regex, tag_replace_callback, this);
if (!in_recursion) {
this.send(lines[i]);
code.push(c);
} }
} }
}


if (in_recursion) {
return lines.join("\n");
}
},

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'
);
}
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 = trim(name);

// Checks whether a value is thruthy or false or 0
function is_kinda_truthy(bool) {
return bool === false || bool === 0 || bool;
}
if (sectionStack.length != 0) {
throw debug(new Error('Section "' + sectionStack[sectionStack.length - 1].name + '" was not closed properly'), template, line, options.file);
}


var value;
// Clean up any whitespace from a closing {{tag}} that was at the end
// of the template without a trailing \n.
stripSpace();


// check for dot notation eg. foo.bar
if (name.match(/([a-z_]+)\./ig)) {
var childValue = this.walk_context(name, context);
if (is_kinda_truthy(childValue)) {
value = childValue;
}
} else {
if (is_kinda_truthy(context[name])) {
value = context[name];
} else if (is_kinda_truthy(this.context[name])) {
value = this.context[name];
}
}
code.push(
'");',
"\nsend(null);", // Send null as the last operation.
"\n} catch (e) { throw {error: e, line: line}; }"
);


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()];
// Ignore empty send("") statements.
var body = code.join("").replace(/send\(""\);\n/g, "");

if (options.debug) {
if (typeof console != "undefined" && console.log) {
console.log(body);
} else if (typeof print === "function") {
print(body);
} }
// if the value is a function, call it, binding the correct context
if (typeof value == "function") {
return value.apply(value_context);
}

return body;
}

/**
* Used by `compile` to generate a reusable function for the given `template`.
*/
function _compile(template, options) {
var args = "view,partials,send,stack,find,escapeHTML,sendSection,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, callback) {
if (typeof partials === "function") {
callback = partials;
partials = {};
} }
return value;
},


// Utility methods
partials = partials || {};


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


// 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;
}
var ctx = {};
ctx[iterator] = _context;
return ctx;
}
},

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

/*
Why, why, why? Because IE. Cry, cry cry.
*/
map: function (array, fn) {
if (typeof array.map == "function") {
return array.map(fn);
} else {
var r = [];
var l = array.length;
for(var i = 0; i < l; i++) {
r.push(fn(array[i]));
}
return r;
}
},
var send = callback || function (chunk) {
buffer.push(chunk);
};


getCachedRegex: function (name, generator) {
var byOtag = regexCache[this.otag];
if (!byOtag) {
byOtag = regexCache[this.otag] = {};
}
var stack = [view]; // context stack


var byCtag = byOtag[this.ctag];
if (!byCtag) {
byCtag = byOtag[this.ctag] = {};
}
var find = function (name) {
return findName(name, stack);
};


var regex = byCtag[name];
if (!regex) {
regex = byCtag[name] = generator(this.otag, this.ctag);
try {
fn(view, partials, send, stack, find, escapeHTML, sendSection, render);
} catch (e) {
throw debug(e.error, template, e.line, options.file);
} }


return regex;
}
};
return buffer.join("");
};
}


return({
name: "mustache.js",
version: "0.5.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;
}
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`, `partials`, and `callback`. The `callback` is used to return the
* output piece by piece, as it is rendered. When finished, the callback will
* receive `null` as its argument, after which it will not be called any more.
* If no callback is given, the complete rendered template will be used as the
* return value for the function.
*
* Note: If no partials are needed, the third argument may be the callback.
* 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, callback) {
return compile(template)(view, partials, callback);
}

})(Mustache);

+ 8
- 0
package.json Wyświetl plik

@@ -0,0 +1,8 @@
{
"name": "mustache",
"version": "0.4.0",
"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
- 3
spec/_files/array_of_partials_implicit_partial.js Wyświetl plik

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

+ 1
- 1
spec/_files/array_of_strings.mustache Wyświetl plik

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

+ 0
- 1
spec/_files/array_of_strings_options.js Wyświetl plik

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

+ 0
- 2
spec/_files/array_of_strings_options.mustache Wyświetl plik

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

+ 0
- 1
spec/_files/array_of_strings_options.txt Wyświetl plik

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

+ 0
- 5
spec/_files/array_partial.js Wyświetl plik

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

+ 1
- 1
spec/_files/complex.mustache Wyświetl plik

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

+ 3
- 3
spec/_files/complex.txt Wyświetl plik

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

+ 1
- 1
spec/_files/delimiters.mustache Wyświetl plik

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

+ 0
- 1
spec/_files/empty_partial.2.mustache Wyświetl plik

@@ -1 +0,0 @@
yo

+ 0
- 3
spec/_files/empty_partial.js Wyświetl plik

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

+ 1
- 1
spec/_files/empty_template.txt Wyświetl plik

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

+ 0
- 1
spec/_files/error_not_found.txt Wyświetl plik

@@ -1 +0,0 @@


+ 1
- 0
spec/_files/inverted_section.txt Wyświetl plik

@@ -1 +1,2 @@

No repos :( No repos :(

+ 3
- 4
spec/_files/nesting.txt Wyświetl plik

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

+ 1
- 0
spec/_files/null_string.js Wyświetl plik

@@ -3,6 +3,7 @@ var null_string = {
glytch: true, glytch: true,
binary: false, binary: false,
value: null, value: null,
undef: undefined,
numeric: function() { numeric: function() {
return NaN; return NaN;
} }


+ 2
- 1
spec/_files/null_string.mustache Wyświetl plik

@@ -2,4 +2,5 @@ Hello {{name}}
glytch {{glytch}} glytch {{glytch}}
binary {{binary}} binary {{binary}}
value {{value}} value {{value}}
numeric {{numeric}}
undef {{undef}}
numeric {{numeric}}

+ 1
- 0
spec/_files/null_string.txt Wyświetl plik

@@ -2,4 +2,5 @@ Hello Elise
glytch true glytch true
binary false binary false
value value
undef
numeric NaN numeric NaN

+ 3
- 0
spec/_files/partial_array.js Wyświetl plik

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

spec/_files/array_partial.mustache → spec/_files/partial_array.mustache Wyświetl plik


spec/_files/array_partial.2.mustache → spec/_files/partial_array.partial Wyświetl plik

@@ -1,4 +1,4 @@
Here's a non-sense array of values Here's a non-sense array of values
{{#array}} {{#array}}
{{.}} {{.}}
{{/array}}
{{/array}}

spec/_files/array_partial.txt → spec/_files/partial_array.txt Wyświetl plik

@@ -3,4 +3,3 @@ Here's a non-sense array of values
2 2
3 3
4 4


spec/_files/array_of_partials_partial.js → spec/_files/partial_array_of_partials.js Wyświetl plik

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

spec/_files/array_of_partials_implicit_partial.mustache → spec/_files/partial_array_of_partials.mustache Wyświetl plik


spec/_files/array_of_partials_partial.2.mustache → spec/_files/partial_array_of_partials.partial Wyświetl plik


spec/_files/array_of_partials_implicit_partial.txt → spec/_files/partial_array_of_partials.txt Wyświetl plik


+ 3
- 0
spec/_files/partial_array_of_partials_implicit.js Wyświetl plik

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

spec/_files/array_of_partials_partial.mustache → spec/_files/partial_array_of_partials_implicit.mustache Wyświetl plik


spec/_files/array_of_partials_implicit_partial.2.mustache → spec/_files/partial_array_of_partials_implicit.partial Wyświetl plik


spec/_files/array_of_partials_partial.txt → spec/_files/partial_array_of_partials_implicit.txt Wyświetl plik


+ 3
- 0
spec/_files/partial_empty.js Wyświetl plik

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

spec/_files/empty_partial.mustache → spec/_files/partial_empty.mustache Wyświetl plik


+ 1
- 0
spec/_files/partial_empty.partial Wyświetl plik

@@ -0,0 +1 @@
yo

spec/_files/empty_partial.txt → spec/_files/partial_empty.txt Wyświetl plik


+ 1
- 1
spec/_files/partial_recursion.js Wyświetl plik

@@ -1,4 +1,4 @@
var partial_context = {
var partial_recursion = {
name: '1', name: '1',
kids: [ kids: [
{ {


+ 1
- 1
spec/_files/partial_recursion.mustache Wyświetl plik

@@ -1,4 +1,4 @@
{{name}} {{name}}
{{#kids}} {{#kids}}
{{>partial}} {{>partial}}
{{/kids}}
{{/kids}}

spec/_files/partial_recursion.2.mustache → spec/_files/partial_recursion.partial Wyświetl plik

@@ -1,4 +1,4 @@
{{name}} {{name}}
{{#children}} {{#children}}
{{>partial}} {{>partial}}
{{/children}}
{{/children}}

+ 6
- 0
spec/_files/partial_template.js Wyświetl plik

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

spec/_files/template_partial.mustache → spec/_files/partial_template.mustache Wyświetl plik

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

+ 1
- 0
spec/_files/partial_template.partial Wyświetl plik

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

spec/_files/template_partial.txt → spec/_files/partial_template.txt Wyświetl plik


+ 16
- 0
spec/_files/partial_view.js Wyświetl plik

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

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

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

spec/_files/view_partial.mustache → spec/_files/partial_view.mustache Wyświetl plik

@@ -1,3 +1,3 @@
<h1>{{greeting}}</h1> <h1>{{greeting}}</h1>
{{>partial}} {{>partial}}
<h3>{{farewell}}</h3>
<h3>{{farewell}}</h3>

spec/_files/view_partial.2.mustache → spec/_files/partial_view.partial Wyświetl plik


spec/_files/view_partial.txt → spec/_files/partial_view.txt Wyświetl plik

@@ -2,5 +2,4 @@
Hello Chris Hello Chris
You have just won $10000! You have just won $10000!
Well, $6000, after taxes. Well, $6000, after taxes.

<h3>Fair enough, right?</h3> <h3>Fair enough, right?</h3>

+ 17
- 0
spec/_files/partial_whitespace.js Wyświetl plik

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

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

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


spec/_files/whitespace_partial.mustache → spec/_files/partial_whitespace.mustache Wyświetl plik

@@ -1,3 +1,3 @@
<h1>{{ greeting }}</h1> <h1>{{ greeting }}</h1>
{{> partial }} {{> partial }}
<h3>{{ farewell }}</h3>
<h3>{{ farewell }}</h3>

spec/_files/whitespace_partial.2.mustache → spec/_files/partial_whitespace.partial Wyświetl plik


spec/_files/whitespace_partial.txt → spec/_files/partial_whitespace.txt Wyświetl plik

@@ -2,5 +2,4 @@
Hello Chris Hello Chris
You have just won $10000! You have just won $10000!
Well, $6000, after taxes. Well, $6000, after taxes.

<h3>Fair enough, right?</h3> <h3>Fair enough, right?</h3>

+ 1
- 1
spec/_files/reuse_of_enumerables.js Wyświetl plik

@@ -1,6 +1,6 @@
var reuse_of_enumerables = { var reuse_of_enumerables = {
terms: [ terms: [
{name: 't1', index: 0}, {name: 't1', index: 0},
{name: 't2', index: 1},
{name: 't2', index: 1}
] ]
}; };

+ 3
- 3
spec/_files/section_as_context.txt Wyświetl plik

@@ -1,6 +1,6 @@
<h1>this is an object</h1> <h1>this is an object</h1>
<p>one of its attributes is a list</p> <p>one of its attributes is a list</p>
<ul> <ul>
<li>listitem1</li>
<li>listitem2</li>
</ul>
<li>listitem1</li>
<li>listitem2</li>
</ul>

+ 1
- 1
spec/_files/simple.mustache Wyświetl plik

@@ -2,4 +2,4 @@ Hello {{name}}
You have just won ${{value}}! You have just won ${{value}}!
{{#in_ca}} {{#in_ca}}
Well, ${{ taxed_value }}, after taxes. Well, ${{ taxed_value }}, after taxes.
{{/in_ca}}
{{/in_ca}}

+ 0
- 1
spec/_files/template_partial.2.mustache Wyświetl plik

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

+ 0
- 8
spec/_files/template_partial.js Wyświetl plik

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

+ 1
- 1
spec/_files/two_in_a_row.mustache Wyświetl plik

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

+ 0
- 1
spec/_files/two_sections.txt Wyświetl plik

@@ -1 +0,0 @@


+ 1
- 1
spec/_files/unescaped.mustache Wyświetl plik

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

+ 0
- 1
spec/_files/unknown_pragma.js Wyświetl plik

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

+ 0
- 1
spec/_files/unknown_pragma.mustache Wyświetl plik

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

+ 0
- 1
spec/_files/unknown_pragma.txt Wyświetl plik

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

+ 0
- 19
spec/_files/view_partial.js Wyświetl plik

@@ -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
spec/_files/whitespace_partial.js Wyświetl plik

@@ -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
}
};


+ 40
- 98
spec/mustache_spec.rb Wyświetl plik

@@ -11,26 +11,21 @@ TESTS = Dir.glob(File.join(FILES, '*.js')).map do |name|
File.basename name, '.js' File.basename name, '.js'
end end


PARTIALS = TESTS.select {|t| t.include? "partial" }
NON_PARTIALS = TESTS.select {|t| not t.include? "partial" }

NODE_PATH = `which node`.strip NODE_PATH = `which node`.strip
JS_PATH = `which js`.strip JS_PATH = `which js`.strip
JSC_PATH = "/System/Library/Frameworks/JavaScriptCore.framework/Versions/A/Resources/jsc" JSC_PATH = "/System/Library/Frameworks/JavaScriptCore.framework/Versions/A/Resources/jsc"
RHINO_JAR = "org.mozilla.javascript.tools.shell.Main" RHINO_JAR = "org.mozilla.javascript.tools.shell.Main"


def load_test(name, is_partial=false)
def load_test(name)
template = File.read(File.join(FILES, "#{name}.mustache"))
view = File.read(File.join(FILES, "#{name}.js")) view = File.read(File.join(FILES, "#{name}.js"))
template = File.read(File.join(FILES, "#{name}.mustache")).to_json
expect = File.read(File.join(FILES, "#{name}.txt"))

test = [view, template, expect]

if is_partial
test << File.read(File.join(FILES, "#{name}.2.mustache")).to_json
partial_file = File.join(FILES, "#{name}.partial")
partial = if File.exist?(partial_file)
File.read(partial_file)
end end
expect = File.read(File.join(FILES, "#{name}.txt"))


test
[template, view, partial, expect]
end end


def run_js(runner, js) def run_js(runner, js)
@@ -41,7 +36,7 @@ def run_js(runner, js)
JSC_PATH JSC_PATH
when :rhino when :rhino
"java #{RHINO_JAR}" "java #{RHINO_JAR}"
when :node
when :v8
NODE_PATH NODE_PATH
end end


@@ -60,11 +55,11 @@ describe "mustache" do
$engines_run += 1 $engines_run += 1
end end


it "should return the same when invoked multiple times" do
it "should return the same result when invoked multiple times" do
js = <<-JS js = <<-JS
#{@boilerplate} #{@boilerplate}
Mustache.to_html("x")
print(Mustache.to_html("x"));
Mustache.render("x")
print(Mustache.render("x"));
JS JS


run_js(@runner, js).should == "x\n" run_js(@runner, js).should == "x\n"
@@ -73,9 +68,9 @@ describe "mustache" do
it "should clear the context after each run" do it "should clear the context after each run" do
js = <<-JS js = <<-JS
#{@boilerplate} #{@boilerplate}
Mustache.to_html("{{#list}}{{x}}{{/list}}", {list: [{x: 1}]})
Mustache.render("{{#list}}{{x}}{{/list}}", {list: [{x: 1}]})
try { try {
print(Mustache.to_html("{{#list}}{{x}}{{/list}}", {list: [{}]}));
print(Mustache.render("{{#list}}{{x}}{{/list}}", {list: [{}]}));
} catch(e) { } catch(e) {
print('ERROR: ' + e.message); print('ERROR: ' + e.message);
} }
@@ -84,111 +79,62 @@ describe "mustache" do
run_js(@runner, js).should == "\n" run_js(@runner, js).should == "\n"
end end


NON_PARTIALS.each do |test|
TESTS.each do |test|
describe test do describe test do
it "should generate the correct html" do
view, template, expect = load_test(test)
it "should render the correct output" do
template, view, partial, expect = load_test(test)


js = <<-JS js = <<-JS
try { try {
#{@boilerplate} #{@boilerplate}
var template = #{template.to_json};
#{view} #{view}
var template = #{template};
var result = Mustache.to_html(template, #{test});
print(result);
var partials = {partial: #{partial ? partial.to_json : '""'}};
print(Mustache.render(template, #{test}, partials));
} catch(e) { } catch(e) {
print('ERROR: ' + e.message); print('ERROR: ' + e.message);
} }
JS JS


run_js(@runner, js).should == expect
run_js(@runner, js).chomp.should == expect
end end


it "should sendFun the correct html" do
view, template, expect = load_test(test)
it "should send the correct output" do
template, view, partial, expect = load_test(test)


js = <<-JS js = <<-JS
try { try {
#{@boilerplate} #{@boilerplate}
var template = #{template.to_json};
#{view} #{view}
var chunks = [];
var sendFun = function(chunk) {
if (chunk != "") {
chunks.push(chunk);
}
}
var template = #{template};
Mustache.to_html(template, #{test}, null, sendFun);
print(chunks.join("\\n"));
var partials = {
"partial": #{(partial || '').to_json}
};
var buffer = [];
var send = function (chunk) {
buffer.push(chunk);
};
Mustache.render(template, #{test}, partials, send);
print(buffer.join(""));
} catch(e) { } catch(e) {
print('ERROR: ' + e.message); print('ERROR: ' + e.message);
} }
JS JS


run_js(@runner, js).strip.should == expect.strip
end
end
end

PARTIALS.each do |test|
describe test do
it "should generate the correct html" do
view, template, expect, partial = load_test(test, true)

js = <<-JS
try {
#{@boilerplate}
#{view}
var template = #{template};
var partials = {"partial": #{partial}};
var result = Mustache.to_html(template, partial_context, partials);
print(result);
} catch(e) {
print('ERROR: ' + e.message);
}
JS

run_js(@runner, js).should == expect
end

it "should sendFun the correct html" do
view, template, expect, partial = load_test(test, true)

js = <<-JS
try {
#{@boilerplate}
#{view};
var template = #{template};
var partials = {"partial": #{partial}};
var chunks = [];
var sendFun = function(chunk) {
if (chunk != "") {
chunks.push(chunk);
}
}
Mustache.to_html(template, partial_context, partials, sendFun);
print(chunks.join("\\n"));
} catch(e) {
print('ERROR: ' + e.message);
}
JS

run_js(@runner, js).strip.should == expect.strip
run_js(@runner, js).chomp.should == expect
end end
end end
end end
end end


context "running in node" do
context "running in V8 (Chrome, node)" do
if File.exist?(NODE_PATH) if File.exist?(NODE_PATH)
before(:all) do before(:all) do
$stdout.write "Testing in node "
@runner = :node
$stdout.write "Testing in V8 "
@runner = :v8
@boilerplate = MUSTACHE.dup @boilerplate = MUSTACHE.dup
@boilerplate << <<-JS @boilerplate << <<-JS
function print(message) {
console.log(message);
}
var print = console.log;
JS JS
end end


@@ -198,7 +144,7 @@ describe "mustache" do


it_should_behave_like "mustache rendering" it_should_behave_like "mustache rendering"
else else
puts "Skipping tests in node (node not found)"
puts "Skipping tests in V8 (node not found)"
end end
end end


@@ -258,15 +204,11 @@ describe "mustache" do


context "suite" do context "suite" do
before(:each) do before(:each) do
$stdout.write "Verifying that we ran at the tests in at least one engine ... "
$stdout.write "Verifying that we ran the tests in at least one engine ... "
end end


after(:each) do after(:each) do
if @exception.nil?
puts "OK"
else
puts "ERROR!"
end
puts @exception.nil? ? "OK" : "ERROR"
end end


it "should have run at least one time" do it "should have run at least one time" do


+ 0
- 8
wrappers/commonjs/mustache.js.tpl.post Wyświetl plik

@@ -1,8 +0,0 @@
if (typeof module !== 'undefined' && module.exports) {
exports.name = Mustache.name;
exports.version = Mustache.version;

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

+ 0
- 6
wrappers/commonjs/mustache.js.tpl.pre Wyświetl plik

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


+ 0
- 8
wrappers/commonjs/package.json Wyświetl plik

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

Ładowanie…
Anuluj
Zapisz