Add optimized compiled version of handlebars, which should be significantly faster. Use Handlebars.VM.compile instead of Handlebars.compile to use the optimized version.
Major TODOS: * clean up a bunch of code duplication in the compiler * reorganize the compiler * add support for debug symbols which would make it possible to provide information about what part of the source caused a runtime error.
This commit is contained in:
+40
-26
@@ -43,7 +43,7 @@ test("compiling with a basic context", function() {
|
||||
});
|
||||
|
||||
test("comments", function() {
|
||||
shouldCompileTo("{{! Goodbye}}Goodbye\n{{cruel}}\n{{world}}!",
|
||||
shouldCompileTo("{{! Goodbye}}Goodbye\n{{cruel}}\n{{world}}!",
|
||||
{cruel: "cruel", world: "world"}, "Goodbye\ncruel\nworld!",
|
||||
"comments are ignored");
|
||||
});
|
||||
@@ -90,7 +90,7 @@ test("escaping expressions", function() {
|
||||
|
||||
test("functions returning safestrings shouldn't be escaped", function() {
|
||||
var hash = {awesome: function() { return new Handlebars.SafeString("&\"\\<>"); }};
|
||||
shouldCompileTo("{{awesome}}", hash, '&\"\\<>',
|
||||
shouldCompileTo("{{awesome}}", hash, '&\"\\<>',
|
||||
"functions returning safestrings aren't escaped");
|
||||
});
|
||||
|
||||
@@ -100,7 +100,7 @@ test("functions", function() {
|
||||
});
|
||||
|
||||
test("functions with context argument", function() {
|
||||
shouldCompileTo("{{awesome frank}}",
|
||||
shouldCompileTo("{{awesome frank}}",
|
||||
{awesome: function(context) { return context; },
|
||||
frank: "Frank"},
|
||||
"Frank", "functions are called with context arguments");
|
||||
@@ -121,7 +121,7 @@ test("--- TODO --- bad idea nested paths", function() {
|
||||
var hash = {goodbyes: [{text: "goodbye"}, {text: "Goodbye"}, {text: "GOODBYE"}], world: "world"};
|
||||
shouldThrow(function() {
|
||||
Handlebars.compile("{{#goodbyes}}{{../name/../name}}{{/goodbyes}}")(hash);
|
||||
}, Handlebars.Exception,
|
||||
}, Handlebars.Exception,
|
||||
"Cannot jump (..) into previous context after moving into a context.");
|
||||
|
||||
var string = "{{#goodbyes}}{{.././world}} {{/goodbyes}}";
|
||||
@@ -140,8 +140,8 @@ test("complex but empty paths", function() {
|
||||
test("this keyword in paths", function() {
|
||||
var string = "{{#goodbyes}}{{this}}{{/goodbyes}}";
|
||||
var hash = {goodbyes: ["goodbye", "Goodbye", "GOODBYE"]};
|
||||
shouldCompileTo(string, hash, "goodbyeGoodbyeGOODBYE",
|
||||
"This keyword in paths evaluates to current context");
|
||||
shouldCompileTo(string, hash, "goodbyeGoodbyeGOODBYE",
|
||||
"This keyword in paths evaluates to current context");
|
||||
|
||||
string = "{{#hellos}}{{this/text}}{{/hellos}}"
|
||||
hash = {hellos: [{text: "hello"}, {text: "Hello"}, {text: "HELLO"}]};
|
||||
@@ -161,7 +161,7 @@ test("inverted section with false value", function() {
|
||||
var hash = {goodbyes: false};
|
||||
shouldCompileTo(string, hash, "Right On!", "Inverted section rendered when value is false.");
|
||||
});
|
||||
|
||||
|
||||
test("inverted section with empty set", function() {
|
||||
var string = "{{#goodbyes}}{{this}}{{/goodbyes}}{{^goodbyes}}Right On!{{/goodbyes}}";
|
||||
var hash = {goodbyes: []};
|
||||
@@ -218,10 +218,10 @@ test("block with complex lookup", function() {
|
||||
});
|
||||
|
||||
test("helper with complex lookup", function() {
|
||||
var string = "{{#goodbyes}}{{{link}}}{{/goodbyes}}"
|
||||
var string = "{{#goodbyes}}{{{link ../prefix}}}{{/goodbyes}}"
|
||||
var hash = {prefix: "/root", goodbyes: [{text: "Goodbye", url: "goodbye"}]};
|
||||
var fallback = {link: function() {
|
||||
return "<a href='" + this.__get__("../prefix") + "/" + this.url + "'>" + this.text + "</a>"
|
||||
var fallback = {link: function(prefix) {
|
||||
return "<a href='" + prefix + "/" + this.url + "'>" + this.text + "</a>"
|
||||
}};
|
||||
shouldCompileTo(string, [hash, fallback], "<a href='/root/goodbye'>Goodbye</a>")
|
||||
});
|
||||
@@ -229,11 +229,11 @@ test("helper with complex lookup", function() {
|
||||
test("helper block with complex lookup expression", function() {
|
||||
var string = "{{#goodbyes}}{{../name}}{{/goodbyes}}"
|
||||
var hash = {name: "Alan"};
|
||||
var fallback = {goodbyes: function(context, fn) {
|
||||
var fallback = {goodbyes: function(fn) {
|
||||
var out = "";
|
||||
var byes = ["Goodbye", "goodbye", "GOODBYE"];
|
||||
for (var i = 0,j = byes.length; i < j; i++) {
|
||||
out += byes[i] + " " + fn(context) + "! ";
|
||||
out += byes[i] + " " + fn(this) + "! ";
|
||||
}
|
||||
return out;
|
||||
}};
|
||||
@@ -241,10 +241,10 @@ test("helper block with complex lookup expression", function() {
|
||||
});
|
||||
|
||||
test("helper with complex lookup and nested template", function() {
|
||||
var string = "{{#goodbyes}}{{#link}}{{text}}{{/link}}{{/goodbyes}}";
|
||||
var string = "{{#goodbyes}}{{#link ../prefix}}{{text}}{{/link}}{{/goodbyes}}";
|
||||
var hash = {prefix: '/root', goodbyes: [{text: "Goodbye", url: "goodbye"}]};
|
||||
var fallback = {link: function (context, fn) {
|
||||
return "<a href='" + this.__get__("../prefix") + "/" + this.url + "'>" + fn(this) + "</a>";
|
||||
var fallback = {link: function (prefix, fn) {
|
||||
return "<a href='" + prefix + "/" + this.url + "'>" + fn(this) + "</a>";
|
||||
}};
|
||||
shouldCompileTo(string, [hash, fallback], "<a href='/root/goodbye'>Goodbye</a>")
|
||||
});
|
||||
@@ -272,10 +272,10 @@ test("block helper staying in the same context", function() {
|
||||
equal(result, "<form><p>Yehuda</p></form>");
|
||||
});
|
||||
|
||||
test("block helper should have wrapped context in this", function() {
|
||||
test("block helper should have context in this", function() {
|
||||
var source = "<ul>{{#people}}<li>{{#link}}{{name}}{{/link}}</li>{{/people}}</ul>";
|
||||
var link = function(context, fn) {
|
||||
return '<a href="/people/' + this.__get__("id") + '">' + fn(this) + '</a>';
|
||||
var link = function(fn) {
|
||||
return '<a href="/people/' + this.id + '">' + fn(this) + '</a>';
|
||||
};
|
||||
var data = { "people": [
|
||||
{ "name": "Alan", "id": 1 },
|
||||
@@ -315,33 +315,47 @@ test("nested block helpers", function() {
|
||||
});
|
||||
|
||||
test("block inverted sections", function() {
|
||||
shouldCompileTo("{{#people}}{{name}}{{^}}{{../none}}{{/people}}", {none: "No people"},
|
||||
shouldCompileTo("{{#people}}{{name}}{{^}}{{none}}{{/people}}", {none: "No people"},
|
||||
"No people");
|
||||
});
|
||||
|
||||
test("block inverted sections with empty arrays", function() {
|
||||
shouldCompileTo("{{#people}}{{name}}{{^}}{{none}}{{/people}}", {none: "No people", people: []},
|
||||
"No people");
|
||||
});
|
||||
|
||||
test("block helper inverted sections", function() {
|
||||
var string = "{{#list people}}{{name}}{{^}}<em>Nobody's here</em>{{/list}}"
|
||||
var list = function(context, fn) {
|
||||
var list = function(context, fn, inverse) {
|
||||
if (context.length > 0) {
|
||||
var out = "<ul>";
|
||||
for(var i = 0,j=context.length; i < j; i++) {
|
||||
out += "<li>";
|
||||
out += "<li>";
|
||||
out += fn(context[i]);
|
||||
out += "</li>";
|
||||
}
|
||||
out += "</ul>";
|
||||
return out;
|
||||
} else {
|
||||
return "<p>" + inverse(this) + "</p>";
|
||||
}
|
||||
};
|
||||
|
||||
list.not = function(context, fn) {
|
||||
return "<p>" + fn(context, this) + "</p>";
|
||||
};
|
||||
var hash = {list: list, people: [{name: "Alan"}, {name: "Yehuda"}]};
|
||||
var empty = {list: list, people: []};
|
||||
var rootMessage = {
|
||||
list: function(context, fn, inverse) { if(context.length === 0) { return "<p>" + inverse(this) + "</p>"; } },
|
||||
people: [],
|
||||
message: "Nobody's here"
|
||||
}
|
||||
|
||||
var messageString = "{{#list people}}Hello{{^}}{{message}}{{/list}}";
|
||||
|
||||
// the meaning here may be kind of hard to catch, but list.not is always called,
|
||||
// so we should see the output of both
|
||||
shouldCompileTo(string, hash, "<ul><li>Alan</li><li>Yehuda</li></ul><p><em>Nobody's here</em></p>", "Not is called when block inverted section is encountered.");
|
||||
shouldCompileTo(string, hash, "<ul><li>Alan</li><li>Yehuda</li></ul>", "an inverse wrapper is passed in as a new context");
|
||||
shouldCompileTo(string, empty, "<p><em>Nobody's here</em></p>", "an inverse wrapper can be optionally called");
|
||||
shouldCompileTo(messageString, rootMessage, "<p>Nobody's here</p>", "the context of an inverse is the parent of the block");
|
||||
});
|
||||
|
||||
module("fallback hash");
|
||||
@@ -390,7 +404,7 @@ test("partial in a partial", function() {
|
||||
|
||||
test("rendering undefined partial throws an exception", function() {
|
||||
shouldThrow(function() {
|
||||
var template = Handlebars.compile("{{> whatever}}");
|
||||
var template = Handlebars.compile("{{> whatever}}");
|
||||
template();
|
||||
}, Handlebars.Exception, "Should throw exception");
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user