3 Copyright (C) 2011 by Yehuda Katz
5 Permission is hereby granted, free of charge, to any person obtaining a copy
6 of this software and associated documentation files (the "Software"), to deal
7 in the Software without restriction, including without limitation the rights
8 to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 copies of the Software, and to permit persons to whom the Software is
10 furnished to do so, subject to the following conditions:
12 The above copyright notice and this permission notice shall be included in
13 all copies or substantial portions of the Software.
15 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
25 // lib/handlebars/browser-prefix.js
28 (function(Handlebars, undefined) {
30 // lib/handlebars/base.js
32 Handlebars.VERSION = "1.0.0";
33 Handlebars.COMPILER_REVISION = 4;
35 Handlebars.REVISION_CHANGES = {
36 1: '<= 1.0.rc.2', // 1.0.rc.2 is actually rev2 but doesn't report it
42 Handlebars.helpers = {};
43 Handlebars.partials = {};
45 var toString = Object.prototype.toString,
46 functionType = '[object Function]',
47 objectType = '[object Object]';
49 Handlebars.registerHelper = function(name, fn, inverse) {
50 if (toString.call(name) === objectType) {
51 if (inverse || fn) { throw new Handlebars.Exception('Arg not supported with multiple helpers'); }
52 Handlebars.Utils.extend(this.helpers, name);
54 if (inverse) { fn.not = inverse; }
55 this.helpers[name] = fn;
59 Handlebars.registerPartial = function(name, str) {
60 if (toString.call(name) === objectType) {
61 Handlebars.Utils.extend(this.partials, name);
63 this.partials[name] = str;
67 Handlebars.registerHelper('helperMissing', function(arg) {
68 if(arguments.length === 2) {
71 throw new Error("Missing helper: '" + arg + "'");
75 Handlebars.registerHelper('blockHelperMissing', function(context, options) {
76 var inverse = options.inverse || function() {}, fn = options.fn;
78 var type = toString.call(context);
80 if(type === functionType) { context = context.call(this); }
82 if(context === true) {
84 } else if(context === false || context == null) {
86 } else if(type === "[object Array]") {
87 if(context.length > 0) {
88 return Handlebars.helpers.each(context, options);
97 Handlebars.K = function() {};
99 Handlebars.createFrame = Object.create || function(object) {
100 Handlebars.K.prototype = object;
101 var obj = new Handlebars.K();
102 Handlebars.K.prototype = null;
106 Handlebars.logger = {
107 DEBUG: 0, INFO: 1, WARN: 2, ERROR: 3, level: 3,
109 methodMap: {0: 'debug', 1: 'info', 2: 'warn', 3: 'error'},
111 // can be overridden in the host environment
112 log: function(level, obj) {
113 if (Handlebars.logger.level <= level) {
114 var method = Handlebars.logger.methodMap[level];
115 if (typeof console !== 'undefined' && console[method]) {
116 console[method].call(console, obj);
122 Handlebars.log = function(level, obj) { Handlebars.logger.log(level, obj); };
124 Handlebars.registerHelper('each', function(context, options) {
125 var fn = options.fn, inverse = options.inverse;
126 var i = 0, ret = "", data;
128 var type = toString.call(context);
129 if(type === functionType) { context = context.call(this); }
132 data = Handlebars.createFrame(options.data);
135 if(context && typeof context === 'object') {
136 if(context instanceof Array){
137 for(var j = context.length; i<j; i++) {
138 if (data) { data.index = i; }
139 ret = ret + fn(context[i], { data: data });
142 for(var key in context) {
143 if(context.hasOwnProperty(key)) {
144 if(data) { data.key = key; }
145 ret = ret + fn(context[key], {data: data});
159 Handlebars.registerHelper('if', function(conditional, options) {
160 var type = toString.call(conditional);
161 if(type === functionType) { conditional = conditional.call(this); }
163 if(!conditional || Handlebars.Utils.isEmpty(conditional)) {
164 return options.inverse(this);
166 return options.fn(this);
170 Handlebars.registerHelper('unless', function(conditional, options) {
171 return Handlebars.helpers['if'].call(this, conditional, {fn: options.inverse, inverse: options.fn});
174 Handlebars.registerHelper('with', function(context, options) {
175 var type = toString.call(context);
176 if(type === functionType) { context = context.call(this); }
178 if (!Handlebars.Utils.isEmpty(context)) return options.fn(context);
181 Handlebars.registerHelper('log', function(context, options) {
182 var level = options.data && options.data.level != null ? parseInt(options.data.level, 10) : 1;
183 Handlebars.log(level, context);
186 // lib/handlebars/compiler/parser.js
187 /* Jison generated parser */
188 var handlebars = (function(){
189 var parser = {trace: function trace() { },
191 symbols_: {"error":2,"root":3,"program":4,"EOF":5,"simpleInverse":6,"statements":7,"statement":8,"openInverse":9,"closeBlock":10,"openBlock":11,"mustache":12,"partial":13,"CONTENT":14,"COMMENT":15,"OPEN_BLOCK":16,"inMustache":17,"CLOSE":18,"OPEN_INVERSE":19,"OPEN_ENDBLOCK":20,"path":21,"OPEN":22,"OPEN_UNESCAPED":23,"CLOSE_UNESCAPED":24,"OPEN_PARTIAL":25,"partialName":26,"params":27,"hash":28,"dataName":29,"param":30,"STRING":31,"INTEGER":32,"BOOLEAN":33,"hashSegments":34,"hashSegment":35,"ID":36,"EQUALS":37,"DATA":38,"pathSegments":39,"SEP":40,"$accept":0,"$end":1},
192 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",31:"STRING",32:"INTEGER",33:"BOOLEAN",36:"ID",37:"EQUALS",38:"DATA",40:"SEP"},
193 productions_: [0,[3,2],[4,2],[4,3],[4,2],[4,1],[4,1],[4,0],[7,1],[7,2],[8,3],[8,3],[8,1],[8,1],[8,1],[8,1],[11,3],[9,3],[10,3],[12,3],[12,3],[13,3],[13,4],[6,2],[17,3],[17,2],[17,2],[17,1],[17,1],[27,2],[27,1],[30,1],[30,1],[30,1],[30,1],[30,1],[28,1],[34,2],[34,1],[35,3],[35,3],[35,3],[35,3],[35,3],[26,1],[26,1],[26,1],[29,2],[21,1],[39,3],[39,1]],
194 performAction: function anonymous(yytext,yyleng,yylineno,yy,yystate,$$,_$) {
196 var $0 = $$.length - 1;
198 case 1: return $$[$0-1];
200 case 2: this.$ = new yy.ProgramNode([], $$[$0]);
202 case 3: this.$ = new yy.ProgramNode($$[$0-2], $$[$0]);
204 case 4: this.$ = new yy.ProgramNode($$[$0-1], []);
206 case 5: this.$ = new yy.ProgramNode($$[$0]);
208 case 6: this.$ = new yy.ProgramNode([], []);
210 case 7: this.$ = new yy.ProgramNode([]);
212 case 8: this.$ = [$$[$0]];
214 case 9: $$[$0-1].push($$[$0]); this.$ = $$[$0-1];
216 case 10: this.$ = new yy.BlockNode($$[$0-2], $$[$0-1].inverse, $$[$0-1], $$[$0]);
218 case 11: this.$ = new yy.BlockNode($$[$0-2], $$[$0-1], $$[$0-1].inverse, $$[$0]);
220 case 12: this.$ = $$[$0];
222 case 13: this.$ = $$[$0];
224 case 14: this.$ = new yy.ContentNode($$[$0]);
226 case 15: this.$ = new yy.CommentNode($$[$0]);
228 case 16: this.$ = new yy.MustacheNode($$[$0-1][0], $$[$0-1][1]);
230 case 17: this.$ = new yy.MustacheNode($$[$0-1][0], $$[$0-1][1]);
232 case 18: this.$ = $$[$0-1];
235 // Parsing out the '&' escape token at this level saves ~500 bytes after min due to the removal of one parser node.
236 this.$ = new yy.MustacheNode($$[$0-1][0], $$[$0-1][1], $$[$0-2][2] === '&');
239 case 20: this.$ = new yy.MustacheNode($$[$0-1][0], $$[$0-1][1], true);
241 case 21: this.$ = new yy.PartialNode($$[$0-1]);
243 case 22: this.$ = new yy.PartialNode($$[$0-2], $$[$0-1]);
247 case 24: this.$ = [[$$[$0-2]].concat($$[$0-1]), $$[$0]];
249 case 25: this.$ = [[$$[$0-1]].concat($$[$0]), null];
251 case 26: this.$ = [[$$[$0-1]], $$[$0]];
253 case 27: this.$ = [[$$[$0]], null];
255 case 28: this.$ = [[$$[$0]], null];
257 case 29: $$[$0-1].push($$[$0]); this.$ = $$[$0-1];
259 case 30: this.$ = [$$[$0]];
261 case 31: this.$ = $$[$0];
263 case 32: this.$ = new yy.StringNode($$[$0]);
265 case 33: this.$ = new yy.IntegerNode($$[$0]);
267 case 34: this.$ = new yy.BooleanNode($$[$0]);
269 case 35: this.$ = $$[$0];
271 case 36: this.$ = new yy.HashNode($$[$0]);
273 case 37: $$[$0-1].push($$[$0]); this.$ = $$[$0-1];
275 case 38: this.$ = [$$[$0]];
277 case 39: this.$ = [$$[$0-2], $$[$0]];
279 case 40: this.$ = [$$[$0-2], new yy.StringNode($$[$0])];
281 case 41: this.$ = [$$[$0-2], new yy.IntegerNode($$[$0])];
283 case 42: this.$ = [$$[$0-2], new yy.BooleanNode($$[$0])];
285 case 43: this.$ = [$$[$0-2], $$[$0]];
287 case 44: this.$ = new yy.PartialNameNode($$[$0]);
289 case 45: this.$ = new yy.PartialNameNode(new yy.StringNode($$[$0]));
291 case 46: this.$ = new yy.PartialNameNode(new yy.IntegerNode($$[$0]));
293 case 47: this.$ = new yy.DataNode($$[$0]);
295 case 48: this.$ = new yy.IdNode($$[$0]);
297 case 49: $$[$0-2].push({part: $$[$0], separator: $$[$0-1]}); this.$ = $$[$0-2];
299 case 50: this.$ = [{part: $$[$0]}];
303 table: [{3:1,4:2,5:[2,7],6:3,7:4,8:6,9:7,11:8,12:9,13:10,14:[1,11],15:[1,12],16:[1,13],19:[1,5],22:[1,14],23:[1,15],25:[1,16]},{1:[3]},{5:[1,17]},{5:[2,6],7:18,8:6,9:7,11:8,12:9,13:10,14:[1,11],15:[1,12],16:[1,13],19:[1,19],20:[2,6],22:[1,14],23:[1,15],25:[1,16]},{5:[2,5],6:20,8:21,9:7,11:8,12:9,13:10,14:[1,11],15:[1,12],16:[1,13],19:[1,5],20:[2,5],22:[1,14],23:[1,15],25:[1,16]},{17:23,18:[1,22],21:24,29:25,36:[1,28],38:[1,27],39:26},{5:[2,8],14:[2,8],15:[2,8],16:[2,8],19:[2,8],20:[2,8],22:[2,8],23:[2,8],25:[2,8]},{4:29,6:3,7:4,8:6,9:7,11:8,12:9,13:10,14:[1,11],15:[1,12],16:[1,13],19:[1,5],20:[2,7],22:[1,14],23:[1,15],25:[1,16]},{4:30,6:3,7:4,8:6,9:7,11:8,12:9,13:10,14:[1,11],15:[1,12],16:[1,13],19:[1,5],20:[2,7],22:[1,14],23:[1,15],25:[1,16]},{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]},{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]},{17:31,21:24,29:25,36:[1,28],38:[1,27],39:26},{17:32,21:24,29:25,36:[1,28],38:[1,27],39:26},{17:33,21:24,29:25,36:[1,28],38:[1,27],39:26},{21:35,26:34,31:[1,36],32:[1,37],36:[1,28],39:26},{1:[2,1]},{5:[2,2],8:21,9:7,11:8,12:9,13:10,14:[1,11],15:[1,12],16:[1,13],19:[1,19],20:[2,2],22:[1,14],23:[1,15],25:[1,16]},{17:23,21:24,29:25,36:[1,28],38:[1,27],39:26},{5:[2,4],7:38,8:6,9:7,11:8,12:9,13:10,14:[1,11],15:[1,12],16:[1,13],19:[1,19],20:[2,4],22:[1,14],23:[1,15],25:[1,16]},{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]},{5:[2,23],14:[2,23],15:[2,23],16:[2,23],19:[2,23],20:[2,23],22:[2,23],23:[2,23],25:[2,23]},{18:[1,39]},{18:[2,27],21:44,24:[2,27],27:40,28:41,29:48,30:42,31:[1,45],32:[1,46],33:[1,47],34:43,35:49,36:[1,50],38:[1,27],39:26},{18:[2,28],24:[2,28]},{18:[2,48],24:[2,48],31:[2,48],32:[2,48],33:[2,48],36:[2,48],38:[2,48],40:[1,51]},{21:52,36:[1,28],39:26},{18:[2,50],24:[2,50],31:[2,50],32:[2,50],33:[2,50],36:[2,50],38:[2,50],40:[2,50]},{10:53,20:[1,54]},{10:55,20:[1,54]},{18:[1,56]},{18:[1,57]},{24:[1,58]},{18:[1,59],21:60,36:[1,28],39:26},{18:[2,44],36:[2,44]},{18:[2,45],36:[2,45]},{18:[2,46],36:[2,46]},{5:[2,3],8:21,9:7,11:8,12:9,13:10,14:[1,11],15:[1,12],16:[1,13],19:[1,19],20:[2,3],22:[1,14],23:[1,15],25:[1,16]},{14:[2,17],15:[2,17],16:[2,17],19:[2,17],20:[2,17],22:[2,17],23:[2,17],25:[2,17]},{18:[2,25],21:44,24:[2,25],28:61,29:48,30:62,31:[1,45],32:[1,46],33:[1,47],34:43,35:49,36:[1,50],38:[1,27],39:26},{18:[2,26],24:[2,26]},{18:[2,30],24:[2,30],31:[2,30],32:[2,30],33:[2,30],36:[2,30],38:[2,30]},{18:[2,36],24:[2,36],35:63,36:[1,64]},{18:[2,31],24:[2,31],31:[2,31],32:[2,31],33:[2,31],36:[2,31],38:[2,31]},{18:[2,32],24:[2,32],31:[2,32],32:[2,32],33:[2,32],36:[2,32],38:[2,32]},{18:[2,33],24:[2,33],31:[2,33],32:[2,33],33:[2,33],36:[2,33],38:[2,33]},{18:[2,34],24:[2,34],31:[2,34],32:[2,34],33:[2,34],36:[2,34],38:[2,34]},{18:[2,35],24:[2,35],31:[2,35],32:[2,35],33:[2,35],36:[2,35],38:[2,35]},{18:[2,38],24:[2,38],36:[2,38]},{18:[2,50],24:[2,50],31:[2,50],32:[2,50],33:[2,50],36:[2,50],37:[1,65],38:[2,50],40:[2,50]},{36:[1,66]},{18:[2,47],24:[2,47],31:[2,47],32:[2,47],33:[2,47],36:[2,47],38:[2,47]},{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]},{21:67,36:[1,28],39:26},{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]},{14:[2,16],15:[2,16],16:[2,16],19:[2,16],20:[2,16],22:[2,16],23:[2,16],25:[2,16]},{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]},{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,68]},{18:[2,24],24:[2,24]},{18:[2,29],24:[2,29],31:[2,29],32:[2,29],33:[2,29],36:[2,29],38:[2,29]},{18:[2,37],24:[2,37],36:[2,37]},{37:[1,65]},{21:69,29:73,31:[1,70],32:[1,71],33:[1,72],36:[1,28],38:[1,27],39:26},{18:[2,49],24:[2,49],31:[2,49],32:[2,49],33:[2,49],36:[2,49],38:[2,49],40:[2,49]},{18:[1,74]},{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]},{18:[2,39],24:[2,39],36:[2,39]},{18:[2,40],24:[2,40],36:[2,40]},{18:[2,41],24:[2,41],36:[2,41]},{18:[2,42],24:[2,42],36:[2,42]},{18:[2,43],24:[2,43],36:[2,43]},{5:[2,18],14:[2,18],15:[2,18],16:[2,18],19:[2,18],20:[2,18],22:[2,18],23:[2,18],25:[2,18]}],
304 defaultActions: {17:[2,1]},
305 parseError: function parseError(str, hash) {
306 throw new Error(str);
308 parse: function parse(input) {
309 var self = this, stack = [0], vstack = [null], lstack = [], table = this.table, yytext = "", yylineno = 0, yyleng = 0, recovering = 0, TERROR = 2, EOF = 1;
310 this.lexer.setInput(input);
311 this.lexer.yy = this.yy;
312 this.yy.lexer = this.lexer;
313 this.yy.parser = this;
314 if (typeof this.lexer.yylloc == "undefined")
315 this.lexer.yylloc = {};
316 var yyloc = this.lexer.yylloc;
318 var ranges = this.lexer.options && this.lexer.options.ranges;
319 if (typeof this.yy.parseError === "function")
320 this.parseError = this.yy.parseError;
321 function popStack(n) {
322 stack.length = stack.length - 2 * n;
323 vstack.length = vstack.length - n;
324 lstack.length = lstack.length - n;
328 token = self.lexer.lex() || 1;
329 if (typeof token !== "number") {
330 token = self.symbols_[token] || token;
334 var symbol, preErrorSymbol, state, action, a, r, yyval = {}, p, len, newState, expected;
336 state = stack[stack.length - 1];
337 if (this.defaultActions[state]) {
338 action = this.defaultActions[state];
340 if (symbol === null || typeof symbol == "undefined") {
343 action = table[state] && table[state][symbol];
345 if (typeof action === "undefined" || !action.length || !action[0]) {
349 for (p in table[state])
350 if (this.terminals_[p] && p > 2) {
351 expected.push("'" + this.terminals_[p] + "'");
353 if (this.lexer.showPosition) {
354 errStr = "Parse error on line " + (yylineno + 1) + ":\n" + this.lexer.showPosition() + "\nExpecting " + expected.join(", ") + ", got '" + (this.terminals_[symbol] || symbol) + "'";
356 errStr = "Parse error on line " + (yylineno + 1) + ": Unexpected " + (symbol == 1?"end of input":"'" + (this.terminals_[symbol] || symbol) + "'");
358 this.parseError(errStr, {text: this.lexer.match, token: this.terminals_[symbol] || symbol, line: this.lexer.yylineno, loc: yyloc, expected: expected});
361 if (action[0] instanceof Array && action.length > 1) {
362 throw new Error("Parse Error: multiple actions possible at state: " + state + ", token: " + symbol);
367 vstack.push(this.lexer.yytext);
368 lstack.push(this.lexer.yylloc);
369 stack.push(action[1]);
371 if (!preErrorSymbol) {
372 yyleng = this.lexer.yyleng;
373 yytext = this.lexer.yytext;
374 yylineno = this.lexer.yylineno;
375 yyloc = this.lexer.yylloc;
379 symbol = preErrorSymbol;
380 preErrorSymbol = null;
384 len = this.productions_[action[1]][1];
385 yyval.$ = vstack[vstack.length - len];
386 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};
388 yyval._$.range = [lstack[lstack.length - (len || 1)].range[0], lstack[lstack.length - 1].range[1]];
390 r = this.performAction.call(yyval, yytext, yyleng, yylineno, this.yy, action[1], vstack, lstack);
391 if (typeof r !== "undefined") {
395 stack = stack.slice(0, -1 * len * 2);
396 vstack = vstack.slice(0, -1 * len);
397 lstack = lstack.slice(0, -1 * len);
399 stack.push(this.productions_[action[1]][0]);
400 vstack.push(yyval.$);
401 lstack.push(yyval._$);
402 newState = table[stack[stack.length - 2]][stack[stack.length - 1]];
403 stack.push(newState);
412 /* Jison generated lexer */
413 var lexer = (function(){
415 parseError:function parseError(str, hash) {
416 if (this.yy.parser) {
417 this.yy.parser.parseError(str, hash);
419 throw new Error(str);
422 setInput:function (input) {
424 this._more = this._less = this.done = false;
425 this.yylineno = this.yyleng = 0;
426 this.yytext = this.matched = this.match = '';
427 this.conditionStack = ['INITIAL'];
428 this.yylloc = {first_line:1,first_column:0,last_line:1,last_column:0};
429 if (this.options.ranges) this.yylloc.range = [0,0];
434 var ch = this._input[0];
440 var lines = ch.match(/(?:\r\n?|\n).*/g);
443 this.yylloc.last_line++;
445 this.yylloc.last_column++;
447 if (this.options.ranges) this.yylloc.range[1]++;
449 this._input = this._input.slice(1);
452 unput:function (ch) {
454 var lines = ch.split(/(?:\r\n?|\n)/g);
456 this._input = ch + this._input;
457 this.yytext = this.yytext.substr(0, this.yytext.length-len-1);
458 //this.yyleng -= len;
460 var oldLines = this.match.split(/(?:\r\n?|\n)/g);
461 this.match = this.match.substr(0, this.match.length-1);
462 this.matched = this.matched.substr(0, this.matched.length-1);
464 if (lines.length-1) this.yylineno -= lines.length-1;
465 var r = this.yylloc.range;
467 this.yylloc = {first_line: this.yylloc.first_line,
468 last_line: this.yylineno+1,
469 first_column: this.yylloc.first_column,
471 (lines.length === oldLines.length ? this.yylloc.first_column : 0) + oldLines[oldLines.length - lines.length].length - lines[0].length:
472 this.yylloc.first_column - len
475 if (this.options.ranges) {
476 this.yylloc.range = [r[0], r[0] + this.yyleng - len];
485 this.unput(this.match.slice(n));
487 pastInput:function () {
488 var past = this.matched.substr(0, this.matched.length - this.match.length);
489 return (past.length > 20 ? '...':'') + past.substr(-20).replace(/\n/g, "");
491 upcomingInput:function () {
492 var next = this.match;
493 if (next.length < 20) {
494 next += this._input.substr(0, 20-next.length);
496 return (next.substr(0,20)+(next.length > 20 ? '...':'')).replace(/\n/g, "");
498 showPosition:function () {
499 var pre = this.pastInput();
500 var c = new Array(pre.length + 1).join("-");
501 return pre + this.upcomingInput() + "\n" + c+"^";
507 if (!this._input) this.done = true;
519 var rules = this._currentRules();
520 for (var i=0;i < rules.length; i++) {
521 tempMatch = this._input.match(this.rules[rules[i]]);
522 if (tempMatch && (!match || tempMatch[0].length > match[0].length)) {
525 if (!this.options.flex) break;
529 lines = match[0].match(/(?:\r\n?|\n).*/g);
530 if (lines) this.yylineno += lines.length;
531 this.yylloc = {first_line: this.yylloc.last_line,
532 last_line: this.yylineno+1,
533 first_column: this.yylloc.last_column,
534 last_column: lines ? lines[lines.length-1].length-lines[lines.length-1].match(/\r?\n?/)[0].length : this.yylloc.last_column + match[0].length};
535 this.yytext += match[0];
536 this.match += match[0];
537 this.matches = match;
538 this.yyleng = this.yytext.length;
539 if (this.options.ranges) {
540 this.yylloc.range = [this.offset, this.offset += this.yyleng];
543 this._input = this._input.slice(match[0].length);
544 this.matched += match[0];
545 token = this.performAction.call(this, this.yy, this, rules[index],this.conditionStack[this.conditionStack.length-1]);
546 if (this.done && this._input) this.done = false;
547 if (token) return token;
550 if (this._input === "") {
553 return this.parseError('Lexical error on line '+(this.yylineno+1)+'. Unrecognized text.\n'+this.showPosition(),
554 {text: "", token: null, line: this.yylineno});
559 if (typeof r !== 'undefined') {
565 begin:function begin(condition) {
566 this.conditionStack.push(condition);
568 popState:function popState() {
569 return this.conditionStack.pop();
571 _currentRules:function _currentRules() {
572 return this.conditions[this.conditionStack[this.conditionStack.length-1]].rules;
574 topState:function () {
575 return this.conditionStack[this.conditionStack.length-2];
577 pushState:function begin(condition) {
578 this.begin(condition);
581 lexer.performAction = function anonymous(yy,yy_,$avoiding_name_collisions,YY_START) {
584 switch($avoiding_name_collisions) {
585 case 0: yy_.yytext = "\\"; return 14;
588 if(yy_.yytext.slice(-1) !== "\\") this.begin("mu");
589 if(yy_.yytext.slice(-1) === "\\") yy_.yytext = yy_.yytext.substr(0,yy_.yyleng-1), this.begin("emu");
590 if(yy_.yytext) return 14;
596 if(yy_.yytext.slice(-1) !== "\\") this.popState();
597 if(yy_.yytext.slice(-1) === "\\") yy_.yytext = yy_.yytext.substr(0,yy_.yyleng-1);
601 case 4: yy_.yytext = yy_.yytext.substr(0, yy_.yyleng-4); this.popState(); return 15;
617 case 12: this.popState(); this.begin('com');
619 case 13: yy_.yytext = yy_.yytext.substr(3,yy_.yyleng-5); this.popState(); return 15;
631 case 19: /*ignore whitespace*/
633 case 20: this.popState(); return 24;
635 case 21: this.popState(); return 18;
637 case 22: yy_.yytext = yy_.yytext.substr(1,yy_.yyleng-2).replace(/\\"/g,'"'); return 31;
639 case 23: yy_.yytext = yy_.yytext.substr(1,yy_.yyleng-2).replace(/\\'/g,"'"); return 31;
651 case 29: yy_.yytext = yy_.yytext.substr(1, yy_.yyleng-2); return 36;
653 case 30: return 'INVALID';
659 lexer.rules = [/^(?:\\\\(?=(\{\{)))/,/^(?:[^\x00]*?(?=(\{\{)))/,/^(?:[^\x00]+)/,/^(?:[^\x00]{2,}?(?=(\{\{|$)))/,/^(?:[\s\S]*?--\}\})/,/^(?:\{\{>)/,/^(?:\{\{#)/,/^(?:\{\{\/)/,/^(?:\{\{\^)/,/^(?:\{\{\s*else\b)/,/^(?:\{\{\{)/,/^(?:\{\{&)/,/^(?:\{\{!--)/,/^(?:\{\{![\s\S]*?\}\})/,/^(?:\{\{)/,/^(?:=)/,/^(?:\.(?=[}\/ ]))/,/^(?:\.\.)/,/^(?:[\/.])/,/^(?:\s+)/,/^(?:\}\}\})/,/^(?:\}\})/,/^(?:"(\\["]|[^"])*")/,/^(?:'(\\[']|[^'])*')/,/^(?:@)/,/^(?:true(?=[}\s]))/,/^(?:false(?=[}\s]))/,/^(?:-?[0-9]+(?=[}\s]))/,/^(?:[^\s!"#%-,\.\/;->@\[-\^`\{-~]+(?=[=}\s\/.]))/,/^(?:\[[^\]]*\])/,/^(?:.)/,/^(?:$)/];
660 lexer.conditions = {"mu":{"rules":[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],"inclusive":false},"emu":{"rules":[3],"inclusive":false},"com":{"rules":[4],"inclusive":false},"INITIAL":{"rules":[0,1,2,31],"inclusive":true}};
662 parser.lexer = lexer;
663 function Parser () { this.yy = {}; }Parser.prototype = parser;parser.Parser = Parser;
666 // lib/handlebars/compiler/base.js
668 Handlebars.Parser = handlebars;
670 Handlebars.parse = function(input) {
672 // Just return if an already-compile AST was passed in.
673 if(input.constructor === Handlebars.AST.ProgramNode) { return input; }
675 Handlebars.Parser.yy = Handlebars.AST;
676 return Handlebars.Parser.parse(input);
679 // lib/handlebars/compiler/ast.js
682 Handlebars.AST.ProgramNode = function(statements, inverse) {
683 this.type = "program";
684 this.statements = statements;
685 if(inverse) { this.inverse = new Handlebars.AST.ProgramNode(inverse); }
688 Handlebars.AST.MustacheNode = function(rawParams, hash, unescaped) {
689 this.type = "mustache";
690 this.escaped = !unescaped;
693 var id = this.id = rawParams[0];
694 var params = this.params = rawParams.slice(1);
696 // a mustache is an eligible helper if:
697 // * its id is simple (a single part, not `this` or `..`)
698 var eligibleHelper = this.eligibleHelper = id.isSimple;
700 // a mustache is definitely a helper if:
701 // * it is an eligible helper, and
702 // * it has at least one parameter or hash segment
703 this.isHelper = eligibleHelper && (params.length || hash);
705 // if a mustache is an eligible helper but not a definite
706 // helper, it is ambiguous, and will be resolved in a later
707 // pass or at runtime.
710 Handlebars.AST.PartialNode = function(partialName, context) {
711 this.type = "partial";
712 this.partialName = partialName;
713 this.context = context;
716 Handlebars.AST.BlockNode = function(mustache, program, inverse, close) {
717 var verifyMatch = function(open, close) {
718 if(open.original !== close.original) {
719 throw new Handlebars.Exception(open.original + " doesn't match " + close.original);
723 verifyMatch(mustache.id, close);
725 this.mustache = mustache;
726 this.program = program;
727 this.inverse = inverse;
729 if (this.inverse && !this.program) {
730 this.isInverse = true;
734 Handlebars.AST.ContentNode = function(string) {
735 this.type = "content";
736 this.string = string;
739 Handlebars.AST.HashNode = function(pairs) {
744 Handlebars.AST.IdNode = function(parts) {
751 for(var i=0,l=parts.length; i<l; i++) {
752 var part = parts[i].part;
753 original += (parts[i].separator || '') + part;
755 if (part === ".." || part === "." || part === "this") {
756 if (dig.length > 0) { throw new Handlebars.Exception("Invalid path: " + original); }
757 else if (part === "..") { depth++; }
758 else { this.isScoped = true; }
760 else { dig.push(part); }
763 this.original = original;
765 this.string = dig.join('.');
768 // an ID is simple if it only has one part, and that part is not
770 this.isSimple = parts.length === 1 && !this.isScoped && depth === 0;
772 this.stringModeValue = this.string;
775 Handlebars.AST.PartialNameNode = function(name) {
776 this.type = "PARTIAL_NAME";
777 this.name = name.original;
780 Handlebars.AST.DataNode = function(id) {
785 Handlebars.AST.StringNode = function(string) {
786 this.type = "STRING";
789 this.stringModeValue = string;
792 Handlebars.AST.IntegerNode = function(integer) {
793 this.type = "INTEGER";
795 this.integer = integer;
796 this.stringModeValue = Number(integer);
799 Handlebars.AST.BooleanNode = function(bool) {
800 this.type = "BOOLEAN";
802 this.stringModeValue = bool === "true";
805 Handlebars.AST.CommentNode = function(comment) {
806 this.type = "comment";
807 this.comment = comment;
810 // lib/handlebars/utils.js
812 var errorProps = ['description', 'fileName', 'lineNumber', 'message', 'name', 'number', 'stack'];
814 Handlebars.Exception = function(message) {
815 var tmp = Error.prototype.constructor.apply(this, arguments);
817 // Unfortunately errors are not enumerable in Chrome (at least), so `for prop in tmp` doesn't work.
818 for (var idx = 0; idx < errorProps.length; idx++) {
819 this[errorProps[idx]] = tmp[errorProps[idx]];
822 Handlebars.Exception.prototype = new Error();
824 // Build out our basic SafeString type
825 Handlebars.SafeString = function(string) {
826 this.string = string;
828 Handlebars.SafeString.prototype.toString = function() {
829 return this.string.toString();
841 var badChars = /[&<>"'`]/g;
842 var possible = /[&<>"'`]/;
844 var escapeChar = function(chr) {
845 return escape[chr] || "&";
849 extend: function(obj, value) {
850 for(var key in value) {
851 if(value.hasOwnProperty(key)) {
852 obj[key] = value[key];
857 escapeExpression: function(string) {
858 // don't escape SafeStrings, since they're already safe
859 if (string instanceof Handlebars.SafeString) {
860 return string.toString();
861 } else if (string == null || string === false) {
865 // Force a string conversion as this will be done by the append regardless and
866 // the regex test will do this transparently behind the scenes, causing issues if
867 // an object's to string has escaped characters in it.
868 string = string.toString();
870 if(!possible.test(string)) { return string; }
871 return string.replace(badChars, escapeChar);
874 isEmpty: function(value) {
875 if (!value && value !== 0) {
877 } else if(toString.call(value) === "[object Array]" && value.length === 0) {
885 // lib/handlebars/compiler/compiler.js
887 /*jshint eqnull:true*/
888 var Compiler = Handlebars.Compiler = function() {};
889 var JavaScriptCompiler = Handlebars.JavaScriptCompiler = function() {};
891 // the foundHelper register will disambiguate helper lookup from finding a
892 // function in a context. This is necessary for mustache compatibility, which
893 // requires that context functions in blocks are evaluated by blockHelperMissing,
894 // and then proceed as if the resulting value was provided to blockHelperMissing.
896 Compiler.prototype = {
899 disassemble: function() {
900 var opcodes = this.opcodes, opcode, out = [], params, param;
902 for (var i=0, l=opcodes.length; i<l; i++) {
905 if (opcode.opcode === 'DECLARE') {
906 out.push("DECLARE " + opcode.name + "=" + opcode.value);
909 for (var j=0; j<opcode.args.length; j++) {
910 param = opcode.args[j];
911 if (typeof param === "string") {
912 param = "\"" + param.replace("\n", "\\n") + "\"";
916 out.push(opcode.opcode + " " + params.join(" "));
920 return out.join("\n");
922 equals: function(other) {
923 var len = this.opcodes.length;
924 if (other.opcodes.length !== len) {
928 for (var i = 0; i < len; i++) {
929 var opcode = this.opcodes[i],
930 otherOpcode = other.opcodes[i];
931 if (opcode.opcode !== otherOpcode.opcode || opcode.args.length !== otherOpcode.args.length) {
934 for (var j = 0; j < opcode.args.length; j++) {
935 if (opcode.args[j] !== otherOpcode.args[j]) {
941 len = this.children.length;
942 if (other.children.length !== len) {
945 for (i = 0; i < len; i++) {
946 if (!this.children[i].equals(other.children[i])) {
956 compile: function(program, options) {
958 this.depths = {list: []};
959 this.options = options;
961 // These changes will propagate to the other compiler components
962 var knownHelpers = this.options.knownHelpers;
963 this.options.knownHelpers = {
964 'helperMissing': true,
965 'blockHelperMissing': true,
973 for (var name in knownHelpers) {
974 this.options.knownHelpers[name] = knownHelpers[name];
978 return this.program(program);
981 accept: function(node) {
982 return this[node.type](node);
985 program: function(program) {
986 var statements = program.statements, statement;
989 for(var i=0, l=statements.length; i<l; i++) {
990 statement = statements[i];
991 this[statement.type](statement);
993 this.isSimple = l === 1;
995 this.depths.list = this.depths.list.sort(function(a, b) {
1002 compileProgram: function(program) {
1003 var result = new this.compiler().compile(program, this.options);
1004 var guid = this.guid++, depth;
1006 this.usePartial = this.usePartial || result.usePartial;
1008 this.children[guid] = result;
1010 for(var i=0, l=result.depths.list.length; i<l; i++) {
1011 depth = result.depths.list[i];
1013 if(depth < 2) { continue; }
1014 else { this.addDepth(depth - 1); }
1020 block: function(block) {
1021 var mustache = block.mustache,
1022 program = block.program,
1023 inverse = block.inverse;
1026 program = this.compileProgram(program);
1030 inverse = this.compileProgram(inverse);
1033 var type = this.classifyMustache(mustache);
1035 if (type === "helper") {
1036 this.helperMustache(mustache, program, inverse);
1037 } else if (type === "simple") {
1038 this.simpleMustache(mustache);
1040 // now that the simple mustache is resolved, we need to
1041 // evaluate it by executing `blockHelperMissing`
1042 this.opcode('pushProgram', program);
1043 this.opcode('pushProgram', inverse);
1044 this.opcode('emptyHash');
1045 this.opcode('blockValue');
1047 this.ambiguousMustache(mustache, program, inverse);
1049 // now that the simple mustache is resolved, we need to
1050 // evaluate it by executing `blockHelperMissing`
1051 this.opcode('pushProgram', program);
1052 this.opcode('pushProgram', inverse);
1053 this.opcode('emptyHash');
1054 this.opcode('ambiguousBlockValue');
1057 this.opcode('append');
1060 hash: function(hash) {
1061 var pairs = hash.pairs, pair, val;
1063 this.opcode('pushHash');
1065 for(var i=0, l=pairs.length; i<l; i++) {
1069 if (this.options.stringParams) {
1071 this.addDepth(val.depth);
1073 this.opcode('getContext', val.depth || 0);
1074 this.opcode('pushStringParam', val.stringModeValue, val.type);
1079 this.opcode('assignToHash', pair[0]);
1081 this.opcode('popHash');
1084 partial: function(partial) {
1085 var partialName = partial.partialName;
1086 this.usePartial = true;
1088 if(partial.context) {
1089 this.ID(partial.context);
1091 this.opcode('push', 'depth0');
1094 this.opcode('invokePartial', partialName.name);
1095 this.opcode('append');
1098 content: function(content) {
1099 this.opcode('appendContent', content.string);
1102 mustache: function(mustache) {
1103 var options = this.options;
1104 var type = this.classifyMustache(mustache);
1106 if (type === "simple") {
1107 this.simpleMustache(mustache);
1108 } else if (type === "helper") {
1109 this.helperMustache(mustache);
1111 this.ambiguousMustache(mustache);
1114 if(mustache.escaped && !options.noEscape) {
1115 this.opcode('appendEscaped');
1117 this.opcode('append');
1121 ambiguousMustache: function(mustache, program, inverse) {
1122 var id = mustache.id,
1124 isBlock = program != null || inverse != null;
1126 this.opcode('getContext', id.depth);
1128 this.opcode('pushProgram', program);
1129 this.opcode('pushProgram', inverse);
1131 this.opcode('invokeAmbiguous', name, isBlock);
1134 simpleMustache: function(mustache) {
1135 var id = mustache.id;
1137 if (id.type === 'DATA') {
1139 } else if (id.parts.length) {
1142 // Simplified ID for `this`
1143 this.addDepth(id.depth);
1144 this.opcode('getContext', id.depth);
1145 this.opcode('pushContext');
1148 this.opcode('resolvePossibleLambda');
1151 helperMustache: function(mustache, program, inverse) {
1152 var params = this.setupFullMustacheParams(mustache, program, inverse),
1153 name = mustache.id.parts[0];
1155 if (this.options.knownHelpers[name]) {
1156 this.opcode('invokeKnownHelper', params.length, name);
1157 } else if (this.options.knownHelpersOnly) {
1158 throw new Error("You specified knownHelpersOnly, but used the unknown helper " + name);
1160 this.opcode('invokeHelper', params.length, name);
1165 this.addDepth(id.depth);
1166 this.opcode('getContext', id.depth);
1168 var name = id.parts[0];
1170 this.opcode('pushContext');
1172 this.opcode('lookupOnContext', id.parts[0]);
1175 for(var i=1, l=id.parts.length; i<l; i++) {
1176 this.opcode('lookup', id.parts[i]);
1180 DATA: function(data) {
1181 this.options.data = true;
1182 if (data.id.isScoped || data.id.depth) {
1183 throw new Handlebars.Exception('Scoped data references are not supported: ' + data.original);
1186 this.opcode('lookupData');
1187 var parts = data.id.parts;
1188 for(var i=0, l=parts.length; i<l; i++) {
1189 this.opcode('lookup', parts[i]);
1193 STRING: function(string) {
1194 this.opcode('pushString', string.string);
1197 INTEGER: function(integer) {
1198 this.opcode('pushLiteral', integer.integer);
1201 BOOLEAN: function(bool) {
1202 this.opcode('pushLiteral', bool.bool);
1205 comment: function() {},
1208 opcode: function(name) {
1209 this.opcodes.push({ opcode: name, args: [].slice.call(arguments, 1) });
1212 declare: function(name, value) {
1213 this.opcodes.push({ opcode: 'DECLARE', name: name, value: value });
1216 addDepth: function(depth) {
1217 if(isNaN(depth)) { throw new Error("EWOT"); }
1218 if(depth === 0) { return; }
1220 if(!this.depths[depth]) {
1221 this.depths[depth] = true;
1222 this.depths.list.push(depth);
1226 classifyMustache: function(mustache) {
1227 var isHelper = mustache.isHelper;
1228 var isEligible = mustache.eligibleHelper;
1229 var options = this.options;
1231 // if ambiguous, we can possibly resolve the ambiguity now
1232 if (isEligible && !isHelper) {
1233 var name = mustache.id.parts[0];
1235 if (options.knownHelpers[name]) {
1237 } else if (options.knownHelpersOnly) {
1242 if (isHelper) { return "helper"; }
1243 else if (isEligible) { return "ambiguous"; }
1244 else { return "simple"; }
1247 pushParams: function(params) {
1248 var i = params.length, param;
1253 if(this.options.stringParams) {
1255 this.addDepth(param.depth);
1258 this.opcode('getContext', param.depth || 0);
1259 this.opcode('pushStringParam', param.stringModeValue, param.type);
1261 this[param.type](param);
1266 setupMustacheParams: function(mustache) {
1267 var params = mustache.params;
1268 this.pushParams(params);
1271 this.hash(mustache.hash);
1273 this.opcode('emptyHash');
1279 // this will replace setupMustacheParams when we're done
1280 setupFullMustacheParams: function(mustache, program, inverse) {
1281 var params = mustache.params;
1282 this.pushParams(params);
1284 this.opcode('pushProgram', program);
1285 this.opcode('pushProgram', inverse);
1288 this.hash(mustache.hash);
1290 this.opcode('emptyHash');
1297 var Literal = function(value) {
1301 JavaScriptCompiler.prototype = {
1302 // PUBLIC API: You can override these methods in a subclass to provide
1303 // alternative compiled forms for name lookup and buffering semantics
1304 nameLookup: function(parent, name /* , type*/) {
1305 if (/^[0-9]+$/.test(name)) {
1306 return parent + "[" + name + "]";
1307 } else if (JavaScriptCompiler.isValidJavaScriptVariableName(name)) {
1308 return parent + "." + name;
1311 return parent + "['" + name + "']";
1315 appendToBuffer: function(string) {
1316 if (this.environment.isSimple) {
1317 return "return " + string + ";";
1320 appendToBuffer: true,
1322 toString: function() { return "buffer += " + string + ";"; }
1327 initializeBuffer: function() {
1328 return this.quotedString("");
1331 namespace: "Handlebars",
1334 compile: function(environment, options, context, asObject) {
1335 this.environment = environment;
1336 this.options = options || {};
1338 Handlebars.log(Handlebars.logger.DEBUG, this.environment.disassemble() + "\n\n");
1340 this.name = this.environment.name;
1341 this.isChild = !!context;
1342 this.context = context || {
1351 this.stackVars = [];
1352 this.registers = { list: [] };
1353 this.compileStack = [];
1354 this.inlineStack = [];
1356 this.compileChildren(environment, options);
1358 var opcodes = environment.opcodes, opcode;
1362 for(l=opcodes.length; this.i<l; this.i++) {
1363 opcode = opcodes[this.i];
1365 if(opcode.opcode === 'DECLARE') {
1366 this[opcode.name] = opcode.value;
1368 this[opcode.opcode].apply(this, opcode.args);
1372 return this.createFunctionContext(asObject);
1375 nextOpcode: function() {
1376 var opcodes = this.environment.opcodes;
1377 return opcodes[this.i + 1];
1381 this.i = this.i + 1;
1384 preamble: function() {
1387 if (!this.isChild) {
1388 var namespace = this.namespace;
1390 var copies = "helpers = this.merge(helpers, " + namespace + ".helpers);";
1391 if (this.environment.usePartial) { copies = copies + " partials = this.merge(partials, " + namespace + ".partials);"; }
1392 if (this.options.data) { copies = copies + " data = data || {};"; }
1398 if (!this.environment.isSimple) {
1399 out.push(", buffer = " + this.initializeBuffer());
1404 // track the last context pushed into place to allow skipping the
1405 // getContext opcode when it would be a noop
1406 this.lastContext = 0;
1410 createFunctionContext: function(asObject) {
1411 var locals = this.stackVars.concat(this.registers.list);
1413 if(locals.length > 0) {
1414 this.source[1] = this.source[1] + ", " + locals.join(", ");
1417 // Generate minimizer alias mappings
1418 if (!this.isChild) {
1419 for (var alias in this.context.aliases) {
1420 if (this.context.aliases.hasOwnProperty(alias)) {
1421 this.source[1] = this.source[1] + ', ' + alias + '=' + this.context.aliases[alias];
1426 if (this.source[1]) {
1427 this.source[1] = "var " + this.source[1].substring(2) + ";";
1431 if (!this.isChild) {
1432 this.source[1] += '\n' + this.context.programs.join('\n') + '\n';
1435 if (!this.environment.isSimple) {
1436 this.source.push("return buffer;");
1439 var params = this.isChild ? ["depth0", "data"] : ["Handlebars", "depth0", "helpers", "partials", "data"];
1441 for(var i=0, l=this.environment.depths.list.length; i<l; i++) {
1442 params.push("depth" + this.environment.depths.list[i]);
1445 // Perform a second pass over the output to merge content when possible
1446 var source = this.mergeSource();
1448 if (!this.isChild) {
1449 var revision = Handlebars.COMPILER_REVISION,
1450 versions = Handlebars.REVISION_CHANGES[revision];
1451 source = "this.compilerInfo = ["+revision+",'"+versions+"'];\n"+source;
1455 params.push(source);
1457 return Function.apply(this, params);
1459 var functionSource = 'function ' + (this.name || '') + '(' + params.join(',') + ') {\n ' + source + '}';
1460 Handlebars.log(Handlebars.logger.DEBUG, functionSource + "\n\n");
1461 return functionSource;
1464 mergeSource: function() {
1465 // WARN: We are not handling the case where buffer is still populated as the source should
1466 // not have buffer append operations as their final action.
1469 for (var i = 0, len = this.source.length; i < len; i++) {
1470 var line = this.source[i];
1471 if (line.appendToBuffer) {
1473 buffer = buffer + '\n + ' + line.content;
1475 buffer = line.content;
1479 source += 'buffer += ' + buffer + ';\n ';
1482 source += line + '\n ';
1490 // On stack, before: hash, inverse, program, value
1491 // On stack, after: return value of blockHelperMissing
1493 // The purpose of this opcode is to take a block of the form
1494 // `{{#foo}}...{{/foo}}`, resolve the value of `foo`, and
1495 // replace it on the stack with the result of properly
1496 // invoking blockHelperMissing.
1497 blockValue: function() {
1498 this.context.aliases.blockHelperMissing = 'helpers.blockHelperMissing';
1500 var params = ["depth0"];
1501 this.setupParams(0, params);
1503 this.replaceStack(function(current) {
1504 params.splice(1, 0, current);
1505 return "blockHelperMissing.call(" + params.join(", ") + ")";
1509 // [ambiguousBlockValue]
1511 // On stack, before: hash, inverse, program, value
1512 // Compiler value, before: lastHelper=value of last found helper, if any
1513 // On stack, after, if no lastHelper: same as [blockValue]
1514 // On stack, after, if lastHelper: value
1515 ambiguousBlockValue: function() {
1516 this.context.aliases.blockHelperMissing = 'helpers.blockHelperMissing';
1518 var params = ["depth0"];
1519 this.setupParams(0, params);
1521 var current = this.topStack();
1522 params.splice(1, 0, current);
1524 // Use the options value generated from the invocation
1525 params[params.length-1] = 'options';
1527 this.source.push("if (!" + this.lastHelper + ") { " + current + " = blockHelperMissing.call(" + params.join(", ") + "); }");
1532 // On stack, before: ...
1533 // On stack, after: ...
1535 // Appends the string value of `content` to the current buffer
1536 appendContent: function(content) {
1537 this.source.push(this.appendToBuffer(this.quotedString(content)));
1542 // On stack, before: value, ...
1543 // On stack, after: ...
1545 // Coerces `value` to a String and appends it to the current buffer.
1547 // If `value` is truthy, or 0, it is coerced into a string and appended
1548 // Otherwise, the empty string is appended
1549 append: function() {
1550 // Force anything that is inlined onto the stack so we don't have duplication
1551 // when we examine local
1553 var local = this.popStack();
1554 this.source.push("if(" + local + " || " + local + " === 0) { " + this.appendToBuffer(local) + " }");
1555 if (this.environment.isSimple) {
1556 this.source.push("else { " + this.appendToBuffer("''") + " }");
1562 // On stack, before: value, ...
1563 // On stack, after: ...
1565 // Escape `value` and append it to the buffer
1566 appendEscaped: function() {
1567 this.context.aliases.escapeExpression = 'this.escapeExpression';
1569 this.source.push(this.appendToBuffer("escapeExpression(" + this.popStack() + ")"));
1574 // On stack, before: ...
1575 // On stack, after: ...
1576 // Compiler value, after: lastContext=depth
1578 // Set the value of the `lastContext` compiler value to the depth
1579 getContext: function(depth) {
1580 if(this.lastContext !== depth) {
1581 this.lastContext = depth;
1585 // [lookupOnContext]
1587 // On stack, before: ...
1588 // On stack, after: currentContext[name], ...
1590 // Looks up the value of `name` on the current context and pushes
1591 // it onto the stack.
1592 lookupOnContext: function(name) {
1593 this.push(this.nameLookup('depth' + this.lastContext, name, 'context'));
1598 // On stack, before: ...
1599 // On stack, after: currentContext, ...
1601 // Pushes the value of the current context onto the stack.
1602 pushContext: function() {
1603 this.pushStackLiteral('depth' + this.lastContext);
1606 // [resolvePossibleLambda]
1608 // On stack, before: value, ...
1609 // On stack, after: resolved value, ...
1611 // If the `value` is a lambda, replace it on the stack by
1612 // the return value of the lambda
1613 resolvePossibleLambda: function() {
1614 this.context.aliases.functionType = '"function"';
1616 this.replaceStack(function(current) {
1617 return "typeof " + current + " === functionType ? " + current + ".apply(depth0) : " + current;
1623 // On stack, before: value, ...
1624 // On stack, after: value[name], ...
1626 // Replace the value on the stack with the result of looking
1627 // up `name` on `value`
1628 lookup: function(name) {
1629 this.replaceStack(function(current) {
1630 return current + " == null || " + current + " === false ? " + current + " : " + this.nameLookup(current, name, 'context');
1636 // On stack, before: ...
1637 // On stack, after: data[id], ...
1639 // Push the result of looking up `id` on the current data
1640 lookupData: function(id) {
1644 // [pushStringParam]
1646 // On stack, before: ...
1647 // On stack, after: string, currentContext, ...
1649 // This opcode is designed for use in string mode, which
1650 // provides the string value of a parameter along with its
1651 // depth rather than resolving it immediately.
1652 pushStringParam: function(string, type) {
1653 this.pushStackLiteral('depth' + this.lastContext);
1655 this.pushString(type);
1657 if (typeof string === 'string') {
1658 this.pushString(string);
1660 this.pushStackLiteral(string);
1664 emptyHash: function() {
1665 this.pushStackLiteral('{}');
1667 if (this.options.stringParams) {
1668 this.register('hashTypes', '{}');
1669 this.register('hashContexts', '{}');
1672 pushHash: function() {
1673 this.hash = {values: [], types: [], contexts: []};
1675 popHash: function() {
1676 var hash = this.hash;
1677 this.hash = undefined;
1679 if (this.options.stringParams) {
1680 this.register('hashContexts', '{' + hash.contexts.join(',') + '}');
1681 this.register('hashTypes', '{' + hash.types.join(',') + '}');
1683 this.push('{\n ' + hash.values.join(',\n ') + '\n }');
1688 // On stack, before: ...
1689 // On stack, after: quotedString(string), ...
1691 // Push a quoted version of `string` onto the stack
1692 pushString: function(string) {
1693 this.pushStackLiteral(this.quotedString(string));
1698 // On stack, before: ...
1699 // On stack, after: expr, ...
1701 // Push an expression onto the stack
1702 push: function(expr) {
1703 this.inlineStack.push(expr);
1709 // On stack, before: ...
1710 // On stack, after: value, ...
1712 // Pushes a value onto the stack. This operation prevents
1713 // the compiler from creating a temporary variable to hold
1715 pushLiteral: function(value) {
1716 this.pushStackLiteral(value);
1721 // On stack, before: ...
1722 // On stack, after: program(guid), ...
1724 // Push a program expression onto the stack. This takes
1725 // a compile-time guid and converts it into a runtime-accessible
1727 pushProgram: function(guid) {
1729 this.pushStackLiteral(this.programExpression(guid));
1731 this.pushStackLiteral(null);
1737 // On stack, before: hash, inverse, program, params..., ...
1738 // On stack, after: result of helper invocation
1740 // Pops off the helper's parameters, invokes the helper,
1741 // and pushes the helper's return value onto the stack.
1743 // If the helper is not found, `helperMissing` is called.
1744 invokeHelper: function(paramSize, name) {
1745 this.context.aliases.helperMissing = 'helpers.helperMissing';
1747 var helper = this.lastHelper = this.setupHelper(paramSize, name, true);
1748 var nonHelper = this.nameLookup('depth' + this.lastContext, name, 'context');
1750 this.push(helper.name + ' || ' + nonHelper);
1751 this.replaceStack(function(name) {
1752 return name + ' ? ' + name + '.call(' +
1753 helper.callParams + ") " + ": helperMissing.call(" +
1754 helper.helperMissingParams + ")";
1758 // [invokeKnownHelper]
1760 // On stack, before: hash, inverse, program, params..., ...
1761 // On stack, after: result of helper invocation
1763 // This operation is used when the helper is known to exist,
1764 // so a `helperMissing` fallback is not required.
1765 invokeKnownHelper: function(paramSize, name) {
1766 var helper = this.setupHelper(paramSize, name);
1767 this.push(helper.name + ".call(" + helper.callParams + ")");
1770 // [invokeAmbiguous]
1772 // On stack, before: hash, inverse, program, params..., ...
1773 // On stack, after: result of disambiguation
1775 // This operation is used when an expression like `{{foo}}`
1776 // is provided, but we don't know at compile-time whether it
1777 // is a helper or a path.
1779 // This operation emits more code than the other options,
1780 // and can be avoided by passing the `knownHelpers` and
1781 // `knownHelpersOnly` flags at compile-time.
1782 invokeAmbiguous: function(name, helperCall) {
1783 this.context.aliases.functionType = '"function"';
1785 this.pushStackLiteral('{}'); // Hash value
1786 var helper = this.setupHelper(0, name, helperCall);
1788 var helperName = this.lastHelper = this.nameLookup('helpers', name, 'helper');
1790 var nonHelper = this.nameLookup('depth' + this.lastContext, name, 'context');
1791 var nextStack = this.nextStack();
1793 this.source.push('if (' + nextStack + ' = ' + helperName + ') { ' + nextStack + ' = ' + nextStack + '.call(' + helper.callParams + '); }');
1794 this.source.push('else { ' + nextStack + ' = ' + nonHelper + '; ' + nextStack + ' = typeof ' + nextStack + ' === functionType ? ' + nextStack + '.apply(depth0) : ' + nextStack + '; }');
1799 // On stack, before: context, ...
1800 // On stack after: result of partial invocation
1802 // This operation pops off a context, invokes a partial with that context,
1803 // and pushes the result of the invocation back.
1804 invokePartial: function(name) {
1805 var params = [this.nameLookup('partials', name, 'partial'), "'" + name + "'", this.popStack(), "helpers", "partials"];
1807 if (this.options.data) {
1808 params.push("data");
1811 this.context.aliases.self = "this";
1812 this.push("self.invokePartial(" + params.join(", ") + ")");
1817 // On stack, before: value, hash, ...
1818 // On stack, after: hash, ...
1820 // Pops a value and hash off the stack, assigns `hash[key] = value`
1821 // and pushes the hash back onto the stack.
1822 assignToHash: function(key) {
1823 var value = this.popStack(),
1827 if (this.options.stringParams) {
1828 type = this.popStack();
1829 context = this.popStack();
1832 var hash = this.hash;
1834 hash.contexts.push("'" + key + "': " + context);
1837 hash.types.push("'" + key + "': " + type);
1839 hash.values.push("'" + key + "': (" + value + ")");
1844 compiler: JavaScriptCompiler,
1846 compileChildren: function(environment, options) {
1847 var children = environment.children, child, compiler;
1849 for(var i=0, l=children.length; i<l; i++) {
1850 child = children[i];
1851 compiler = new this.compiler();
1853 var index = this.matchExistingProgram(child);
1855 if (index == null) {
1856 this.context.programs.push(''); // Placeholder to prevent name conflicts for nested children
1857 index = this.context.programs.length;
1858 child.index = index;
1859 child.name = 'program' + index;
1860 this.context.programs[index] = compiler.compile(child, options, this.context);
1861 this.context.environments[index] = child;
1863 child.index = index;
1864 child.name = 'program' + index;
1868 matchExistingProgram: function(child) {
1869 for (var i = 0, len = this.context.environments.length; i < len; i++) {
1870 var environment = this.context.environments[i];
1871 if (environment && environment.equals(child)) {
1877 programExpression: function(guid) {
1878 this.context.aliases.self = "this";
1884 var child = this.environment.children[guid],
1885 depths = child.depths.list, depth;
1887 var programParams = [child.index, child.name, "data"];
1889 for(var i=0, l = depths.length; i<l; i++) {
1892 if(depth === 1) { programParams.push("depth0"); }
1893 else { programParams.push("depth" + (depth - 1)); }
1896 return (depths.length === 0 ? "self.program(" : "self.programWithDepth(") + programParams.join(", ") + ")";
1899 register: function(name, val) {
1900 this.useRegister(name);
1901 this.source.push(name + " = " + val + ";");
1904 useRegister: function(name) {
1905 if(!this.registers[name]) {
1906 this.registers[name] = true;
1907 this.registers.list.push(name);
1911 pushStackLiteral: function(item) {
1912 return this.push(new Literal(item));
1915 pushStack: function(item) {
1918 var stack = this.incrStack();
1920 this.source.push(stack + " = " + item + ";");
1922 this.compileStack.push(stack);
1926 replaceStack: function(callback) {
1928 inline = this.isInline(),
1931 // If we are currently inline then we want to merge the inline statement into the
1932 // replacement statement via ','
1934 var top = this.popStack(true);
1936 if (top instanceof Literal) {
1937 // Literals do not need to be inlined
1940 // Get or create the current stack name for use by the inline
1941 var name = this.stackSlot ? this.topStackName() : this.incrStack();
1943 prefix = '(' + this.push(name) + ' = ' + top + '),';
1944 stack = this.topStack();
1947 stack = this.topStack();
1950 var item = callback.call(this, stack);
1953 if (this.inlineStack.length || this.compileStack.length) {
1956 this.push('(' + prefix + item + ')');
1958 // Prevent modification of the context depth variable. Through replaceStack
1959 if (!/^stack/.test(stack)) {
1960 stack = this.nextStack();
1963 this.source.push(stack + " = (" + prefix + item + ");");
1968 nextStack: function() {
1969 return this.pushStack();
1972 incrStack: function() {
1974 if(this.stackSlot > this.stackVars.length) { this.stackVars.push("stack" + this.stackSlot); }
1975 return this.topStackName();
1977 topStackName: function() {
1978 return "stack" + this.stackSlot;
1980 flushInline: function() {
1981 var inlineStack = this.inlineStack;
1982 if (inlineStack.length) {
1983 this.inlineStack = [];
1984 for (var i = 0, len = inlineStack.length; i < len; i++) {
1985 var entry = inlineStack[i];
1986 if (entry instanceof Literal) {
1987 this.compileStack.push(entry);
1989 this.pushStack(entry);
1994 isInline: function() {
1995 return this.inlineStack.length;
1998 popStack: function(wrapped) {
1999 var inline = this.isInline(),
2000 item = (inline ? this.inlineStack : this.compileStack).pop();
2002 if (!wrapped && (item instanceof Literal)) {
2012 topStack: function(wrapped) {
2013 var stack = (this.isInline() ? this.inlineStack : this.compileStack),
2014 item = stack[stack.length - 1];
2016 if (!wrapped && (item instanceof Literal)) {
2023 quotedString: function(str) {
2025 .replace(/\\/g, '\\\\')
2026 .replace(/"/g, '\\"')
2027 .replace(/\n/g, '\\n')
2028 .replace(/\r/g, '\\r')
2029 .replace(/\u2028/g, '\\u2028') // Per Ecma-262 7.3 + 7.8.4
2030 .replace(/\u2029/g, '\\u2029') + '"';
2033 setupHelper: function(paramSize, name, missingParams) {
2035 this.setupParams(paramSize, params, missingParams);
2036 var foundHelper = this.nameLookup('helpers', name, 'helper');
2041 callParams: ["depth0"].concat(params).join(", "),
2042 helperMissingParams: missingParams && ["depth0", this.quotedString(name)].concat(params).join(", ")
2046 // the params and contexts arguments are passed in arrays
2048 setupParams: function(paramSize, params, useRegister) {
2049 var options = [], contexts = [], types = [], param, inverse, program;
2051 options.push("hash:" + this.popStack());
2053 inverse = this.popStack();
2054 program = this.popStack();
2056 // Avoid setting fn and inverse if neither are set. This allows
2057 // helpers to do a check for `if (options.fn)`
2058 if (program || inverse) {
2060 this.context.aliases.self = "this";
2061 program = "self.noop";
2065 this.context.aliases.self = "this";
2066 inverse = "self.noop";
2069 options.push("inverse:" + inverse);
2070 options.push("fn:" + program);
2073 for(var i=0; i<paramSize; i++) {
2074 param = this.popStack();
2077 if(this.options.stringParams) {
2078 types.push(this.popStack());
2079 contexts.push(this.popStack());
2083 if (this.options.stringParams) {
2084 options.push("contexts:[" + contexts.join(",") + "]");
2085 options.push("types:[" + types.join(",") + "]");
2086 options.push("hashContexts:hashContexts");
2087 options.push("hashTypes:hashTypes");
2090 if(this.options.data) {
2091 options.push("data:data");
2094 options = "{" + options.join(",") + "}";
2096 this.register('options', options);
2097 params.push('options');
2099 params.push(options);
2101 return params.join(", ");
2105 var reservedWords = (
2106 "break else new var" +
2107 " case finally return void" +
2108 " catch for switch while" +
2109 " continue function this with" +
2110 " default if throw" +
2112 " do instanceof typeof" +
2113 " abstract enum int short" +
2114 " boolean export interface static" +
2115 " byte extends long super" +
2116 " char final native synchronized" +
2117 " class float package throws" +
2118 " const goto private transient" +
2119 " debugger implements protected volatile" +
2120 " double import public let yield"
2123 var compilerWords = JavaScriptCompiler.RESERVED_WORDS = {};
2125 for(var i=0, l=reservedWords.length; i<l; i++) {
2126 compilerWords[reservedWords[i]] = true;
2129 JavaScriptCompiler.isValidJavaScriptVariableName = function(name) {
2130 if(!JavaScriptCompiler.RESERVED_WORDS[name] && /^[a-zA-Z_$][0-9a-zA-Z_$]+$/.test(name)) {
2136 Handlebars.precompile = function(input, options) {
2137 if (input == null || (typeof input !== 'string' && input.constructor !== Handlebars.AST.ProgramNode)) {
2138 throw new Handlebars.Exception("You must pass a string or Handlebars AST to Handlebars.precompile. You passed " + input);
2141 options = options || {};
2142 if (!('data' in options)) {
2143 options.data = true;
2145 var ast = Handlebars.parse(input);
2146 var environment = new Compiler().compile(ast, options);
2147 return new JavaScriptCompiler().compile(environment, options);
2150 Handlebars.compile = function(input, options) {
2151 if (input == null || (typeof input !== 'string' && input.constructor !== Handlebars.AST.ProgramNode)) {
2152 throw new Handlebars.Exception("You must pass a string or Handlebars AST to Handlebars.compile. You passed " + input);
2155 options = options || {};
2156 if (!('data' in options)) {
2157 options.data = true;
2160 function compile() {
2161 var ast = Handlebars.parse(input);
2162 var environment = new Compiler().compile(ast, options);
2163 var templateSpec = new JavaScriptCompiler().compile(environment, options, undefined, true);
2164 return Handlebars.template(templateSpec);
2167 // Template is only compiled on first use and cached after that point.
2168 return function(context, options) {
2170 compiled = compile();
2172 return compiled.call(this, context, options);
2177 // lib/handlebars/runtime.js
2180 template: function(templateSpec) {
2183 escapeExpression: Handlebars.Utils.escapeExpression,
2184 invokePartial: Handlebars.VM.invokePartial,
2186 program: function(i, fn, data) {
2187 var programWrapper = this.programs[i];
2189 programWrapper = Handlebars.VM.program(i, fn, data);
2190 } else if (!programWrapper) {
2191 programWrapper = this.programs[i] = Handlebars.VM.program(i, fn);
2193 return programWrapper;
2195 merge: function(param, common) {
2196 var ret = param || common;
2198 if (param && common) {
2200 Handlebars.Utils.extend(ret, common);
2201 Handlebars.Utils.extend(ret, param);
2205 programWithDepth: Handlebars.VM.programWithDepth,
2206 noop: Handlebars.VM.noop,
2210 return function(context, options) {
2211 options = options || {};
2212 var result = templateSpec.call(container, Handlebars, context, options.helpers, options.partials, options.data);
2214 var compilerInfo = container.compilerInfo || [],
2215 compilerRevision = compilerInfo[0] || 1,
2216 currentRevision = Handlebars.COMPILER_REVISION;
2218 if (compilerRevision !== currentRevision) {
2219 if (compilerRevision < currentRevision) {
2220 var runtimeVersions = Handlebars.REVISION_CHANGES[currentRevision],
2221 compilerVersions = Handlebars.REVISION_CHANGES[compilerRevision];
2222 throw "Template was precompiled with an older version of Handlebars than the current runtime. "+
2223 "Please update your precompiler to a newer version ("+runtimeVersions+") or downgrade your runtime to an older version ("+compilerVersions+").";
2225 // Use the embedded version info since the runtime doesn't know about this revision yet
2226 throw "Template was precompiled with a newer version of Handlebars than the current runtime. "+
2227 "Please update your runtime to a newer version ("+compilerInfo[1]+").";
2235 programWithDepth: function(i, fn, data /*, $depth */) {
2236 var args = Array.prototype.slice.call(arguments, 3);
2238 var program = function(context, options) {
2239 options = options || {};
2241 return fn.apply(this, [context, options.data || data].concat(args));
2243 program.program = i;
2244 program.depth = args.length;
2247 program: function(i, fn, data) {
2248 var program = function(context, options) {
2249 options = options || {};
2251 return fn(context, options.data || data);
2253 program.program = i;
2257 noop: function() { return ""; },
2258 invokePartial: function(partial, name, context, helpers, partials, data) {
2259 var options = { helpers: helpers, partials: partials, data: data };
2261 if(partial === undefined) {
2262 throw new Handlebars.Exception("The partial " + name + " could not be found");
2263 } else if(partial instanceof Function) {
2264 return partial(context, options);
2265 } else if (!Handlebars.compile) {
2266 throw new Handlebars.Exception("The partial " + name + " could not be compiled when running in runtime-only mode");
2268 partials[name] = Handlebars.compile(partial, {data: data !== undefined});
2269 return partials[name](context, options);
2274 Handlebars.template = Handlebars.VM.template;
2276 // lib/handlebars/browser-suffix.js