diff --git a/.npmignore b/.npmignore index 2f42c9f4..a43e7ecf 100644 --- a/.npmignore +++ b/.npmignore @@ -1,9 +1,14 @@ .DS_Store .gitignore .rvmrc +.jshintrc +.travis.yml +.rspec Gemfile Gemfile.lock Rakefile +*.gemspec +*.nuspec bench/* spec/* src/* diff --git a/README.markdown b/README.markdown index a279c1ac..781113d0 100644 --- a/README.markdown +++ b/README.markdown @@ -355,28 +355,41 @@ Known Issues * Using a variable, helper, or partial named `class` causes errors in IE browsers. (Instead, use `className`) Handlebars in the Wild ------------------ +---------------------- + +* [Assemble](http://assemble.io), by [@jonschlinkert](https://github.com/jonschlinkert) + and [@doowb](https://github.com/doowb), is a static site generator that uses Handlebars.js + as its template engine. +* [Ember.js](http://www.emberjs.com) makes Handlebars.js the primary way to + structure your views, also with automatic data binding support. +* [handlebars_assets](http://github.com/leshill/handlebars_assets): A Rails Asset Pipeline gem + from Les Hill (@leshill). +* [handlebars-helpers](https://github.com/assemble/handlebars-helpers) is an extensive library + with 100+ handlebars helpers. +* [hbs](http://github.com/donpark/hbs): An Express.js view engine adapter for Handlebars.js, + from Don Park. * [jblotus](http://github.com/jblotus) created [http://tryhandlebarsjs.com](http://tryhandlebarsjs.com) for anyone who would like to try out Handlebars.js in their browser. -* Don Park wrote an Express.js view engine adapter for Handlebars.js called - [hbs](http://github.com/donpark/hbs). +* [jQuery plugin](http://71104.github.io/jquery-handlebars/): allows you to use + Handlebars.js with [jQuery](http://jquery.com/). +* [Lumbar](http://walmartlabs.github.io/lumbar) provides easy module-based template management for + handlebars projects. * [sammy.js](http://github.com/quirkey/sammy) by Aaron Quint, a.k.a. quirkey, supports Handlebars.js as one of its template plugins. * [SproutCore](http://www.sproutcore.com) uses Handlebars.js as its main templating engine, extending it with automatic data binding support. -* [Ember.js](http://www.emberjs.com) makes Handlebars.js the primary way to - structure your views, also with automatic data binding support. -* Les Hill (@leshill) wrote a Rails Asset Pipeline gem named - [handlebars_assets](http://github.com/leshill/handlebars_assets). -* [Gist about Synchronous and asynchronous loading of external handlebars templates](https://gist.github.com/2287070) -* [Lumbar](walmartlabs.github.io/lumbar) provides easy module-based template management for handlebars projects. * [YUI](http://yuilibrary.com/yui/docs/handlebars/) implements a port of handlebars -* This [jQuery plugin](http://71104.github.io/jquery-handlebars/) allows you to use Handlebars.js with [jQuery](http://jquery.com/). + +External Resources +------------------ + +* [Gist about Synchronous and asynchronous loading of external handlebars templates](https://gist.github.com/2287070) Have a project using Handlebars? Send us a [pull request](https://github.com/wycats/handlebars.js/pull/new/master)! Helping Out ----------- + To build Handlebars.js you'll need a few things installed. * Node.js diff --git a/Rakefile b/Rakefile index eabeeb07..a99b1365 100644 --- a/Rakefile +++ b/Rakefile @@ -79,8 +79,8 @@ task :build => [:compile] do |task| Rake::Task["dist/handlebars.js"].execute Rake::Task["dist/handlebars.runtime.js"].execute - system "./node_modules/.bin/uglifyjs --comments -o dist/handlebars.min.js dist/handlebars.js" - system "./node_modules/.bin/uglifyjs --comments -o dist/handlebars.runtime.min.js dist/handlebars.runtime.js" + system "./node_modules/.bin/uglifyjs -m -c --comments -o dist/handlebars.min.js dist/handlebars.js" + system "./node_modules/.bin/uglifyjs -m -c --comments -o dist/handlebars.runtime.min.js dist/handlebars.runtime.js" end # Updates the various version numbers. diff --git a/lib/handlebars/base.js b/lib/handlebars/base.js index 2e0411d9..a6cd7ffa 100644 --- a/lib/handlebars/base.js +++ b/lib/handlebars/base.js @@ -117,7 +117,7 @@ function registerDefaultHelpers(instance) { var type = toString.call(conditional); if(type === functionType) { conditional = conditional.call(this); } - if(!conditional || isEmpty(conditional)) { + if (isEmpty(conditional)) { return options.inverse(this); } else { return options.fn(this); diff --git a/lib/handlebars/compiler/javascript-compiler.js b/lib/handlebars/compiler/javascript-compiler.js index a4164311..d3bd2588 100644 --- a/lib/handlebars/compiler/javascript-compiler.js +++ b/lib/handlebars/compiler/javascript-compiler.js @@ -348,10 +348,10 @@ JavaScriptCompiler.prototype = { // [lookupData] // // On stack, before: ... - // On stack, after: data[id], ... + // On stack, after: data, ... // - // Push the result of looking up `id` on the current data - lookupData: function(id) { + // Push the data lookup operator + lookupData: function() { this.push('data'); }, diff --git a/lib/handlebars/utils.js b/lib/handlebars/utils.js index d14795c2..26d2b54a 100644 --- a/lib/handlebars/utils.js +++ b/lib/handlebars/utils.js @@ -19,7 +19,7 @@ export function SafeString(string) { }; SafeString.prototype.toString = function() { - return this.string.toString(); + return "" + this.string; }; var escape = { @@ -57,7 +57,7 @@ export function escapeExpression(string) { // Force a string conversion as this will be done by the append regardless and // the regex test will do this transparently behind the scenes, causing issues if // an object's to string has escaped characters in it. - string = string.toString(); + string = "" + string; if(!possible.test(string)) { return string; } return string.replace(badChars, escapeChar); diff --git a/release-notes.md b/release-notes.md index d2800f92..e7f5ca0f 100644 --- a/release-notes.md +++ b/release-notes.md @@ -2,6 +2,8 @@ ## Development +- [#602](https://github.com/wycats/handlebars.js/pull/602) - Handle zero uniformly between mustache conditionals and the if helper ([@artiee](https://github.com/artiee)) +- [#535](https://github.com/wycats/handlebars.js/pull/535) - Fix for probably JIT error under Safari. ([@sorentwo](https://github.com/sorentwo)) - [#537](https://github.com/wycats/handlebars.js/issues/537) - Add CommonJS and AMD loader support ([@spikebrehm](https://github.com/spikebrehm)) - [#544](https://github.com/wycats/handlebars.js/issues/544) - Push travis builds to build server ([@fivetanley](https://github.com/fivetanley)) diff --git a/spec/builtins.js b/spec/builtins.js index bf77d1a7..c5942941 100644 --- a/spec/builtins.js +++ b/spec/builtins.js @@ -20,6 +20,8 @@ describe('builtin helpers', function() { "if with non-empty array shows the contents"); shouldCompileTo(string, {goodbye: [], world: "world"}, "cruel world!", "if with empty array does not show the contents"); + shouldCompileTo(string, {goodbye: 0, world: "world"}, "GOODBYE cruel world!", + "if with zero does show the contents"); }); it("if with function argument", function() { diff --git a/spec/parser.js b/spec/parser.js index 06a60db3..3397105f 100644 --- a/spec/parser.js +++ b/spec/parser.js @@ -108,11 +108,11 @@ describe('parser', function() { }); it('parses empty blocks with empty inverse section', function() { - ast_for("{{#foo}}{{^}}{{/foo}}").should.equal("BLOCK:\n {{ ID:foo [] }}\n PROGRAM:\n {{^}}\n"); + ast_for("{{#foo}}{{^}}{{/foo}}").should.equal("BLOCK:\n {{ ID:foo [] }}\n PROGRAM:\n"); }); it('parses empty blocks with empty inverse (else-style) section', function() { - ast_for("{{#foo}}{{else}}{{/foo}}").should.equal("BLOCK:\n {{ ID:foo [] }}\n PROGRAM:\n {{^}}\n"); + ast_for("{{#foo}}{{else}}{{/foo}}").should.equal("BLOCK:\n {{ ID:foo [] }}\n PROGRAM:\n"); }); it('parses non-empty blocks with empty inverse section', function() { diff --git a/src/handlebars.l b/src/handlebars.l index 5fd0bcda..018096b0 100644 --- a/src/handlebars.l +++ b/src/handlebars.l @@ -1,49 +1,14 @@ %x mu emu com -%% +%{ -"\\\\"/("{{") yytext = "\\"; return 'CONTENT'; -[^\x00]*?/("{{") { - if(yytext.slice(-1) !== "\\") this.begin("mu"); - if(yytext.slice(-1) === "\\") yytext = yytext.substr(0,yyleng-1), this.begin("emu"); - if(yytext) return 'CONTENT'; - } +function strip(start, end) { + return yytext = yytext.substr(start, yyleng-end); +} -[^\x00]+ return 'CONTENT'; +%} -[^\x00]{2,}?/("{{"|<>) { - if(yytext.slice(-1) !== "\\") this.popState(); - if(yytext.slice(-1) === "\\") yytext = yytext.substr(0,yyleng-1); - return 'CONTENT'; - } - -[\s\S]*?"--}}" yytext = yytext.substr(0, yyleng-4); this.popState(); return 'COMMENT'; - -"{{>" return 'OPEN_PARTIAL'; -"{{#" return 'OPEN_BLOCK'; -"{{/" return 'OPEN_ENDBLOCK'; -"{{^" return 'OPEN_INVERSE'; -"{{"\s*"else" return 'OPEN_INVERSE'; -"{{{" return 'OPEN_UNESCAPED'; -"{{&" return 'OPEN'; -"{{!--" this.popState(); this.begin('com'); -"{{!"[\s\S]*?"}}" yytext = yytext.substr(3,yyleng-5); this.popState(); return 'COMMENT'; -"{{" return 'OPEN'; - -"=" return 'EQUALS'; -"."/[}\/ ] return 'ID'; -".." return 'ID'; -[\/.] return 'SEP'; -\s+ /*ignore whitespace*/ -"}}}" this.popState(); return 'CLOSE_UNESCAPED'; -"}}" this.popState(); return 'CLOSE'; -'"'("\\"["]|[^"])*'"' yytext = yytext.substr(1,yyleng-2).replace(/\\"/g,'"'); return 'STRING'; -"'"("\\"[']|[^'])*"'" yytext = yytext.substr(1,yyleng-2).replace(/\\'/g,"'"); return 'STRING'; -"@" return 'DATA'; -"true"/[}\s] return 'BOOLEAN'; -"false"/[}\s] return 'BOOLEAN'; -\-?[0-9]+/[}\s] return 'INTEGER'; /* ID is the inverse of control characters. @@ -54,10 +19,55 @@ Control characters ranges: [\[-\^`] [, \, ], ^, `, Exceptions in range: _ [\{-~] {, |, }, ~ */ -[^\s!"#%-,\.\/;->@\[-\^`\{-~]+/[=}\s\/.] return 'ID'; +ID [^\s!"#%-,\.\/;->@\[-\^`\{-~]+/[=}\s\/.] -'['[^\]]*']' yytext = yytext.substr(1, yyleng-2); return 'ID'; +%% + +"\\\\"/("{{") yytext = "\\"; return 'CONTENT'; +[^\x00]*?/("{{") { + if(yytext.slice(-1) !== "\\") this.begin("mu"); + if(yytext.slice(-1) === "\\") strip(0,1), this.begin("emu"); + if(yytext) return 'CONTENT'; + } + +[^\x00]+ return 'CONTENT'; + +[^\x00]{2,}?/("{{"|<>) { + if(yytext.slice(-1) !== "\\") this.popState(); + if(yytext.slice(-1) === "\\") strip(0,1); + return 'CONTENT'; + } + +[\s\S]*?"--}}" strip(0,4); this.popState(); return 'COMMENT'; + +"{{>" return 'OPEN_PARTIAL'; +"{{#" return 'OPEN_BLOCK'; +"{{/" return 'OPEN_ENDBLOCK'; +"{{^" return 'OPEN_INVERSE'; +"{{"\s*"else" return 'OPEN_INVERSE'; +"{{{" return 'OPEN_UNESCAPED'; +"{{&" return 'OPEN'; +"{{!--" this.popState(); this.begin('com'); +"{{!"[\s\S]*?"}}" strip(3,5); this.popState(); return 'COMMENT'; +"{{" return 'OPEN'; + +"=" return 'EQUALS'; +"."/[}\/ ] return 'ID'; +".." return 'ID'; +[\/.] return 'SEP'; +\s+ /*ignore whitespace*/ +"}}}" this.popState(); return 'CLOSE_UNESCAPED'; +"}}" this.popState(); return 'CLOSE'; +'"'("\\"["]|[^"])*'"' yytext = strip(1,2).replace(/\\"/g,'"'); return 'STRING'; +"'"("\\"[']|[^'])*"'" yytext = strip(1,2).replace(/\\'/g,"'"); return 'STRING'; +"@" return 'DATA'; +"true"/[}\s] return 'BOOLEAN'; +"false"/[}\s] return 'BOOLEAN'; +\-?[0-9]+/[}\s] return 'INTEGER'; + +{ID} return 'ID'; + +'['[^\]]*']' yytext = strip(1,2); return 'ID'; . return 'INVALID'; <> return 'EOF'; - diff --git a/src/handlebars.yy b/src/handlebars.yy index 5764f6f1..d2f24c45 100644 --- a/src/handlebars.yy +++ b/src/handlebars.yy @@ -13,7 +13,7 @@ program | statements simpleInverse statements -> new yy.ProgramNode($1, $3) | statements simpleInverse -> new yy.ProgramNode($1, []) | statements -> new yy.ProgramNode($1) - | simpleInverse -> new yy.ProgramNode([], []) + | simpleInverse -> new yy.ProgramNode([]) | "" -> new yy.ProgramNode([]) ;