Compare commits

...

27 Commits

Author SHA1 Message Date
Peter Wagenet ce74c36118 Bumped package.json version 2012-09-15 21:11:36 -07:00
Yehuda Katz 26b1438f20 Merge pull request #198 from codekitchen/master
properly handle ampersands when HTML escaping
2012-09-14 20:48:40 -07:00
Yehuda Katz b83e5e0ea6 Merge pull request #297 from karlwestin/fix-reverse-array
Fixed an issue where {{#array}} {{/array}} wouldn't pass in an @index data variable
2012-09-14 20:26:11 -07:00
Yehuda Katz f6aea819f3 Merge pull request #310 from redpie/fix-registerPartial-data
Ensure plain text partials supplied to registerPartials are compiled using data: true if necessary.
2012-09-14 20:25:10 -07:00
Yehuda Katz 647ee28ca1 Merge pull request #313 from redpie/rake-spec-exitcode
Ensure `rake spec` exits with a meaningful exit code
2012-09-14 20:24:29 -07:00
Kiall Mac Innes d117b50fd3 Ensure rake spec exits with a meaningful exit code 2012-09-14 15:27:13 +01:00
kpdecker ed8c2b95b8 Update npm repo to root repo 2012-09-13 17:54:07 -05:00
kpdecker aba2269ddb Add nested hyphen test cases 2012-09-13 17:51:12 -05:00
Kiall Mac Innes 967c69b2da Ensure plain text partials supplied to registerPartials are compiled using data: true if necessary. 2012-09-13 15:04:34 +01:00
tomhuda 5a6e4f1ddd Fix repeated delimiter escaping 2012-09-12 20:44:48 -07:00
Yehuda Katz f55ca6c489 Merge pull request #309 from redpie/use-local-jison
Update Rakefile to use jison from node
2012-09-12 20:05:21 -07:00
Kiall Mac Innes 4b73b51e12 Update Rakefile to use a locally installed version of jison
The means root access is no longer required to build.
2012-09-12 11:15:31 +01:00
Karl Westin acc04c2826 Fixed an issue where {{#array}} {{/array}} wouldn't pass in an
@index data variable.

I just thought it would be nice to add in this feature. I'm using
the two of these a little interchangable, so if other people are doing
that as well, it might help usability.
2012-08-27 12:55:55 -07:00
Kevin Decker 89f5ab8aaf Merge pull request #277 from kpdecker/mocha-tests
Mocha tests

Add and fix node tests running qunit_spec under mocha and revs the node package to the latest.
2012-08-21 10:51:14 -07:00
kpdecker dc0426d836 Merge with upstream/master 2012-08-21 12:49:40 -05:00
Yehuda Katz 6761d4c6d1 Merge pull request #276 from HoffmannP/master
AMD support (second proposal)
2012-08-16 18:00:18 -07:00
Yehuda Katz 3426969221 Merge pull request #291 from leshill/quotes
Recognize bar='baz' hash argument
2012-08-16 17:59:45 -07:00
Les Hill 9ce3032678 Recognize bar='baz' hash argument 2012-08-14 23:12:22 -07:00
Peter Wagenet 2b3e777340 Merge pull request #282 from kpdecker/this-param
This param
2012-07-30 21:43:13 -07:00
kpdecker fc99d90337 Fix loading under node 2012-07-23 13:48:27 -05:00
kpdecker eb509a4b8b Execute mocha test from npm test 2012-07-23 13:47:52 -05:00
kpdecker 09ac8ac24b Make jshint happy(er) 2012-07-23 13:45:53 -05:00
kpdecker adb8486e5f Use suite rather than module for module decl 2012-07-23 13:44:45 -05:00
kpdecker 1e151e2030 Ignore sublime projects 2012-07-23 13:42:38 -05:00
kpdecker fe7d16de91 1.0.6beta 2012-07-23 10:36:13 -05:00
Peter Hoffmann 075076975d cmd flag to compile template with AMD style 2012-07-23 13:45:25 +02:00
Brian Palmer bd9a84a0b7 properly handle amperstands when HTML escaping
escapeExpression, when given a string like ">", was simply returning
">", not escaping the amperstand. This is incorrect, and makes it
impossible to have Handlebars properly escape a
string like "Escaped, <b> looks like: &lt;b&gt;"

If the intention of the user is to not escape these characters, then
{{{}}} or {{&}} should be used
2012-02-29 11:25:51 -07:00
13 changed files with 170 additions and 74 deletions
+3
View File
@@ -4,3 +4,6 @@ vendor
.DS_Store
lib/handlebars/compiler/parser.js
node_modules
*.sublime-project
*.sublime-workspace
npm-debug.log
+2
View File
@@ -10,7 +10,9 @@
"ember_deprecate",
"ember_deprecateFunc",
"require",
"suite",
"equal",
"equals",
"test",
"testBoth",
"raises",
+5 -4
View File
@@ -2,7 +2,7 @@ require "rubygems"
require "bundler/setup"
def compile_parser
system "jison src/handlebars.yy src/handlebars.l"
system "./node_modules/jison/lib/jison/cli-wrapper.js src/handlebars.yy src/handlebars.l"
if $?.success?
File.open("lib/handlebars/compiler/parser.js", "w") do |file|
file.puts File.read("handlebars.js") + ";"
@@ -15,11 +15,11 @@ def compile_parser
end
file "lib/handlebars/compiler/parser.js" => ["src/handlebars.yy","src/handlebars.l"] do
if ENV['PATH'].split(':').any? {|folder| File.exists?(folder+'/jison')}
if File.exists?('./node_modules/jison/lib/jison/cli-wrapper.js')
compile_parser
else
puts "Jison is not installed. Trying `npm install jison`."
sh "npm install jison -g"
sh "npm install jison"
compile_parser
end
end
@@ -28,7 +28,8 @@ task :compile => "lib/handlebars/compiler/parser.js"
desc "run the spec suite"
task :spec => [:release] do
system "rspec -cfs spec"
rc = system "rspec -cfs spec"
fail "rspec spec failed with exit code #{$?.exitstatus}" if (rc.nil? || ! rc || $?.exitstatus != 0)
end
task :default => [:compile, :spec]
+22 -2
View File
@@ -7,6 +7,17 @@ var optimist = require('optimist')
'description': 'Output File',
'alias': 'output'
},
'a': {
'type': 'boolean',
'description': 'Exports amd style (require.js)',
'alias': 'amd'
},
'h': {
'type': 'string',
'description': 'Path to handlebar.js (only valid for amd-style)',
'alias': 'handlebarPath',
'default': ''
},
'k': {
'type': 'string',
'description': 'Known helpers',
@@ -78,7 +89,12 @@ if (argv.known) {
var output = [];
if (!argv.simple) {
output.push('(function() {\n var template = Handlebars.template, templates = Handlebars.templates = Handlebars.templates || {};\n');
if (argv.amd) {
output.push('define([\'' + argv.handlebarPath + 'handlebars\'], function(Handlebars) {\n');
} else {
output.push('(function() {\n');
}
output.push(' var template = Handlebars.template, templates = Handlebars.templates = Handlebars.templates || {};\n');
}
function processTemplate(template, root) {
var path = template,
@@ -121,7 +137,11 @@ argv._.forEach(function(template) {
// Output the content
if (!argv.simple) {
output.push('})();');
if (argv.amd) {
output.push('});');
} else {
output.push('})();');
}
}
output = output.join('');
+5 -8
View File
@@ -3,7 +3,7 @@
/*jshint eqnull:true*/
this.Handlebars = {};
(function() {
(function(Handlebars) {
Handlebars.VERSION = "1.0.rc.1";
@@ -44,13 +44,10 @@ Handlebars.registerHelper('blockHelperMissing', function(context, options) {
return inverse(this);
} else if(type === "[object Array]") {
if(context.length > 0) {
for(var i=0, j=context.length; i<j; i++) {
ret = ret + fn(context[i]);
}
return Handlebars.helpers.each(context, options);
} else {
ret = inverse(this);
return inverse(this);
}
return ret;
} else {
return fn(context);
}
@@ -111,9 +108,9 @@ Handlebars.registerHelper('log', function(context) {
Handlebars.log(context);
});
}());
}(this.Handlebars));
// END(BROWSER)
module.exports = Handlebars;
module.exports = this.Handlebars;
+1 -1
View File
@@ -56,7 +56,7 @@ Handlebars.VM = {
} else if (!Handlebars.compile) {
throw new Handlebars.Exception("The partial " + name + " could not be compiled when running in runtime-only mode");
} else {
partials[name] = Handlebars.compile(partial);
partials[name] = Handlebars.compile(partial, {data: data !== undefined});
return partials[name](context, options);
}
}
+2 -1
View File
@@ -22,6 +22,7 @@ Handlebars.SafeString.prototype.toString = function() {
(function() {
var escape = {
"&": "&amp;",
"<": "&lt;",
">": "&gt;",
'"': "&quot;",
@@ -29,7 +30,7 @@ Handlebars.SafeString.prototype.toString = function() {
"`": "&#x60;"
};
var badChars = /&(?!\w+;)|[<>"'`]/g;
var badChars = /[&<>"'`]/g;
var possible = /[&<>"'`]/;
var escapeChar = function(chr) {
+11 -5
View File
@@ -1,14 +1,14 @@
{
"name": "handlebars",
"description": "Extension of the Mustache logicless template language",
"version": "1.0.5beta",
"version": "1.0.0-rc.1",
"homepage": "http://www.handlebarsjs.com/",
"keywords": [
"handlebars mustache template html"
],
"repository": {
"type": "git",
"url": "git://github.com/kpdecker/handlebars.js.git"
"url": "git://github.com/wycats/handlebars.js.git"
},
"engines": {
"node": ">=0.4.7"
@@ -17,9 +17,15 @@
"optimist": "~0.3",
"uglify-js": "~1.2"
},
"devDependencies": {},
"devDependencies": {
"mocha": "*"
},
"main": "lib/handlebars.js",
"bin": {
"handlebars": "bin/handlebars"
}
}
},
"scripts": {
"test": "node_modules/.bin/mocha -u qunit spec/qunit_spec.js"
},
"optionalDependencies": {}
}
+1 -1
View File
@@ -77,7 +77,7 @@ Module.new do
js_context["equal"] = js_context["equals"]
js_context["module"] = proc do |this, name|
js_context["suite"] = proc do |this, name|
test_context.module(name)
end
+4
View File
@@ -172,6 +172,10 @@ describe "Parser" do
mustache id("foo"), [], hash(["bar", "ID:baz"], ["bat", "\"bam\""])
end
ast_for("{{foo bat='bam'}}").should == root do
mustache id("foo"), [], hash(["bat", "\"bam\""])
end
ast_for("{{foo omg bar=baz bat=\"bam\"}}").should == root do
mustache id("foo"), [id("omg")], hash(["bar", id("baz")], ["bat", string("bam")])
end
+99 -51
View File
@@ -1,4 +1,28 @@
module("basic context");
var Handlebars;
if (!Handlebars) {
// Setup for Node package testing
Handlebars = require('../lib/handlebars');
var assert = require("assert"),
equal = assert.equal,
equals = assert.equal,
ok = assert.ok;
// Note that this doesn't have the same context separation as the rspec test.
// Both should be run for full acceptance of the two libary modes.
var CompilerContext = {
compile: function(template, options) {
var templateSpec = Handlebars.precompile(template, options);
return Handlebars.template(eval('(' + templateSpec + ')'));
},
compileWithPartial: function(template, options) {
return Handlebars.compile(template, options);
}
};
}
suite("basic context");
Handlebars.registerHelper('helperMissing', function(helper, context) {
if(helper === "link_to") {
@@ -6,13 +30,13 @@ Handlebars.registerHelper('helperMissing', function(helper, context) {
}
});
var shouldCompileTo = function(string, hashOrArray, expected, message) {
function shouldCompileTo(string, hashOrArray, expected, message) {
shouldCompileToWithPartials(string, hashOrArray, false, expected, message);
};
var shouldCompileToWithPartials = function(string, hashOrArray, partials, expected, message) {
}
function shouldCompileToWithPartials(string, hashOrArray, partials, expected, message) {
var template = CompilerContext[partials ? 'compileWithPartial' : 'compile'](string), ary;
if(Object.prototype.toString.call(hashOrArray) === "[object Array]") {
helpers = hashOrArray[1];
var helpers = hashOrArray[1];
if(helpers) {
for(var prop in Handlebars.helpers) {
@@ -27,11 +51,11 @@ var shouldCompileToWithPartials = function(string, hashOrArray, partials, expect
ary = [hashOrArray];
}
result = template.apply(this, ary);
var result = template.apply(this, ary);
equal(result, expected, "'" + expected + "' should === '" + result + "': " + message);
};
}
var shouldThrow = function(fn, exception, message) {
function shouldThrow(fn, exception, message) {
var caught = false;
try {
fn();
@@ -99,6 +123,8 @@ test("escaping expressions", function() {
shouldCompileTo("{{awesome}}", {awesome: "&\"'`\\<>"}, '&amp;&quot;&#x27;&#x60;\\&lt;&gt;',
"by default expressions should be escaped");
shouldCompileTo("{{awesome}}", {awesome: "Escaped, <b> looks like: &lt;b&gt;"}, 'Escaped, &lt;b&gt; looks like: &amp;lt;b&amp;gt;',
"escaping should properly handle amperstands");
});
test("functions returning safestrings shouldn't be escaped", function() {
@@ -114,6 +140,8 @@ test("functions", function() {
test("paths with hyphens", function() {
shouldCompileTo("{{foo-bar}}", {"foo-bar": "baz"}, "baz", "Paths can contain hyphens (-)");
shouldCompileTo("{{foo.foo-bar}}", {foo: {"foo-bar": "baz"}}, "baz", "Paths can contain hyphens (-)");
shouldCompileTo("{{foo/foo-bar}}", {foo: {"foo-bar": "baz"}}, "baz", "Paths can contain hyphens (-)");
});
test("nested paths", function() {
@@ -158,7 +186,7 @@ test("this keyword in paths", function() {
shouldCompileTo(string, hash, "goodbyeGoodbyeGOODBYE",
"This keyword in paths evaluates to current context");
string = "{{#hellos}}{{this/text}}{{/hellos}}"
string = "{{#hellos}}{{this/text}}{{/hellos}}";
hash = {hellos: [{text: "hello"}, {text: "Hello"}, {text: "HELLO"}]};
shouldCompileTo(string, hash, "helloHelloHELLO", "This keyword evaluates in more complex paths");
});
@@ -177,7 +205,7 @@ test("this keyword in helpers", function() {
shouldCompileTo(string, [hash, helpers], "bar hellobar Hellobar HELLO", "This keyword evaluates in more complex paths");
});
module("inverted sections");
suite("inverted sections");
test("inverted sections with unset value", function() {
var string = "{{#goodbyes}}{{this}}{{/goodbyes}}{{^goodbyes}}Right On!{{/goodbyes}}";
@@ -197,10 +225,10 @@ test("inverted section with empty set", function() {
shouldCompileTo(string, hash, "Right On!", "Inverted section rendered when value is empty set.");
});
module("blocks");
suite("blocks");
test("array", function() {
var string = "{{#goodbyes}}{{text}}! {{/goodbyes}}cruel {{world}}!"
var string = "{{#goodbyes}}{{text}}! {{/goodbyes}}cruel {{world}}!";
var hash = {goodbyes: [{text: "goodbye"}, {text: "Goodbye"}, {text: "GOODBYE"}], world: "world"};
shouldCompileTo(string, hash, "goodbye! Goodbye! GOODBYE! cruel world!",
"Arrays iterate over the contents when not empty");
@@ -210,8 +238,18 @@ test("array", function() {
});
test("array with @index", function() {
var string = "{{#goodbyes}}{{@index}}. {{text}}! {{/goodbyes}}cruel {{world}}!";
var hash = {goodbyes: [{text: "goodbye"}, {text: "Goodbye"}, {text: "GOODBYE"}], world: "world"};
var template = CompilerContext.compile(string);
var result = template(hash);
equal(result, "0. goodbye! 1. Goodbye! 2. GOODBYE! cruel world!", "The @index variable is used");
});
test("empty block", function() {
var string = "{{#goodbyes}}{{/goodbyes}}cruel {{world}}!"
var string = "{{#goodbyes}}{{/goodbyes}}cruel {{world}}!";
var hash = {goodbyes: [{text: "goodbye"}, {text: "Goodbye"}, {text: "GOODBYE"}], world: "world"};
shouldCompileTo(string, hash, "cruel world!",
"Arrays iterate over the contents when not empty");
@@ -284,7 +322,7 @@ test("block helper", function() {
var string = "{{#goodbyes}}{{text}}! {{/goodbyes}}cruel {{world}}!";
var template = CompilerContext.compile(string);
result = template({world: "world"}, { helpers: {goodbyes: function(options) { return options.fn({text: "GOODBYE"}); }}});
var result = template({world: "world"}, { helpers: {goodbyes: function(options) { return options.fn({text: "GOODBYE"}); }}});
equal(result, "GOODBYE! cruel world!", "Block helper executed");
});
@@ -292,7 +330,7 @@ test("block helper staying in the same context", function() {
var string = "{{#form}}<p>{{name}}</p>{{/form}}";
var template = CompilerContext.compile(string);
result = template({name: "Yehuda"}, {helpers: {form: function(options) { return "<form>" + options.fn(this) + "</form>"; } }});
var result = template({name: "Yehuda"}, {helpers: {form: function(options) { return "<form>" + options.fn(this) + "</form>"; } }});
equal(result, "<form><p>Yehuda</p></form>", "Block helper executed with current context");
});
@@ -317,7 +355,7 @@ test("block helper passing a new context", function() {
var string = "{{#form yehuda}}<p>{{name}}</p>{{/form}}";
var template = CompilerContext.compile(string);
result = template({yehuda: {name: "Yehuda"}}, { helpers: {form: function(context, options) { return "<form>" + options.fn(context) + "</form>"; }}});
var result = template({yehuda: {name: "Yehuda"}}, { helpers: {form: function(context, options) { return "<form>" + options.fn(context) + "</form>"; }}});
equal(result, "<form><p>Yehuda</p></form>", "Context variable resolved");
});
@@ -325,7 +363,7 @@ test("block helper passing a complex path context", function() {
var string = "{{#form yehuda/cat}}<p>{{name}}</p>{{/form}}";
var template = CompilerContext.compile(string);
result = template({yehuda: {name: "Yehuda", cat: {name: "Harold"}}}, { helpers: {form: function(context, options) { return "<form>" + options.fn(context) + "</form>"; }}});
var result = template({yehuda: {name: "Yehuda", cat: {name: "Harold"}}}, { helpers: {form: function(context, options) { return "<form>" + options.fn(context) + "</form>"; }}});
equal(result, "<form><p>Harold</p></form>", "Complex path variable resolved");
});
@@ -333,7 +371,7 @@ test("nested block helpers", function() {
var string = "{{#form yehuda}}<p>{{name}}</p>{{#link}}Hello{{/link}}{{/form}}";
var template = CompilerContext.compile(string);
result = template({
var result = template({
yehuda: {name: "Yehuda" }
}, {
helpers: {
@@ -387,7 +425,7 @@ test("block helper inverted sections", function() {
shouldCompileTo(messageString, [rootMessage, { list: list }], "<p>Nobody&#x27;s here</p>", "the context of an inverse is the parent of the block");
});
module("helpers hash");
suite("helpers hash");
test("providing a helpers hash", function() {
shouldCompileTo("Goodbye {{cruel}} {{world}}!", [{cruel: "cruel"}, {world: function() { return "world"; }}], "Goodbye cruel world!",
@@ -405,7 +443,7 @@ test("the helpers hash is available is nested contexts", function() {
});
module("partials");
suite("partials");
test("basic partials", function() {
var string = "Dudes: {{#dudes}}{{> dude}}{{/dudes}}";
@@ -441,9 +479,6 @@ test("rendering undefined partial throws an exception", function() {
test("rendering template partial in vm mode throws an exception", function() {
shouldThrow(function() {
var template = CompilerContext.compile("{{> whatever}}");
var string = "Dudes: {{>dude}} {{another_dude}}";
var dude = "{{name}}";
var hash = {name:"Jeepers", another_dude:"Creepers"};
template();
}, Handlebars.Exception, "Should throw exception");
});
@@ -472,7 +507,7 @@ test("Partials with literal paths", function() {
shouldCompileToWithPartials(string, [hash, {}, {dude:dude}], true, "Dudes: Jeepers", "Partials can use literal paths");
});
module("String literal parameters");
suite("String literal parameters");
test("simple literals work", function() {
var string = 'Message: {{hello "world" 12 true false}}';
@@ -482,7 +517,7 @@ test("simple literals work", function() {
if(typeof bool1 !== 'boolean') { bool1 = "NaB"; }
if(typeof bool2 !== 'boolean') { bool2 = "NaB"; }
return "Hello " + param + " " + times + " times: " + bool1 + " " + bool2;
}}
}};
shouldCompileTo(string, [hash, helpers], "Message: Hello world 12 times: true false", "template with a simple String literal");
});
@@ -495,8 +530,8 @@ test("using a quote in the middle of a parameter raises an error", function() {
test("escaping a String is possible", function(){
var string = 'Message: {{{hello "\\"world\\""}}}';
var hash = {}
var helpers = {hello: function(param) { return "Hello " + param; }}
var hash = {};
var helpers = {hello: function(param) { return "Hello " + param; }};
shouldCompileTo(string, [hash, helpers], "Message: Hello \"world\"", "template with an escaped String literal");
});
@@ -507,7 +542,7 @@ test("it works with ' marks", function() {
shouldCompileTo(string, [hash, helpers], "Message: Hello Alan's world", "template with a ' mark");
});
module("multiple parameters");
suite("multiple parameters");
test("simple multi-params work", function() {
var string = 'Message: {{goodbye cruel world}}';
@@ -525,7 +560,7 @@ test("block multi-params work", function() {
shouldCompileTo(string, [hash, helpers], "Message: Goodbye cruel world", "block helpers with multiple params");
});
module("safestring");
suite("safestring");
test("constructing a safestring from a string and checking its type", function() {
var safe = new Handlebars.SafeString("testing 1, 2, 3");
@@ -533,7 +568,7 @@ test("constructing a safestring from a string and checking its type", function()
equal(safe, "testing 1, 2, 3", "SafeString is equivalent to its underlying string");
});
module("helperMissing");
suite("helperMissing");
test("if a context is not found, helperMissing is used", function() {
var string = "{{hello}} {{link_to world}}";
@@ -542,47 +577,47 @@ test("if a context is not found, helperMissing is used", function() {
shouldCompileTo(string, context, "Hello <a>world</a>");
});
module("knownHelpers");
suite("knownHelpers");
test("Known helper should render helper", function() {
var template = CompilerContext.compile("{{hello}}", {knownHelpers: {"hello" : true}})
var template = CompilerContext.compile("{{hello}}", {knownHelpers: {"hello" : true}});
var result = template({}, {helpers: {hello: function() { return "foo"; }}});
equal(result, "foo", "'foo' should === '" + result);
});
test("Unknown helper in knownHelpers only mode should be passed as undefined", function() {
var template = CompilerContext.compile("{{typeof hello}}", {knownHelpers: {'typeof': true}, knownHelpersOnly: true})
var template = CompilerContext.compile("{{typeof hello}}", {knownHelpers: {'typeof': true}, knownHelpersOnly: true});
var result = template({}, {helpers: {'typeof': function(arg) { return typeof arg; }, hello: function() { return "foo"; }}});
equal(result, "undefined", "'undefined' should === '" + result);
});
test("Builtin helpers available in knownHelpers only mode", function() {
var template = CompilerContext.compile("{{#unless foo}}bar{{/unless}}", {knownHelpersOnly: true})
var template = CompilerContext.compile("{{#unless foo}}bar{{/unless}}", {knownHelpersOnly: true});
var result = template({});
equal(result, "bar", "'bar' should === '" + result);
});
test("Field lookup works in knownHelpers only mode", function() {
var template = CompilerContext.compile("{{foo}}", {knownHelpersOnly: true})
var template = CompilerContext.compile("{{foo}}", {knownHelpersOnly: true});
var result = template({foo: 'bar'});
equal(result, "bar", "'bar' should === '" + result);
});
test("Conditional blocks work in knownHelpers only mode", function() {
var template = CompilerContext.compile("{{#foo}}bar{{/foo}}", {knownHelpersOnly: true})
var template = CompilerContext.compile("{{#foo}}bar{{/foo}}", {knownHelpersOnly: true});
var result = template({foo: 'baz'});
equal(result, "bar", "'bar' should === '" + result);
});
test("Invert blocks work in knownHelpers only mode", function() {
var template = CompilerContext.compile("{{^foo}}bar{{/foo}}", {knownHelpersOnly: true})
var template = CompilerContext.compile("{{^foo}}bar{{/foo}}", {knownHelpersOnly: true});
var result = template({foo: false});
equal(result, "bar", "'bar' should === '" + result);
});
module("blockHelperMissing");
suite("blockHelperMissing");
test("lambdas are resolved by blockHelperMissing, not handlebars proper", function() {
var string = "{{#truthy}}yep{{/truthy}}";
@@ -591,7 +626,7 @@ test("lambdas are resolved by blockHelperMissing, not handlebars proper", functi
});
var teardown;
module("built-in helpers", {
suite("built-in helpers", {
setup: function(){ teardown = null; },
teardown: function(){ if (teardown) { teardown(); } }
});
@@ -619,13 +654,13 @@ test("if", function() {
test("if with function argument", function() {
var string = "{{#if goodbye}}GOODBYE {{/if}}cruel {{world}}!";
shouldCompileTo(string, {goodbye: function() {return true}, world: "world"}, "GOODBYE cruel world!",
shouldCompileTo(string, {goodbye: function() {return true;}, world: "world"}, "GOODBYE cruel world!",
"if with function shows the contents when function returns true");
shouldCompileTo(string, {goodbye: function() {return this.world}, world: "world"}, "GOODBYE cruel world!",
shouldCompileTo(string, {goodbye: function() {return this.world;}, world: "world"}, "GOODBYE cruel world!",
"if with function shows the contents when function returns string");
shouldCompileTo(string, {goodbye: function() {return false}, world: "world"}, "cruel world!",
shouldCompileTo(string, {goodbye: function() {return false;}, world: "world"}, "cruel world!",
"if with function does not show the contents when returns false");
shouldCompileTo(string, {goodbye: function() {return this.foo}, world: "world"}, "cruel world!",
shouldCompileTo(string, {goodbye: function() {return this.foo;}, world: "world"}, "cruel world!",
"if with function does not show the contents when returns undefined");
});
@@ -649,13 +684,13 @@ test("each with @index", function() {
});
test("log", function() {
var string = "{{log blah}}"
var string = "{{log blah}}";
var hash = { blah: "whee" };
var logArg;
var originalLog = Handlebars.log;
Handlebars.log = function(arg){ logArg = arg; }
teardown = function(){ Handlebars.log = originalLog; }
Handlebars.log = function(arg){ logArg = arg; };
teardown = function(){ Handlebars.log = originalLog; };
shouldCompileTo(string, hash, "", "log should not display");
equals("whee", logArg, "should call log with 'whee'");
@@ -995,6 +1030,19 @@ test("block helpers can take an optional hash", function() {
equals(result, "GOODBYE CRUEL world 12 TIMES", "Hash parameters output");
});
test("block helpers can take an optional hash with single quoted stings", function() {
var template = CompilerContext.compile("{{#goodbye cruel='CRUEL' times=12}}world{{/goodbye}}");
var helpers = {
goodbye: function(options) {
return "GOODBYE " + options.hash.cruel + " " + options.fn(this) + " " + options.hash.times + " TIMES";
}
};
var result = template({}, {helpers: helpers});
equals(result, "GOODBYE CRUEL world 12 TIMES", "Hash parameters output");
});
test("block helpers can take an optional hash with booleans", function() {
var helpers = {
goodbye: function(options) {
@@ -1012,8 +1060,8 @@ test("block helpers can take an optional hash with booleans", function() {
var result = template({}, {helpers: helpers});
equals(result, "GOODBYE CRUEL world", "Boolean hash parameter honored");
var template = CompilerContext.compile('{{#goodbye cruel="CRUEL" print=false}}world{{/goodbye}}');
var result = template({}, {helpers: helpers});
template = CompilerContext.compile('{{#goodbye cruel="CRUEL" print=false}}world{{/goodbye}}');
result = template({}, {helpers: helpers});
equals(result, "NOT PRINTING", "Boolean hash parameter honored");
});
@@ -1094,7 +1142,7 @@ test("when inside a block in String mode, .. passes the appropriate context in t
equals(result, "STOP ME FROM READING HACKER NEWS I need-a dad.joke wot", "Proper context variable output");
});
module("Regressions")
suite("Regressions");
test("GH-94: Cannot read property of undefined", function() {
var data = {"books":[{"title":"The origin of species","author":{"name":"Charles Darwin"}},{"title":"Lazarillo de Tormes"}]};
@@ -1113,13 +1161,13 @@ test("GH-150: Inverted sections print when they shouldn't", function() {
});
test("Mustache man page", function() {
var string = "Hello {{name}}. You have just won ${{value}}!{{#in_ca}} Well, ${{taxed_value}}, after taxes.{{/in_ca}}"
var string = "Hello {{name}}. You have just won ${{value}}!{{#in_ca}} Well, ${{taxed_value}}, after taxes.{{/in_ca}}";
var data = {
"name": "Chris",
"value": 10000,
"taxed_value": 10000 - (10000 * 0.4),
"in_ca": true
}
};
shouldCompileTo(string, data, "Hello Chris. You have just won $10000! Well, $6000, after taxes.", "the hello world mustache example works");
});
+9
View File
@@ -51,6 +51,15 @@ describe "Tokenizer" do
result[4].should be_token("CONTENT", "{{bar}} ")
end
it "supports escaping multiple delimiters" do
result = tokenize("{{foo}} \\{{bar}} \\{{baz}}")
result.should match_tokens(%w(OPEN ID CLOSE CONTENT CONTENT CONTENT))
result[3].should be_token("CONTENT", " ")
result[4].should be_token("CONTENT", "{{bar}} ")
result[5].should be_token("CONTENT", "{{baz}}")
end
it "supports escaping a triple stash" do
result = tokenize("{{foo}} \\{{{bar}}} {{baz}}")
result.should match_tokens(%w(OPEN ID CLOSE CONTENT CONTENT OPEN ID CLOSE))
+6 -1
View File
@@ -11,7 +11,11 @@
[^\x00]+ { return 'CONTENT'; }
<emu>[^\x00]{2,}?/("{{") { this.popState(); return 'CONTENT'; }
<emu>[^\x00]{2,}?/("{{"|<<EOF>>) {
if(yytext.slice(-1) !== "\\") this.popState();
if(yytext.slice(-1) === "\\") yytext = yytext.substr(0,yyleng-1);
return 'CONTENT';
}
<mu>"{{>" { return 'OPEN_PARTIAL'; }
<mu>"{{#" { return 'OPEN_BLOCK'; }
@@ -31,6 +35,7 @@
<mu>"}}}" { this.popState(); return 'CLOSE'; }
<mu>"}}" { this.popState(); return 'CLOSE'; }
<mu>'"'("\\"["]|[^"])*'"' { yytext = yytext.substr(1,yyleng-2).replace(/\\"/g,'"'); return 'STRING'; }
<mu>"'"("\\"[']|[^'])*"'" { yytext = yytext.substr(1,yyleng-2).replace(/\\"/g,'"'); return 'STRING'; }
<mu>"@"[a-zA-Z]+ { yytext = yytext.substr(1); return 'DATA'; }
<mu>"true"/[}\s] { return 'BOOLEAN'; }
<mu>"false"/[}\s] { return 'BOOLEAN'; }