Compare commits
129 Commits
v1.0.5beta
..
v1.0.8
| Author | SHA1 | Date | |
|---|---|---|---|
| 5ac29dcb50 | |||
| 9453c6d9d2 | |||
| 0c4333ae81 | |||
| ce82bef6f1 | |||
| cd9895061b | |||
| ccd6a22ea5 | |||
| 5e5f0dce9c | |||
| f5079765ff | |||
| 24fc3a2288 | |||
| 8961298859 | |||
| 4f54f62abc | |||
| eaa7e97461 | |||
| 59e30ecf37 | |||
| 3396814797 | |||
| fb9a7aa533 | |||
| 72f33c4615 | |||
| 16f2bf8f9b | |||
| 5f56d6582f | |||
| 76882ff75a | |||
| 3cac2672d0 | |||
| 7a3641df5a | |||
| b3f954b0af | |||
| f671c2e3d7 | |||
| 118594f663 | |||
| f4cad086bb | |||
| 51fd64c334 | |||
| 0ffb2a9681 | |||
| ca22ca57e1 | |||
| e9fe73d7ab | |||
| 28f377d169 | |||
| 02d35262f9 | |||
| 8df8488947 | |||
| 17ee7ffaca | |||
| bf4c813db0 | |||
| 070e12f76f | |||
| 9385666e08 | |||
| dfaf698b44 | |||
| 12f160fb31 | |||
| b58c2dd0ad | |||
| 4bb794d814 | |||
| 7ce13a3c2c | |||
| 19bfbe07f8 | |||
| fd0560b951 | |||
| 4cfda479fa | |||
| eabcc8a002 | |||
| cbfe84f1a9 | |||
| bd04901454 | |||
| c3f92c402e | |||
| 39832c0633 | |||
| 2676efb222 | |||
| a927a9b0ad | |||
| 8ed7a1e4e6 | |||
| d89e4b3d33 | |||
| 9589ab8949 | |||
| 3212325173 | |||
| 850304f43b | |||
| ed245c6c64 | |||
| 1018d9bfd1 | |||
| a1bfc80577 | |||
| 1b197be7fc | |||
| 9e582fa0eb | |||
| a1c9acb8b1 | |||
| 42120d1177 | |||
| b5074a88ec | |||
| ce74c36118 | |||
| 26b1438f20 | |||
| b83e5e0ea6 | |||
| f6aea819f3 | |||
| 647ee28ca1 | |||
| d117b50fd3 | |||
| ed8c2b95b8 | |||
| aba2269ddb | |||
| 967c69b2da | |||
| 5a6e4f1ddd | |||
| f55ca6c489 | |||
| 4b73b51e12 | |||
| acc04c2826 | |||
| 8e9bd6742f | |||
| 89f5ab8aaf | |||
| dc0426d836 | |||
| 6761d4c6d1 | |||
| 3426969221 | |||
| 9ce3032678 | |||
| 4cf0869330 | |||
| 201917a1be | |||
| 08e580a23b | |||
| 2b3e777340 | |||
| cfc1d534cb | |||
| 4ffe1b8e5a | |||
| d8825b1961 | |||
| f5289a35ec | |||
| fc99d90337 | |||
| eb509a4b8b | |||
| 09ac8ac24b | |||
| adb8486e5f | |||
| 1e151e2030 | |||
| fe7d16de91 | |||
| 075076975d | |||
| 8be16c64f9 | |||
| f79af6bfa3 | |||
| efb1e25690 | |||
| 36b242a710 | |||
| 659be5a690 | |||
| 46c04fa71e | |||
| 5e8be14d78 | |||
| 72e05d623c | |||
| ff1547ea04 | |||
| 7a393a972b | |||
| 92b6c1401a | |||
| 0afc8b58d2 | |||
| 1082ec2414 | |||
| 727eb26cb6 | |||
| 175c6fae0f | |||
| 3486b530be | |||
| bc5efc1767 | |||
| 90adaa3cc8 | |||
| 8786a6c6e2 | |||
| 246325085f | |||
| 68c76c9b2e | |||
| facefe8fed | |||
| 8e70e3b58b | |||
| 730c412925 | |||
| 2418feaf19 | |||
| c2591f428b | |||
| 98b3327c75 | |||
| 992fdf2071 | |||
| bd9a84a0b7 | |||
| 24e04bad94 | |||
| e6079ca5e2 |
+3
-1
@@ -1,6 +1,8 @@
|
||||
dist
|
||||
vendor
|
||||
.rvmrc
|
||||
.DS_Store
|
||||
lib/handlebars/compiler/parser.js
|
||||
node_modules
|
||||
*.sublime-project
|
||||
*.sublime-workspace
|
||||
npm-debug.log
|
||||
|
||||
@@ -0,0 +1,52 @@
|
||||
{
|
||||
"predef": [
|
||||
"console",
|
||||
"Ember",
|
||||
"DS",
|
||||
"Handlebars",
|
||||
"Metamorph",
|
||||
"ember_assert",
|
||||
"ember_warn",
|
||||
"ember_deprecate",
|
||||
"ember_deprecateFunc",
|
||||
"require",
|
||||
"suite",
|
||||
"equal",
|
||||
"equals",
|
||||
"test",
|
||||
"testBoth",
|
||||
"raises",
|
||||
"deepEqual",
|
||||
"start",
|
||||
"stop",
|
||||
"ok",
|
||||
"strictEqual",
|
||||
"module"
|
||||
],
|
||||
|
||||
"node" : true,
|
||||
"es5" : true,
|
||||
"browser" : true,
|
||||
|
||||
"boss" : true,
|
||||
"curly": false,
|
||||
"debug": false,
|
||||
"devel": false,
|
||||
"eqeqeq": true,
|
||||
"evil": true,
|
||||
"forin": false,
|
||||
"immed": false,
|
||||
"laxbreak": false,
|
||||
"newcap": true,
|
||||
"noarg": true,
|
||||
"noempty": false,
|
||||
"nonew": false,
|
||||
"nomen": false,
|
||||
"onevar": false,
|
||||
"plusplus": false,
|
||||
"regexp": false,
|
||||
"undef": true,
|
||||
"sub": true,
|
||||
"strict": false,
|
||||
"white": false
|
||||
}
|
||||
@@ -1,5 +1,5 @@
|
||||
source "http://rubygems.org"
|
||||
|
||||
gem "rake"
|
||||
gem "therubyracer", ">= 0.9.8"
|
||||
gem "therubyracer", ">= 0.9.8", "< 0.11"
|
||||
gem "rspec"
|
||||
|
||||
+11
-11
@@ -3,16 +3,16 @@ GEM
|
||||
specs:
|
||||
diff-lcs (1.1.3)
|
||||
libv8 (3.3.10.4)
|
||||
rake (0.9.2.2)
|
||||
rspec (2.7.0)
|
||||
rspec-core (~> 2.7.0)
|
||||
rspec-expectations (~> 2.7.0)
|
||||
rspec-mocks (~> 2.7.0)
|
||||
rspec-core (2.7.1)
|
||||
rspec-expectations (2.7.0)
|
||||
diff-lcs (~> 1.1.2)
|
||||
rspec-mocks (2.7.0)
|
||||
therubyracer (0.9.9)
|
||||
rake (10.0.3)
|
||||
rspec (2.12.0)
|
||||
rspec-core (~> 2.12.0)
|
||||
rspec-expectations (~> 2.12.0)
|
||||
rspec-mocks (~> 2.12.0)
|
||||
rspec-core (2.12.2)
|
||||
rspec-expectations (2.12.1)
|
||||
diff-lcs (~> 1.1.3)
|
||||
rspec-mocks (2.12.1)
|
||||
therubyracer (0.10.2)
|
||||
libv8 (~> 3.3.10)
|
||||
|
||||
PLATFORMS
|
||||
@@ -21,4 +21,4 @@ PLATFORMS
|
||||
DEPENDENCIES
|
||||
rake
|
||||
rspec
|
||||
therubyracer (>= 0.9.8)
|
||||
therubyracer (>= 0.9.8, < 0.11)
|
||||
|
||||
+9
-7
@@ -19,7 +19,7 @@ In general, the syntax of Handlebars.js templates is a superset of Mustache temp
|
||||
Once you have a template, use the Handlebars.compile method to compile the template into a function. The generated function takes a context argument, which will be used to render the template.
|
||||
|
||||
```js
|
||||
var source = "<p>Hello, my name is {{name}}. I am from {{hometown}}. I have " +
|
||||
var source = "<p>Hello, my name is {{name}}. I am from {{hometown}}. I have " +
|
||||
"{{kids.length}} kids:</p>" +
|
||||
"<ul>{{#kids}}<li>{{name}} is {{age}}</li>{{/kids}}</ul>";
|
||||
var template = Handlebars.compile(source);
|
||||
@@ -112,7 +112,7 @@ instance:
|
||||
|
||||
```js
|
||||
Handlebars.registerHelper('link_to', function(title, context) {
|
||||
return "<a href='/posts" + context.id + "'>" + title + "</a>"
|
||||
return "<a href='/posts" + context.url + "'>" + title + "!</a>"
|
||||
});
|
||||
|
||||
var context = { posts: [{url: "/hello-world", body: "Hello World!"}] };
|
||||
@@ -124,7 +124,7 @@ template(context);
|
||||
// Would render:
|
||||
//
|
||||
// <ul>
|
||||
// <li><a href='/hello-world'>Post!</a></li>
|
||||
// <li><a href='/posts/hello-world'>Post!</a></li>
|
||||
// </ul>
|
||||
```
|
||||
|
||||
@@ -137,9 +137,9 @@ gets passed to the helper function.
|
||||
Handlebars.js also adds the ability to define block helpers. Block helpers are functions that can be called from anywhere in the template. Here's an example:
|
||||
|
||||
```js
|
||||
var source = "<ul>{{#people}}<li>{{{#link}}}{{name}}{{/link}}</li>{{/people}}</ul>";
|
||||
Handlebars.registerHelper('link', function(context, fn) {
|
||||
return '<a href="/people/' + this.__get__("id") + '">' + fn(this) + '</a>';
|
||||
var source = "<ul>{{#people}}<li>{{#link}}{{name}}{{/link}}</li>{{/people}}</ul>";
|
||||
Handlebars.registerHelper('link', function(options) {
|
||||
return '<a href="/people/' + this.id + '">' + options.fn(this) + '</a>';
|
||||
});
|
||||
var template = Handlebars.compile(source);
|
||||
|
||||
@@ -220,6 +220,7 @@ Precompile handlebar templates.
|
||||
Usage: handlebars template...
|
||||
|
||||
Options:
|
||||
-a, --amd Create an AMD format function (allows loading with RequireJS) [boolean]
|
||||
-f, --output Output File [string]
|
||||
-k, --known Known helpers [string]
|
||||
-o, --knownOnly Known helpers only [boolean]
|
||||
@@ -239,7 +240,7 @@ method and the resulting object may be as normal.
|
||||
### Optimizations
|
||||
|
||||
- Rather than using the full _handlebars.js_ library, implementations that
|
||||
do not need to compile templates at runtime may include _handlebars.vm.js_
|
||||
do not need to compile templates at runtime may include _handlebars.runtime.js_
|
||||
whose min+gzip size is approximately 1k.
|
||||
- If a helper is known to exist in the target environment they may be defined
|
||||
using the `--known name` argument may be used to optimize accesses to these
|
||||
@@ -283,6 +284,7 @@ template(context, {helpers: helpers, partials: partials, data: data})
|
||||
Known Issues
|
||||
------------
|
||||
* Handlebars.js can be cryptic when there's an error while rendering.
|
||||
* Using a variable, helper, or partial named `class` causes errors in IE browsers. (Instead, use `className`)
|
||||
|
||||
Handlebars in the Wild
|
||||
-----------------
|
||||
|
||||
@@ -2,10 +2,10 @@ require "rubygems"
|
||||
require "bundler/setup"
|
||||
|
||||
def compile_parser
|
||||
system "jison src/handlebars.yy src/handlebars.l"
|
||||
system "./node_modules/.bin/jison -m 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") + ";"
|
||||
file.puts File.read("src/parser-prefix.js") + File.read("handlebars.js") + File.read("src/parser-suffix.js")
|
||||
end
|
||||
|
||||
sh "rm 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')
|
||||
compile_parser
|
||||
else
|
||||
puts "Jison is not installed. Trying `npm install jison`."
|
||||
sh "npm install jison -g"
|
||||
sh "npm install"
|
||||
compile_parser
|
||||
end
|
||||
end
|
||||
@@ -28,10 +28,17 @@ 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]
|
||||
desc "run the npm test suite"
|
||||
task :npm_test => [:release] do
|
||||
rc = system "npm test"
|
||||
fail "npm test failed with exit code #{$?.exitstatus}" if (rc.nil? || ! rc || $?.exitstatus != 0)
|
||||
end
|
||||
|
||||
task :default => [:compile, :spec, :npm_test]
|
||||
|
||||
def remove_exports(string)
|
||||
match = string.match(%r{^// BEGIN\(BROWSER\)\n(.*)\n^// END\(BROWSER\)}m)
|
||||
@@ -85,20 +92,6 @@ directory "vendor"
|
||||
desc "benchmark against dust.js and mustache.js"
|
||||
task :bench => "vendor" do
|
||||
require "open-uri"
|
||||
#File.open("vendor/mustache.js", "w") do |file|
|
||||
#file.puts open("https://github.com/janl/mustache.js/raw/master/mustache.js").read
|
||||
#file.puts "module.exports = Mustache;"
|
||||
#end
|
||||
|
||||
File.open("vendor/benchmark.js", "w") do |file|
|
||||
file.puts open("https://raw.github.com/bestiejs/benchmark.js/master/benchmark.js").read
|
||||
end
|
||||
|
||||
#if File.directory?("vendor/dustjs")
|
||||
#system "cd vendor/dustjs && git pull"
|
||||
#else
|
||||
#system "git clone git://github.com/akdubya/dustjs.git vendor/dustjs"
|
||||
#end
|
||||
|
||||
#if File.directory?("vendor/coffee")
|
||||
#system "cd vendor/coffee && git pull"
|
||||
|
||||
@@ -133,7 +133,7 @@ BenchWarmer.prototype = {
|
||||
|
||||
if(!bench.error) {
|
||||
var count = bench.hz,
|
||||
moe = count * bench.stats.RME / 100;
|
||||
moe = count * bench.stats.rme / 100;
|
||||
|
||||
out = Math.round(count / 1000) + " ±" + Math.round(moe / 1000) + " (" + bench.cycles + ")";
|
||||
} else {
|
||||
|
||||
+43
-34
@@ -1,20 +1,22 @@
|
||||
require.paths.push("lib");
|
||||
require.paths.push("vendor");
|
||||
require.paths.push("vendor/dustjs/lib");
|
||||
require.paths.push("vendor/coffee/lib");
|
||||
require.paths.push("vendor/eco/lib");
|
||||
|
||||
|
||||
var BenchWarmer = require("./benchwarmer");
|
||||
Handlebars = require("handlebars");
|
||||
Handlebars = require("../lib/handlebars");
|
||||
|
||||
var dust = require("dust");
|
||||
var Mustache = require("mustache");
|
||||
var ecoExports = require("eco");
|
||||
var dust, Mustache, eco;
|
||||
|
||||
eco = function(str) {
|
||||
return ecoExports(str);
|
||||
}
|
||||
try {
|
||||
dust = require("dust");
|
||||
} catch (err) { /* NOP */ }
|
||||
|
||||
try {
|
||||
Mustache = require("mustache");
|
||||
} catch (err) { /* NOP */ }
|
||||
|
||||
try {
|
||||
var ecoExports = require("eco");
|
||||
eco = function(str) {
|
||||
return ecoExports(str);
|
||||
}
|
||||
} catch (err) { /* NOP */ }
|
||||
|
||||
var benchDetails = {
|
||||
string: {
|
||||
@@ -113,39 +115,46 @@ var makeSuite = function(name) {
|
||||
|
||||
var error = function() { throw new Error("EWOT"); };
|
||||
|
||||
|
||||
//bench("dust", function() {
|
||||
//dust.render(templateName, context, function(err, out) { });
|
||||
//});
|
||||
if (dust) {
|
||||
bench("dust", function() {
|
||||
dust.render(templateName, context, function(err, out) { });
|
||||
});
|
||||
}
|
||||
|
||||
bench("handlebars", function() {
|
||||
handlebarsTemplates[templateName](context);
|
||||
});
|
||||
|
||||
//if(ecoTemplates[templateName]) {
|
||||
//bench("eco", function() {
|
||||
//ecoTemplates[templateName](context);
|
||||
//});
|
||||
//} else {
|
||||
//bench("eco", error);
|
||||
//}
|
||||
if (eco) {
|
||||
if(ecoTemplates[templateName]) {
|
||||
bench("eco", function() {
|
||||
ecoTemplates[templateName](context);
|
||||
});
|
||||
} else {
|
||||
bench("eco", error);
|
||||
}
|
||||
}
|
||||
|
||||
//if(mustacheSource) {
|
||||
//bench("mustache", function() {
|
||||
//Mustache.to_html(mustacheSource, context, mustachePartials);
|
||||
//});
|
||||
//} else {
|
||||
//bench("mustache", error);
|
||||
//}
|
||||
if (Mustache && mustacheSource) {
|
||||
bench("mustache", function() {
|
||||
Mustache.to_html(mustacheSource, context, mustachePartials);
|
||||
});
|
||||
} else {
|
||||
bench("mustache", error);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
for(var name in benchDetails) {
|
||||
if(benchDetails.hasOwnProperty(name)) {
|
||||
dust.loadSource(dust.compile(benchDetails[name].dust, name));
|
||||
if (dust) {
|
||||
dust.loadSource(dust.compile(benchDetails[name].dust, name));
|
||||
}
|
||||
handlebarsTemplates[name] = Handlebars.compile(benchDetails[name].handlebars);
|
||||
|
||||
if(benchDetails[name].eco) { ecoTemplates[name] = eco(benchDetails[name].eco); }
|
||||
if (eco && benchDetails[name].eco) {
|
||||
ecoTemplates[name] = eco(benchDetails[name].eco);
|
||||
}
|
||||
|
||||
var partials = benchDetails[name].partials;
|
||||
if(partials) {
|
||||
|
||||
+56
-2
@@ -7,6 +7,23 @@ var optimist = require('optimist')
|
||||
'description': 'Output File',
|
||||
'alias': 'output'
|
||||
},
|
||||
'a': {
|
||||
'type': 'boolean',
|
||||
'description': 'Exports amd style (require.js)',
|
||||
'alias': 'amd'
|
||||
},
|
||||
'c': {
|
||||
'type': 'string',
|
||||
'description': 'Exports CommonJS style, path to Handlebars module',
|
||||
'alias': 'commonjs',
|
||||
'default': null
|
||||
},
|
||||
'h': {
|
||||
'type': 'string',
|
||||
'description': 'Path to handlebar.js (only valid for amd-style)',
|
||||
'alias': 'handlebarPath',
|
||||
'default': ''
|
||||
},
|
||||
'k': {
|
||||
'type': 'string',
|
||||
'description': 'Known helpers',
|
||||
@@ -22,6 +39,12 @@ var optimist = require('optimist')
|
||||
'description': 'Minimize output',
|
||||
'alias': 'min'
|
||||
},
|
||||
'n': {
|
||||
'type': 'string',
|
||||
'description': 'Template namespace',
|
||||
'alias': 'namespace',
|
||||
'default': 'Handlebars.templates'
|
||||
},
|
||||
's': {
|
||||
'type': 'boolean',
|
||||
'description': 'Output template function only.',
|
||||
@@ -31,6 +54,16 @@ var optimist = require('optimist')
|
||||
'type': 'string',
|
||||
'description': 'Template root. Base value that will be stripped from template names.',
|
||||
'alias': 'root'
|
||||
},
|
||||
'p' : {
|
||||
'type': 'boolean',
|
||||
'description': 'Compiling a partial template',
|
||||
'alias': 'partial'
|
||||
},
|
||||
'd' : {
|
||||
'type': 'boolean',
|
||||
'description': 'Include data when compiling',
|
||||
'alias': 'data'
|
||||
}
|
||||
})
|
||||
|
||||
@@ -78,7 +111,18 @@ 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 if (argv.commonjs) {
|
||||
output.push('var Handlebars = require("' + argv.commonjs + '");');
|
||||
} else {
|
||||
output.push('(function() {\n');
|
||||
}
|
||||
output.push(' var template = Handlebars.template, templates = ');
|
||||
output.push(argv.namespace);
|
||||
output.push(' = ');
|
||||
output.push(argv.namespace);
|
||||
output.push(' || {};\n');
|
||||
}
|
||||
function processTemplate(template, root) {
|
||||
var path = template,
|
||||
@@ -99,6 +143,10 @@ function processTemplate(template, root) {
|
||||
knownHelpersOnly: argv.o
|
||||
};
|
||||
|
||||
if (argv.data) {
|
||||
options.data = true;
|
||||
}
|
||||
|
||||
// Clean the template name
|
||||
if (!root) {
|
||||
template = basename(template);
|
||||
@@ -109,6 +157,8 @@ function processTemplate(template, root) {
|
||||
|
||||
if (argv.simple) {
|
||||
output.push(handlebars.precompile(data, options) + '\n');
|
||||
} else if (argv.partial) {
|
||||
output.push('Handlebars.partials[\'' + template + '\'] = template(' + handlebars.precompile(data, options) + ');\n');
|
||||
} else {
|
||||
output.push('templates[\'' + template + '\'] = template(' + handlebars.precompile(data, options) + ');\n');
|
||||
}
|
||||
@@ -121,7 +171,11 @@ argv._.forEach(function(template) {
|
||||
|
||||
// Output the content
|
||||
if (!argv.simple) {
|
||||
output.push('})();');
|
||||
if (argv.amd) {
|
||||
output.push('});');
|
||||
} else if (!argv.commonjs) {
|
||||
output.push('})();');
|
||||
}
|
||||
}
|
||||
output = output.join('');
|
||||
|
||||
|
||||
Vendored
+1992
File diff suppressed because it is too large
Load Diff
Vendored
+271
@@ -0,0 +1,271 @@
|
||||
// lib/handlebars/base.js
|
||||
|
||||
/*jshint eqnull:true*/
|
||||
this.Handlebars = {};
|
||||
|
||||
(function(Handlebars) {
|
||||
|
||||
Handlebars.VERSION = "1.0.rc.2";
|
||||
|
||||
Handlebars.helpers = {};
|
||||
Handlebars.partials = {};
|
||||
|
||||
Handlebars.registerHelper = function(name, fn, inverse) {
|
||||
if(inverse) { fn.not = inverse; }
|
||||
this.helpers[name] = fn;
|
||||
};
|
||||
|
||||
Handlebars.registerPartial = function(name, str) {
|
||||
this.partials[name] = str;
|
||||
};
|
||||
|
||||
Handlebars.registerHelper('helperMissing', function(arg) {
|
||||
if(arguments.length === 2) {
|
||||
return undefined;
|
||||
} else {
|
||||
throw new Error("Could not find property '" + arg + "'");
|
||||
}
|
||||
});
|
||||
|
||||
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); }
|
||||
|
||||
if(context === true) {
|
||||
return fn(this);
|
||||
} else if(context === false || context == null) {
|
||||
return inverse(this);
|
||||
} else if(type === "[object Array]") {
|
||||
if(context.length > 0) {
|
||||
return Handlebars.helpers.each(context, options);
|
||||
} else {
|
||||
return inverse(this);
|
||||
}
|
||||
} else {
|
||||
return fn(context);
|
||||
}
|
||||
});
|
||||
|
||||
Handlebars.K = function() {};
|
||||
|
||||
Handlebars.createFrame = Object.create || function(object) {
|
||||
Handlebars.K.prototype = object;
|
||||
var obj = new Handlebars.K();
|
||||
Handlebars.K.prototype = null;
|
||||
return obj;
|
||||
};
|
||||
|
||||
Handlebars.logger = {
|
||||
DEBUG: 0, INFO: 1, WARN: 2, ERROR: 3, level: 3,
|
||||
|
||||
methodMap: {0: 'debug', 1: 'info', 2: 'warn', 3: 'error'},
|
||||
|
||||
// can be overridden in the host environment
|
||||
log: function(level, obj) {
|
||||
if (Handlebars.logger.level <= level) {
|
||||
var method = Handlebars.logger.methodMap[level];
|
||||
if (typeof console !== 'undefined' && console[method]) {
|
||||
console[method].call(console, obj);
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
Handlebars.log = function(level, obj) { Handlebars.logger.log(level, obj); };
|
||||
|
||||
Handlebars.registerHelper('each', function(context, options) {
|
||||
var fn = options.fn, inverse = options.inverse;
|
||||
var i = 0, ret = "", data;
|
||||
|
||||
if (options.data) {
|
||||
data = Handlebars.createFrame(options.data);
|
||||
}
|
||||
|
||||
if(context && typeof context === 'object') {
|
||||
if(context instanceof Array){
|
||||
for(var j = context.length; i<j; i++) {
|
||||
if (data) { data.index = i; }
|
||||
ret = ret + fn(context[i], { data: data });
|
||||
}
|
||||
} else {
|
||||
for(var key in context) {
|
||||
if(context.hasOwnProperty(key)) {
|
||||
if(data) { data.key = key; }
|
||||
ret = ret + fn(context[key], {data: data});
|
||||
i++;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if(i === 0){
|
||||
ret = inverse(this);
|
||||
}
|
||||
|
||||
return ret;
|
||||
});
|
||||
|
||||
Handlebars.registerHelper('if', function(context, options) {
|
||||
var type = toString.call(context);
|
||||
if(type === functionType) { context = context.call(this); }
|
||||
|
||||
if(!context || Handlebars.Utils.isEmpty(context)) {
|
||||
return options.inverse(this);
|
||||
} else {
|
||||
return options.fn(this);
|
||||
}
|
||||
});
|
||||
|
||||
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);
|
||||
});
|
||||
|
||||
Handlebars.registerHelper('with', function(context, options) {
|
||||
return options.fn(context);
|
||||
});
|
||||
|
||||
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
|
||||
|
||||
var errorProps = ['description', 'fileName', 'lineNumber', 'message', 'name', 'number', 'stack'];
|
||||
|
||||
Handlebars.Exception = function(message) {
|
||||
var tmp = Error.prototype.constructor.apply(this, arguments);
|
||||
|
||||
// Unfortunately errors are not enumerable in Chrome (at least), so `for prop in tmp` doesn't work.
|
||||
for (var idx = 0; idx < errorProps.length; idx++) {
|
||||
this[errorProps[idx]] = tmp[errorProps[idx]];
|
||||
}
|
||||
};
|
||||
Handlebars.Exception.prototype = new Error();
|
||||
|
||||
// Build out our basic SafeString type
|
||||
Handlebars.SafeString = function(string) {
|
||||
this.string = string;
|
||||
};
|
||||
Handlebars.SafeString.prototype.toString = function() {
|
||||
return this.string.toString();
|
||||
};
|
||||
|
||||
(function() {
|
||||
var escape = {
|
||||
"&": "&",
|
||||
"<": "<",
|
||||
">": ">",
|
||||
'"': """,
|
||||
"'": "'",
|
||||
"`": "`"
|
||||
};
|
||||
|
||||
var badChars = /[&<>"'`]/g;
|
||||
var possible = /[&<>"'`]/;
|
||||
|
||||
var escapeChar = function(chr) {
|
||||
return escape[chr] || "&";
|
||||
};
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
};
|
||||
})();;
|
||||
// lib/handlebars/runtime.js
|
||||
Handlebars.VM = {
|
||||
template: function(templateSpec) {
|
||||
// Just add water
|
||||
var container = {
|
||||
escapeExpression: Handlebars.Utils.escapeExpression,
|
||||
invokePartial: Handlebars.VM.invokePartial,
|
||||
programs: [],
|
||||
program: function(i, fn, data) {
|
||||
var programWrapper = this.programs[i];
|
||||
if(data) {
|
||||
return Handlebars.VM.program(fn, data);
|
||||
} else if(programWrapper) {
|
||||
return programWrapper;
|
||||
} else {
|
||||
programWrapper = this.programs[i] = Handlebars.VM.program(fn);
|
||||
return programWrapper;
|
||||
}
|
||||
},
|
||||
programWithDepth: Handlebars.VM.programWithDepth,
|
||||
noop: Handlebars.VM.noop
|
||||
};
|
||||
|
||||
return function(context, options) {
|
||||
options = options || {};
|
||||
return templateSpec.call(container, Handlebars, context, options.helpers, options.partials, options.data);
|
||||
};
|
||||
},
|
||||
|
||||
programWithDepth: function(fn, data, $depth) {
|
||||
var args = Array.prototype.slice.call(arguments, 2);
|
||||
|
||||
return function(context, options) {
|
||||
options = options || {};
|
||||
|
||||
return fn.apply(this, [context, options.data || data].concat(args));
|
||||
};
|
||||
},
|
||||
program: function(fn, data) {
|
||||
return function(context, options) {
|
||||
options = options || {};
|
||||
|
||||
return fn(context, options.data || data);
|
||||
};
|
||||
},
|
||||
noop: function() { return ""; },
|
||||
invokePartial: function(partial, name, context, helpers, partials, data) {
|
||||
var options = { helpers: helpers, partials: partials, data: data };
|
||||
|
||||
if(partial === undefined) {
|
||||
throw new Handlebars.Exception("The partial " + name + " could not be found");
|
||||
} else if(partial instanceof Function) {
|
||||
return partial(context, options);
|
||||
} 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, {data: data !== undefined});
|
||||
return partials[name](context, options);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
Handlebars.template = Handlebars.VM.template;
|
||||
;
|
||||
+64
-15
@@ -1,7 +1,11 @@
|
||||
// BEGIN(BROWSER)
|
||||
var Handlebars = {};
|
||||
|
||||
Handlebars.VERSION = "1.0.beta.5";
|
||||
/*jshint eqnull:true*/
|
||||
this.Handlebars = {};
|
||||
|
||||
(function(Handlebars) {
|
||||
|
||||
Handlebars.VERSION = "1.0.rc.2";
|
||||
|
||||
Handlebars.helpers = {};
|
||||
Handlebars.partials = {};
|
||||
@@ -40,29 +44,71 @@ 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);
|
||||
}
|
||||
});
|
||||
|
||||
Handlebars.K = function() {};
|
||||
|
||||
Handlebars.createFrame = Object.create || function(object) {
|
||||
Handlebars.K.prototype = object;
|
||||
var obj = new Handlebars.K();
|
||||
Handlebars.K.prototype = null;
|
||||
return obj;
|
||||
};
|
||||
|
||||
Handlebars.logger = {
|
||||
DEBUG: 0, INFO: 1, WARN: 2, ERROR: 3, level: 3,
|
||||
|
||||
methodMap: {0: 'debug', 1: 'info', 2: 'warn', 3: 'error'},
|
||||
|
||||
// can be overridden in the host environment
|
||||
log: function(level, obj) {
|
||||
if (Handlebars.logger.level <= level) {
|
||||
var method = Handlebars.logger.methodMap[level];
|
||||
if (typeof console !== 'undefined' && console[method]) {
|
||||
console[method].call(console, obj);
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
Handlebars.log = function(level, obj) { Handlebars.logger.log(level, obj); };
|
||||
|
||||
Handlebars.registerHelper('each', function(context, options) {
|
||||
var fn = options.fn, inverse = options.inverse;
|
||||
var ret = "";
|
||||
var i = 0, ret = "", data;
|
||||
|
||||
if(context && context.length > 0) {
|
||||
for(var i=0, j=context.length; i<j; i++) {
|
||||
ret = ret + fn(context[i]);
|
||||
if (options.data) {
|
||||
data = Handlebars.createFrame(options.data);
|
||||
}
|
||||
|
||||
if(context && typeof context === 'object') {
|
||||
if(context instanceof Array){
|
||||
for(var j = context.length; i<j; i++) {
|
||||
if (data) { data.index = i; }
|
||||
ret = ret + fn(context[i], { data: data });
|
||||
}
|
||||
} else {
|
||||
for(var key in context) {
|
||||
if(context.hasOwnProperty(key)) {
|
||||
if(data) { data.key = key; }
|
||||
ret = ret + fn(context[key], {data: data});
|
||||
i++;
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
}
|
||||
|
||||
if(i === 0){
|
||||
ret = inverse(this);
|
||||
}
|
||||
|
||||
return ret;
|
||||
});
|
||||
|
||||
@@ -89,11 +135,14 @@ Handlebars.registerHelper('with', function(context, options) {
|
||||
return options.fn(context);
|
||||
});
|
||||
|
||||
Handlebars.registerHelper('log', function(context) {
|
||||
Handlebars.log(context);
|
||||
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));
|
||||
|
||||
// END(BROWSER)
|
||||
|
||||
module.exports = Handlebars;
|
||||
module.exports = this.Handlebars;
|
||||
|
||||
|
||||
@@ -11,21 +11,32 @@ var Handlebars = require('./base');
|
||||
if(inverse) { this.inverse = new Handlebars.AST.ProgramNode(inverse); }
|
||||
};
|
||||
|
||||
Handlebars.AST.MustacheNode = function(params, hash, unescaped) {
|
||||
Handlebars.AST.MustacheNode = function(rawParams, hash, unescaped) {
|
||||
this.type = "mustache";
|
||||
this.id = params[0];
|
||||
this.params = params.slice(1);
|
||||
this.hash = hash;
|
||||
this.escaped = !unescaped;
|
||||
this.hash = hash;
|
||||
|
||||
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 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.
|
||||
};
|
||||
|
||||
Handlebars.AST.PartialNode = function(id, context) {
|
||||
this.type = "partial";
|
||||
|
||||
// TODO: disallow complex IDs
|
||||
|
||||
this.id = id;
|
||||
this.context = context;
|
||||
Handlebars.AST.PartialNode = function(partialName, context) {
|
||||
this.type = "partial";
|
||||
this.partialName = partialName;
|
||||
this.context = context;
|
||||
};
|
||||
|
||||
var verifyMatch = function(open, close) {
|
||||
@@ -34,18 +45,16 @@ var Handlebars = require('./base');
|
||||
}
|
||||
};
|
||||
|
||||
Handlebars.AST.BlockNode = function(mustache, program, close) {
|
||||
Handlebars.AST.BlockNode = function(mustache, program, inverse, close) {
|
||||
verifyMatch(mustache.id, close);
|
||||
this.type = "block";
|
||||
this.mustache = mustache;
|
||||
this.program = program;
|
||||
};
|
||||
this.inverse = inverse;
|
||||
|
||||
Handlebars.AST.InverseNode = function(mustache, program, close) {
|
||||
verifyMatch(mustache.id, close);
|
||||
this.type = "inverse";
|
||||
this.mustache = mustache;
|
||||
this.program = program;
|
||||
if (this.inverse && !this.program) {
|
||||
this.isInverse = true;
|
||||
}
|
||||
};
|
||||
|
||||
Handlebars.AST.ContentNode = function(string) {
|
||||
@@ -75,22 +84,40 @@ var Handlebars = require('./base');
|
||||
this.parts = dig;
|
||||
this.string = dig.join('.');
|
||||
this.depth = depth;
|
||||
this.isSimple = (dig.length === 1) && (depth === 0);
|
||||
|
||||
// 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;
|
||||
|
||||
this.stringModeValue = this.string;
|
||||
};
|
||||
|
||||
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) {
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
var handlebars = require("./parser").parser;
|
||||
var handlebars = require("./parser");
|
||||
var Handlebars = require("../base");
|
||||
|
||||
// BEGIN(BROWSER)
|
||||
@@ -12,16 +12,6 @@ Handlebars.parse = function(string) {
|
||||
Handlebars.print = function(ast) {
|
||||
return new Handlebars.PrintVisitor().accept(ast);
|
||||
};
|
||||
|
||||
Handlebars.logger = {
|
||||
DEBUG: 0, INFO: 1, WARN: 2, ERROR: 3, level: 3,
|
||||
|
||||
// override in the host environment
|
||||
log: function(level, str) {}
|
||||
};
|
||||
|
||||
Handlebars.log = function(level, str) { Handlebars.logger.log(level, str); };
|
||||
|
||||
// END(BROWSER)
|
||||
|
||||
module.exports = Handlebars;
|
||||
|
||||
+605
-295
File diff suppressed because it is too large
Load Diff
@@ -18,31 +18,17 @@ Handlebars.PrintVisitor.prototype.pad = function(string, newline) {
|
||||
};
|
||||
|
||||
Handlebars.PrintVisitor.prototype.program = function(program) {
|
||||
var out = this.pad("PROGRAM:"),
|
||||
var out = "",
|
||||
statements = program.statements,
|
||||
inverse = program.inverse,
|
||||
i, l;
|
||||
|
||||
this.padding++;
|
||||
|
||||
for(i=0, l=statements.length; i<l; i++) {
|
||||
out = out + this.accept(statements[i]);
|
||||
}
|
||||
|
||||
this.padding--;
|
||||
|
||||
if(inverse) {
|
||||
out = out + this.pad("{{^}}");
|
||||
|
||||
this.padding++;
|
||||
|
||||
for(i=0, l=inverse.statements.length; i<l; i++) {
|
||||
out = out + this.accept(inverse.statements[i]);
|
||||
}
|
||||
}
|
||||
|
||||
this.padding--;
|
||||
|
||||
return out;
|
||||
};
|
||||
|
||||
@@ -52,25 +38,25 @@ Handlebars.PrintVisitor.prototype.block = function(block) {
|
||||
out = out + this.pad("BLOCK:");
|
||||
this.padding++;
|
||||
out = out + this.accept(block.mustache);
|
||||
out = out + this.accept(block.program);
|
||||
if (block.program) {
|
||||
out = out + this.pad("PROGRAM:");
|
||||
this.padding++;
|
||||
out = out + this.accept(block.program);
|
||||
this.padding--;
|
||||
}
|
||||
if (block.inverse) {
|
||||
if (block.program) { this.padding++; }
|
||||
out = out + this.pad("{{^}}");
|
||||
this.padding++;
|
||||
out = out + this.accept(block.inverse);
|
||||
this.padding--;
|
||||
if (block.program) { this.padding--; }
|
||||
}
|
||||
this.padding--;
|
||||
|
||||
return out;
|
||||
};
|
||||
|
||||
Handlebars.PrintVisitor.prototype.inverse = function(block) {
|
||||
var out = "";
|
||||
|
||||
out = out + this.pad("INVERSE:");
|
||||
this.padding++;
|
||||
out = out + this.accept(block.mustache);
|
||||
out = out + this.accept(block.program);
|
||||
this.padding--;
|
||||
|
||||
return out;
|
||||
};
|
||||
|
||||
|
||||
Handlebars.PrintVisitor.prototype.mustache = function(mustache) {
|
||||
var params = mustache.params, paramStrings = [], hash;
|
||||
|
||||
@@ -86,7 +72,7 @@ Handlebars.PrintVisitor.prototype.mustache = function(mustache) {
|
||||
};
|
||||
|
||||
Handlebars.PrintVisitor.prototype.partial = function(partial) {
|
||||
var content = this.accept(partial.id);
|
||||
var content = this.accept(partial.partialName);
|
||||
if(partial.context) { content = content + " " + this.accept(partial.context); }
|
||||
return this.pad("{{> " + content + " }}");
|
||||
};
|
||||
@@ -125,6 +111,14 @@ Handlebars.PrintVisitor.prototype.ID = function(id) {
|
||||
}
|
||||
};
|
||||
|
||||
Handlebars.PrintVisitor.prototype.PARTIAL_NAME = function(partialName) {
|
||||
return "PARTIAL:" + partialName.name;
|
||||
};
|
||||
|
||||
Handlebars.PrintVisitor.prototype.DATA = function(data) {
|
||||
return "@" + data.id;
|
||||
};
|
||||
|
||||
Handlebars.PrintVisitor.prototype.content = function(content) {
|
||||
return this.pad("CONTENT[ '" + content.string + "' ]");
|
||||
};
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
+9
-10
@@ -1,14 +1,16 @@
|
||||
var Handlebars = require("./base");
|
||||
|
||||
// BEGIN(BROWSER)
|
||||
|
||||
var errorProps = ['description', 'fileName', 'lineNumber', 'message', 'name', 'number', 'stack'];
|
||||
|
||||
Handlebars.Exception = function(message) {
|
||||
var tmp = Error.prototype.constructor.apply(this, arguments);
|
||||
|
||||
for (var p in tmp) {
|
||||
if (tmp.hasOwnProperty(p)) { this[p] = tmp[p]; }
|
||||
// Unfortunately errors are not enumerable in Chrome (at least), so `for prop in tmp` doesn't work.
|
||||
for (var idx = 0; idx < errorProps.length; idx++) {
|
||||
this[errorProps[idx]] = tmp[errorProps[idx]];
|
||||
}
|
||||
|
||||
this.message = tmp.message;
|
||||
};
|
||||
Handlebars.Exception.prototype = new Error();
|
||||
|
||||
@@ -22,6 +24,7 @@ Handlebars.SafeString.prototype.toString = function() {
|
||||
|
||||
(function() {
|
||||
var escape = {
|
||||
"&": "&",
|
||||
"<": "<",
|
||||
">": ">",
|
||||
'"': """,
|
||||
@@ -29,7 +32,7 @@ Handlebars.SafeString.prototype.toString = function() {
|
||||
"`": "`"
|
||||
};
|
||||
|
||||
var badChars = /&(?!\w+;)|[<>"'`]/g;
|
||||
var badChars = /[&<>"'`]/g;
|
||||
var possible = /[&<>"'`]/;
|
||||
|
||||
var escapeChar = function(chr) {
|
||||
@@ -50,11 +53,7 @@ Handlebars.SafeString.prototype.toString = function() {
|
||||
},
|
||||
|
||||
isEmpty: function(value) {
|
||||
if (typeof value === "undefined") {
|
||||
return true;
|
||||
} else if (value === null) {
|
||||
return true;
|
||||
} else if (value === false) {
|
||||
if (!value && value !== 0) {
|
||||
return true;
|
||||
} else if(Object.prototype.toString.call(value) === "[object Array]" && value.length === 0) {
|
||||
return true;
|
||||
|
||||
+15
-5
@@ -1,14 +1,14 @@
|
||||
{
|
||||
"name": "handlebars",
|
||||
"description": "Extension of the Mustache logicless template language",
|
||||
"version": "1.0.5beta",
|
||||
"version": "1.0.8",
|
||||
"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,19 @@
|
||||
"optimist": "~0.3",
|
||||
"uglify-js": "~1.2"
|
||||
},
|
||||
"devDependencies": {},
|
||||
"devDependencies": {
|
||||
"benchmark": "~1.0",
|
||||
"dust": "~0.3",
|
||||
"jison": "~0.3",
|
||||
"mocha": "*",
|
||||
"mustache": "~0.7.2"
|
||||
},
|
||||
"main": "lib/handlebars.js",
|
||||
"bin": {
|
||||
"handlebars": "bin/handlebars"
|
||||
}
|
||||
}
|
||||
},
|
||||
"scripts": {
|
||||
"test": "node_modules/.bin/mocha -u qunit spec/qunit_spec.js"
|
||||
},
|
||||
"optionalDependencies": {}
|
||||
}
|
||||
|
||||
@@ -39,11 +39,11 @@ Module.new do
|
||||
end
|
||||
end
|
||||
|
||||
js_context["p"] = proc do |str|
|
||||
js_context["p"] = proc do |this, str|
|
||||
p str
|
||||
end
|
||||
|
||||
js_context["ok"] = proc do |ok, message|
|
||||
js_context["ok"] = proc do |this, ok, message|
|
||||
js_context["$$RSPEC1$$"] = ok
|
||||
|
||||
result = js_context.eval("!!$$RSPEC1$$")
|
||||
@@ -58,7 +58,7 @@ Module.new do
|
||||
assert result, message
|
||||
end
|
||||
|
||||
js_context["equals"] = proc do |first, second, message|
|
||||
js_context["equals"] = proc do |this, first, second, message|
|
||||
js_context["$$RSPEC1$$"] = first
|
||||
js_context["$$RSPEC2$$"] = second
|
||||
|
||||
@@ -77,11 +77,11 @@ Module.new do
|
||||
|
||||
js_context["equal"] = js_context["equals"]
|
||||
|
||||
js_context["module"] = proc do |name|
|
||||
js_context["suite"] = proc do |this, name|
|
||||
test_context.module(name)
|
||||
end
|
||||
|
||||
js_context["test"] = proc do |name, function|
|
||||
js_context["test"] = proc do |this, name, function|
|
||||
test_context.test(name, function)
|
||||
end
|
||||
|
||||
|
||||
+182
-32
@@ -7,11 +7,9 @@ describe "Parser" do
|
||||
@compiles = true
|
||||
end
|
||||
|
||||
def program(&block)
|
||||
def root(&block)
|
||||
ASTBuilder.build do
|
||||
program do
|
||||
instance_eval(&block)
|
||||
end
|
||||
instance_eval(&block)
|
||||
end
|
||||
end
|
||||
|
||||
@@ -112,115 +110,143 @@ describe "Parser" do
|
||||
"ID:#{id}"
|
||||
end
|
||||
|
||||
def data(id)
|
||||
"@#{id}"
|
||||
end
|
||||
|
||||
def partial_name(name)
|
||||
"PARTIAL:#{name}"
|
||||
end
|
||||
|
||||
def path(*parts)
|
||||
"PATH:#{parts.join("/")}"
|
||||
end
|
||||
end
|
||||
|
||||
it "parses simple mustaches" do
|
||||
ast_for("{{foo}}").should == program { mustache id("foo") }
|
||||
ast_for("{{foo}}").should == root { mustache id("foo") }
|
||||
end
|
||||
|
||||
it "parses simple mustaches with data" do
|
||||
ast_for("{{@foo}}").should == root { mustache data("foo") }
|
||||
end
|
||||
|
||||
it "parses mustaches with paths" do
|
||||
ast_for("{{foo/bar}}").should == program { mustache path("foo", "bar") }
|
||||
ast_for("{{foo/bar}}").should == root { mustache path("foo", "bar") }
|
||||
end
|
||||
|
||||
it "parses mustaches with this/foo" do
|
||||
ast_for("{{this/foo}}").should == program { mustache id("foo") }
|
||||
ast_for("{{this/foo}}").should == root { mustache id("foo") }
|
||||
end
|
||||
|
||||
it "parses mustaches with - in a path" do
|
||||
ast_for("{{foo-bar}}").should == program { mustache id("foo-bar") }
|
||||
ast_for("{{foo-bar}}").should == root { mustache id("foo-bar") }
|
||||
end
|
||||
|
||||
it "parses mustaches with parameters" do
|
||||
ast_for("{{foo bar}}").should == program { mustache id("foo"), [id("bar")] }
|
||||
ast_for("{{foo bar}}").should == root { mustache id("foo"), [id("bar")] }
|
||||
end
|
||||
|
||||
it "parses mustaches with hash arguments" do
|
||||
ast_for("{{foo bar=baz}}").should == program do
|
||||
ast_for("{{foo bar=baz}}").should == root do
|
||||
mustache id("foo"), [], hash(["bar", id("baz")])
|
||||
end
|
||||
|
||||
ast_for("{{foo bar=1}}").should == program do
|
||||
ast_for("{{foo bar=1}}").should == root do
|
||||
mustache id("foo"), [], hash(["bar", integer("1")])
|
||||
end
|
||||
|
||||
ast_for("{{foo bar=true}}").should == program do
|
||||
ast_for("{{foo bar=true}}").should == root do
|
||||
mustache id("foo"), [], hash(["bar", boolean("true")])
|
||||
end
|
||||
|
||||
ast_for("{{foo bar=false}}").should == program do
|
||||
ast_for("{{foo bar=false}}").should == root do
|
||||
mustache id("foo"), [], hash(["bar", boolean("false")])
|
||||
end
|
||||
|
||||
ast_for("{{foo bar=baz bat=bam}}").should == program do
|
||||
ast_for("{{foo bar=@baz}}").should == root do
|
||||
mustache id("foo"), [], hash(["bar", data("baz")])
|
||||
end
|
||||
|
||||
ast_for("{{foo bar=baz bat=bam}}").should == root do
|
||||
mustache id("foo"), [], hash(["bar", "ID:baz"], ["bat", "ID:bam"])
|
||||
end
|
||||
|
||||
ast_for("{{foo bar=baz bat=\"bam\"}}").should == program do
|
||||
ast_for("{{foo bar=baz bat=\"bam\"}}").should == root do
|
||||
mustache id("foo"), [], hash(["bar", "ID:baz"], ["bat", "\"bam\""])
|
||||
end
|
||||
|
||||
ast_for("{{foo omg bar=baz bat=\"bam\"}}").should == program do
|
||||
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
|
||||
|
||||
ast_for("{{foo omg bar=baz bat=\"bam\" baz=1}}").should == program do
|
||||
ast_for("{{foo omg bar=baz bat=\"bam\" baz=1}}").should == root do
|
||||
mustache id("foo"), [id("omg")], hash(["bar", id("baz")], ["bat", string("bam")], ["baz", integer("1")])
|
||||
end
|
||||
|
||||
ast_for("{{foo omg bar=baz bat=\"bam\" baz=true}}").should == program do
|
||||
ast_for("{{foo omg bar=baz bat=\"bam\" baz=true}}").should == root do
|
||||
mustache id("foo"), [id("omg")], hash(["bar", id("baz")], ["bat", string("bam")], ["baz", boolean("true")])
|
||||
end
|
||||
|
||||
ast_for("{{foo omg bar=baz bat=\"bam\" baz=false}}").should == program do
|
||||
ast_for("{{foo omg bar=baz bat=\"bam\" baz=false}}").should == root do
|
||||
mustache id("foo"), [id("omg")], hash(["bar", id("baz")], ["bat", string("bam")], ["baz", boolean("false")])
|
||||
end
|
||||
end
|
||||
|
||||
it "parses mustaches with string parameters" do
|
||||
ast_for("{{foo bar \"baz\" }}").should == program { mustache id("foo"), [id("bar"), string("baz")] }
|
||||
ast_for("{{foo bar \"baz\" }}").should == root { mustache id("foo"), [id("bar"), string("baz")] }
|
||||
end
|
||||
|
||||
it "parses mustaches with INTEGER parameters" do
|
||||
ast_for("{{foo 1}}").should == program { mustache id("foo"), [integer("1")] }
|
||||
ast_for("{{foo 1}}").should == root { mustache id("foo"), [integer("1")] }
|
||||
end
|
||||
|
||||
it "parses mustaches with BOOLEAN parameters" do
|
||||
ast_for("{{foo true}}").should == program { mustache id("foo"), [boolean("true")] }
|
||||
ast_for("{{foo false}}").should == program { mustache id("foo"), [boolean("false")] }
|
||||
ast_for("{{foo true}}").should == root { mustache id("foo"), [boolean("true")] }
|
||||
ast_for("{{foo false}}").should == root { mustache id("foo"), [boolean("false")] }
|
||||
end
|
||||
|
||||
it "parses mutaches with DATA parameters" do
|
||||
ast_for("{{foo @bar}}").should == root { mustache id("foo"), [data("bar")] }
|
||||
end
|
||||
|
||||
it "parses contents followed by a mustache" do
|
||||
ast_for("foo bar {{baz}}").should == program do
|
||||
ast_for("foo bar {{baz}}").should == root do
|
||||
content "foo bar "
|
||||
mustache id("baz")
|
||||
end
|
||||
end
|
||||
|
||||
it "parses a partial" do
|
||||
ast_for("{{> foo }}").should == program { partial id("foo") }
|
||||
ast_for("{{> foo }}").should == root { partial partial_name("foo") }
|
||||
end
|
||||
|
||||
it "parses a partial with context" do
|
||||
ast_for("{{> foo bar}}").should == program { partial id("foo"), id("bar") }
|
||||
ast_for("{{> foo bar}}").should == root { partial partial_name("foo"), id("bar") }
|
||||
end
|
||||
|
||||
it "parses a partial with a complex name" do
|
||||
ast_for("{{> shared/partial}}").should == root { partial partial_name("shared/partial") }
|
||||
end
|
||||
|
||||
it "parses a comment" do
|
||||
ast_for("{{! this is a comment }}").should == program do
|
||||
ast_for("{{! this is a comment }}").should == root do
|
||||
comment " this is a comment "
|
||||
end
|
||||
end
|
||||
|
||||
it "parses a multi-line comment" do
|
||||
ast_for("{{!\nthis is a multi-line comment\n}}").should == program do
|
||||
ast_for("{{!\nthis is a multi-line comment\n}}").should == root do
|
||||
multiline_comment "this is a multi-line comment"
|
||||
end
|
||||
end
|
||||
|
||||
it "parses an inverse section" do
|
||||
ast_for("{{#foo}} bar {{^}} baz {{/foo}}").should == program do
|
||||
ast_for("{{#foo}} bar {{^}} baz {{/foo}}").should == root do
|
||||
block do
|
||||
mustache id("foo")
|
||||
|
||||
@@ -235,12 +261,136 @@ describe "Parser" do
|
||||
end
|
||||
end
|
||||
|
||||
it "parses a standalone inverse section" do
|
||||
ast_for("{{^foo}}bar{{/foo}}").should == program do
|
||||
inverted_block do
|
||||
it "parses an inverse ('else'-style) section" do
|
||||
ast_for("{{#foo}} bar {{else}} baz {{/foo}}").should == root do
|
||||
block do
|
||||
mustache id("foo")
|
||||
|
||||
program do
|
||||
content " bar "
|
||||
end
|
||||
|
||||
inverse do
|
||||
content " baz "
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
it "parses empty blocks" do
|
||||
ast_for("{{#foo}}{{/foo}}").should == root do
|
||||
block do
|
||||
mustache id("foo")
|
||||
|
||||
program do
|
||||
# empty program
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
it "parses empty blocks with empty inverse section" do
|
||||
ast_for("{{#foo}}{{^}}{{/foo}}").should == root do
|
||||
block do
|
||||
mustache id("foo")
|
||||
|
||||
program do
|
||||
# empty program
|
||||
end
|
||||
|
||||
inverse do
|
||||
# empty inverse
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
it "parses empty blocks with empty inverse ('else'-style) section" do
|
||||
ast_for("{{#foo}}{{else}}{{/foo}}").should == root do
|
||||
block do
|
||||
mustache id("foo")
|
||||
|
||||
program do
|
||||
# empty program
|
||||
end
|
||||
|
||||
inverse do
|
||||
# empty inverse
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
it "parses non-empty blocks with empty inverse section" do
|
||||
ast_for("{{#foo}} bar {{^}}{{/foo}}").should == root do
|
||||
block do
|
||||
mustache id("foo")
|
||||
|
||||
program do
|
||||
content " bar "
|
||||
end
|
||||
|
||||
inverse do
|
||||
# empty inverse
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
it "parses non-empty blocks with empty inverse ('else'-style) section" do
|
||||
ast_for("{{#foo}} bar {{else}}{{/foo}}").should == root do
|
||||
block do
|
||||
mustache id("foo")
|
||||
|
||||
program do
|
||||
content " bar "
|
||||
end
|
||||
|
||||
inverse do
|
||||
# empty inverse
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
it "parses empty blocks with non-empty inverse section" do
|
||||
ast_for("{{#foo}}{{^}} bar {{/foo}}").should == root do
|
||||
block do
|
||||
mustache id("foo")
|
||||
|
||||
program do
|
||||
# empty program
|
||||
end
|
||||
|
||||
inverse do
|
||||
content " bar "
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
it "parses empty blocks with non-empty inverse ('else'-style) section" do
|
||||
ast_for("{{#foo}}{{else}} bar {{/foo}}").should == root do
|
||||
block do
|
||||
mustache id("foo")
|
||||
|
||||
program do
|
||||
# empty program
|
||||
end
|
||||
|
||||
inverse do
|
||||
content " bar "
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
it "parses a standalone inverse section" do
|
||||
ast_for("{{^foo}}bar{{/foo}}").should == root do
|
||||
block do
|
||||
mustache id("foo")
|
||||
|
||||
inverse do
|
||||
content "bar"
|
||||
end
|
||||
end
|
||||
|
||||
+429
-161
File diff suppressed because it is too large
Load Diff
+7
-7
@@ -47,15 +47,15 @@ module Handlebars
|
||||
def self.load_helpers(context)
|
||||
context["exports"] = nil
|
||||
|
||||
context["p"] = proc do |val|
|
||||
context["p"] = proc do |this, val|
|
||||
p val if ENV["DEBUG_JS"]
|
||||
end
|
||||
|
||||
context["puts"] = proc do |val|
|
||||
context["puts"] = proc do |this, val|
|
||||
puts val if ENV["DEBUG_JS"]
|
||||
end
|
||||
|
||||
context["puts_node"] = proc do |val|
|
||||
context["puts_node"] = proc do |this, val|
|
||||
puts context["Handlebars"]["PrintVisitor"].new.accept(val)
|
||||
puts
|
||||
end
|
||||
@@ -82,12 +82,12 @@ module Handlebars
|
||||
|
||||
context["CompilerContext"] = {}
|
||||
CompilerContext = context["CompilerContext"]
|
||||
CompilerContext["compile"] = proc do |*args|
|
||||
CompilerContext["compile"] = proc do |this, *args|
|
||||
template, options = args[0], args[1] || nil
|
||||
templateSpec = COMPILE_CONTEXT["Handlebars"]["precompile"].call(template, options);
|
||||
context["Handlebars"]["template"].call(context.eval("(#{templateSpec})"));
|
||||
end
|
||||
CompilerContext["compileWithPartial"] = proc do |*args|
|
||||
CompilerContext["compileWithPartial"] = proc do |this, *args|
|
||||
template, options = args[0], args[1] || nil
|
||||
FULL_CONTEXT["Handlebars"]["compile"].call(template, options);
|
||||
end
|
||||
@@ -108,7 +108,7 @@ module Handlebars
|
||||
|
||||
context["Handlebars"]["logger"]["level"] = ENV["DEBUG_JS"] ? context["Handlebars"]["logger"][ENV["DEBUG_JS"]] : 4
|
||||
|
||||
context["Handlebars"]["logger"]["log"] = proc do |level, str|
|
||||
context["Handlebars"]["logger"]["log"] = proc do |this, level, str|
|
||||
logger_level = context["Handlebars"]["logger"]["level"].to_i
|
||||
|
||||
if logger_level <= level
|
||||
@@ -133,7 +133,7 @@ module Handlebars
|
||||
|
||||
context["Handlebars"]["logger"]["level"] = ENV["DEBUG_JS"] ? context["Handlebars"]["logger"][ENV["DEBUG_JS"]] : 4
|
||||
|
||||
context["Handlebars"]["logger"]["log"] = proc do |level, str|
|
||||
context["Handlebars"]["logger"]["log"] = proc do |this, level, str|
|
||||
logger_level = context["Handlebars"]["logger"]["level"].to_i
|
||||
|
||||
if logger_level <= level
|
||||
|
||||
+55
-8
@@ -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))
|
||||
@@ -123,24 +132,24 @@ describe "Tokenizer" do
|
||||
result[4].should be_token("CONTENT", " baz")
|
||||
end
|
||||
|
||||
it "tokenizes a partial as 'OPEN_PARTIAL ID CLOSE'" do
|
||||
it "tokenizes a partial as 'OPEN_PARTIAL PARTIAL_NAME CLOSE'" do
|
||||
result = tokenize("{{> foo}}")
|
||||
result.should match_tokens(%w(OPEN_PARTIAL ID CLOSE))
|
||||
result.should match_tokens(%w(OPEN_PARTIAL PARTIAL_NAME CLOSE))
|
||||
end
|
||||
|
||||
it "tokenizes a partial with context as 'OPEN_PARTIAL ID ID CLOSE'" do
|
||||
it "tokenizes a partial with context as 'OPEN_PARTIAL PARTIAL_NAME ID CLOSE'" do
|
||||
result = tokenize("{{> foo bar }}")
|
||||
result.should match_tokens(%w(OPEN_PARTIAL ID ID CLOSE))
|
||||
result.should match_tokens(%w(OPEN_PARTIAL PARTIAL_NAME ID CLOSE))
|
||||
end
|
||||
|
||||
it "tokenizes a partial without spaces as 'OPEN_PARTIAL ID CLOSE'" do
|
||||
it "tokenizes a partial without spaces as 'OPEN_PARTIAL PARTIAL_NAME CLOSE'" do
|
||||
result = tokenize("{{>foo}}")
|
||||
result.should match_tokens(%w(OPEN_PARTIAL ID CLOSE))
|
||||
result.should match_tokens(%w(OPEN_PARTIAL PARTIAL_NAME CLOSE))
|
||||
end
|
||||
|
||||
it "tokenizes a partial space at the end as 'OPEN_PARTIAL ID CLOSE'" do
|
||||
it "tokenizes a partial space at the end as 'OPEN_PARTIAL PARTIAL_NAME CLOSE'" do
|
||||
result = tokenize("{{>foo }}")
|
||||
result.should match_tokens(%w(OPEN_PARTIAL ID CLOSE))
|
||||
result.should match_tokens(%w(OPEN_PARTIAL PARTIAL_NAME CLOSE))
|
||||
end
|
||||
|
||||
it "tokenizes a comment as 'COMMENT'" do
|
||||
@@ -149,6 +158,18 @@ describe "Tokenizer" do
|
||||
result[1].should be_token("COMMENT", " this is a comment ")
|
||||
end
|
||||
|
||||
it "tokenizes a block comment as 'COMMENT'" do
|
||||
result = tokenize("foo {{!-- this is a {{comment}} --}} bar {{ baz }}")
|
||||
result.should match_tokens(%w(CONTENT COMMENT CONTENT OPEN ID CLOSE))
|
||||
result[1].should be_token("COMMENT", " this is a {{comment}} ")
|
||||
end
|
||||
|
||||
it "tokenizes a block comment with whitespace as 'COMMENT'" do
|
||||
result = tokenize("foo {{!-- this is a\n{{comment}}\n--}} bar {{ baz }}")
|
||||
result.should match_tokens(%w(CONTENT COMMENT CONTENT OPEN ID CLOSE))
|
||||
result[1].should be_token("COMMENT", " this is a\n{{comment}}\n")
|
||||
end
|
||||
|
||||
it "tokenizes open and closing blocks as 'OPEN_BLOCK ID CLOSE ... OPEN_ENDBLOCK ID CLOSE'" do
|
||||
result = tokenize("{{#foo}}content{{/foo}}")
|
||||
result.should match_tokens(%w(OPEN_BLOCK ID CLOSE CONTENT OPEN_ENDBLOCK ID CLOSE))
|
||||
@@ -186,6 +207,12 @@ describe "Tokenizer" do
|
||||
result[3].should be_token("STRING", "baz")
|
||||
end
|
||||
|
||||
it "tokenizes mustaches with String params using single quotes as 'OPEN ID ID STRING CLOSE'" do
|
||||
result = tokenize("{{ foo bar \'baz\' }}")
|
||||
result.should match_tokens(%w(OPEN ID ID STRING CLOSE))
|
||||
result[3].should be_token("STRING", "baz")
|
||||
end
|
||||
|
||||
it "tokenizes String params with spaces inside as 'STRING'" do
|
||||
result = tokenize("{{ foo bar \"baz bat\" }}")
|
||||
result.should match_tokens(%w(OPEN ID ID STRING CLOSE))
|
||||
@@ -198,6 +225,12 @@ describe "Tokenizer" do
|
||||
result[2].should be_token("STRING", %{bar"baz})
|
||||
end
|
||||
|
||||
it "tokenizes String params using single quotes with escapes quotes as 'STRING'" do
|
||||
result = tokenize(%|{{ foo 'bar\\'baz' }}|)
|
||||
result.should match_tokens(%w(OPEN ID STRING CLOSE))
|
||||
result[2].should be_token("STRING", %{bar'baz})
|
||||
end
|
||||
|
||||
it "tokenizes numbers" do
|
||||
result = tokenize(%|{{ foo 1 }}|)
|
||||
result.should match_tokens(%w(OPEN ID INTEGER CLOSE))
|
||||
@@ -244,6 +277,20 @@ describe "Tokenizer" do
|
||||
result[2].should be_token("ID", "omg")
|
||||
end
|
||||
|
||||
it "tokenizes special @ identifiers" do
|
||||
result = tokenize("{{ @foo }}")
|
||||
result.should match_tokens %w( OPEN DATA CLOSE )
|
||||
result[1].should be_token("DATA", "foo")
|
||||
|
||||
result = tokenize("{{ foo @bar }}")
|
||||
result.should match_tokens %w( OPEN ID DATA CLOSE )
|
||||
result[2].should be_token("DATA", "bar")
|
||||
|
||||
result = tokenize("{{ foo bar=@baz }}")
|
||||
result.should match_tokens %w( OPEN ID ID EQUALS DATA CLOSE )
|
||||
result[4].should be_token("DATA", "baz")
|
||||
end
|
||||
|
||||
it "does not time out in a mustache with a single } followed by EOF" do
|
||||
Timeout.timeout(1) { tokenize("{{foo}").should match_tokens(%w(OPEN ID)) }
|
||||
end
|
||||
|
||||
+14
-3
@@ -1,5 +1,5 @@
|
||||
|
||||
%x mu emu
|
||||
%x mu emu com par
|
||||
|
||||
%%
|
||||
|
||||
@@ -11,15 +11,22 @@
|
||||
|
||||
[^\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'; }
|
||||
<com>[\s\S]*?"--}}" { yytext = yytext.substr(0, yyleng-4); this.popState(); return 'COMMENT'; }
|
||||
|
||||
<mu>"{{>" { this.begin("par"); return 'OPEN_PARTIAL'; }
|
||||
<mu>"{{#" { return 'OPEN_BLOCK'; }
|
||||
<mu>"{{/" { return 'OPEN_ENDBLOCK'; }
|
||||
<mu>"{{^" { return 'OPEN_INVERSE'; }
|
||||
<mu>"{{"\s*"else" { return 'OPEN_INVERSE'; }
|
||||
<mu>"{{{" { return 'OPEN_UNESCAPED'; }
|
||||
<mu>"{{&" { return 'OPEN_UNESCAPED'; }
|
||||
<mu>"{{!--" { this.popState(); this.begin('com'); }
|
||||
<mu>"{{!"[\s\S]*?"}}" { yytext = yytext.substr(3,yyleng-5); this.popState(); return 'COMMENT'; }
|
||||
<mu>"{{" { return 'OPEN'; }
|
||||
|
||||
@@ -31,12 +38,16 @@
|
||||
<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'; }
|
||||
<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'; }
|
||||
<par>\s+ { /*ignore whitespace*/ }
|
||||
<par>[a-zA-Z0-9_$-/]+ { this.popState(); return 'PARTIAL_NAME'; }
|
||||
|
||||
<INITIAL,mu><<EOF>> { return 'EOF'; }
|
||||
|
||||
|
||||
+15
-5
@@ -7,8 +7,11 @@ root
|
||||
;
|
||||
|
||||
program
|
||||
: statements simpleInverse statements { $$ = new yy.ProgramNode($1, $3); }
|
||||
: simpleInverse statements { $$ = new yy.ProgramNode([], $2); }
|
||||
| statements simpleInverse statements { $$ = new yy.ProgramNode($1, $3); }
|
||||
| statements simpleInverse { $$ = new yy.ProgramNode($1, []); }
|
||||
| statements { $$ = new yy.ProgramNode($1); }
|
||||
| simpleInverse { $$ = new yy.ProgramNode([], []); }
|
||||
| "" { $$ = new yy.ProgramNode([]); }
|
||||
;
|
||||
|
||||
@@ -18,8 +21,8 @@ statements
|
||||
;
|
||||
|
||||
statement
|
||||
: openInverse program closeBlock { $$ = new yy.InverseNode($1, $2, $3); }
|
||||
| openBlock program closeBlock { $$ = new yy.BlockNode($1, $2, $3); }
|
||||
: openInverse program closeBlock { $$ = new yy.BlockNode($1, $2.inverse, $2, $3); }
|
||||
| openBlock program closeBlock { $$ = new yy.BlockNode($1, $2, $2.inverse, $3); }
|
||||
| mustache { $$ = $1; }
|
||||
| partial { $$ = $1; }
|
||||
| CONTENT { $$ = new yy.ContentNode($1); }
|
||||
@@ -45,8 +48,8 @@ mustache
|
||||
|
||||
|
||||
partial
|
||||
: OPEN_PARTIAL path CLOSE { $$ = new yy.PartialNode($2); }
|
||||
| OPEN_PARTIAL path path CLOSE { $$ = new yy.PartialNode($2, $3); }
|
||||
: OPEN_PARTIAL partialName CLOSE { $$ = new yy.PartialNode($2); }
|
||||
| OPEN_PARTIAL partialName path CLOSE { $$ = new yy.PartialNode($2, $3); }
|
||||
;
|
||||
|
||||
simpleInverse
|
||||
@@ -58,6 +61,7 @@ inMustache
|
||||
| path params { $$ = [[$1].concat($2), null]; }
|
||||
| path hash { $$ = [[$1], $2]; }
|
||||
| path { $$ = [[$1], null]; }
|
||||
| DATA { $$ = [[new yy.DataNode($1)], null]; }
|
||||
;
|
||||
|
||||
params
|
||||
@@ -70,6 +74,7 @@ param
|
||||
| STRING { $$ = new yy.StringNode($1); }
|
||||
| INTEGER { $$ = new yy.IntegerNode($1); }
|
||||
| BOOLEAN { $$ = new yy.BooleanNode($1); }
|
||||
| DATA { $$ = new yy.DataNode($1); }
|
||||
;
|
||||
|
||||
hash
|
||||
@@ -86,6 +91,11 @@ hashSegment
|
||||
| ID EQUALS STRING { $$ = [$1, new yy.StringNode($3)]; }
|
||||
| ID EQUALS INTEGER { $$ = [$1, new yy.IntegerNode($3)]; }
|
||||
| ID EQUALS BOOLEAN { $$ = [$1, new yy.BooleanNode($3)]; }
|
||||
| ID EQUALS DATA { $$ = [$1, new yy.DataNode($3)]; }
|
||||
;
|
||||
|
||||
partialName
|
||||
: PARTIAL_NAME { $$ = new yy.PartialNameNode($1); }
|
||||
;
|
||||
|
||||
path
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
// BEGIN(BROWSER)
|
||||
@@ -0,0 +1,4 @@
|
||||
|
||||
// END(BROWSER)
|
||||
|
||||
module.exports = handlebars;
|
||||
Reference in New Issue
Block a user