diff --git a/index.html b/index.html
index 042003d1..8d77e9b9 100644
--- a/index.html
+++ b/index.html
@@ -43,9 +43,9 @@
-
+
-
+
diff --git a/js/app.js b/js/app.js
index b17ee338..2c7b7655 100644
--- a/js/app.js
+++ b/js/app.js
@@ -5,7 +5,7 @@ app = Sammy('#main', function (sam) {
*
*/
// Plugins
- sam.use('Mustache', 'ms');
+ sam.use('Handlebars', 'ms');
// Look for supported type of storage to use
var storageType;
diff --git a/js/vendor/handlebars-v1.3.0.js b/js/vendor/handlebars-v1.3.0.js
new file mode 100644
index 00000000..bec7085c
--- /dev/null
+++ b/js/vendor/handlebars-v1.3.0.js
@@ -0,0 +1,2746 @@
+/*!
+
+ handlebars v1.3.0
+
+Copyright (C) 2011 by Yehuda Katz
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+THE SOFTWARE.
+
+@license
+*/
+/* exported Handlebars */
+var Handlebars = (function() {
+// handlebars/safe-string.js
+var __module4__ = (function() {
+ "use strict";
+ var __exports__;
+ // Build out our basic SafeString type
+ function SafeString(string) {
+ this.string = string;
+ }
+
+ SafeString.prototype.toString = function() {
+ return "" + this.string;
+ };
+
+ __exports__ = SafeString;
+ return __exports__;
+})();
+
+// handlebars/utils.js
+var __module3__ = (function(__dependency1__) {
+ "use strict";
+ var __exports__ = {};
+ /*jshint -W004 */
+ var SafeString = __dependency1__;
+
+ var escape = {
+ "&": "&",
+ "<": "<",
+ ">": ">",
+ '"': """,
+ "'": "'",
+ "`": "`"
+ };
+
+ var badChars = /[&<>"'`]/g;
+ var possible = /[&<>"'`]/;
+
+ function escapeChar(chr) {
+ return escape[chr] || "&";
+ }
+
+ function extend(obj, value) {
+ for(var key in value) {
+ if(Object.prototype.hasOwnProperty.call(value, key)) {
+ obj[key] = value[key];
+ }
+ }
+ }
+
+ __exports__.extend = extend;var toString = Object.prototype.toString;
+ __exports__.toString = toString;
+ // Sourced from lodash
+ // https://github.com/bestiejs/lodash/blob/master/LICENSE.txt
+ var isFunction = function(value) {
+ return typeof value === 'function';
+ };
+ // fallback for older versions of Chrome and Safari
+ if (isFunction(/x/)) {
+ isFunction = function(value) {
+ return typeof value === 'function' && toString.call(value) === '[object Function]';
+ };
+ }
+ var isFunction;
+ __exports__.isFunction = isFunction;
+ var isArray = Array.isArray || function(value) {
+ return (value && typeof value === 'object') ? toString.call(value) === '[object Array]' : false;
+ };
+ __exports__.isArray = isArray;
+
+ function escapeExpression(string) {
+ // don't escape SafeStrings, since they're already safe
+ if (string instanceof SafeString) {
+ return string.toString();
+ } else if (!string && string !== 0) {
+ return "";
+ }
+
+ // Force a string conversion as this will be done by the append regardless and
+ // the regex test will do this transparently behind the scenes, causing issues if
+ // an object's to string has escaped characters in it.
+ string = "" + string;
+
+ if(!possible.test(string)) { return string; }
+ return string.replace(badChars, escapeChar);
+ }
+
+ __exports__.escapeExpression = escapeExpression;function isEmpty(value) {
+ if (!value && value !== 0) {
+ return true;
+ } else if (isArray(value) && value.length === 0) {
+ return true;
+ } else {
+ return false;
+ }
+ }
+
+ __exports__.isEmpty = isEmpty;
+ return __exports__;
+})(__module4__);
+
+// handlebars/exception.js
+var __module5__ = (function() {
+ "use strict";
+ var __exports__;
+
+ var errorProps = ['description', 'fileName', 'lineNumber', 'message', 'name', 'number', 'stack'];
+
+ function Exception(message, node) {
+ var line;
+ if (node && node.firstLine) {
+ line = node.firstLine;
+
+ message += ' - ' + line + ':' + node.firstColumn;
+ }
+
+ var tmp = Error.prototype.constructor.call(this, message);
+
+ // 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]];
+ }
+
+ if (line) {
+ this.lineNumber = line;
+ this.column = node.firstColumn;
+ }
+ }
+
+ Exception.prototype = new Error();
+
+ __exports__ = Exception;
+ return __exports__;
+})();
+
+// handlebars/base.js
+var __module2__ = (function(__dependency1__, __dependency2__) {
+ "use strict";
+ var __exports__ = {};
+ var Utils = __dependency1__;
+ var Exception = __dependency2__;
+
+ var VERSION = "1.3.0";
+ __exports__.VERSION = VERSION;var COMPILER_REVISION = 4;
+ __exports__.COMPILER_REVISION = COMPILER_REVISION;
+ var REVISION_CHANGES = {
+ 1: '<= 1.0.rc.2', // 1.0.rc.2 is actually rev2 but doesn't report it
+ 2: '== 1.0.0-rc.3',
+ 3: '== 1.0.0-rc.4',
+ 4: '>= 1.0.0'
+ };
+ __exports__.REVISION_CHANGES = REVISION_CHANGES;
+ var isArray = Utils.isArray,
+ isFunction = Utils.isFunction,
+ toString = Utils.toString,
+ objectType = '[object Object]';
+
+ function HandlebarsEnvironment(helpers, partials) {
+ this.helpers = helpers || {};
+ this.partials = partials || {};
+
+ registerDefaultHelpers(this);
+ }
+
+ __exports__.HandlebarsEnvironment = HandlebarsEnvironment;HandlebarsEnvironment.prototype = {
+ constructor: HandlebarsEnvironment,
+
+ logger: logger,
+ log: log,
+
+ registerHelper: function(name, fn, inverse) {
+ if (toString.call(name) === objectType) {
+ if (inverse || fn) { throw new Exception('Arg not supported with multiple helpers'); }
+ Utils.extend(this.helpers, name);
+ } else {
+ if (inverse) { fn.not = inverse; }
+ this.helpers[name] = fn;
+ }
+ },
+
+ registerPartial: function(name, str) {
+ if (toString.call(name) === objectType) {
+ Utils.extend(this.partials, name);
+ } else {
+ this.partials[name] = str;
+ }
+ }
+ };
+
+ function registerDefaultHelpers(instance) {
+ instance.registerHelper('helperMissing', function(arg) {
+ if(arguments.length === 2) {
+ return undefined;
+ } else {
+ throw new Exception("Missing helper: '" + arg + "'");
+ }
+ });
+
+ instance.registerHelper('blockHelperMissing', function(context, options) {
+ var inverse = options.inverse || function() {}, fn = options.fn;
+
+ if (isFunction(context)) { context = context.call(this); }
+
+ if(context === true) {
+ return fn(this);
+ } else if(context === false || context == null) {
+ return inverse(this);
+ } else if (isArray(context)) {
+ if(context.length > 0) {
+ return instance.helpers.each(context, options);
+ } else {
+ return inverse(this);
+ }
+ } else {
+ return fn(context);
+ }
+ });
+
+ instance.registerHelper('each', function(context, options) {
+ var fn = options.fn, inverse = options.inverse;
+ var i = 0, ret = "", data;
+
+ if (isFunction(context)) { context = context.call(this); }
+
+ if (options.data) {
+ data = createFrame(options.data);
+ }
+
+ if(context && typeof context === 'object') {
+ if (isArray(context)) {
+ for(var j = context.length; i 0) {
+ throw new Exception("Invalid path: " + original, this);
+ } else if (part === "..") {
+ depth++;
+ } else {
+ this.isScoped = true;
+ }
+ } else {
+ dig.push(part);
+ }
+ }
+
+ this.original = original;
+ this.parts = dig;
+ this.string = dig.join('.');
+ this.depth = depth;
+
+ // 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;
+ },
+
+ PartialNameNode: function(name, locInfo) {
+ LocationInfo.call(this, locInfo);
+ this.type = "PARTIAL_NAME";
+ this.name = name.original;
+ },
+
+ DataNode: function(id, locInfo) {
+ LocationInfo.call(this, locInfo);
+ this.type = "DATA";
+ this.id = id;
+ },
+
+ StringNode: function(string, locInfo) {
+ LocationInfo.call(this, locInfo);
+ this.type = "STRING";
+ this.original =
+ this.string =
+ this.stringModeValue = string;
+ },
+
+ IntegerNode: function(integer, locInfo) {
+ LocationInfo.call(this, locInfo);
+ this.type = "INTEGER";
+ this.original =
+ this.integer = integer;
+ this.stringModeValue = Number(integer);
+ },
+
+ BooleanNode: function(bool, locInfo) {
+ LocationInfo.call(this, locInfo);
+ this.type = "BOOLEAN";
+ this.bool = bool;
+ this.stringModeValue = bool === "true";
+ },
+
+ CommentNode: function(comment, locInfo) {
+ LocationInfo.call(this, locInfo);
+ this.type = "comment";
+ this.comment = comment;
+ }
+ };
+
+ // Must be exported as an object rather than the root of the module as the jison lexer
+ // most modify the object to operate properly.
+ __exports__ = AST;
+ return __exports__;
+})(__module5__);
+
+// handlebars/compiler/parser.js
+var __module9__ = (function() {
+ "use strict";
+ var __exports__;
+ /* jshint ignore:start */
+ /* Jison generated parser */
+ var handlebars = (function(){
+ var parser = {trace: function trace() { },
+ yy: {},
+ symbols_: {"error":2,"root":3,"statements":4,"EOF":5,"program":6,"simpleInverse":7,"statement":8,"openInverse":9,"closeBlock":10,"openBlock":11,"mustache":12,"partial":13,"CONTENT":14,"COMMENT":15,"OPEN_BLOCK":16,"sexpr":17,"CLOSE":18,"OPEN_INVERSE":19,"OPEN_ENDBLOCK":20,"path":21,"OPEN":22,"OPEN_UNESCAPED":23,"CLOSE_UNESCAPED":24,"OPEN_PARTIAL":25,"partialName":26,"partial_option0":27,"sexpr_repetition0":28,"sexpr_option0":29,"dataName":30,"param":31,"STRING":32,"INTEGER":33,"BOOLEAN":34,"OPEN_SEXPR":35,"CLOSE_SEXPR":36,"hash":37,"hash_repetition_plus0":38,"hashSegment":39,"ID":40,"EQUALS":41,"DATA":42,"pathSegments":43,"SEP":44,"$accept":0,"$end":1},
+ terminals_: {2:"error",5:"EOF",14:"CONTENT",15:"COMMENT",16:"OPEN_BLOCK",18:"CLOSE",19:"OPEN_INVERSE",20:"OPEN_ENDBLOCK",22:"OPEN",23:"OPEN_UNESCAPED",24:"CLOSE_UNESCAPED",25:"OPEN_PARTIAL",32:"STRING",33:"INTEGER",34:"BOOLEAN",35:"OPEN_SEXPR",36:"CLOSE_SEXPR",40:"ID",41:"EQUALS",42:"DATA",44:"SEP"},
+ productions_: [0,[3,2],[3,1],[6,2],[6,3],[6,2],[6,1],[6,1],[6,0],[4,1],[4,2],[8,3],[8,3],[8,1],[8,1],[8,1],[8,1],[11,3],[9,3],[10,3],[12,3],[12,3],[13,4],[7,2],[17,3],[17,1],[31,1],[31,1],[31,1],[31,1],[31,1],[31,3],[37,1],[39,3],[26,1],[26,1],[26,1],[30,2],[21,1],[43,3],[43,1],[27,0],[27,1],[28,0],[28,2],[29,0],[29,1],[38,1],[38,2]],
+ performAction: function anonymous(yytext,yyleng,yylineno,yy,yystate,$$,_$) {
+
+ var $0 = $$.length - 1;
+ switch (yystate) {
+ case 1: return new yy.ProgramNode($$[$0-1], this._$);
+ break;
+ case 2: return new yy.ProgramNode([], this._$);
+ break;
+ case 3:this.$ = new yy.ProgramNode([], $$[$0-1], $$[$0], this._$);
+ break;
+ case 4:this.$ = new yy.ProgramNode($$[$0-2], $$[$0-1], $$[$0], this._$);
+ break;
+ case 5:this.$ = new yy.ProgramNode($$[$0-1], $$[$0], [], this._$);
+ break;
+ case 6:this.$ = new yy.ProgramNode($$[$0], this._$);
+ break;
+ case 7:this.$ = new yy.ProgramNode([], this._$);
+ break;
+ case 8:this.$ = new yy.ProgramNode([], this._$);
+ break;
+ case 9:this.$ = [$$[$0]];
+ break;
+ case 10: $$[$0-1].push($$[$0]); this.$ = $$[$0-1];
+ break;
+ case 11:this.$ = new yy.BlockNode($$[$0-2], $$[$0-1].inverse, $$[$0-1], $$[$0], this._$);
+ break;
+ case 12:this.$ = new yy.BlockNode($$[$0-2], $$[$0-1], $$[$0-1].inverse, $$[$0], this._$);
+ break;
+ case 13:this.$ = $$[$0];
+ break;
+ case 14:this.$ = $$[$0];
+ break;
+ case 15:this.$ = new yy.ContentNode($$[$0], this._$);
+ break;
+ case 16:this.$ = new yy.CommentNode($$[$0], this._$);
+ break;
+ case 17:this.$ = new yy.MustacheNode($$[$0-1], null, $$[$0-2], stripFlags($$[$0-2], $$[$0]), this._$);
+ break;
+ case 18:this.$ = new yy.MustacheNode($$[$0-1], null, $$[$0-2], stripFlags($$[$0-2], $$[$0]), this._$);
+ break;
+ case 19:this.$ = {path: $$[$0-1], strip: stripFlags($$[$0-2], $$[$0])};
+ break;
+ case 20:this.$ = new yy.MustacheNode($$[$0-1], null, $$[$0-2], stripFlags($$[$0-2], $$[$0]), this._$);
+ break;
+ case 21:this.$ = new yy.MustacheNode($$[$0-1], null, $$[$0-2], stripFlags($$[$0-2], $$[$0]), this._$);
+ break;
+ case 22:this.$ = new yy.PartialNode($$[$0-2], $$[$0-1], stripFlags($$[$0-3], $$[$0]), this._$);
+ break;
+ case 23:this.$ = stripFlags($$[$0-1], $$[$0]);
+ break;
+ case 24:this.$ = new yy.SexprNode([$$[$0-2]].concat($$[$0-1]), $$[$0], this._$);
+ break;
+ case 25:this.$ = new yy.SexprNode([$$[$0]], null, this._$);
+ break;
+ case 26:this.$ = $$[$0];
+ break;
+ case 27:this.$ = new yy.StringNode($$[$0], this._$);
+ break;
+ case 28:this.$ = new yy.IntegerNode($$[$0], this._$);
+ break;
+ case 29:this.$ = new yy.BooleanNode($$[$0], this._$);
+ break;
+ case 30:this.$ = $$[$0];
+ break;
+ case 31:$$[$0-1].isHelper = true; this.$ = $$[$0-1];
+ break;
+ case 32:this.$ = new yy.HashNode($$[$0], this._$);
+ break;
+ case 33:this.$ = [$$[$0-2], $$[$0]];
+ break;
+ case 34:this.$ = new yy.PartialNameNode($$[$0], this._$);
+ break;
+ case 35:this.$ = new yy.PartialNameNode(new yy.StringNode($$[$0], this._$), this._$);
+ break;
+ case 36:this.$ = new yy.PartialNameNode(new yy.IntegerNode($$[$0], this._$));
+ break;
+ case 37:this.$ = new yy.DataNode($$[$0], this._$);
+ break;
+ case 38:this.$ = new yy.IdNode($$[$0], this._$);
+ break;
+ case 39: $$[$0-2].push({part: $$[$0], separator: $$[$0-1]}); this.$ = $$[$0-2];
+ break;
+ case 40:this.$ = [{part: $$[$0]}];
+ break;
+ case 43:this.$ = [];
+ break;
+ case 44:$$[$0-1].push($$[$0]);
+ break;
+ case 47:this.$ = [$$[$0]];
+ break;
+ case 48:$$[$0-1].push($$[$0]);
+ break;
+ }
+ },
+ table: [{3:1,4:2,5:[1,3],8:4,9:5,11:6,12:7,13:8,14:[1,9],15:[1,10],16:[1,12],19:[1,11],22:[1,13],23:[1,14],25:[1,15]},{1:[3]},{5:[1,16],8:17,9:5,11:6,12:7,13:8,14:[1,9],15:[1,10],16:[1,12],19:[1,11],22:[1,13],23:[1,14],25:[1,15]},{1:[2,2]},{5:[2,9],14:[2,9],15:[2,9],16:[2,9],19:[2,9],20:[2,9],22:[2,9],23:[2,9],25:[2,9]},{4:20,6:18,7:19,8:4,9:5,11:6,12:7,13:8,14:[1,9],15:[1,10],16:[1,12],19:[1,21],20:[2,8],22:[1,13],23:[1,14],25:[1,15]},{4:20,6:22,7:19,8:4,9:5,11:6,12:7,13:8,14:[1,9],15:[1,10],16:[1,12],19:[1,21],20:[2,8],22:[1,13],23:[1,14],25:[1,15]},{5:[2,13],14:[2,13],15:[2,13],16:[2,13],19:[2,13],20:[2,13],22:[2,13],23:[2,13],25:[2,13]},{5:[2,14],14:[2,14],15:[2,14],16:[2,14],19:[2,14],20:[2,14],22:[2,14],23:[2,14],25:[2,14]},{5:[2,15],14:[2,15],15:[2,15],16:[2,15],19:[2,15],20:[2,15],22:[2,15],23:[2,15],25:[2,15]},{5:[2,16],14:[2,16],15:[2,16],16:[2,16],19:[2,16],20:[2,16],22:[2,16],23:[2,16],25:[2,16]},{17:23,21:24,30:25,40:[1,28],42:[1,27],43:26},{17:29,21:24,30:25,40:[1,28],42:[1,27],43:26},{17:30,21:24,30:25,40:[1,28],42:[1,27],43:26},{17:31,21:24,30:25,40:[1,28],42:[1,27],43:26},{21:33,26:32,32:[1,34],33:[1,35],40:[1,28],43:26},{1:[2,1]},{5:[2,10],14:[2,10],15:[2,10],16:[2,10],19:[2,10],20:[2,10],22:[2,10],23:[2,10],25:[2,10]},{10:36,20:[1,37]},{4:38,8:4,9:5,11:6,12:7,13:8,14:[1,9],15:[1,10],16:[1,12],19:[1,11],20:[2,7],22:[1,13],23:[1,14],25:[1,15]},{7:39,8:17,9:5,11:6,12:7,13:8,14:[1,9],15:[1,10],16:[1,12],19:[1,21],20:[2,6],22:[1,13],23:[1,14],25:[1,15]},{17:23,18:[1,40],21:24,30:25,40:[1,28],42:[1,27],43:26},{10:41,20:[1,37]},{18:[1,42]},{18:[2,43],24:[2,43],28:43,32:[2,43],33:[2,43],34:[2,43],35:[2,43],36:[2,43],40:[2,43],42:[2,43]},{18:[2,25],24:[2,25],36:[2,25]},{18:[2,38],24:[2,38],32:[2,38],33:[2,38],34:[2,38],35:[2,38],36:[2,38],40:[2,38],42:[2,38],44:[1,44]},{21:45,40:[1,28],43:26},{18:[2,40],24:[2,40],32:[2,40],33:[2,40],34:[2,40],35:[2,40],36:[2,40],40:[2,40],42:[2,40],44:[2,40]},{18:[1,46]},{18:[1,47]},{24:[1,48]},{18:[2,41],21:50,27:49,40:[1,28],43:26},{18:[2,34],40:[2,34]},{18:[2,35],40:[2,35]},{18:[2,36],40:[2,36]},{5:[2,11],14:[2,11],15:[2,11],16:[2,11],19:[2,11],20:[2,11],22:[2,11],23:[2,11],25:[2,11]},{21:51,40:[1,28],43:26},{8:17,9:5,11:6,12:7,13:8,14:[1,9],15:[1,10],16:[1,12],19:[1,11],20:[2,3],22:[1,13],23:[1,14],25:[1,15]},{4:52,8:4,9:5,11:6,12:7,13:8,14:[1,9],15:[1,10],16:[1,12],19:[1,11],20:[2,5],22:[1,13],23:[1,14],25:[1,15]},{14:[2,23],15:[2,23],16:[2,23],19:[2,23],20:[2,23],22:[2,23],23:[2,23],25:[2,23]},{5:[2,12],14:[2,12],15:[2,12],16:[2,12],19:[2,12],20:[2,12],22:[2,12],23:[2,12],25:[2,12]},{14:[2,18],15:[2,18],16:[2,18],19:[2,18],20:[2,18],22:[2,18],23:[2,18],25:[2,18]},{18:[2,45],21:56,24:[2,45],29:53,30:60,31:54,32:[1,57],33:[1,58],34:[1,59],35:[1,61],36:[2,45],37:55,38:62,39:63,40:[1,64],42:[1,27],43:26},{40:[1,65]},{18:[2,37],24:[2,37],32:[2,37],33:[2,37],34:[2,37],35:[2,37],36:[2,37],40:[2,37],42:[2,37]},{14:[2,17],15:[2,17],16:[2,17],19:[2,17],20:[2,17],22:[2,17],23:[2,17],25:[2,17]},{5:[2,20],14:[2,20],15:[2,20],16:[2,20],19:[2,20],20:[2,20],22:[2,20],23:[2,20],25:[2,20]},{5:[2,21],14:[2,21],15:[2,21],16:[2,21],19:[2,21],20:[2,21],22:[2,21],23:[2,21],25:[2,21]},{18:[1,66]},{18:[2,42]},{18:[1,67]},{8:17,9:5,11:6,12:7,13:8,14:[1,9],15:[1,10],16:[1,12],19:[1,11],20:[2,4],22:[1,13],23:[1,14],25:[1,15]},{18:[2,24],24:[2,24],36:[2,24]},{18:[2,44],24:[2,44],32:[2,44],33:[2,44],34:[2,44],35:[2,44],36:[2,44],40:[2,44],42:[2,44]},{18:[2,46],24:[2,46],36:[2,46]},{18:[2,26],24:[2,26],32:[2,26],33:[2,26],34:[2,26],35:[2,26],36:[2,26],40:[2,26],42:[2,26]},{18:[2,27],24:[2,27],32:[2,27],33:[2,27],34:[2,27],35:[2,27],36:[2,27],40:[2,27],42:[2,27]},{18:[2,28],24:[2,28],32:[2,28],33:[2,28],34:[2,28],35:[2,28],36:[2,28],40:[2,28],42:[2,28]},{18:[2,29],24:[2,29],32:[2,29],33:[2,29],34:[2,29],35:[2,29],36:[2,29],40:[2,29],42:[2,29]},{18:[2,30],24:[2,30],32:[2,30],33:[2,30],34:[2,30],35:[2,30],36:[2,30],40:[2,30],42:[2,30]},{17:68,21:24,30:25,40:[1,28],42:[1,27],43:26},{18:[2,32],24:[2,32],36:[2,32],39:69,40:[1,70]},{18:[2,47],24:[2,47],36:[2,47],40:[2,47]},{18:[2,40],24:[2,40],32:[2,40],33:[2,40],34:[2,40],35:[2,40],36:[2,40],40:[2,40],41:[1,71],42:[2,40],44:[2,40]},{18:[2,39],24:[2,39],32:[2,39],33:[2,39],34:[2,39],35:[2,39],36:[2,39],40:[2,39],42:[2,39],44:[2,39]},{5:[2,22],14:[2,22],15:[2,22],16:[2,22],19:[2,22],20:[2,22],22:[2,22],23:[2,22],25:[2,22]},{5:[2,19],14:[2,19],15:[2,19],16:[2,19],19:[2,19],20:[2,19],22:[2,19],23:[2,19],25:[2,19]},{36:[1,72]},{18:[2,48],24:[2,48],36:[2,48],40:[2,48]},{41:[1,71]},{21:56,30:60,31:73,32:[1,57],33:[1,58],34:[1,59],35:[1,61],40:[1,28],42:[1,27],43:26},{18:[2,31],24:[2,31],32:[2,31],33:[2,31],34:[2,31],35:[2,31],36:[2,31],40:[2,31],42:[2,31]},{18:[2,33],24:[2,33],36:[2,33],40:[2,33]}],
+ defaultActions: {3:[2,2],16:[2,1],50:[2,42]},
+ parseError: function parseError(str, hash) {
+ throw new Error(str);
+ },
+ parse: function parse(input) {
+ var self = this, stack = [0], vstack = [null], lstack = [], table = this.table, yytext = "", yylineno = 0, yyleng = 0, recovering = 0, TERROR = 2, EOF = 1;
+ this.lexer.setInput(input);
+ this.lexer.yy = this.yy;
+ this.yy.lexer = this.lexer;
+ this.yy.parser = this;
+ if (typeof this.lexer.yylloc == "undefined")
+ this.lexer.yylloc = {};
+ var yyloc = this.lexer.yylloc;
+ lstack.push(yyloc);
+ var ranges = this.lexer.options && this.lexer.options.ranges;
+ if (typeof this.yy.parseError === "function")
+ this.parseError = this.yy.parseError;
+ function popStack(n) {
+ stack.length = stack.length - 2 * n;
+ vstack.length = vstack.length - n;
+ lstack.length = lstack.length - n;
+ }
+ function lex() {
+ var token;
+ token = self.lexer.lex() || 1;
+ if (typeof token !== "number") {
+ token = self.symbols_[token] || token;
+ }
+ return token;
+ }
+ var symbol, preErrorSymbol, state, action, a, r, yyval = {}, p, len, newState, expected;
+ while (true) {
+ state = stack[stack.length - 1];
+ if (this.defaultActions[state]) {
+ action = this.defaultActions[state];
+ } else {
+ if (symbol === null || typeof symbol == "undefined") {
+ symbol = lex();
+ }
+ action = table[state] && table[state][symbol];
+ }
+ if (typeof action === "undefined" || !action.length || !action[0]) {
+ var errStr = "";
+ if (!recovering) {
+ expected = [];
+ for (p in table[state])
+ if (this.terminals_[p] && p > 2) {
+ expected.push("'" + this.terminals_[p] + "'");
+ }
+ if (this.lexer.showPosition) {
+ errStr = "Parse error on line " + (yylineno + 1) + ":\n" + this.lexer.showPosition() + "\nExpecting " + expected.join(", ") + ", got '" + (this.terminals_[symbol] || symbol) + "'";
+ } else {
+ errStr = "Parse error on line " + (yylineno + 1) + ": Unexpected " + (symbol == 1?"end of input":"'" + (this.terminals_[symbol] || symbol) + "'");
+ }
+ this.parseError(errStr, {text: this.lexer.match, token: this.terminals_[symbol] || symbol, line: this.lexer.yylineno, loc: yyloc, expected: expected});
+ }
+ }
+ if (action[0] instanceof Array && action.length > 1) {
+ throw new Error("Parse Error: multiple actions possible at state: " + state + ", token: " + symbol);
+ }
+ switch (action[0]) {
+ case 1:
+ stack.push(symbol);
+ vstack.push(this.lexer.yytext);
+ lstack.push(this.lexer.yylloc);
+ stack.push(action[1]);
+ symbol = null;
+ if (!preErrorSymbol) {
+ yyleng = this.lexer.yyleng;
+ yytext = this.lexer.yytext;
+ yylineno = this.lexer.yylineno;
+ yyloc = this.lexer.yylloc;
+ if (recovering > 0)
+ recovering--;
+ } else {
+ symbol = preErrorSymbol;
+ preErrorSymbol = null;
+ }
+ break;
+ case 2:
+ len = this.productions_[action[1]][1];
+ yyval.$ = vstack[vstack.length - len];
+ yyval._$ = {first_line: lstack[lstack.length - (len || 1)].first_line, last_line: lstack[lstack.length - 1].last_line, first_column: lstack[lstack.length - (len || 1)].first_column, last_column: lstack[lstack.length - 1].last_column};
+ if (ranges) {
+ yyval._$.range = [lstack[lstack.length - (len || 1)].range[0], lstack[lstack.length - 1].range[1]];
+ }
+ r = this.performAction.call(yyval, yytext, yyleng, yylineno, this.yy, action[1], vstack, lstack);
+ if (typeof r !== "undefined") {
+ return r;
+ }
+ if (len) {
+ stack = stack.slice(0, -1 * len * 2);
+ vstack = vstack.slice(0, -1 * len);
+ lstack = lstack.slice(0, -1 * len);
+ }
+ stack.push(this.productions_[action[1]][0]);
+ vstack.push(yyval.$);
+ lstack.push(yyval._$);
+ newState = table[stack[stack.length - 2]][stack[stack.length - 1]];
+ stack.push(newState);
+ break;
+ case 3:
+ return true;
+ }
+ }
+ return true;
+ }
+ };
+
+
+ function stripFlags(open, close) {
+ return {
+ left: open.charAt(2) === '~',
+ right: close.charAt(0) === '~' || close.charAt(1) === '~'
+ };
+ }
+
+ /* Jison generated lexer */
+ var lexer = (function(){
+ var lexer = ({EOF:1,
+ parseError:function parseError(str, hash) {
+ if (this.yy.parser) {
+ this.yy.parser.parseError(str, hash);
+ } else {
+ throw new Error(str);
+ }
+ },
+ setInput:function (input) {
+ this._input = input;
+ this._more = this._less = this.done = false;
+ this.yylineno = this.yyleng = 0;
+ this.yytext = this.matched = this.match = '';
+ this.conditionStack = ['INITIAL'];
+ this.yylloc = {first_line:1,first_column:0,last_line:1,last_column:0};
+ if (this.options.ranges) this.yylloc.range = [0,0];
+ this.offset = 0;
+ return this;
+ },
+ input:function () {
+ var ch = this._input[0];
+ this.yytext += ch;
+ this.yyleng++;
+ this.offset++;
+ this.match += ch;
+ this.matched += ch;
+ var lines = ch.match(/(?:\r\n?|\n).*/g);
+ if (lines) {
+ this.yylineno++;
+ this.yylloc.last_line++;
+ } else {
+ this.yylloc.last_column++;
+ }
+ if (this.options.ranges) this.yylloc.range[1]++;
+
+ this._input = this._input.slice(1);
+ return ch;
+ },
+ unput:function (ch) {
+ var len = ch.length;
+ var lines = ch.split(/(?:\r\n?|\n)/g);
+
+ this._input = ch + this._input;
+ this.yytext = this.yytext.substr(0, this.yytext.length-len-1);
+ //this.yyleng -= len;
+ this.offset -= len;
+ var oldLines = this.match.split(/(?:\r\n?|\n)/g);
+ this.match = this.match.substr(0, this.match.length-1);
+ this.matched = this.matched.substr(0, this.matched.length-1);
+
+ if (lines.length-1) this.yylineno -= lines.length-1;
+ var r = this.yylloc.range;
+
+ this.yylloc = {first_line: this.yylloc.first_line,
+ last_line: this.yylineno+1,
+ first_column: this.yylloc.first_column,
+ last_column: lines ?
+ (lines.length === oldLines.length ? this.yylloc.first_column : 0) + oldLines[oldLines.length - lines.length].length - lines[0].length:
+ this.yylloc.first_column - len
+ };
+
+ if (this.options.ranges) {
+ this.yylloc.range = [r[0], r[0] + this.yyleng - len];
+ }
+ return this;
+ },
+ more:function () {
+ this._more = true;
+ return this;
+ },
+ less:function (n) {
+ this.unput(this.match.slice(n));
+ },
+ pastInput:function () {
+ var past = this.matched.substr(0, this.matched.length - this.match.length);
+ return (past.length > 20 ? '...':'') + past.substr(-20).replace(/\n/g, "");
+ },
+ upcomingInput:function () {
+ var next = this.match;
+ if (next.length < 20) {
+ next += this._input.substr(0, 20-next.length);
+ }
+ return (next.substr(0,20)+(next.length > 20 ? '...':'')).replace(/\n/g, "");
+ },
+ showPosition:function () {
+ var pre = this.pastInput();
+ var c = new Array(pre.length + 1).join("-");
+ return pre + this.upcomingInput() + "\n" + c+"^";
+ },
+ next:function () {
+ if (this.done) {
+ return this.EOF;
+ }
+ if (!this._input) this.done = true;
+
+ var token,
+ match,
+ tempMatch,
+ index,
+ col,
+ lines;
+ if (!this._more) {
+ this.yytext = '';
+ this.match = '';
+ }
+ var rules = this._currentRules();
+ for (var i=0;i < rules.length; i++) {
+ tempMatch = this._input.match(this.rules[rules[i]]);
+ if (tempMatch && (!match || tempMatch[0].length > match[0].length)) {
+ match = tempMatch;
+ index = i;
+ if (!this.options.flex) break;
+ }
+ }
+ if (match) {
+ lines = match[0].match(/(?:\r\n?|\n).*/g);
+ if (lines) this.yylineno += lines.length;
+ this.yylloc = {first_line: this.yylloc.last_line,
+ last_line: this.yylineno+1,
+ first_column: this.yylloc.last_column,
+ last_column: lines ? lines[lines.length-1].length-lines[lines.length-1].match(/\r?\n?/)[0].length : this.yylloc.last_column + match[0].length};
+ this.yytext += match[0];
+ this.match += match[0];
+ this.matches = match;
+ this.yyleng = this.yytext.length;
+ if (this.options.ranges) {
+ this.yylloc.range = [this.offset, this.offset += this.yyleng];
+ }
+ this._more = false;
+ this._input = this._input.slice(match[0].length);
+ this.matched += match[0];
+ token = this.performAction.call(this, this.yy, this, rules[index],this.conditionStack[this.conditionStack.length-1]);
+ if (this.done && this._input) this.done = false;
+ if (token) return token;
+ else return;
+ }
+ if (this._input === "") {
+ return this.EOF;
+ } else {
+ return this.parseError('Lexical error on line '+(this.yylineno+1)+'. Unrecognized text.\n'+this.showPosition(),
+ {text: "", token: null, line: this.yylineno});
+ }
+ },
+ lex:function lex() {
+ var r = this.next();
+ if (typeof r !== 'undefined') {
+ return r;
+ } else {
+ return this.lex();
+ }
+ },
+ begin:function begin(condition) {
+ this.conditionStack.push(condition);
+ },
+ popState:function popState() {
+ return this.conditionStack.pop();
+ },
+ _currentRules:function _currentRules() {
+ return this.conditions[this.conditionStack[this.conditionStack.length-1]].rules;
+ },
+ topState:function () {
+ return this.conditionStack[this.conditionStack.length-2];
+ },
+ pushState:function begin(condition) {
+ this.begin(condition);
+ }});
+ lexer.options = {};
+ lexer.performAction = function anonymous(yy,yy_,$avoiding_name_collisions,YY_START) {
+
+
+ function strip(start, end) {
+ return yy_.yytext = yy_.yytext.substr(start, yy_.yyleng-end);
+ }
+
+
+ var YYSTATE=YY_START
+ switch($avoiding_name_collisions) {
+ case 0:
+ if(yy_.yytext.slice(-2) === "\\\\") {
+ strip(0,1);
+ this.begin("mu");
+ } else if(yy_.yytext.slice(-1) === "\\") {
+ strip(0,1);
+ this.begin("emu");
+ } else {
+ this.begin("mu");
+ }
+ if(yy_.yytext) return 14;
+
+ break;
+ case 1:return 14;
+ break;
+ case 2:
+ this.popState();
+ return 14;
+
+ break;
+ case 3:strip(0,4); this.popState(); return 15;
+ break;
+ case 4:return 35;
+ break;
+ case 5:return 36;
+ break;
+ case 6:return 25;
+ break;
+ case 7:return 16;
+ break;
+ case 8:return 20;
+ break;
+ case 9:return 19;
+ break;
+ case 10:return 19;
+ break;
+ case 11:return 23;
+ break;
+ case 12:return 22;
+ break;
+ case 13:this.popState(); this.begin('com');
+ break;
+ case 14:strip(3,5); this.popState(); return 15;
+ break;
+ case 15:return 22;
+ break;
+ case 16:return 41;
+ break;
+ case 17:return 40;
+ break;
+ case 18:return 40;
+ break;
+ case 19:return 44;
+ break;
+ case 20:// ignore whitespace
+ break;
+ case 21:this.popState(); return 24;
+ break;
+ case 22:this.popState(); return 18;
+ break;
+ case 23:yy_.yytext = strip(1,2).replace(/\\"/g,'"'); return 32;
+ break;
+ case 24:yy_.yytext = strip(1,2).replace(/\\'/g,"'"); return 32;
+ break;
+ case 25:return 42;
+ break;
+ case 26:return 34;
+ break;
+ case 27:return 34;
+ break;
+ case 28:return 33;
+ break;
+ case 29:return 40;
+ break;
+ case 30:yy_.yytext = strip(1,2); return 40;
+ break;
+ case 31:return 'INVALID';
+ break;
+ case 32:return 5;
+ break;
+ }
+ };
+ lexer.rules = [/^(?:[^\x00]*?(?=(\{\{)))/,/^(?:[^\x00]+)/,/^(?:[^\x00]{2,}?(?=(\{\{|\\\{\{|\\\\\{\{|$)))/,/^(?:[\s\S]*?--\}\})/,/^(?:\()/,/^(?:\))/,/^(?:\{\{(~)?>)/,/^(?:\{\{(~)?#)/,/^(?:\{\{(~)?\/)/,/^(?:\{\{(~)?\^)/,/^(?:\{\{(~)?\s*else\b)/,/^(?:\{\{(~)?\{)/,/^(?:\{\{(~)?&)/,/^(?:\{\{!--)/,/^(?:\{\{![\s\S]*?\}\})/,/^(?:\{\{(~)?)/,/^(?:=)/,/^(?:\.\.)/,/^(?:\.(?=([=~}\s\/.)])))/,/^(?:[\/.])/,/^(?:\s+)/,/^(?:\}(~)?\}\})/,/^(?:(~)?\}\})/,/^(?:"(\\["]|[^"])*")/,/^(?:'(\\[']|[^'])*')/,/^(?:@)/,/^(?:true(?=([~}\s)])))/,/^(?:false(?=([~}\s)])))/,/^(?:-?[0-9]+(?=([~}\s)])))/,/^(?:([^\s!"#%-,\.\/;->@\[-\^`\{-~]+(?=([=~}\s\/.)]))))/,/^(?:\[[^\]]*\])/,/^(?:.)/,/^(?:$)/];
+ lexer.conditions = {"mu":{"rules":[4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32],"inclusive":false},"emu":{"rules":[2],"inclusive":false},"com":{"rules":[3],"inclusive":false},"INITIAL":{"rules":[0,1,32],"inclusive":true}};
+ return lexer;})()
+ parser.lexer = lexer;
+ function Parser () { this.yy = {}; }Parser.prototype = parser;parser.Parser = Parser;
+ return new Parser;
+ })();__exports__ = handlebars;
+ /* jshint ignore:end */
+ return __exports__;
+})();
+
+// handlebars/compiler/base.js
+var __module8__ = (function(__dependency1__, __dependency2__) {
+ "use strict";
+ var __exports__ = {};
+ var parser = __dependency1__;
+ var AST = __dependency2__;
+
+ __exports__.parser = parser;
+
+ function parse(input) {
+ // Just return if an already-compile AST was passed in.
+ if(input.constructor === AST.ProgramNode) { return input; }
+
+ parser.yy = AST;
+ return parser.parse(input);
+ }
+
+ __exports__.parse = parse;
+ return __exports__;
+})(__module9__, __module7__);
+
+// handlebars/compiler/compiler.js
+var __module10__ = (function(__dependency1__) {
+ "use strict";
+ var __exports__ = {};
+ var Exception = __dependency1__;
+
+ function Compiler() {}
+
+ __exports__.Compiler = Compiler;// the foundHelper register will disambiguate helper lookup from finding a
+ // function in a context. This is necessary for mustache compatibility, which
+ // requires that context functions in blocks are evaluated by blockHelperMissing,
+ // and then proceed as if the resulting value was provided to blockHelperMissing.
+
+ Compiler.prototype = {
+ compiler: Compiler,
+
+ disassemble: function() {
+ var opcodes = this.opcodes, opcode, out = [], params, param;
+
+ for (var i=0, l=opcodes.length; i 0) {
+ this.source[1] = this.source[1] + ", " + locals.join(", ");
+ }
+
+ // Generate minimizer alias mappings
+ if (!this.isChild) {
+ for (var alias in this.context.aliases) {
+ if (this.context.aliases.hasOwnProperty(alias)) {
+ this.source[1] = this.source[1] + ', ' + alias + '=' + this.context.aliases[alias];
+ }
+ }
+ }
+
+ if (this.source[1]) {
+ this.source[1] = "var " + this.source[1].substring(2) + ";";
+ }
+
+ // Merge children
+ if (!this.isChild) {
+ this.source[1] += '\n' + this.context.programs.join('\n') + '\n';
+ }
+
+ if (!this.environment.isSimple) {
+ this.pushSource("return buffer;");
+ }
+
+ var params = this.isChild ? ["depth0", "data"] : ["Handlebars", "depth0", "helpers", "partials", "data"];
+
+ for(var i=0, l=this.environment.depths.list.length; i this.stackVars.length) { this.stackVars.push("stack" + this.stackSlot); }
+ return this.topStackName();
+ },
+ topStackName: function() {
+ return "stack" + this.stackSlot;
+ },
+ flushInline: function() {
+ var inlineStack = this.inlineStack;
+ if (inlineStack.length) {
+ this.inlineStack = [];
+ for (var i = 0, len = inlineStack.length; i < len; i++) {
+ var entry = inlineStack[i];
+ if (entry instanceof Literal) {
+ this.compileStack.push(entry);
+ } else {
+ this.pushStack(entry);
+ }
+ }
+ }
+ },
+ isInline: function() {
+ return this.inlineStack.length;
+ },
+
+ popStack: function(wrapped) {
+ var inline = this.isInline(),
+ item = (inline ? this.inlineStack : this.compileStack).pop();
+
+ if (!wrapped && (item instanceof Literal)) {
+ return item.value;
+ } else {
+ if (!inline) {
+ if (!this.stackSlot) {
+ throw new Exception('Invalid stack pop');
+ }
+ this.stackSlot--;
+ }
+ return item;
+ }
+ },
+
+ topStack: function(wrapped) {
+ var stack = (this.isInline() ? this.inlineStack : this.compileStack),
+ item = stack[stack.length - 1];
+
+ if (!wrapped && (item instanceof Literal)) {
+ return item.value;
+ } else {
+ return item;
+ }
+ },
+
+ quotedString: function(str) {
+ return '"' + str
+ .replace(/\\/g, '\\\\')
+ .replace(/"/g, '\\"')
+ .replace(/\n/g, '\\n')
+ .replace(/\r/g, '\\r')
+ .replace(/\u2028/g, '\\u2028') // Per Ecma-262 7.3 + 7.8.4
+ .replace(/\u2029/g, '\\u2029') + '"';
+ },
+
+ setupHelper: function(paramSize, name, missingParams) {
+ var params = [],
+ paramsInit = this.setupParams(paramSize, params, missingParams);
+ var foundHelper = this.nameLookup('helpers', name, 'helper');
+
+ return {
+ params: params,
+ paramsInit: paramsInit,
+ name: foundHelper,
+ callParams: ["depth0"].concat(params).join(", "),
+ helperMissingParams: missingParams && ["depth0", this.quotedString(name)].concat(params).join(", ")
+ };
+ },
+
+ setupOptions: function(paramSize, params) {
+ var options = [], contexts = [], types = [], param, inverse, program;
+
+ options.push("hash:" + this.popStack());
+
+ if (this.options.stringParams) {
+ options.push("hashTypes:" + this.popStack());
+ options.push("hashContexts:" + this.popStack());
+ }
+
+ inverse = this.popStack();
+ program = this.popStack();
+
+ // Avoid setting fn and inverse if neither are set. This allows
+ // helpers to do a check for `if (options.fn)`
+ if (program || inverse) {
+ if (!program) {
+ this.context.aliases.self = "this";
+ program = "self.noop";
+ }
+
+ if (!inverse) {
+ this.context.aliases.self = "this";
+ inverse = "self.noop";
+ }
+
+ options.push("inverse:" + inverse);
+ options.push("fn:" + program);
+ }
+
+ for(var i=0; i
- }
- }
-}(this, function (mustache) {
-
- var whiteRe = /\s*/;
- var spaceRe = /\s+/;
- var nonSpaceRe = /\S/;
- var eqRe = /\s*=/;
- var curlyRe = /\s*\}/;
- var tagRe = /#|\^|\/|>|\{|&|=|!/;
-
- // Workaround for https://issues.apache.org/jira/browse/COUCHDB-577
- // See https://github.com/janl/mustache.js/issues/189
- var RegExp_test = RegExp.prototype.test;
- function testRegExp(re, string) {
- return RegExp_test.call(re, string);
- }
-
- function isWhitespace(string) {
- return !testRegExp(nonSpaceRe, string);
- }
-
- var Object_toString = Object.prototype.toString;
- var isArray = Array.isArray || function (obj) {
- return Object_toString.call(obj) === '[object Array]';
- };
-
- function escapeRegExp(string) {
- return string.replace(/[\-\[\]{}()*+?.,\\\^$|#\s]/g, "\\$&");
- }
-
- var entityMap = {
- "&": "&",
- "<": "<",
- ">": ">",
- '"': '"',
- "'": ''',
- "/": '/'
- };
-
- function escapeHtml(string) {
- return String(string).replace(/[&<>"'\/]/g, function (s) {
- return entityMap[s];
- });
- }
-
- function Scanner(string) {
- this.string = string;
- this.tail = string;
- this.pos = 0;
- }
-
- /**
- * Returns `true` if the tail is empty (end of string).
- */
- Scanner.prototype.eos = function () {
- return this.tail === "";
- };
-
- /**
- * Tries to match the given regular expression at the current position.
- * Returns the matched text if it can match, the empty string otherwise.
- */
- Scanner.prototype.scan = function (re) {
- var match = this.tail.match(re);
-
- if (match && match.index === 0) {
- this.tail = this.tail.substring(match[0].length);
- this.pos += match[0].length;
- return match[0];
- }
-
- return "";
- };
-
- /**
- * Skips all text until the given regular expression can be matched. Returns
- * the skipped string, which is the entire tail if no match can be made.
- */
- Scanner.prototype.scanUntil = function (re) {
- var match, pos = this.tail.search(re);
-
- switch (pos) {
- case -1:
- match = this.tail;
- this.pos += this.tail.length;
- this.tail = "";
- break;
- case 0:
- match = "";
- break;
- default:
- match = this.tail.substring(0, pos);
- this.tail = this.tail.substring(pos);
- this.pos += pos;
- }
-
- return match;
- };
-
- function Context(view, parent) {
- this.view = view || {};
- this.parent = parent;
- this._cache = {};
- }
-
- Context.make = function (view) {
- return (view instanceof Context) ? view : new Context(view);
- };
-
- Context.prototype.push = function (view) {
- return new Context(view, this);
- };
-
- Context.prototype.lookup = function (name) {
- var value = this._cache[name];
-
- if (!value) {
- if (name == '.') {
- value = this.view;
- } else {
- var context = this;
-
- while (context) {
- if (name.indexOf('.') > 0) {
- value = context.view;
- var names = name.split('.'), i = 0;
- while (value && i < names.length) {
- value = value[names[i++]];
- }
- } else {
- value = context.view[name];
- }
-
- if (value != null) break;
-
- context = context.parent;
- }
- }
-
- this._cache[name] = value;
- }
-
- if (typeof value === 'function') value = value.call(this.view);
-
- return value;
- };
-
- function Writer() {
- this.clearCache();
- }
-
- Writer.prototype.clearCache = function () {
- this._cache = {};
- this._partialCache = {};
- };
-
- Writer.prototype.compile = function (template, tags) {
- var fn = this._cache[template];
-
- if (!fn) {
- var tokens = mustache.parse(template, tags);
- fn = this._cache[template] = this.compileTokens(tokens, template);
- }
-
- return fn;
- };
-
- Writer.prototype.compilePartial = function (name, template, tags) {
- var fn = this.compile(template, tags);
- this._partialCache[name] = fn;
- return fn;
- };
-
- Writer.prototype.getPartial = function (name) {
- if (!(name in this._partialCache) && this._loadPartial) {
- this.compilePartial(name, this._loadPartial(name));
- }
-
- return this._partialCache[name];
- };
-
- Writer.prototype.compileTokens = function (tokens, template) {
- var self = this;
- return function (view, partials) {
- if (partials) {
- if (typeof partials === 'function') {
- self._loadPartial = partials;
- } else {
- for (var name in partials) {
- self.compilePartial(name, partials[name]);
- }
- }
- }
-
- return renderTokens(tokens, self, Context.make(view), template);
- };
- };
-
- Writer.prototype.render = function (template, view, partials) {
- return this.compile(template)(view, partials);
- };
-
- /**
- * Low-level function that renders the given `tokens` using the given `writer`
- * and `context`. The `template` string is only needed for templates that use
- * higher-order sections to extract the portion of the original template that
- * was contained in that section.
- */
- function renderTokens(tokens, writer, context, template) {
- var buffer = '';
-
- var token, tokenValue, value;
- for (var i = 0, len = tokens.length; i < len; ++i) {
- token = tokens[i];
- tokenValue = token[1];
-
- switch (token[0]) {
- case '#':
- value = context.lookup(tokenValue);
-
- if (typeof value === 'object') {
- if (isArray(value)) {
- for (var j = 0, jlen = value.length; j < jlen; ++j) {
- buffer += renderTokens(token[4], writer, context.push(value[j]), template);
- }
- } else if (value) {
- buffer += renderTokens(token[4], writer, context.push(value), template);
- }
- } else if (typeof value === 'function') {
- var text = template == null ? null : template.slice(token[3], token[5]);
- value = value.call(context.view, text, function (template) {
- return writer.render(template, context);
- });
- if (value != null) buffer += value;
- } else if (value) {
- buffer += renderTokens(token[4], writer, context, template);
- }
-
- break;
- case '^':
- value = context.lookup(tokenValue);
-
- // Use JavaScript's definition of falsy. Include empty arrays.
- // See https://github.com/janl/mustache.js/issues/186
- if (!value || (isArray(value) && value.length === 0)) {
- buffer += renderTokens(token[4], writer, context, template);
- }
-
- break;
- case '>':
- value = writer.getPartial(tokenValue);
- if (typeof value === 'function') buffer += value(context);
- break;
- case '&':
- value = context.lookup(tokenValue);
- if (value != null) buffer += value;
- break;
- case 'name':
- value = context.lookup(tokenValue);
- if (value != null) buffer += mustache.escape(value);
- break;
- case 'text':
- buffer += tokenValue;
- break;
- }
- }
-
- return buffer;
- }
-
- /**
- * Forms the given array of `tokens` into a nested tree structure where
- * tokens that represent a section have two additional items: 1) an array of
- * all tokens that appear in that section and 2) the index in the original
- * template that represents the end of that section.
- */
- function nestTokens(tokens) {
- var tree = [];
- var collector = tree;
- var sections = [];
-
- var token;
- for (var i = 0, len = tokens.length; i < len; ++i) {
- token = tokens[i];
- switch (token[0]) {
- case '#':
- case '^':
- sections.push(token);
- collector.push(token);
- collector = token[4] = [];
- break;
- case '/':
- var section = sections.pop();
- section[5] = token[2];
- collector = sections.length > 0 ? sections[sections.length - 1][4] : tree;
- break;
- default:
- collector.push(token);
- }
- }
-
- return tree;
- }
-
- /**
- * Combines the values of consecutive text tokens in the given `tokens` array
- * to a single token.
- */
- function squashTokens(tokens) {
- var squashedTokens = [];
-
- var token, lastToken;
- for (var i = 0, len = tokens.length; i < len; ++i) {
- token = tokens[i];
- if (token) {
- if (token[0] === 'text' && lastToken && lastToken[0] === 'text') {
- lastToken[1] += token[1];
- lastToken[3] = token[3];
- } else {
- lastToken = token;
- squashedTokens.push(token);
- }
- }
- }
-
- return squashedTokens;
- }
-
- function escapeTags(tags) {
- return [
- new RegExp(escapeRegExp(tags[0]) + "\\s*"),
- new RegExp("\\s*" + escapeRegExp(tags[1]))
- ];
- }
-
- /**
- * Breaks up the given `template` string into a tree of token objects. If
- * `tags` is given here it must be an array with two string values: the
- * opening and closing tags used in the template (e.g. ["<%", "%>"]). Of
- * course, the default is to use mustaches (i.e. Mustache.tags).
- */
- function parseTemplate(template, tags) {
- template = template || '';
- tags = tags || mustache.tags;
-
- if (typeof tags === 'string') tags = tags.split(spaceRe);
- if (tags.length !== 2) throw new Error('Invalid tags: ' + tags.join(', '));
-
- var tagRes = escapeTags(tags);
- var scanner = new Scanner(template);
-
- var sections = []; // Stack to hold section tokens
- var tokens = []; // Buffer to hold the tokens
- var spaces = []; // Indices of whitespace tokens on the current line
- var hasTag = false; // Is there a {{tag}} on the current line?
- var nonSpace = false; // Is there a non-space char on the current line?
-
- // Strips all whitespace tokens array for the current line
- // if there was a {{#tag}} on it and otherwise only space.
- function stripSpace() {
- if (hasTag && !nonSpace) {
- while (spaces.length) {
- delete tokens[spaces.pop()];
- }
- } else {
- spaces = [];
- }
-
- hasTag = false;
- nonSpace = false;
- }
-
- var start, type, value, chr, token;
- while (!scanner.eos()) {
- start = scanner.pos;
-
- // Match any text between tags.
- value = scanner.scanUntil(tagRes[0]);
- if (value) {
- for (var i = 0, len = value.length; i < len; ++i) {
- chr = value.charAt(i);
-
- if (isWhitespace(chr)) {
- spaces.push(tokens.length);
- } else {
- nonSpace = true;
- }
-
- tokens.push(['text', chr, start, start + 1]);
- start += 1;
-
- // Check for whitespace on the current line.
- if (chr == '\n') stripSpace();
- }
- }
-
- // Match the opening tag.
- if (!scanner.scan(tagRes[0])) break;
- hasTag = true;
-
- // Get the tag type.
- type = scanner.scan(tagRe) || 'name';
- scanner.scan(whiteRe);
-
- // Get the tag value.
- if (type === '=') {
- value = scanner.scanUntil(eqRe);
- scanner.scan(eqRe);
- scanner.scanUntil(tagRes[1]);
- } else if (type === '{') {
- value = scanner.scanUntil(new RegExp('\\s*' + escapeRegExp('}' + tags[1])));
- scanner.scan(curlyRe);
- scanner.scanUntil(tagRes[1]);
- type = '&';
- } else {
- value = scanner.scanUntil(tagRes[1]);
- }
-
- // Match the closing tag.
- if (!scanner.scan(tagRes[1])) throw new Error('Unclosed tag at ' + scanner.pos);
-
- token = [type, value, start, scanner.pos];
- tokens.push(token);
-
- if (type === '#' || type === '^') {
- sections.push(token);
- } else if (type === '/') {
- // Check section nesting.
- if (sections.length === 0) throw new Error('Unopened section "' + value + '" at ' + start);
- var openSection = sections.pop();
- if (openSection[1] !== value) throw new Error('Unclosed section "' + openSection[1] + '" at ' + start);
- } else if (type === 'name' || type === '{' || type === '&') {
- nonSpace = true;
- } else if (type === '=') {
- // Set the tags for the next time around.
- tags = value.split(spaceRe);
- if (tags.length !== 2) throw new Error('Invalid tags at ' + start + ': ' + tags.join(', '));
- tagRes = escapeTags(tags);
- }
- }
-
- // Make sure there are no open sections when we're done.
- var openSection = sections.pop();
- if (openSection) throw new Error('Unclosed section "' + openSection[1] + '" at ' + scanner.pos);
-
- tokens = squashTokens(tokens);
-
- return nestTokens(tokens);
- }
-
- mustache.name = "mustache.js";
- mustache.version = "0.7.2";
- mustache.tags = ["{{", "}}"];
-
- mustache.Scanner = Scanner;
- mustache.Context = Context;
- mustache.Writer = Writer;
-
- mustache.parse = parseTemplate;
-
- // Export the escaping function so that the user may override it.
- // See https://github.com/janl/mustache.js/issues/244
- mustache.escape = escapeHtml;
-
- // All Mustache.* functions use this writer.
- var defaultWriter = new Writer();
-
- /**
- * Clears all cached templates and partials in the default writer.
- */
- mustache.clearCache = function () {
- return defaultWriter.clearCache();
- };
-
- /**
- * Compiles the given `template` to a reusable function using the default
- * writer.
- */
- mustache.compile = function (template, tags) {
- return defaultWriter.compile(template, tags);
- };
-
- /**
- * Compiles the partial with the given `name` and `template` to a reusable
- * function using the default writer.
- */
- mustache.compilePartial = function (name, template, tags) {
- return defaultWriter.compilePartial(name, template, tags);
- };
-
- /**
- * Compiles the given array of tokens (the output of a parse) to a reusable
- * function using the default writer.
- */
- mustache.compileTokens = function (tokens, template) {
- return defaultWriter.compileTokens(tokens, template);
- };
-
- /**
- * Renders the `template` with the given `view` and `partials` using the
- * default writer.
- */
- mustache.render = function (template, view, partials) {
- return defaultWriter.render(template, view, partials);
- };
-
- // This is here for backwards compatibility with 0.4.x.
- mustache.to_html = function (template, view, partials, send) {
- var result = mustache.render(template, view, partials);
-
- if (typeof send === "function") {
- send(result);
- } else {
- return result;
- }
- };
-
-}));
diff --git a/js/vendor/sammy.handlebars.js b/js/vendor/sammy.handlebars.js
new file mode 100644
index 00000000..c94a2aa6
--- /dev/null
+++ b/js/vendor/sammy.handlebars.js
@@ -0,0 +1,133 @@
+(function (factory) {
+ if (typeof define === 'function' && define.amd) {
+ define(['jquery', 'sammy', 'handlebars'], factory);
+ } else {
+ (window.Sammy = window.Sammy || {}).Handlebars = factory(window.jQuery, window.Sammy);
+ }
+}(function ($, Sammy, Handlebars) {
+ // version 1.0.0 has no support for AMD but upwards does, this way we support both.
+ Handlebars = Handlebars || window.Handlebars;
+
+ // Sammy.Handlebars provides a quick way of using Handlebars templates in your app.
+ //
+ // Note: As of Sammy 0.7 Handlebars itself is not included in the source. Please download and
+ // include handlebars.js before Sammy.Handlebars.
+ //
+ // Handlebars.js is an extension to the Mustache templating language 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.
+ //
+ // By default using Sammy.Handlbars in your app adds the handlebars() method to the EventContext
+ // prototype. However, just like Sammy.Template you can change the default name of the method
+ // by passing a second argument (e.g. you could use the hbr() as the method alias so that all the template
+ // files could be in the form file.hbr instead of file.handlebars)
+ //
+ // ### Example #1
+ //
+ // The template (mytemplate.hb):
+ //
+ // {{title}}
+ //
+ // Hey, {{name}}! Welcome to Handlebars!
+ //
+ // The app:
+ //
+ // var app = $.sammy(function() {
+ // // include the plugin and alias handlebars() to hb()
+ // this.use('Handlebars', 'hb');
+ //
+ // this.get('#/hello/:name', function() {
+ // // set local vars
+ // this.title = 'Hello!'
+ // this.name = this.params.name;
+ // // render the template and pass it through handlebars
+ // this.partial('mytemplate.hb');
+ // });
+ // });
+ //
+ // $(function() {
+ // app.run()
+ // });
+ //
+ // If I go to #/hello/AQ in the browser, Sammy will render this to the body:
+ //
+ // Hello!
+ //
+ // Hey, AQ! Welcome to Handlebars!
+ //
+ //
+ // ### Example #2 - Handlebars partials
+ //
+ // The template (mytemplate.hb)
+ //
+ // Hey, {{name}}! {{>hello_friend}}
+ //
+ //
+ // The partial (mypartial.hb)
+ //
+ // Say hello to your friend {{friend}}!
+ //
+ // The app:
+ //
+ // var app = $.sammy(function() {
+ // // include the plugin and alias handlebars() to hb()
+ // this.use('Handlebars', 'hb');
+ //
+ // this.get('#/hello/:name/to/:friend', function(context) {
+ // // fetch handlebars-partial first
+ // this.load('mypartial.hb')
+ // .then(function(partial) {
+ // // set local vars
+ // context.partials = {hello_friend: partial};
+ // context.name = context.params.name;
+ // context.friend = context.params.friend;
+ //
+ // // render the template and pass it through handlebars
+ // context.partial('mytemplate.hb');
+ // });
+ // });
+ // });
+ //
+ // $(function() {
+ // app.run()
+ // });
+ //
+ // If I go to #/hello/AQ/to/dP in the browser, Sammy will render this to the body:
+ //
+ // Hey, AQ! Say hello to your friend dP!
+ //
+ // Note: You dont have to include the handlebars.js file on top of the plugin as the plugin
+ // includes the full source.
+ //
+ Sammy.Handlebars = function(app, method_alias) {
+ var handlebars_cache = {};
+ // *Helper* Uses handlebars.js to parse a template and interpolate and work with the passed data
+ //
+ // ### Arguments
+ //
+ // * `template` A String template.
+ // * `data` An Object containing the replacement values for the template.
+ // data is extended with the EventContext allowing you to call its methods within the template.
+ //
+ var handlebars = function(template, data, partials, name) {
+ // use name for caching
+ if (typeof name == 'undefined') { name = template; }
+ var fn = handlebars_cache[name];
+ if (!fn) {
+ fn = handlebars_cache[name] = Handlebars.compile(template);
+ }
+
+ data = $.extend({}, this, data);
+ partials = $.extend({}, data.partials, partials);
+
+ return fn(data, {"partials":partials});
+ };
+
+ // set the default method name/extension
+ if (!method_alias) { method_alias = 'handlebars'; }
+ app.helper(method_alias, handlebars);
+ };
+
+ return Sammy.Handlebars;
+
+}));
\ No newline at end of file
diff --git a/js/vendor/sammy.mustache.js b/js/vendor/sammy.mustache.js
deleted file mode 100644
index 1f5ff091..00000000
--- a/js/vendor/sammy.mustache.js
+++ /dev/null
@@ -1,126 +0,0 @@
-(function (factory) {
- if (typeof define === 'function' && define.amd) {
- define(['jquery', 'sammy', 'mustache'], factory);
- } else {
- (window.Sammy = window.Sammy || {}).Mustache = factory(window.jQuery, window.Sammy, window.Mustache);
- }
-}(function ($, Sammy, Mustache) {
-
- // Sammy.Mustache provides a quick way of using mustache style templates in your app.
- // The plugin wraps the awesome mustache.js lib created and maintained by Jan Lehnardt
- // at http://github.com/janl/mustache.js
- //
- // Note: As of Sammy 0.7 the Mustache lib is not included in the templates source. Please download
- // mustache.js and include it before Sammy.Mustache.
- //
- // Mustache is a clever templating system that relys on double brackets {{}} for interpolation.
- // For full details on syntax check out the original Ruby implementation created by Chris Wanstrath at
- // http://github.com/defunkt/mustache
- //
- // By default using Sammy.Mustache in your app adds the mustache() method to the EventContext
- // prototype. However, just like Sammy.Template you can change the default name of the method
- // by passing a second argument (e.g. you could use the ms() as the method alias so that all the template
- // files could be in the form file.ms instead of file.mustache)
- //
- // ### Example #1
- //
- // The template (mytemplate.ms):
- //
- // {{title}}
- //
- // Hey, {{name}}! Welcome to Mustache!
- //
- // The app:
- //
- // var app = $.sammy(function() {
- // // include the plugin and alias mustache() to ms()
- // this.use('Mustache', 'ms');
- //
- // this.get('#/hello/:name', function() {
- // // set local vars
- // this.title = 'Hello!'
- // this.name = this.params.name;
- // // render the template and pass it through mustache
- // this.partial('mytemplate.ms');
- // });
- // });
- //
- // $(function() {
- // app.run()
- // });
- //
- // If I go to #/hello/AQ in the browser, Sammy will render this to the body:
- //
- // Hello!
- //
- // Hey, AQ! Welcome to Mustache!
- //
- //
- // ### Example #2 - Mustache partials
- //
- // The template (mytemplate.ms)
- //
- // Hey, {{name}}! {{>hello_friend}}
- //
- //
- // The partial (mypartial.ms)
- //
- // Say hello to your friend {{friend}}!
- //
- // The app:
- //
- // var app = $.sammy(function() {
- // // include the plugin and alias mustache() to ms()
- // this.use('Mustache', 'ms');
- //
- // this.get('#/hello/:name/to/:friend', function(context) {
- // // fetch mustache-partial first
- // this.load('mypartial.ms')
- // .then(function(partial) {
- // // set local vars
- // context.partials = {hello_friend: partial};
- // context.name = context.params.name;
- // context.friend = context.params.friend;
- //
- // // render the template and pass it through mustache
- // context.partial('mytemplate.ms');
- // });
- // });
- // });
- //
- // $(function() {
- // app.run()
- // });
- //
- // If I go to #/hello/AQ/to/dP in the browser, Sammy will render this to the body:
- //
- // Hey, AQ! Say hello to your friend dP!
- //
- // Note: You need to include the mustache.js file before this plugin.
- //
- Sammy.Mustache = function(app, method_alias) {
-
- // *Helper* Uses Mustache.js to parse a template and interpolate and work with the passed data
- //
- // ### Arguments
- //
- // * `template` A String template. {{}} Tags are evaluated and interpolated by Mustache.js
- // * `data` An Object containing the replacement values for the template.
- // data is extended with the EventContext allowing you to call its methods within the template.
- // * `partials` An Object containing one or more partials (String templates
- // that are called from the main template).
- //
- var mustache = function(template, data, partials) {
- data = $.extend({}, this, data);
- partials = $.extend({}, data.partials, partials);
- return Mustache.to_html(template, data, partials);
- };
-
- // set the default method name/extension
- if (!method_alias) { method_alias = 'mustache'; }
- app.helper(method_alias, mustache);
- };
-
- return Sammy.Mustache;
-
-}));