Compare commits

...

73 Commits

Author SHA1 Message Date
kpdecker 948231a31d 1.0.10 2013-02-27 07:52:34 -06:00
kpdecker 3657457d81 Merge branch 'master' of github.com:wycats/handlebars.js 2013-02-27 07:49:56 -06:00
Kevin Decker 4bfb137e81 Use master build for travis badge 2013-02-26 17:46:19 -06:00
Yehuda Katz 835496f0b6 Merge pull request #443 from machty/gemified
Added handlebars-source gemspec
2013-02-19 21:12:51 -08:00
machty 519156138d Added handlebars-source gemspec for sharing with 3rd party precompilation libs 2013-02-20 00:04:39 -05:00
kpdecker ac59d6d060 Always rebuild dist files 2013-02-17 04:34:04 -06:00
kpdecker c95b3d6fc5 Create new options object for unless nesting
Fixes #343
2013-02-16 16:18:48 -06:00
kpdecker d6f146f8a5 Fix #428 nested if else rendering
The program equality checker was not taking children into account
when determining equality, causing breakages under similar cases.
2013-02-16 16:18:17 -06:00
kpdecker 1f00504d25 Rebuild 2013-02-16 16:02:08 -06:00
Kevin Decker 8b2c1e5d23 Merge pull request #442 from tricknotes/fix-method-name-in-error-message
Fix method name in error message
2013-02-16 14:01:31 -08:00
kpdecker fa400d9dcb Merge branch 'iife' 2013-02-16 15:58:29 -06:00
kpdecker 10453d1ef8 Use local vars 2013-02-16 13:56:19 -06:00
kpdecker 30623ce903 Add explicit SafeString property test 2013-02-16 13:55:55 -06:00
kpdecker da5cde5d1f Move Handlerbars.print into printer file 2013-02-16 13:36:34 -06:00
kpdecker 3e86bb0f64 Remove unnecessary child scopes 2013-02-16 13:08:46 -06:00
kpdecker 16bfebbf49 Use dist output in spec helper 2013-02-16 13:07:56 -06:00
kpdecker 1f76b065fc browser-prefix/suffix templates 2013-02-16 13:07:39 -06:00
kpdecker e2481cd66e Use PARSER_CONTEXT for tokenizer tests 2013-02-16 10:47:42 -06:00
kpdecker 8a05dcb70e Remove unused scope function 2013-02-16 10:47:04 -06:00
Ryunosuke SATO 261a2deb51 Fix method name in error message
- compile -> precompile
2013-02-16 20:14:38 +09:00
kpdecker 75a4f0d931 Negative number literal support
Fixes #422
2013-02-15 20:22:11 -06:00
kpdecker b74711e63b 1.0.9 2013-02-15 19:43:52 -06:00
kpdecker e3454a0187 Restore module scope in base.js 2013-02-15 19:41:10 -06:00
kpdecker f72cbed479 Merge branch 'master' of github.com:tommydudebreaux/handlebars.js into tommydudebreaux-master 2013-02-15 19:24:15 -06:00
Peter Wagenet bc8214cb7c Updated to 1.0.0-rc.3 2013-02-14 21:58:20 -08:00
Tommy Messbauer 7f9e3fea81 tests passing 2013-02-11 23:13:37 -06:00
Tommy Messbauer 1ca7462497 merge 2013-02-11 23:00:47 -06:00
Tommy Messbauer 514c9391e0 restored scope of var verifyMatch 2013-02-11 22:50:23 -06:00
Peter Wagenet 7c633b604d Better error messages for precompiler runtime mismatch 2013-02-09 19:25:53 -08:00
Peter Wagenet 51e9259fac Decouple compiler version checks from release versions 2013-02-08 18:42:20 -08:00
Yehuda Katz 0844d74fbe Merge pull request #429 from machty/extension-support
Open Handlebars compilation to externally-compiled AST
2013-02-07 10:02:38 -08:00
machty a48e32bbf6 Handlebars.parse/precompile/compile can now be passed an already-compiled Handlebars AST. 2013-02-07 02:06:17 -05:00
Yehuda Katz 448e43c41e Merge pull request #424 from wagenet/version-check-v2
Improved precompile template version check
2013-01-30 22:57:24 -08:00
Peter Wagenet 6fe7f17c89 Improved precompile template version check
This check reduces duplicated code as well as also failing if the
template was precompiled on a version before the check was added.
2013-01-30 22:44:30 -08:00
Yehuda Katz edb6f11b42 Merge pull request #420 from wagenet/version-check
Check version when evaluating templates.
2013-01-29 13:29:58 -08:00
Peter Wagenet d7758fe1a9 Check version when evaluating templates.
This makes sure that we throw an error when a template was precompiled
with a version of Handlebars that doesn't match the evaluating version.
2013-01-29 13:29:19 -08:00
kpdecker a3376e24b1 Rebuild 2013-01-21 19:49:16 -06:00
Kevin Decker 6a51198a9b Merge pull request #412 from wycats/inline-ops
Inline ops
2013-01-21 17:47:31 -08:00
Yehuda Katz a68a5ad5ba Merge pull request #414 from leshill/leading_context_only
Only allow `this` or `..` to lead a path
2013-01-21 11:30:19 -08:00
Les Hill 6ab92eee6d Only allow 'this' or '..' to lead a path
Paths like 'outer/../key' raise an exception when compiling.
2013-01-21 11:21:43 -08:00
kpdecker b0c9f3d83b mergeSource helper 2013-01-21 01:03:42 -06:00
kpdecker b661977966 Merge duplicate programs 2013-01-21 00:49:28 -06:00
kpdecker 969b418291 Remove inline option from pushStack 2013-01-21 00:23:08 -06:00
kpdecker 8e9688792b Use push rather than pushStack for inline ops 2013-01-21 00:21:17 -06:00
kpdecker 9ac47df7dd Make replaceStack always inline 2013-01-20 23:58:42 -06:00
kpdecker 6210d0b040 Merge branch 'master' into inline-ops 2013-01-20 21:43:10 -06:00
kpdecker 69d46e008b Rebuild dist 2013-01-20 21:42:37 -06:00
kpdecker f8a346b3ce Add LICENSE to built file
Fixes #411
2013-01-20 21:42:29 -06:00
kpdecker 0f6651bad9 Formatting updates for inlined content 2013-01-20 21:19:39 -06:00
kpdecker 231543feaf Merge consecutive buffer appends 2013-01-20 21:18:49 -06:00
kpdecker b3a39ccefd Use options register for blockHelperMissing 2013-01-20 17:30:40 -06:00
kpdecker 956b0a62a6 Inline known helper calls 2013-01-20 17:05:17 -06:00
kpdecker 94e70043f9 Use stack rather than register for invoke lookup 2013-01-20 17:04:30 -06:00
kpdecker bf201a66c2 Prevent duplication of inline content in append 2013-01-20 17:00:46 -06:00
kpdecker 39cc7c6816 Update 2nd level opcodes to use inlines 2013-01-19 18:33:49 -06:00
kpdecker e9ac494ffd Allow replaceStack to work with the inline stack 2013-01-19 18:21:05 -06:00
kpdecker cb50caaf53 Use inline values for simple terminals 2013-01-19 17:39:13 -06:00
kpdecker ae95407775 Inline stack helper implementation 2013-01-19 17:35:05 -06:00
kpdecker 7277284132 Merge branch 'master' into inline-ops 2013-01-19 17:01:20 -06:00
kpdecker d486e7fa47 Remove unused vars 2013-01-19 17:01:08 -06:00
kpdecker 3c20641a2e Reuse stack var to avoid register 2013-01-19 16:57:54 -06:00
kpdecker 5e20c809dc Output options only once to unknownHelper case 2013-01-19 16:44:43 -06:00
kpdecker 2b875cfb33 Merge branch 'master' into inline-ops 2013-01-19 13:50:41 -06:00
kpdecker 228c75669f Expand literal syntax tests 2013-01-19 13:19:17 -06:00
kpdecker d1fdb82291 Make equals without messages more resilient
When called without a message parameter the tests would fail under some
environments. I'm not clear on what is causing the failure, perhaps
ruby version, but this patch cleans up the issues there.
2013-01-19 13:19:03 -06:00
kpdecker 247babbe1a Empty hash literal output 2013-01-19 12:15:43 -06:00
kpdecker b1ea697b7d Inline foundHelper lookup 2013-01-19 11:39:23 -06:00
kpdecker 822d98a1ef Includ dist in npm package 2013-01-19 02:17:26 -06:00
kpdecker 5ac29dcb50 1.0.8 2013-01-19 02:12:36 -06:00
Tommy Messbauer db975b42a0 Merged upstream master and ran unit tests 2012-11-26 10:26:15 -06:00
Tommy Messbauer 7963218495 Factory update with tabs to spaces.. sorry :( 2012-11-26 09:39:08 -06:00
Tommy Messbauer eccc7c35ca Finished factory pattern 2012-08-29 15:07:15 -05:00
Tommy Messbauer 7c4813b417 Commiting initial factory code 2012-08-29 12:48:22 -05:00
26 changed files with 2902 additions and 2293 deletions
-1
View File
@@ -5,7 +5,6 @@ Gemfile
Gemfile.lock
Rakefile
bench/*
dist/*
spec/*
src/*
vendor/*
-1
View File
@@ -17,4 +17,3 @@ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
+1 -1
View File
@@ -1,4 +1,4 @@
[![Build Status](https://secure.travis-ci.org/wycats/handlebars.js.png)](http://travis-ci.org/wycats/handlebars.js)
[![Build Status](https://travis-ci.org/wycats/handlebars.js.png?branch=master)](https://travis-ci.org/wycats/handlebars.js)
Handlebars.js
=============
+11 -5
View File
@@ -45,11 +45,11 @@ def remove_exports(string)
match ? match[1] : string
end
minimal_deps = %w(base compiler/parser compiler/base compiler/ast utils compiler/compiler runtime).map do |file|
minimal_deps = %w(browser-prefix base compiler/parser compiler/base compiler/ast utils compiler/compiler runtime browser-suffix).map do |file|
"lib/handlebars/#{file}.js"
end
runtime_deps = %w(base utils runtime).map do |file|
runtime_deps = %w(browser-prefix base utils runtime browser-suffix).map do |file|
"lib/handlebars/#{file}.js"
end
@@ -61,7 +61,7 @@ def build_for_task(task)
FileUtils.rm_rf("dist/*") if File.directory?("dist")
FileUtils.mkdir_p("dist")
contents = []
contents = ["/*\n\n" + File.read('LICENSE') + "\n*/\n"]
task.prerequisites.each do |filename|
next if filename == "dist"
@@ -81,8 +81,14 @@ file "dist/handlebars.runtime.js" => runtime_deps do |task|
build_for_task(task)
end
task :build => [:compile, "dist/handlebars.js"]
task :runtime => [:compile, "dist/handlebars.runtime.js"]
task :build => [:compile] do |task|
# Since the tests are dependent on this always rebuild.
Rake::Task["dist/handlebars.js"].execute
end
task :runtime => [:compile] do |task|
# Since the tests are dependent on this always rebuild.
Rake::Task["dist/handlebars.runtime.js"].execute
end
desc "build the build and runtime version of handlebars"
task :release => [:build, :runtime]
+1302 -1094
View File
File diff suppressed because it is too large Load Diff
+98 -53
View File
@@ -1,11 +1,41 @@
/*
Copyright (C) 2011 by Yehuda Katz
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
*/
// lib/handlebars/browser-prefix.js
var Handlebars = {};
(function(Handlebars, undefined) {
;
// lib/handlebars/base.js
/*jshint eqnull:true*/
this.Handlebars = {};
Handlebars.VERSION = "1.0.0-rc.3";
Handlebars.COMPILER_REVISION = 2;
(function(Handlebars) {
Handlebars.VERSION = "1.0.rc.2";
Handlebars.REVISION_CHANGES = {
1: '<= 1.0.rc.2', // 1.0.rc.2 is actually rev2 but doesn't report it
2: '>= 1.0.0-rc.3'
};
Handlebars.helpers = {};
Handlebars.partials = {};
@@ -32,8 +62,6 @@ var toString = Object.prototype.toString, functionType = "[object Function]";
Handlebars.registerHelper('blockHelperMissing', function(context, options) {
var inverse = options.inverse || function() {}, fn = options.fn;
var ret = "";
var type = toString.call(context);
if(type === functionType) { context = context.call(this); }
@@ -124,11 +152,7 @@ Handlebars.registerHelper('if', function(context, options) {
});
Handlebars.registerHelper('unless', function(context, options) {
var fn = options.fn, inverse = options.inverse;
options.fn = inverse;
options.inverse = fn;
return Handlebars.helpers['if'].call(this, context, options);
return Handlebars.helpers['if'].call(this, context, {fn: options.inverse, inverse: options.fn});
});
Handlebars.registerHelper('with', function(context, options) {
@@ -139,8 +163,6 @@ Handlebars.registerHelper('log', function(context, options) {
var level = options.data && options.data.level != null ? parseInt(options.data.level, 10) : 1;
Handlebars.log(level, context);
});
}(this.Handlebars));
;
// lib/handlebars/utils.js
@@ -164,48 +186,48 @@ Handlebars.SafeString.prototype.toString = function() {
return this.string.toString();
};
(function() {
var escape = {
"&": "&amp;",
"<": "&lt;",
">": "&gt;",
'"': "&quot;",
"'": "&#x27;",
"`": "&#x60;"
};
var escape = {
"&": "&amp;",
"<": "&lt;",
">": "&gt;",
'"': "&quot;",
"'": "&#x27;",
"`": "&#x60;"
};
var badChars = /[&<>"'`]/g;
var possible = /[&<>"'`]/;
var badChars = /[&<>"'`]/g;
var possible = /[&<>"'`]/;
var escapeChar = function(chr) {
return escape[chr] || "&amp;";
};
var escapeChar = function(chr) {
return escape[chr] || "&amp;";
};
Handlebars.Utils = {
escapeExpression: function(string) {
// don't escape SafeStrings, since they're already safe
if (string instanceof Handlebars.SafeString) {
return string.toString();
} else if (string == null || string === false) {
return "";
}
if(!possible.test(string)) { return string; }
return string.replace(badChars, escapeChar);
},
isEmpty: function(value) {
if (!value && value !== 0) {
return true;
} else if(Object.prototype.toString.call(value) === "[object Array]" && value.length === 0) {
return true;
} else {
return false;
}
Handlebars.Utils = {
escapeExpression: function(string) {
// don't escape SafeStrings, since they're already safe
if (string instanceof Handlebars.SafeString) {
return string.toString();
} else if (string == null || string === false) {
return "";
}
};
})();;
if(!possible.test(string)) { return string; }
return string.replace(badChars, escapeChar);
},
isEmpty: function(value) {
if (!value && value !== 0) {
return true;
} else if(toString.call(value) === "[object Array]" && value.length === 0) {
return true;
} else {
return false;
}
}
};
;
// lib/handlebars/runtime.js
Handlebars.VM = {
template: function(templateSpec) {
// Just add water
@@ -225,12 +247,32 @@ Handlebars.VM = {
}
},
programWithDepth: Handlebars.VM.programWithDepth,
noop: Handlebars.VM.noop
noop: Handlebars.VM.noop,
compilerInfo: null
};
return function(context, options) {
options = options || {};
return templateSpec.call(container, Handlebars, context, options.helpers, options.partials, options.data);
var result = templateSpec.call(container, Handlebars, context, options.helpers, options.partials, options.data);
var compilerInfo = container.compilerInfo || [],
compilerRevision = compilerInfo[0] || 1,
currentRevision = Handlebars.COMPILER_REVISION;
if (compilerRevision !== currentRevision) {
if (compilerRevision < currentRevision) {
var runtimeVersions = Handlebars.REVISION_CHANGES[currentRevision],
compilerVersions = Handlebars.REVISION_CHANGES[compilerRevision];
throw "Template was precompiled with an older version of Handlebars than the current runtime. "+
"Please update your precompiler to a newer version ("+runtimeVersions+") or downgrade your runtime to an older version ("+compilerVersions+").";
} else {
// Use the embedded version info since the runtime doesn't know about this revision yet
throw "Template was precompiled with a newer version of Handlebars than the current runtime. "+
"Please update your runtime to a newer version ("+compilerInfo[1]+").";
}
}
return result;
};
},
@@ -269,3 +311,6 @@ Handlebars.VM = {
Handlebars.template = Handlebars.VM.template;
;
// lib/handlebars/browser-suffix.js
})(Handlebars);
;
+21
View File
@@ -0,0 +1,21 @@
# -*- encoding: utf-8 -*-
require 'json'
package = JSON.parse(File.read('package.json'))
Gem::Specification.new do |gem|
gem.name = "handlebars-source"
gem.authors = ["Yehuda Katz"]
gem.email = ["wycats@gmail.com"]
gem.date = Time.now.strftime("%Y-%m-%d")
gem.description = %q{Handlebars.js source code wrapper for (pre)compilation gems.}
gem.summary = %q{Handlebars.js source code wrapper}
gem.homepage = "https://github.com/wycats/handlebars.js/"
gem.version = package["version"]
gem.files = [
'dist/handlebars.js',
'dist/handlebars.runtime.js',
'lib/handlebars/source.rb'
]
end
+23 -5
View File
@@ -1,14 +1,32 @@
var Handlebars = require("./handlebars/base");
module.exports = Handlebars;
var handlebars = require("./handlebars/base"),
// Each of these augment the Handlebars object. No need to setup here.
// (This is done to easily share code between commonjs and browse envs)
require("./handlebars/utils");
utils = require("./handlebars/utils"),
compiler = require("./handlebars/compiler"),
runtime = require("./handlebars/runtime");
require("./handlebars/compiler");
require("./handlebars/runtime");
var create = function() {
var hb = handlebars.create();
utils.attach(hb);
compiler.attach(hb);
runtime.attach(hb);
return hb;
};
var Handlebars = create();
Handlebars.create = create;
module.exports = Handlebars; // instantiate an instance
// BEGIN(BROWSER)
// END(BROWSER)
// USAGE:
// var handlebars = require('handlebars');
// var singleton = handlebars.Handlebars,
// local = handlebars.create();
+15 -16
View File
@@ -1,11 +1,18 @@
/*jshint eqnull: true */
module.exports.create = function() {
var Handlebars = {};
// BEGIN(BROWSER)
/*jshint eqnull:true*/
this.Handlebars = {};
Handlebars.VERSION = "1.0.0-rc.3";
Handlebars.COMPILER_REVISION = 2;
(function(Handlebars) {
Handlebars.VERSION = "1.0.rc.2";
Handlebars.REVISION_CHANGES = {
1: '<= 1.0.rc.2', // 1.0.rc.2 is actually rev2 but doesn't report it
2: '>= 1.0.0-rc.3'
};
Handlebars.helpers = {};
Handlebars.partials = {};
@@ -32,8 +39,6 @@ var toString = Object.prototype.toString, functionType = "[object Function]";
Handlebars.registerHelper('blockHelperMissing', function(context, options) {
var inverse = options.inverse || function() {}, fn = options.fn;
var ret = "";
var type = toString.call(context);
if(type === functionType) { context = context.call(this); }
@@ -124,11 +129,7 @@ Handlebars.registerHelper('if', function(context, options) {
});
Handlebars.registerHelper('unless', function(context, options) {
var fn = options.fn, inverse = options.inverse;
options.fn = inverse;
options.inverse = fn;
return Handlebars.helpers['if'].call(this, context, options);
return Handlebars.helpers['if'].call(this, context, {fn: options.inverse, inverse: options.fn});
});
Handlebars.registerHelper('with', function(context, options) {
@@ -140,9 +141,7 @@ Handlebars.registerHelper('log', function(context, options) {
Handlebars.log(level, context);
});
}(this.Handlebars));
// END(BROWSER)
module.exports = this.Handlebars;
return Handlebars;
};
+3
View File
@@ -0,0 +1,3 @@
var Handlebars = {};
(function(Handlebars, undefined) {
+1
View File
@@ -0,0 +1 @@
})(Handlebars);
+103 -100
View File
@@ -1,130 +1,133 @@
var Handlebars = require('./base');
exports.attach = function(Handlebars) {
// BEGIN(BROWSER)
(function() {
Handlebars.AST = {};
Handlebars.AST = {};
Handlebars.AST.ProgramNode = function(statements, inverse) {
this.type = "program";
this.statements = statements;
if(inverse) { this.inverse = new Handlebars.AST.ProgramNode(inverse); }
};
Handlebars.AST.ProgramNode = function(statements, inverse) {
this.type = "program";
this.statements = statements;
if(inverse) { this.inverse = new Handlebars.AST.ProgramNode(inverse); }
};
Handlebars.AST.MustacheNode = function(rawParams, hash, unescaped) {
this.type = "mustache";
this.escaped = !unescaped;
this.hash = hash;
Handlebars.AST.MustacheNode = function(rawParams, hash, unescaped) {
this.type = "mustache";
this.escaped = !unescaped;
this.hash = hash;
var id = this.id = rawParams[0];
var params = this.params = rawParams.slice(1);
var id = this.id = rawParams[0];
var params = this.params = rawParams.slice(1);
// a mustache is an eligible helper if:
// * its id is simple (a single part, not `this` or `..`)
var eligibleHelper = this.eligibleHelper = id.isSimple;
// a mustache is an eligible helper if:
// * its id is simple (a single part, not `this` or `..`)
var eligibleHelper = this.eligibleHelper = id.isSimple;
// a mustache is definitely a helper if:
// * it is an eligible helper, and
// * it has at least one parameter or hash segment
this.isHelper = eligibleHelper && (params.length || hash);
// a mustache is definitely a helper if:
// * it is an eligible helper, and
// * it has at least one parameter or hash segment
this.isHelper = eligibleHelper && (params.length || hash);
// if a mustache is an eligible helper but not a definite
// helper, it is ambiguous, and will be resolved in a later
// pass or at runtime.
};
// if a mustache is an eligible helper but not a definite
// helper, it is ambiguous, and will be resolved in a later
// pass or at runtime.
};
Handlebars.AST.PartialNode = function(partialName, context) {
this.type = "partial";
this.partialName = partialName;
this.context = context;
};
Handlebars.AST.PartialNode = function(partialName, context) {
this.type = "partial";
this.partialName = partialName;
this.context = context;
};
Handlebars.AST.BlockNode = function(mustache, program, inverse, close) {
var verifyMatch = function(open, close) {
if(open.original !== close.original) {
throw new Handlebars.Exception(open.original + " doesn't match " + close.original);
}
};
Handlebars.AST.BlockNode = function(mustache, program, inverse, close) {
verifyMatch(mustache.id, close);
this.type = "block";
this.mustache = mustache;
this.program = program;
this.inverse = inverse;
verifyMatch(mustache.id, close);
this.type = "block";
this.mustache = mustache;
this.program = program;
this.inverse = inverse;
if (this.inverse && !this.program) {
this.isInverse = true;
if (this.inverse && !this.program) {
this.isInverse = true;
}
};
Handlebars.AST.ContentNode = function(string) {
this.type = "content";
this.string = string;
};
Handlebars.AST.HashNode = function(pairs) {
this.type = "hash";
this.pairs = pairs;
};
Handlebars.AST.IdNode = function(parts) {
this.type = "ID";
this.original = parts.join(".");
var dig = [], depth = 0;
for(var i=0,l=parts.length; i<l; i++) {
var part = parts[i];
if (part === ".." || part === "." || part === "this") {
if (dig.length > 0) { throw new Handlebars.Exception("Invalid path: " + this.original); }
else if (part === "..") { depth++; }
else { this.isScoped = true; }
}
};
else { dig.push(part); }
}
Handlebars.AST.ContentNode = function(string) {
this.type = "content";
this.string = string;
};
this.parts = dig;
this.string = dig.join('.');
this.depth = depth;
Handlebars.AST.HashNode = function(pairs) {
this.type = "hash";
this.pairs = pairs;
};
// an ID is simple if it only has one part, and that part is not
// `..` or `this`.
this.isSimple = parts.length === 1 && !this.isScoped && depth === 0;
Handlebars.AST.IdNode = function(parts) {
this.type = "ID";
this.original = parts.join(".");
this.stringModeValue = this.string;
};
var dig = [], depth = 0;
Handlebars.AST.PartialNameNode = function(name) {
this.type = "PARTIAL_NAME";
this.name = name;
};
for(var i=0,l=parts.length; i<l; i++) {
var part = parts[i];
Handlebars.AST.DataNode = function(id) {
this.type = "DATA";
this.id = id;
};
if(part === "..") { depth++; }
else if(part === "." || part === "this") { this.isScoped = true; }
else { dig.push(part); }
}
Handlebars.AST.StringNode = function(string) {
this.type = "STRING";
this.string = string;
this.stringModeValue = string;
};
this.parts = dig;
this.string = dig.join('.');
this.depth = depth;
Handlebars.AST.IntegerNode = function(integer) {
this.type = "INTEGER";
this.integer = integer;
this.stringModeValue = Number(integer);
};
// an ID is simple if it only has one part, and that part is not
// `..` or `this`.
this.isSimple = parts.length === 1 && !this.isScoped && depth === 0;
Handlebars.AST.BooleanNode = function(bool) {
this.type = "BOOLEAN";
this.bool = bool;
this.stringModeValue = bool === "true";
};
this.stringModeValue = this.string;
};
Handlebars.AST.CommentNode = function(comment) {
this.type = "comment";
this.comment = comment;
};
Handlebars.AST.PartialNameNode = function(name) {
this.type = "PARTIAL_NAME";
this.name = name;
};
Handlebars.AST.DataNode = function(id) {
this.type = "DATA";
this.id = id;
};
Handlebars.AST.StringNode = function(string) {
this.type = "STRING";
this.string = string;
this.stringModeValue = string;
};
Handlebars.AST.IntegerNode = function(integer) {
this.type = "INTEGER";
this.integer = integer;
this.stringModeValue = Number(integer);
};
Handlebars.AST.BooleanNode = function(bool) {
this.type = "BOOLEAN";
this.bool = bool;
this.stringModeValue = bool === "true";
};
Handlebars.AST.CommentNode = function(comment) {
this.type = "comment";
this.comment = comment;
};
})();
// END(BROWSER)
return Handlebars;
};
+11 -7
View File
@@ -1,17 +1,21 @@
var handlebars = require("./parser");
var Handlebars = require("../base");
exports.attach = function(Handlebars) {
// BEGIN(BROWSER)
Handlebars.Parser = handlebars;
Handlebars.parse = function(string) {
Handlebars.parse = function(input) {
// Just return if an already-compile AST was passed in.
if(input.constructor === Handlebars.AST.ProgramNode) { return input; }
Handlebars.Parser.yy = Handlebars.AST;
return Handlebars.Parser.parse(string);
return Handlebars.Parser.parse(input);
};
Handlebars.print = function(ast) {
return new Handlebars.PrintVisitor().accept(ast);
};
// END(BROWSER)
module.exports = Handlebars;
return Handlebars;
};
File diff suppressed because it is too large Load Diff
+14 -5
View File
@@ -1,7 +1,16 @@
// Each of these module will augment the Handlebars object as it loads. No need to perform addition operations
module.exports = require("./base");
require("./visitor");
require("./printer");
module.exports.attach = function(Handlebars) {
require("./ast");
require("./compiler");
var visitor = require("./visitor"),
printer = require("./printer"),
ast = require("./ast"),
compiler = require("./compiler");
visitor.attach(Handlebars);
printer.attach(Handlebars);
ast.attach(Handlebars);
compiler.attach(Handlebars);
return Handlebars;
};
+9 -2
View File
@@ -1,6 +1,11 @@
var Handlebars = require("./base");
exports.attach = function(Handlebars) {
// BEGIN(BROWSER)
Handlebars.print = function(ast) {
return new Handlebars.PrintVisitor().accept(ast);
};
Handlebars.PrintVisitor = function() { this.padding = 0; };
Handlebars.PrintVisitor.prototype = new Handlebars.Visitor();
@@ -128,4 +133,6 @@ Handlebars.PrintVisitor.prototype.comment = function(comment) {
};
// END(BROWSER)
exports.PrintVisitor = Handlebars.PrintVisitor;
return Handlebars;
};
+6 -1
View File
@@ -1,4 +1,4 @@
var Handlebars = require("./base");
exports.attach = function(Handlebars) {
// BEGIN(BROWSER)
@@ -9,5 +9,10 @@ Handlebars.Visitor.prototype = {
return this[object.type](object);
}
};
// END(BROWSER)
return Handlebars;
};
+27 -3
View File
@@ -1,6 +1,7 @@
var Handlebars = require("./base");
exports.attach = function(Handlebars) {
// BEGIN(BROWSER)
Handlebars.VM = {
template: function(templateSpec) {
// Just add water
@@ -20,12 +21,32 @@ Handlebars.VM = {
}
},
programWithDepth: Handlebars.VM.programWithDepth,
noop: Handlebars.VM.noop
noop: Handlebars.VM.noop,
compilerInfo: null
};
return function(context, options) {
options = options || {};
return templateSpec.call(container, Handlebars, context, options.helpers, options.partials, options.data);
var result = templateSpec.call(container, Handlebars, context, options.helpers, options.partials, options.data);
var compilerInfo = container.compilerInfo || [],
compilerRevision = compilerInfo[0] || 1,
currentRevision = Handlebars.COMPILER_REVISION;
if (compilerRevision !== currentRevision) {
if (compilerRevision < currentRevision) {
var runtimeVersions = Handlebars.REVISION_CHANGES[currentRevision],
compilerVersions = Handlebars.REVISION_CHANGES[compilerRevision];
throw "Template was precompiled with an older version of Handlebars than the current runtime. "+
"Please update your precompiler to a newer version ("+runtimeVersions+") or downgrade your runtime to an older version ("+compilerVersions+").";
} else {
// Use the embedded version info since the runtime doesn't know about this revision yet
throw "Template was precompiled with a newer version of Handlebars than the current runtime. "+
"Please update your runtime to a newer version ("+compilerInfo[1]+").";
}
}
return result;
};
},
@@ -66,3 +87,6 @@ Handlebars.template = Handlebars.VM.template;
// END(BROWSER)
return Handlebars;
};
+11
View File
@@ -0,0 +1,11 @@
module Handlebars
module Source
def self.bundled_path
File.expand_path("../../../dist/handlebars.js", __FILE__)
end
def self.runtime_bundled_path
File.expand_path("../../../dist/handlebars.runtime.js", __FILE__)
end
end
end
+39 -38
View File
@@ -1,4 +1,4 @@
var Handlebars = require("./base");
exports.attach = function(Handlebars) {
// BEGIN(BROWSER)
@@ -22,46 +22,47 @@ Handlebars.SafeString.prototype.toString = function() {
return this.string.toString();
};
(function() {
var escape = {
"&": "&amp;",
"<": "&lt;",
">": "&gt;",
'"': "&quot;",
"'": "&#x27;",
"`": "&#x60;"
};
var escape = {
"&": "&amp;",
"<": "&lt;",
">": "&gt;",
'"': "&quot;",
"'": "&#x27;",
"`": "&#x60;"
};
var badChars = /[&<>"'`]/g;
var possible = /[&<>"'`]/;
var badChars = /[&<>"'`]/g;
var possible = /[&<>"'`]/;
var escapeChar = function(chr) {
return escape[chr] || "&amp;";
};
var escapeChar = function(chr) {
return escape[chr] || "&amp;";
};
Handlebars.Utils = {
escapeExpression: function(string) {
// don't escape SafeStrings, since they're already safe
if (string instanceof Handlebars.SafeString) {
return string.toString();
} else if (string == null || string === false) {
return "";
}
if(!possible.test(string)) { return string; }
return string.replace(badChars, escapeChar);
},
isEmpty: function(value) {
if (!value && value !== 0) {
return true;
} else if(Object.prototype.toString.call(value) === "[object Array]" && value.length === 0) {
return true;
} else {
return false;
}
Handlebars.Utils = {
escapeExpression: function(string) {
// don't escape SafeStrings, since they're already safe
if (string instanceof Handlebars.SafeString) {
return string.toString();
} else if (string == null || string === false) {
return "";
}
};
})();
if(!possible.test(string)) { return string; }
return string.replace(badChars, escapeChar);
},
isEmpty: function(value) {
if (!value && value !== 0) {
return true;
} else if(toString.call(value) === "[object Array]" && value.length === 0) {
return true;
} else {
return false;
}
}
};
// END(BROWSER)
return Handlebars;
};
+1 -1
View File
@@ -1,7 +1,7 @@
{
"name": "handlebars",
"description": "Extension of the Mustache logicless template language",
"version": "1.0.7",
"version": "1.0.10",
"homepage": "http://www.handlebarsjs.com/",
"keywords": [
"handlebars mustache template html"
+19
View File
@@ -411,4 +411,23 @@ describe "Parser" do
it "knows how to report the correct line number in errors when the first character is a newline" do
lambda { ast_for("\n\nhello\n\nmy\n\n{{foo}") }.should raise_error(V8::JSError, /Parse error on line 7/m)
end
context "externally compiled AST" do
it "can pass through an already-compiled AST" do
ast_for(@context.eval('new Handlebars.AST.ProgramNode([ new Handlebars.AST.ContentNode("Hello")]);')).should == root do
content "Hello"
end
end
it "can pass through an already-compiled AST via compile/precompile" do
@context = Handlebars::Spec::FULL_CONTEXT
code = 'Handlebars.compile(new Handlebars.AST.ProgramNode([ new Handlebars.AST.ContentNode("Hello")]))();'
@context.eval(code).should == "Hello"
code = @context.eval 'Handlebars.precompile(new Handlebars.AST.ProgramNode([ new Handlebars.AST.ContentNode("Hello")]))'
@context.eval("(#{code})(this)").should == "Hello"
end
end
end
+67 -3
View File
@@ -20,6 +20,12 @@ if (!Handlebars) {
return Handlebars.compile(template, options);
}
};
} else {
var _equal = equal;
equals = equal = function(a, b, msg) {
// Allow exec with missing message params
_equal(a, b, msg || '');
};
}
suite("basic context");
@@ -170,8 +176,15 @@ test("nested paths with empty string value", function() {
});
test("literal paths", function() {
shouldCompileTo("Goodbye {{[@alan]/expression}} world!", {"@alan": {expression: "beautiful"}},
"Goodbye beautiful world!", "Literal paths can be used");
shouldCompileTo("Goodbye {{[@alan]/expression}} world!", {"@alan": {expression: "beautiful"}},
"Goodbye beautiful world!", "Literal paths can be used");
shouldCompileTo("Goodbye {{[foo bar]/expression}} world!", {"foo bar": {expression: "beautiful"}},
"Goodbye beautiful world!", "Literal paths can be used");
});
test('literal references', function() {
shouldCompileTo("Goodbye {{[foo bar]}} world!", {"foo bar": "beautiful"},
"Goodbye beautiful world!", "Literal paths can be used");
});
test("that current context path ({{.}}) doesn't hit helpers", function() {
@@ -194,6 +207,13 @@ test("this keyword in paths", function() {
shouldCompileTo(string, hash, "helloHelloHELLO", "This keyword evaluates in more complex paths");
});
test("this keyword nested inside path", function() {
var string = "{{#hellos}}{{text/this/foo}}{{/hellos}}";
shouldThrow(function() {
CompilerContext.compile(string);
}, Error, "Should throw exception");
});
test("this keyword in helpers", function() {
var helpers = {foo: function(value) {
return 'bar ' + value;
@@ -208,6 +228,13 @@ test("this keyword in helpers", function() {
shouldCompileTo(string, [hash, helpers], "bar hellobar Hellobar HELLO", "This keyword evaluates in more complex paths");
});
test("this keyword nested inside helpers param", function() {
var string = "{{#hellos}}{{foo text/this/foo}}{{/hellos}}";
shouldThrow(function() {
CompilerContext.compile(string);
}, Error, "Should throw exception");
});
suite("inverted sections");
test("inverted sections with unset value", function() {
@@ -273,6 +300,14 @@ test("block with complex lookup", function() {
"Templates can access variables in contexts up the stack with relative path syntax");
});
test("block with complex lookup using nested context", function() {
var string = "{{#goodbyes}}{{text}} cruel {{foo/../name}}! {{/goodbyes}}";
shouldThrow(function() {
CompilerContext.compile(string);
}, Error, "Should throw exception");
});
test("helper with complex lookup$", function() {
var string = "{{#goodbyes}}{{{link ../prefix}}}{{/goodbyes}}";
var hash = {prefix: "/root", goodbyes: [{text: "Goodbye", url: "goodbye"}]};
@@ -536,6 +571,16 @@ test("simple literals work", function() {
}};
shouldCompileTo(string, [hash, helpers], "Message: Hello world 12 times: true false", "template with a simple String literal");
});
test("negative number literals work", function() {
var string = 'Message: {{hello -12}}';
var hash = {};
var helpers = {hello: function(times) {
if(typeof times !== 'number') { times = "NaN"; }
return "Hello " + times + " times";
}};
shouldCompileTo(string, [hash, helpers], "Message: Hello -12 times", "template with a negative integer literal");
});
test("using a quote in the middle of a parameter raises an error", function() {
shouldThrow(function() {
@@ -584,6 +629,12 @@ 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");
});
test("it should not escape SafeString properties", function() {
var name = new Handlebars.SafeString("<em>Sean O&#x27;Malley</em>");
shouldCompileTo('{{name}}', [{ name: name }], "<em>Sean O&#x27;Malley</em>");
});
suite("helperMissing");
test("if a context is not found, helperMissing is used", function() {
@@ -1319,7 +1370,7 @@ test("bug reported by @fat where lambdas weren't being properly resolved", funct
test("Passing falsy values to Handlebars.compile throws an error", function() {
shouldThrow(function() {
CompilerContext.compile(null);
}, "You must pass a string to Handlebars.compile. You passed null");
}, "You must pass a string or Handlebars AST to Handlebars.precompile. You passed null");
});
test('GH-408: Multiple loops fail', function() {
@@ -1333,3 +1384,16 @@ test('GH-408: Multiple loops fail', function() {
var result = template(context);
equals(result, "John DoeJane DoeJohn DoeJane DoeJohn DoeJane Doe", 'It should output multiple times');
});
test('GS-428: Nested if else rendering', function() {
var succeedingTemplate = '{{#inverse}} {{#blk}} Unexpected {{/blk}} {{else}} {{#blk}} Expected {{/blk}} {{/inverse}}';
var failingTemplate = '{{#inverse}} {{#blk}} Unexpected {{/blk}} {{else}} {{#blk}} Expected {{/blk}} {{/inverse}}';
var helpers = {
blk: function(block) { return block.fn(''); },
inverse: function(block) { return block.inverse(''); }
};
shouldCompileTo(succeedingTemplate, [{}, helpers], ' Expected ');
shouldCompileTo(failingTemplate, [{}, helpers], ' Expected ');
});
+10 -18
View File
@@ -76,9 +76,7 @@ module Handlebars
CONTEXT.instance_eval do |context|
Handlebars::Spec.load_helpers(context);
Handlebars::Spec.js_load(context, 'lib/handlebars/base.js');
Handlebars::Spec.js_load(context, 'lib/handlebars/utils.js');
Handlebars::Spec.js_load(context, 'lib/handlebars/runtime.js');
Handlebars::Spec.js_load(context, 'dist/handlebars.js');
context["CompilerContext"] = {}
CompilerContext = context["CompilerContext"]
@@ -93,18 +91,20 @@ module Handlebars
end
end
PARSER_CONTEXT = V8::Context.new
PARSER_CONTEXT.instance_eval do |context|
Handlebars::Spec.load_helpers(context);
Handlebars::Spec.js_load(context, 'lib/handlebars/compiler/parser.js');
end
COMPILE_CONTEXT = V8::Context.new
COMPILE_CONTEXT.instance_eval do |context|
Handlebars::Spec.load_helpers(context);
Handlebars::Spec.js_load(context, 'lib/handlebars/base.js');
Handlebars::Spec.js_load(context, 'lib/handlebars/utils.js');
Handlebars::Spec.js_load(context, 'lib/handlebars/compiler/parser.js');
Handlebars::Spec.js_load(context, 'lib/handlebars/compiler/base.js');
Handlebars::Spec.js_load(context, 'lib/handlebars/compiler/ast.js');
Handlebars::Spec.js_load(context, 'dist/handlebars.js');
Handlebars::Spec.js_load(context, 'lib/handlebars/compiler/visitor.js');
Handlebars::Spec.js_load(context, 'lib/handlebars/compiler/printer.js');
Handlebars::Spec.js_load(context, 'lib/handlebars/compiler/compiler.js');
context["Handlebars"]["logger"]["level"] = ENV["DEBUG_JS"] ? context["Handlebars"]["logger"][ENV["DEBUG_JS"]] : 4
@@ -121,15 +121,7 @@ module Handlebars
FULL_CONTEXT.instance_eval do |context|
Handlebars::Spec.load_helpers(context);
Handlebars::Spec.js_load(context, 'lib/handlebars/base.js');
Handlebars::Spec.js_load(context, 'lib/handlebars/utils.js');
Handlebars::Spec.js_load(context, 'lib/handlebars/compiler/parser.js');
Handlebars::Spec.js_load(context, 'lib/handlebars/compiler/base.js');
Handlebars::Spec.js_load(context, 'lib/handlebars/compiler/ast.js');
Handlebars::Spec.js_load(context, 'lib/handlebars/compiler/visitor.js');
Handlebars::Spec.js_load(context, 'lib/handlebars/compiler/printer.js');
Handlebars::Spec.js_load(context, 'lib/handlebars/compiler/compiler.js');
Handlebars::Spec.js_load(context, 'lib/handlebars/runtime.js');
Handlebars::Spec.js_load(context, 'dist/handlebars.js');
context["Handlebars"]["logger"]["level"] = ENV["DEBUG_JS"] ? context["Handlebars"]["logger"][ENV["DEBUG_JS"]] : 4
+6 -5
View File
@@ -2,12 +2,9 @@ require "spec_helper"
require "timeout"
describe "Tokenizer" do
let(:parser) { @context["handlebars"] }
let(:lexer) { @context["handlebars"]["lexer"] }
let(:parser) { Handlebars::Spec::PARSER_CONTEXT["handlebars"] }
let(:lexer) { Handlebars::Spec::PARSER_CONTEXT["handlebars"]["lexer"] }
before(:all) do
@compiles = true
end
Token = Struct.new(:name, :text)
def tokenize(string)
@@ -235,6 +232,10 @@ describe "Tokenizer" do
result = tokenize(%|{{ foo 1 }}|)
result.should match_tokens(%w(OPEN ID INTEGER CLOSE))
result[2].should be_token("INTEGER", "1")
result = tokenize(%|{{ foo -1 }}|)
result.should match_tokens(%w(OPEN ID INTEGER CLOSE))
result[2].should be_token("INTEGER", "-1")
end
it "tokenizes booleans" do
+1 -1
View File
@@ -42,7 +42,7 @@
<mu>"@"[a-zA-Z]+ { yytext = yytext.substr(1); return 'DATA'; }
<mu>"true"/[}\s] { return 'BOOLEAN'; }
<mu>"false"/[}\s] { return 'BOOLEAN'; }
<mu>[0-9]+/[}\s] { return 'INTEGER'; }
<mu>\-?[0-9]+/[}\s] { return 'INTEGER'; }
<mu>[a-zA-Z0-9_$-]+/[=}\s\/.] { return 'ID'; }
<mu>'['[^\]]*']' { yytext = yytext.substr(1, yyleng-2); return 'ID'; }
<mu>. { return 'INVALID'; }