...
 
Commits (2)
......@@ -2,6 +2,7 @@ var fs = require('fs');
var express = require('express');
var port = 80;
var stylesheet = fs.readFileSync('public/css.css');
var app = express();
app.set('view engine', 'html');
......@@ -13,12 +14,29 @@ app.get('/', function(req, res) {
});
});
app.get('/css.css', function(req, res) {
res.setHeader('Content-Type', 'text/css');
res.end(stylesheet);
});
app.get('/app.js', function(req, res) {
fs.readFile('public/js/app.js', function(err, body) {
res.end(body);
});
});
app.get('/modal', function(req, res) {
fs.readFile('views/modal.html', function(err, body) {
res.end(body);
});
});
app.get('/capabilities', function(req, res) {
fs.readFile('views/capabilities.html', function(err, body) {
res.end(body);
});
});
app.get('/blocks', function(req, res) {
fs.readFile('views/blocks.html', function(err, body) {
res.end(body);
......
This diff is collapsed.
forEachAsync
===
Analogous to `[].forEach`, but handles items asynchronously with a final callback passed to `then`.
This is the most essential piece of the [`ArrayAsync`](https://github.com/FuturesJS/ArrayAsync) package.
v3.x - Diet Cola Edition
---
As I do every few years, I decided to rewrite FuturesJS.
This year's remake is extremely lightweight.
Usage
===
It's as simple as you could guess:
```javascript
// waits for one request to finish before beginning the next
forEachAsync(['dogs', 'cats', 'octocats'], function (next, element, index, array) {
getPics(element, next);
// then after all of the elements have been handled
// the final callback fires to let you know it's all done
}).then(function () {
console.log('All requests have finished');
});
// where `getPics` might be an asynchronous web request such as this
function getPics(animal, cb) {
var flickerAPI = "http://api.flickr.com/services/feeds/photos_public.gne?jsoncallback=?";
$.getJSON(
flickerAPI
, { tags: thing
, tagmode: "any"
, format: "json"
, success: function (data) {
console.log('teh animals:', data);
}
, complete: cb
}
);
}
```
Browser Installation
===
You can install from bower:
```bash
bower install forEachAsync
```
Or download the raw file from <https://raw.github.com/FuturesJS/forEachAsync/master/forEachAsync.js>:
```bash
wget https://raw.github.com/FuturesJS/forEachAsync/master/forEachAsync.js
```
```javascript
(function () {
'use strict';
var forEachAsync = window.forEachAsync
;
// do stuff ...
}());
```
Or you can build it alongside other libraries:
```bash
npm install -g pakmanager
npm install forEachAsync --save
pakmanager -e browser build
```
```html
<script src="pakmanaged.js"></script>
```
```javascript
(function () {
'use strict';
var forEachAsync = require('forEachAsync').forEachAsync
;
// do stuff ...
}());
```
Node Installation
===
```bash
npm install --save forEachAsync@3.x
```
API
===
**`forEachAsync(array, callback[, thisArg])`**
Parameters
* `array` Array of elements to iterate over
* `callback` Function to execute for each element, takes 4 arguments
* `next` the function to call when the current element has been dealt with
* `element` a single element of the aforementioned array
* `index` the index of the current element
* `array` the same array mentioned above
* `thisArg` Object to use as `this` when executing `callback`
**`forEachAsync#then(done)`**
Parameters
* `then` is in the return value of `forEachAsync` and accepts a final `done` callback.
* `done` called after `forEachAsync` is complete, takes no arguments
Internal API
===
`forEachAsync.__BREAK`
This is used internally for the purposes of the `ArrayAsync` library.
Please don't `break` stuff; use [`ArrayAsync`](https://github.com/FuturesJS/ArrayAsync)`.someAsync` or [`ArrayAsync`](https://github.com/FuturesJS/ArrayAsync)`.everyAsync` instead.
{
"name": "forEachAsync",
"main": "forEachAsync.js",
"version": "3.0.0",
"homepage": "https://github.com/FuturesJS/forEachAsync",
"authors": [
"AJ ONeal <coolaj86@gmail.com>"
],
"description": "A node- and browser-ready async counterpart of Array.prototype.forEach",
"keywords": [
"futuresjs",
"forEachAsync",
"forEach",
"for",
"async",
"each",
"asynchronous"
],
"license": "Apache-v2",
"ignore": [
"**/.*",
"node_modules",
"bower_components",
"test",
"tests"
]
}
/*jshint -W054 */
;(function (exports) {
'use strict';
function forEachAsync(arr, fn, thisArg) {
var dones = []
, index = -1
;
function next(BREAK, result) {
index += 1;
if (index === arr.length || BREAK === forEachAsync.__BREAK) {
dones.forEach(function (done) {
done.call(thisArg, result);
});
return;
}
fn.call(thisArg, next, arr[index], index, arr);
}
setTimeout(next, 4);
return {
then: function (_done) {
dones.push(_done);
return this;
}
};
}
forEachAsync.__BREAK = {};
exports.forEachAsync = forEachAsync;
}('undefined' !== typeof exports && exports || new Function('return this')()));
{
"_args": [
[
{
"raw": "foreachasync@^3.0.0",
"scope": null,
"escapedName": "foreachasync",
"name": "foreachasync",
"rawSpec": "^3.0.0",
"spec": ">=3.0.0 <4.0.0",
"type": "range"
},
"/root/DC25_party/keygen/node_modules/walk"
]
],
"_from": "foreachasync@>=3.0.0 <4.0.0",
"_id": "foreachasync@3.0.0",
"_inCache": true,
"_location": "/foreachasync",
"_npmUser": {
"name": "coolaj86",
"email": "coolaj86@gmail.com"
},
"_npmVersion": "1.3.22",
"_phantomChildren": {},
"_requested": {
"raw": "foreachasync@^3.0.0",
"scope": null,
"escapedName": "foreachasync",
"name": "foreachasync",
"rawSpec": "^3.0.0",
"spec": ">=3.0.0 <4.0.0",
"type": "range"
},
"_requiredBy": [
"/walk"
],
"_resolved": "https://registry.npmjs.org/foreachasync/-/foreachasync-3.0.0.tgz",
"_shasum": "5502987dc8714be3392097f32e0071c9dee07cf6",
"_shrinkwrap": null,
"_spec": "foreachasync@^3.0.0",
"_where": "/root/DC25_party/keygen/node_modules/walk",
"author": {
"name": "AJ ONeal",
"email": "coolaj86@gmail.com",
"url": "http://coolaj86.com/"
},
"bugs": {
"url": "https://github.com/FuturesJS/forEachAsync/issues"
},
"dependencies": {},
"description": "A node- and browser-ready async counterpart of Array.prototype.forEach",
"devDependencies": {},
"directories": {
"test": "test"
},
"dist": {
"shasum": "5502987dc8714be3392097f32e0071c9dee07cf6",
"tarball": "https://registry.npmjs.org/foreachasync/-/foreachasync-3.0.0.tgz"
},
"homepage": "https://github.com/FuturesJS/forEachAsync",
"keywords": [
"futuresjs",
"forEach",
"for",
"forEachAsync",
"async",
"futures",
"each"
],
"license": "Apache2",
"main": "forEachAsync.js",
"maintainers": [
{
"name": "coolaj86",
"email": "coolaj86@gmail.com"
}
],
"name": "foreachasync",
"optionalDependencies": {},
"readme": "ERROR: No README data found!",
"repository": {
"type": "git",
"url": "git://github.com/FuturesJS/forEachAsync.git"
},
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
"version": "3.0.0"
}
(function () {
"use strict";
var forEachAsync = require('./forEachAsync').forEachAsync
;
forEachAsync([0, 500, 70, 200, 400, 100], function (next, element, i, arr) {
// test that array order is as expected
console.log(element, 'is element', i, 'of', arr.length);
// test that thisness is applied
this[element] = i;
if (i > 2) {
// test that synchronous callbacks don't mess things up
next();
} else {
// test asynchronous callbacks
setTimeout(next, element);
}
}, {}).then(function () {
// test that thisness carries
console.log(this);
}).then(function () {
// test then chaining
console.log("now wasn't that nice?");
});
}());
[submodule "support/handlebars"]
path = support/handlebars
url = http://github.com/wycats/handlebars.js.git
language: node_js
sudo: false
node_js:
- "0.10"
- "4"
- "6"
before_install:
- npm install -g npm
# 4.0.1 (2016-09-18)
* update references of donpark to pillarjs (repo move)
# 4.0.0 (2015-11-02)
* update handlebars to v4
* fix caching of non default filename layouts
# 3.1.1 (2015-09-11)
* fix localsAsTemplateData when cache is enabled
# 3.1.0 (2015-06-10)
* make @data available to layouts
# 3.0.1 (2015-03-12)
* honor custom extension when using view engine layouts
# 3.0.0 (2015-03-09)
* update handlebars to 3.x
# 2.8.0 (2014-12-26)
* update to handlebars 2.x
# 2.7.0 (2014-06-02)
* fix registering directories of partials on windows
* add api to expose locals as template data
# 2.5.0 / 2014-02-19
* updated handlebars to 1.3.0
# 2.4.0 / 2013-09-13
(The MIT License)
Copyright (c) 2011 Don Park <donpark@docuverse.com>
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.
\ No newline at end of file
# hbs [![Build Status](https://travis-ci.org/pillarjs/hbs.svg?branch=master)](https://travis-ci.org/pillarjs/hbs)
[Express.js](http://github.com/visionmedia/express) view engine for
[handlebars.js](http://github.com/wycats/handlebars.js)
## Install ##
```
npm install hbs
```
## Use ##
Using *hbs* as the default view engine requires just one line of code in your app setup. This will render `.hbs` files when `res.render` is called.
```javascript
app.set('view engine', 'hbs');
```
To use a different extension (i.e. html) for your template files:
```javascript
app.set('view engine', 'html');
app.engine('html', require('hbs').__express);
```
## Helpers and Partials ##
hbs exposes the `registerHelper` and `registerPartial` method from handlebars.
```javascript
var hbs = require('hbs');
hbs.registerHelper('helper_name', function(...) { ... });
hbs.registerPartial('partial_name', 'partial value');
```
For convenience, `registerPartials` provides a quick way to load all partials from a specific directory:
```javascript
var hbs = require('hbs');
hbs.registerPartials(__dirname + '/views/partials' [, callback]);
```
Partials that are loaded from a directory are named based on their filename, where spaces and hyphens are replaced with an underscore character:
```
template.html -> {{> template}}
template 2.html -> {{> template_2}}
login view.hbs -> {{> login_view}}
template-file.html -> {{> template_file}}
```
See the [handlebars.js](http://github.com/wycats/handlebars.js) README and docs for more information.
**Note:** This method is async; meaning that the directory is walked in a non-blocking manner to app startup.
## Exposing locals as template data ##
hbs has the ability to expose the application and request locals within any context inside a view. To enable this functionality, simply call the `localsAsTemplateData` method and pass in your Express application instance.
```javascript
var hbs = require('hbs');
var express = require('express');
var app = express();
hbs.localsAsTemplateData(app);
app.locals.foo = "bar";
```
The local data can then be accessed using the `@property` syntax:
```
top level: {{@foo}}
{{#each items}}
{{label}}: {{@foo}}
{{/each}}
```
Note: In partials and templates, local data can be accessed without using `@` prefix.
## handlebars ##
The handlebars require used by hbs can be accessed via the `handlebars` property on the `hbs` module.
If you wish to use handlebars methods like `SafeString` please do so on this property. Do not register helpers or partials in this way.
```
// hbs.handlebars is the handlebars module
hbs.handlebars === require('handlebars');
```
## Recipes ##
### more than one instance ###
You can create isolated instances of hbs using the `create()` function on the module object.
```
var hbs = require('hbs');
var instance1 = hbs.create();
var instance2 = hbs.create();
app.engine('html', instance1.__express);
app.engine('hbs', instance2.__express);
```
Each instance has the same methods/properties as the `hbs` module object. The module object is actually just an instance created for you automatically.
### extra scripts or styles ##
Sometimes it is useful to have custom scripts or stylesheets on your pages. Handlebars does not provide a way to import or extend a template, but through the use of helpers you can create a similar result.
We can take advantage of the fact that our body template is processed before the layout template. Knowing this, we can create two helpers `block` and `extend` which can be used to 'inject' custom stylesheets or scripts into the layout template. The `block` helper will act as a placeholder for values specified in earlier `extend` helpers.
See examples/extend for a working example. Note how the index.hbs file defines extra stylesheets and scripts to be injected into the layout. They are put into the head section and at the end of the body respectively. If this was not done, the stylesheet would be in the body and the script would print `foo bar` too soon.
## Helpful Modules ##
- **[hbs-utils](https://github.com/dpolivy/hbs-utils)**: A small utility library that provides helpers for registering and compiling partials, including automatic updates when partials are changed.
// 3rd party
var express = require('express');
var hbs = require('hbs');
var app = express();
// set the view engine to use handlebars
app.set('view engine', 'hbs');
app.set('views', __dirname + '/views');
app.use(express.static(__dirname + '/public'));
var blocks = {};
hbs.registerHelper('extend', function(name, context) {
var block = blocks[name];
if (!block) {
block = blocks[name] = [];
}
block.push(context.fn(this)); // for older versions of handlebars, use block.push(context(this));
});
hbs.registerHelper('block', function(name) {
var val = (blocks[name] || []).join('\n');
// clear the block
blocks[name] = [];
return val;
});
app.get('/', function(req, res){
res.render('index');
});
app.listen(3000);
body {
padding: 50px;
font: 16px "Lucida Grande", Helvetica, Arial, sans-serif;
}
{{#extend "stylesheets"}}
<link rel="stylesheet" href="/css/index.css"/>
{{/extend}}
let the magic begin
{{#extend "scripts"}}
<script>
document.write('foo bar!');
</script>
{{/extend}}
<!doctype html>
<html>
<head>
<title>{{title}}</title>
<link rel='stylesheet' href='/css/style.css'>
{{{block "stylesheets"}}}
</head>
<body>
body: {{{body}}}
<hr/>
post body
<hr/>
{{{block "scripts"}}}
</body>
</html>
// builtin
var fs = require('fs');
// 3rd party
var express = require('express');
var hbs = require('hbs');
var app = express();
hbs.registerPartial('partial', fs.readFileSync(__dirname + '/views/partial.hbs', 'utf8'));
hbs.registerPartials(__dirname + '/views/partials');
// set the view engine to use handlebars
app.set('view engine', 'hbs');
app.set('views', __dirname + '/views');
app.use(express.static(__dirname + '/public'));
app.get('/', function(req, res) {
res.locals = {
some_value: 'foo bar',
list: ['cat', 'dog']
}
res.render('index');
});
app.listen(3000);
body {
padding: 50px;
font: 16px "Lucida Grande", Helvetica, Arial, sans-serif;
}
regular body
<hr/>
{{> partial}}
{{> partial1}}
{{> partial2}}
{{> partial3}}
{{> partial4}}
{{> partial5}}
<!doctype html>
<html>
<head>
<title>{{title}}</title>
<link rel='stylesheet' href='/css/style.css'>
</head>
<body>
body: {{{body}}}
<hr/>
post body
<hr/>
{{> partial5 }}
</body>
</html>
partial
<div>{{some_value}}</div>
<ul>
{{#each list}}
<li>{{this}}</li>
{{/each}}
</ul>
/// provides the async helper functionality
function Waiter() {
if (!(this instanceof Waiter)) {
return new Waiter();
}
var self = this;
// found values
self.values = {};
// callback when done
self.callback = null;
self.resolved = false;
self.count = 0;
};
Waiter.prototype.wait = function() {
var self = this;
++self.count;
};
// resolve the promise
Waiter.prototype.resolve = function(name, val) {
var self = this;
self.values[name] = val;
// done with all items
if (--self.count === 0) {
self.resolved = true;
// we may not have a done callback yet
if (self.callback) {
self.callback(self.values);
}
}
};
// sets the done callback for the waiter
// notifies when the promise is complete
Waiter.prototype.done = function(fn) {
var self = this;
self.callback = fn;
if (self.resolved) {
fn(self.values);
}
};
var alphabet = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz_';
var gen_id = function() {
var res = '';
for (var i=0 ; i<8 ; ++i) {
res += alphabet[Math.floor(Math.random() * alphabet.length)];
}
return res;
};
module.exports = function() {
// baton which contains the current
// set of deferreds
var waiter;
var obj = Object.create(null);
obj.done = function done(fn) {
// no async things called
if (!waiter) {
return fn({});
}
waiter.done(fn);
// clear the waiter for the next template
waiter = undefined;
};
obj.resolve = function resolve(fn, args) {
// we want to do async things, need a waiter for that
if (!waiter) {
waiter = new Waiter();
}
var id = '__' + gen_id() + '__';
var cur_waiter = waiter;
waiter.wait();
args = [].slice.call(args);
args.push(function(res) {
cur_waiter.resolve(id, res);
})
fn.apply(null, args);
// return the id placeholder
// this will be replaced later
return id;
};
return obj;
};
var fs = require('fs');
var path = require('path');
var walk = require('walk').walk;
var async = require('./async');
function Instance(handlebars) {
if (!(this instanceof Instance)) {
return new Instance(handlebars);
}
// expose handlebars, allows users to use their versions
// by overriding this early in their apps
var self = this;
self.handlebars = handlebars || require('handlebars').create();
// cache for templates, express 3.x doesn't do this for us
self.cache = {};
self.__express = middleware.bind(this);
// DEPRECATED, kept for backwards compatibility
self.SafeString = this.handlebars.SafeString;
self.Utils = this.handlebars.Utils;
};
// express 3.x template engine compliance
function middleware(filename, options, cb) {
var self = this;
var cache = self.cache;
var handlebars = self.handlebars;
self.async = async();
// grab extension from filename
// if we need a layout, we will look for one matching out extension
var extension = path.extname(filename);
// If passing the locals as data, create the handlebars options object now
var handlebarsOpts = (self.__localsAsData) ? { data: options._locals } : undefined;
// render the original file
// cb(err, str)
function render_file(locals, cb) {
// cached?
var template = cache[filename];
if (template) {
return cb(null, template(locals, handlebarsOpts));
}
fs.readFile(filename, 'utf8', function(err, str){
if (err) {
return cb(err);
}
var template = handlebars.compile(str);
if (locals.cache) {
cache[filename] = template;
}
try {
var res = template(locals, handlebarsOpts);
self.async.done(function(values) {
Object.keys(values).forEach(function(id) {
res = res.replace(id, values[id]);
});
cb(null, res);
});
} catch (err) {
err.message = filename + ': ' + err.message;
cb(err);
}
});
}
// render with a layout
function render_with_layout(template, locals, cb) {
render_file(locals, function(err, str) {
if (err) {
return cb(err);
}
locals.body = str;
var res = template(locals, handlebarsOpts);
self.async.done(function(values) {
Object.keys(values).forEach(function(id) {
res = res.replace(id, values[id]);
});
cb(null, res);
});
});
}
var layout = options.layout;
// user did not specify a layout in the locals
// check global layout state
if (layout === undefined && options.settings && options.settings['view options']) {
layout = options.settings['view options'].layout;
}
// user explicitly request no layout
// either by specifying false for layout: false in locals
// or by settings the false view options
if (layout !== undefined && !layout) {
return render_file(options, cb);
}
var view_dirs = options.settings.views;
var layout_filename = [].concat(view_dirs).map(function (view_dir) {
var view_path = path.join(view_dir, layout || 'layout');
if (!path.extname(view_path)) {
view_path += extension;
}
return view_path;
});
var layout_template = layout_filename.reduce(function (cached, filename) {
if (cached) {
return cached;
}
var cached_file = cache[filename];
if (cached_file) {
return cache[filename];
}
return undefined;
}, undefined);
if (layout_template) {
return render_with_layout(layout_template, options, cb);
}
// TODO check if layout path has .hbs extension
function cacheAndCompile(filename, str) {
var layout_template = handlebars.compile(str);
if (options.cache) {
cache[filename] = layout_template;
}
render_with_layout(layout_template, options, cb);
}
function tryReadFileAndCache(templates) {
var template = templates.shift();
fs.readFile(template, 'utf8', function(err, str) {
if (err) {
if (layout && templates.length === 0) {
// Only return error if user explicitly asked for layout.
return cb(err);
}
if (templates.length > 0) {
return tryReadFileAndCache(templates);
}
return render_file(options, cb);
}
cacheAndCompile(template, str);
});
}
tryReadFileAndCache(layout_filename);
}
// express 2.x template engine compliance
Instance.prototype.compile = function (str) {
if (typeof str !== 'string') {
return str;
}
var template = this.handlebars.compile(str);
return function (locals) {
return template(locals, {
helpers: locals.blockHelpers,
partials: null,
data: null
});
};
};
Instance.prototype.registerHelper = function () {
this.handlebars.registerHelper.apply(this.handlebars, arguments);
};
Instance.prototype.registerPartial = function () {
this.handlebars.registerPartial.apply(this.handlebars, arguments);
};
Instance.prototype.registerPartials = function (directory, done) {
var handlebars = this.handlebars;
var register = function(filepath, done) {
var isValidTemplate = /\.(html|hbs)$/.test(filepath);
if (!isValidTemplate) {
return done(null);
}
fs.readFile(filepath, 'utf8', function(err, data) {
if (!err) {
var ext = path.extname(filepath);
var templateName = path.relative(directory, filepath)
.slice(0, -(ext.length)).replace(/[ -]/g, '_').replace('\\', '/');
handlebars.registerPartial(templateName, data);
}
done(err);
});
};
walk(directory).on('file', function(root, stat, next) {
register(path.join(root, stat.name), next);
}).on('end', done || function() {});
};
Instance.prototype.registerAsyncHelper = function(name, fn) {
var self = this;
self.handlebars.registerHelper(name, function() {
return self.async.resolve(fn, arguments);
});
};
Instance.prototype.localsAsTemplateData = function(app) {
// Set a flag to indicate we should pass locals as data
this.__localsAsData = true;
app.render = (function(render) {
return function(view, options, callback) {
if (typeof options === "function") {
callback = options;
options = {};
}
// Mix response.locals (options._locals) with app.locals (this.locals)
options._locals = options._locals || {};
for (var key in this.locals) {
options._locals[key] = this.locals[key];
}
return render.call(this, view, options, callback);
};
})(app.render);
};
module.exports = new Instance();
module.exports.create = function(handlebars) {
return new Instance(handlebars);
};
../handlebars/bin/handlebars
\ No newline at end of file
[submodule "spec/mustache"]
path = spec/mustache
url = git://github.com/mustache/spec.git
.DS_Store
.gitignore
.rvmrc
.eslintrc
.travis.yml
.rspec
Gemfile
Gemfile.lock
Rakefile
Gruntfile.js
*.gemspec
*.nuspec
*.log
bench/*
configurations/*
components/*
coverage/*
dist/cdnjs/*
dist/components/*
spec/*
src/*
tasks/*
tmp/*
publish/*
vendor/*
# How to Contribute
## Reporting Issues
Please see our [FAQ](https://github.com/wycats/handlebars.js/blob/master/FAQ.md) for common issues that people run into.
Should you run into other issues with the project, please don't hesitate to let us know by filing an [issue][issue]! In general we are going to ask for an example of the problem failing, which can be as simple as a jsfiddle/jsbin/etc. We've put together a jsfiddle [template][jsfiddle] to ease this. (We will keep this link up to date as new releases occur, so feel free to check back here)
Pull requests containing only failing thats demonstrating the issue are welcomed and this also helps ensure that your issue won't regress in the future once it's fixed.
Documentation issues on the handlebarsjs.com site should be reported on [handlebars-site](https://github.com/wycats/handlebars-site).
## Pull Requests
We also accept [pull requests][pull-request]!
Generally we like to see pull requests that
- Maintain the existing code style
- Are focused on a single change (i.e. avoid large refactoring or style adjustments in untouched code if not the primary goal of the pull request)
- Have [good commit messages](http://tbaggery.com/2008/04/19/a-note-about-git-commit-messages.html)
- Have tests
- Don't significantly decrease the current code coverage (see coverage/lcov-report/index.html)
## Building
To build Handlebars.js you'll need a few things installed.
* Node.js
* [Grunt](http://gruntjs.com/getting-started)
Before building, you need to make sure that the Git submodule `spec/mustache` is included (i.e. the directory `spec/mustache` should not be empty). To include it, if using Git version 1.6.5 or newer, use `git clone --recursive` rather than `git clone`. Or, if you already cloned without `--recursive`, use `git submodule update --init`.
Project dependencies may be installed via `npm install`.
To build Handlebars.js from scratch, you'll want to run `grunt`
in the root of the project. That will build Handlebars and output the
results to the dist/ folder. To re-run tests, run `grunt test` or `npm test`.
You can also run our set of benchmarks with `grunt bench`.
The `grunt dev` implements watching for tests and allows for in browser testing at `http://localhost:9999/spec/`.
If you notice any problems, please report them to the GitHub issue tracker at
[http://github.com/wycats/handlebars.js/issues](http://github.com/wycats/handlebars.js/issues).
## Ember testing
The current ember distribution should be tested as part of the handlebars release process. This requires building the `handlebars-source` gem locally and then executing the ember test script.
```sh
npm link
grunt build release
cp dist/*.js $emberRepoDir/bower_components/handlebars/
cd $emberRepoDir
npm link handlebars
npm test
```
## Releasing
Handlebars utilizes the [release yeoman generator][generator-release] to perform most release tasks.
A full release may be completed with the following:
```
yo release
npm publish
yo release:publish components handlebars.js dist/components/
cd dist/components/
gem build handlebars-source.gemspec
gem push handlebars-source-*.gem
```
After this point the handlebars site needs to be updated to point to the new version numbers. The jsfiddle link should be updated to point to the most recent distribution for all instances in our documentation.
[generator-release]: https://github.com/walmartlabs/generator-release
[pull-request]: https://github.com/wycats/handlebars.js/pull/new/master
[issue]: https://github.com/wycats/handlebars.js/issues/new
[jsfiddle]: http://jsfiddle.net/9D88g/46/
# Frequently Asked Questions
1. How can I file a bug report:
See our guidelines on [reporting issues](https://github.com/wycats/handlebars.js/blob/master/CONTRIBUTING.md#reporting-issues).
1. Why isn't my Mustache template working?
Handlebars deviates from Mustache slightly on a few behaviors. These variations are documented in our [readme](https://github.com/wycats/handlebars.js#differences-between-handlebarsjs-and-mustache).
1. Why is it slower when compiling?
The Handlebars compiler must parse the template and construct a JavaScript program which can then be run. Under some environments such as older mobile devices this can have a performance impact which can be avoided by precompiling. Generally it's recommended that precompilation and the runtime library be used on all clients.
1. Why doesn't this work with Content Security Policy restrictions?
When not using the precompiler, Handlebars generates a dynamic function for each template which can cause issues with pages that have enabled Content Policy. It's recommended that templates are precompiled or the `unsafe-eval` policy is enabled for sites that must generate dynamic templates at runtime.
1. How can I include script tags in my template?
If loading the template via an inlined `<script type="text/x-handlebars">` tag then you may need to break up the script tag with an empty comment to avoid browser parser errors:
```html
<script type="text/x-handlebars">
foo
<scr{{!}}ipt src="bar"></scr{{!}}ipt>
</script>
```
It's generally recommended that templates are served through external, precompiled, files, which do not suffer from this issue.
1. Why are my precompiled scripts throwing exceptions?
When using the precompiler, it's important that a supporting version of the Handlebars runtime be loaded on the target page. In version 1.x there were rudimentary checks to compare the version but these did not always work. This is fixed under 2.x but the version checking does not work between these two versions. If you see unexpected errors such as `undefined is not a function` or similar, please verify that the same version is being used for both the precompiler and the client. This can be checked via:
```sh
handlebars --version
```
If using the integrated precompiler and
```javascript
console.log(Handlebars.VERSION);
```
On the client side.
We include the built client libraries in the npm package for those who want to be certain that they are using the same client libraries as the compiler.
Should these match, please file an issue with us, per our [issue filing guidelines](https://github.com/wycats/handlebars.js/blob/master/CONTRIBUTING.md#reporting-issues).
1. Why doesn't IE like the `default` name in the AMD module?
Some browsers such as particular versions of IE treat `default` as a reserved word in JavaScript source files. To safely use this you need to reference this via the `Handlebars['default']` lookup method. This is an unfortunate side effect of the shims necessary to backport the Handlebars ES6 code to all current browsers.
1. How do I load the runtime library when using AMD?
There are two options for loading under AMD environments. The first is to use the `handlebars.runtime.amd.js` file. This may require a [path mapping](https://github.com/wycats/handlebars.js/blob/master/spec/amd-runtime.html#L31) as well as access via the `default` field.
The other option is to load the `handlebars.runtime.js` UMD build, which might not require path configuration and exposes the library as both the module root and the `default` field for compatibility.
If not using ES6 transpilers or accessing submodules in the build the former option should be sufficient for most use cases.
Copyright (C) 2011-2015 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.
[![Travis Build Status](https://img.shields.io/travis/wycats/handlebars.js/master.svg)](https://travis-ci.org/wycats/handlebars.js)
[![Selenium Test Status](https://saucelabs.com/buildstatus/handlebars)](https://saucelabs.com/u/handlebars)
Handlebars.js
=============
Handlebars.js is an extension to the [Mustache templating
language](http://mustache.github.com/) created by Chris Wanstrath.
Handlebars.js and Mustache are both logicless templating languages that
keep the view and the code separated like we all know they should be.
Checkout the official Handlebars docs site at
[http://www.handlebarsjs.com](http://www.handlebarsjs.com) and the live demo at [http://tryhandlebarsjs.com/](http://tryhandlebarsjs.com/).
Installing
----------
See our [installation documentation](http://handlebarsjs.com/installation.html).
Usage
-----
In general, the syntax of Handlebars.js templates is a superset
of Mustache templates. For basic syntax, check out the [Mustache
manpage](http://mustache.github.com/mustache.5.html).
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 " +
"{{kids.length}} kids:</p>" +
"<ul>{{#kids}}<li>{{name}} is {{age}}</li>{{/kids}}</ul>";
var template = Handlebars.compile(source);
var data = { "name": "Alan", "hometown": "Somewhere, TX",
"kids": [{"name": "Jimmy", "age": "12"}, {"name": "Sally", "age": "4"}]};
var result = template(data);
// Would render:
// <p>Hello, my name is Alan. I am from Somewhere, TX. I have 2 kids:</p>
// <ul>
// <li>Jimmy is 12</li>
// <li>Sally is 4</li>
// </ul>
```
Full documentation and more examples are at [handlebarsjs.com](http://handlebarsjs.com/).
Precompiling Templates
----------------------
Handlebars allows templates to be precompiled and included as javascript code rather than the handlebars template allowing for faster startup time. Full details are located [here](http://handlebarsjs.com/precompilation.html).
Differences Between Handlebars.js and Mustache
----------------------------------------------
Handlebars.js adds a couple of additional features to make writing
templates easier and also changes a tiny detail of how partials work.
- [Nested Paths](http://handlebarsjs.com/#paths)
- [Helpers](http://handlebarsjs.com/#helpers)
- [Block Expressions](http://handlebarsjs.com/#block-expressions)
- [Literal Values](http://handlebarsjs.com/#literals)
- [Delimited Comments](http://handlebarsjs.com/#comments)
Block expressions have the same syntax as mustache sections but should not be confused with one another. Sections are akin to an implicit `each` or `with` statement depending on the input data and helpers are explicit pieces of code that are free to implement whatever behavior they like. The [mustache spec](http://mustache.github.io/mustache.5.html) defines the exact behavior of sections. In the case of name conflicts, helpers are given priority.
### Compatibility
There are a few Mustache behaviors that Handlebars does not implement.
- Handlebars deviates from Mustache slightly in that it does not perform recursive lookup by default. The compile time `compat` flag must be set to enable this functionality. Users should note that there is a performance cost for enabling this flag. The exact cost varies by template, but it's recommended that performance sensitive operations should avoid this mode and instead opt for explicit path references.
- The optional Mustache-style lambdas are not supported. Instead Handlebars provides its own lambda resolution that follows the behaviors of helpers.
- Alternative delimiters are not supported.
Supported Environments
----------------------
Handlebars has been designed to work in any ECMAScript 3 environment. This includes
- Node.js
- Chrome
- Firefox
- Safari 5+
- Opera 11+
- IE 6+
Older versions and other runtimes are likely to work but have not been formally
tested. The compiler requires `JSON.stringify` to be implemented natively or via a polyfill. If using the precompiler this is not necessary.
[![Selenium Test Status](https://saucelabs.com/browser-matrix/handlebars.svg)](https://saucelabs.com/u/handlebars)