templebars 0.2.2 → 0.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,39 +1,80 @@
1
- // lib/handlebars/base.js
1
+ /*
2
2
 
3
- /*jshint eqnull:true*/
4
- this.Handlebars = {};
3
+ Copyright (C) 2011 by Yehuda Katz
4
+
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:
11
+
12
+ The above copyright notice and this permission notice shall be included in
13
+ all copies or substantial portions of the Software.
14
+
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
21
+ THE SOFTWARE.
22
+
23
+ */
24
+
25
+ // lib/handlebars/browser-prefix.js
26
+ var Handlebars = {};
27
+
28
+ (function(Handlebars, undefined) {
29
+ ;
30
+ // lib/handlebars/base.js
5
31
 
6
- (function(Handlebars) {
32
+ Handlebars.VERSION = "1.0.0";
33
+ Handlebars.COMPILER_REVISION = 4;
7
34
 
8
- Handlebars.VERSION = "1.0.rc.1";
35
+ Handlebars.REVISION_CHANGES = {
36
+ 1: '<= 1.0.rc.2', // 1.0.rc.2 is actually rev2 but doesn't report it
37
+ 2: '== 1.0.0-rc.3',
38
+ 3: '== 1.0.0-rc.4',
39
+ 4: '>= 1.0.0'
40
+ };
9
41
 
10
42
  Handlebars.helpers = {};
11
43
  Handlebars.partials = {};
12
44
 
45
+ var toString = Object.prototype.toString,
46
+ functionType = '[object Function]',
47
+ objectType = '[object Object]';
48
+
13
49
  Handlebars.registerHelper = function(name, fn, inverse) {
14
- if(inverse) { fn.not = inverse; }
15
- this.helpers[name] = fn;
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);
53
+ } else {
54
+ if (inverse) { fn.not = inverse; }
55
+ this.helpers[name] = fn;
56
+ }
16
57
  };
17
58
 
18
59
  Handlebars.registerPartial = function(name, str) {
19
- this.partials[name] = str;
60
+ if (toString.call(name) === objectType) {
61
+ Handlebars.Utils.extend(this.partials, name);
62
+ } else {
63
+ this.partials[name] = str;
64
+ }
20
65
  };
21
66
 
22
67
  Handlebars.registerHelper('helperMissing', function(arg) {
23
68
  if(arguments.length === 2) {
24
69
  return undefined;
25
70
  } else {
26
- throw new Error("Could not find property '" + arg + "'");
71
+ throw new Error("Missing helper: '" + arg + "'");
27
72
  }
28
73
  });
29
74
 
30
- var toString = Object.prototype.toString, functionType = "[object Function]";
31
-
32
75
  Handlebars.registerHelper('blockHelperMissing', function(context, options) {
33
76
  var inverse = options.inverse || function() {}, fn = options.fn;
34
77
 
35
-
36
- var ret = "";
37
78
  var type = toString.call(context);
38
79
 
39
80
  if(type === functionType) { context = context.call(this); }
@@ -62,349 +103,318 @@ Handlebars.createFrame = Object.create || function(object) {
62
103
  return obj;
63
104
  };
64
105
 
106
+ Handlebars.logger = {
107
+ DEBUG: 0, INFO: 1, WARN: 2, ERROR: 3, level: 3,
108
+
109
+ methodMap: {0: 'debug', 1: 'info', 2: 'warn', 3: 'error'},
110
+
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);
117
+ }
118
+ }
119
+ }
120
+ };
121
+
122
+ Handlebars.log = function(level, obj) { Handlebars.logger.log(level, obj); };
123
+
65
124
  Handlebars.registerHelper('each', function(context, options) {
66
125
  var fn = options.fn, inverse = options.inverse;
67
- var ret = "", data;
126
+ var i = 0, ret = "", data;
127
+
128
+ var type = toString.call(context);
129
+ if(type === functionType) { context = context.call(this); }
68
130
 
69
131
  if (options.data) {
70
132
  data = Handlebars.createFrame(options.data);
71
133
  }
72
134
 
73
- if(context && context.length > 0) {
74
- for(var i=0, j=context.length; i<j; i++) {
75
- if (data) { data.index = i; }
76
- ret = ret + fn(context[i], { data: 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 });
140
+ }
141
+ } else {
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});
146
+ i++;
147
+ }
148
+ }
77
149
  }
78
- } else {
150
+ }
151
+
152
+ if(i === 0){
79
153
  ret = inverse(this);
80
154
  }
155
+
81
156
  return ret;
82
157
  });
83
158
 
84
- Handlebars.registerHelper('if', function(context, options) {
85
- var type = toString.call(context);
86
- if(type === functionType) { context = context.call(this); }
159
+ Handlebars.registerHelper('if', function(conditional, options) {
160
+ var type = toString.call(conditional);
161
+ if(type === functionType) { conditional = conditional.call(this); }
87
162
 
88
- if(!context || Handlebars.Utils.isEmpty(context)) {
163
+ if(!conditional || Handlebars.Utils.isEmpty(conditional)) {
89
164
  return options.inverse(this);
90
165
  } else {
91
166
  return options.fn(this);
92
167
  }
93
168
  });
94
169
 
95
- Handlebars.registerHelper('unless', function(context, options) {
96
- var fn = options.fn, inverse = options.inverse;
97
- options.fn = inverse;
98
- options.inverse = fn;
99
-
100
- return Handlebars.helpers['if'].call(this, context, options);
170
+ Handlebars.registerHelper('unless', function(conditional, options) {
171
+ return Handlebars.helpers['if'].call(this, conditional, {fn: options.inverse, inverse: options.fn});
101
172
  });
102
173
 
103
174
  Handlebars.registerHelper('with', function(context, options) {
104
- return options.fn(context);
105
- });
175
+ var type = toString.call(context);
176
+ if(type === functionType) { context = context.call(this); }
106
177
 
107
- Handlebars.registerHelper('log', function(context) {
108
- Handlebars.log(context);
178
+ if (!Handlebars.Utils.isEmpty(context)) return options.fn(context);
109
179
  });
110
180
 
111
- }(this.Handlebars));
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);
184
+ });
112
185
  ;
113
186
  // lib/handlebars/compiler/parser.js
114
187
  /* Jison generated parser */
115
188
  var handlebars = (function(){
116
189
  var parser = {trace: function trace() { },
117
190
  yy: {},
118
- symbols_: {"error":2,"root":3,"program":4,"EOF":5,"statements":6,"simpleInverse":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,"OPEN_PARTIAL":24,"params":25,"hash":26,"DATA":27,"param":28,"STRING":29,"INTEGER":30,"BOOLEAN":31,"hashSegments":32,"hashSegment":33,"ID":34,"EQUALS":35,"pathSegments":36,"SEP":37,"$accept":0,"$end":1},
119
- 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:"OPEN_PARTIAL",27:"DATA",29:"STRING",30:"INTEGER",31:"BOOLEAN",34:"ID",35:"EQUALS",37:"SEP"},
120
- productions_: [0,[3,2],[4,3],[4,1],[4,0],[6,1],[6,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],[7,2],[17,3],[17,2],[17,2],[17,1],[17,1],[25,2],[25,1],[28,1],[28,1],[28,1],[28,1],[28,1],[26,1],[32,2],[32,1],[33,3],[33,3],[33,3],[33,3],[33,3],[21,1],[36,3],[36,1]],
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]],
121
194
  performAction: function anonymous(yytext,yyleng,yylineno,yy,yystate,$$,_$) {
122
195
 
123
196
  var $0 = $$.length - 1;
124
197
  switch (yystate) {
125
198
  case 1: return $$[$0-1];
126
199
  break;
127
- case 2: this.$ = new yy.ProgramNode($$[$0-2], $$[$0]);
200
+ case 2: this.$ = new yy.ProgramNode([], $$[$0]);
128
201
  break;
129
- case 3: this.$ = new yy.ProgramNode($$[$0]);
202
+ case 3: this.$ = new yy.ProgramNode($$[$0-2], $$[$0]);
130
203
  break;
131
- case 4: this.$ = new yy.ProgramNode([]);
204
+ case 4: this.$ = new yy.ProgramNode($$[$0-1], []);
132
205
  break;
133
- case 5: this.$ = [$$[$0]];
206
+ case 5: this.$ = new yy.ProgramNode($$[$0]);
134
207
  break;
135
- case 6: $$[$0-1].push($$[$0]); this.$ = $$[$0-1];
208
+ case 6: this.$ = new yy.ProgramNode([], []);
136
209
  break;
137
- case 7: this.$ = new yy.BlockNode($$[$0-2], $$[$0-1].inverse, $$[$0-1], $$[$0]);
210
+ case 7: this.$ = new yy.ProgramNode([]);
138
211
  break;
139
- case 8: this.$ = new yy.BlockNode($$[$0-2], $$[$0-1], $$[$0-1].inverse, $$[$0]);
212
+ case 8: this.$ = [$$[$0]];
140
213
  break;
141
- case 9: this.$ = $$[$0];
214
+ case 9: $$[$0-1].push($$[$0]); this.$ = $$[$0-1];
142
215
  break;
143
- case 10: this.$ = $$[$0];
216
+ case 10: this.$ = new yy.BlockNode($$[$0-2], $$[$0-1].inverse, $$[$0-1], $$[$0]);
144
217
  break;
145
- case 11: this.$ = new yy.ContentNode($$[$0]);
218
+ case 11: this.$ = new yy.BlockNode($$[$0-2], $$[$0-1], $$[$0-1].inverse, $$[$0]);
146
219
  break;
147
- case 12: this.$ = new yy.CommentNode($$[$0]);
220
+ case 12: this.$ = $$[$0];
148
221
  break;
149
- case 13: this.$ = new yy.MustacheNode($$[$0-1][0], $$[$0-1][1]);
222
+ case 13: this.$ = $$[$0];
150
223
  break;
151
- case 14: this.$ = new yy.MustacheNode($$[$0-1][0], $$[$0-1][1]);
224
+ case 14: this.$ = new yy.ContentNode($$[$0]);
152
225
  break;
153
- case 15: this.$ = $$[$0-1];
226
+ case 15: this.$ = new yy.CommentNode($$[$0]);
154
227
  break;
155
228
  case 16: this.$ = new yy.MustacheNode($$[$0-1][0], $$[$0-1][1]);
156
229
  break;
157
- case 17: this.$ = new yy.MustacheNode($$[$0-1][0], $$[$0-1][1], true);
230
+ case 17: this.$ = new yy.MustacheNode($$[$0-1][0], $$[$0-1][1]);
231
+ break;
232
+ case 18: this.$ = $$[$0-1];
158
233
  break;
159
- case 18: this.$ = new yy.PartialNode($$[$0-1]);
234
+ case 19:
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] === '&');
237
+
160
238
  break;
161
- case 19: this.$ = new yy.PartialNode($$[$0-2], $$[$0-1]);
239
+ case 20: this.$ = new yy.MustacheNode($$[$0-1][0], $$[$0-1][1], true);
162
240
  break;
163
- case 20:
241
+ case 21: this.$ = new yy.PartialNode($$[$0-1]);
164
242
  break;
165
- case 21: this.$ = [[$$[$0-2]].concat($$[$0-1]), $$[$0]];
243
+ case 22: this.$ = new yy.PartialNode($$[$0-2], $$[$0-1]);
166
244
  break;
167
- case 22: this.$ = [[$$[$0-1]].concat($$[$0]), null];
245
+ case 23:
168
246
  break;
169
- case 23: this.$ = [[$$[$0-1]], $$[$0]];
247
+ case 24: this.$ = [[$$[$0-2]].concat($$[$0-1]), $$[$0]];
170
248
  break;
171
- case 24: this.$ = [[$$[$0]], null];
249
+ case 25: this.$ = [[$$[$0-1]].concat($$[$0]), null];
172
250
  break;
173
- case 25: this.$ = [[new yy.DataNode($$[$0])], null];
251
+ case 26: this.$ = [[$$[$0-1]], $$[$0]];
174
252
  break;
175
- case 26: $$[$0-1].push($$[$0]); this.$ = $$[$0-1];
253
+ case 27: this.$ = [[$$[$0]], null];
176
254
  break;
177
- case 27: this.$ = [$$[$0]];
255
+ case 28: this.$ = [[$$[$0]], null];
178
256
  break;
179
- case 28: this.$ = $$[$0];
257
+ case 29: $$[$0-1].push($$[$0]); this.$ = $$[$0-1];
180
258
  break;
181
- case 29: this.$ = new yy.StringNode($$[$0]);
259
+ case 30: this.$ = [$$[$0]];
182
260
  break;
183
- case 30: this.$ = new yy.IntegerNode($$[$0]);
261
+ case 31: this.$ = $$[$0];
184
262
  break;
185
- case 31: this.$ = new yy.BooleanNode($$[$0]);
263
+ case 32: this.$ = new yy.StringNode($$[$0]);
186
264
  break;
187
- case 32: this.$ = new yy.DataNode($$[$0]);
265
+ case 33: this.$ = new yy.IntegerNode($$[$0]);
188
266
  break;
189
- case 33: this.$ = new yy.HashNode($$[$0]);
267
+ case 34: this.$ = new yy.BooleanNode($$[$0]);
190
268
  break;
191
- case 34: $$[$0-1].push($$[$0]); this.$ = $$[$0-1];
269
+ case 35: this.$ = $$[$0];
192
270
  break;
193
- case 35: this.$ = [$$[$0]];
271
+ case 36: this.$ = new yy.HashNode($$[$0]);
194
272
  break;
195
- case 36: this.$ = [$$[$0-2], $$[$0]];
273
+ case 37: $$[$0-1].push($$[$0]); this.$ = $$[$0-1];
196
274
  break;
197
- case 37: this.$ = [$$[$0-2], new yy.StringNode($$[$0])];
275
+ case 38: this.$ = [$$[$0]];
198
276
  break;
199
- case 38: this.$ = [$$[$0-2], new yy.IntegerNode($$[$0])];
277
+ case 39: this.$ = [$$[$0-2], $$[$0]];
200
278
  break;
201
- case 39: this.$ = [$$[$0-2], new yy.BooleanNode($$[$0])];
279
+ case 40: this.$ = [$$[$0-2], new yy.StringNode($$[$0])];
202
280
  break;
203
- case 40: this.$ = [$$[$0-2], new yy.DataNode($$[$0])];
281
+ case 41: this.$ = [$$[$0-2], new yy.IntegerNode($$[$0])];
204
282
  break;
205
- case 41: this.$ = new yy.IdNode($$[$0]);
283
+ case 42: this.$ = [$$[$0-2], new yy.BooleanNode($$[$0])];
206
284
  break;
207
- case 42: $$[$0-2].push($$[$0]); this.$ = $$[$0-2];
285
+ case 43: this.$ = [$$[$0-2], $$[$0]];
208
286
  break;
209
- case 43: this.$ = [$$[$0]];
287
+ case 44: this.$ = new yy.PartialNameNode($$[$0]);
288
+ break;
289
+ case 45: this.$ = new yy.PartialNameNode(new yy.StringNode($$[$0]));
290
+ break;
291
+ case 46: this.$ = new yy.PartialNameNode(new yy.IntegerNode($$[$0]));
292
+ break;
293
+ case 47: this.$ = new yy.DataNode($$[$0]);
294
+ break;
295
+ case 48: this.$ = new yy.IdNode($$[$0]);
296
+ break;
297
+ case 49: $$[$0-2].push({part: $$[$0], separator: $$[$0-1]}); this.$ = $$[$0-2];
298
+ break;
299
+ case 50: this.$ = [{part: $$[$0]}];
210
300
  break;
211
301
  }
212
302
  },
213
- table: [{3:1,4:2,5:[2,4],6: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],24:[1,15]},{1:[3]},{5:[1,16]},{5:[2,3],7:17,8:18,9:5,11:6,12:7,13:8,14:[1,9],15:[1,10],16:[1,12],19:[1,19],20:[2,3],22:[1,13],23:[1,14],24:[1,15]},{5:[2,5],14:[2,5],15:[2,5],16:[2,5],19:[2,5],20:[2,5],22:[2,5],23:[2,5],24:[2,5]},{4:20,6:3,8:4,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],24:[1,15]},{4:21,6:3,8:4,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],24:[1,15]},{5:[2,9],14:[2,9],15:[2,9],16:[2,9],19:[2,9],20:[2,9],22:[2,9],23:[2,9],24:[2,9]},{5:[2,10],14:[2,10],15:[2,10],16:[2,10],19:[2,10],20:[2,10],22:[2,10],23:[2,10],24:[2,10]},{5:[2,11],14:[2,11],15:[2,11],16:[2,11],19:[2,11],20:[2,11],22:[2,11],23:[2,11],24:[2,11]},{5:[2,12],14:[2,12],15:[2,12],16:[2,12],19:[2,12],20:[2,12],22:[2,12],23:[2,12],24:[2,12]},{17:22,21:23,27:[1,24],34:[1,26],36:25},{17:27,21:23,27:[1,24],34:[1,26],36:25},{17:28,21:23,27:[1,24],34:[1,26],36:25},{17:29,21:23,27:[1,24],34:[1,26],36:25},{21:30,34:[1,26],36:25},{1:[2,1]},{6:31,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],24:[1,15]},{5:[2,6],14:[2,6],15:[2,6],16:[2,6],19:[2,6],20:[2,6],22:[2,6],23:[2,6],24:[2,6]},{17:22,18:[1,32],21:23,27:[1,24],34:[1,26],36:25},{10:33,20:[1,34]},{10:35,20:[1,34]},{18:[1,36]},{18:[2,24],21:41,25:37,26:38,27:[1,45],28:39,29:[1,42],30:[1,43],31:[1,44],32:40,33:46,34:[1,47],36:25},{18:[2,25]},{18:[2,41],27:[2,41],29:[2,41],30:[2,41],31:[2,41],34:[2,41],37:[1,48]},{18:[2,43],27:[2,43],29:[2,43],30:[2,43],31:[2,43],34:[2,43],37:[2,43]},{18:[1,49]},{18:[1,50]},{18:[1,51]},{18:[1,52],21:53,34:[1,26],36:25},{5:[2,2],8:18,9:5,11:6,12:7,13:8,14:[1,9],15:[1,10],16:[1,12],19:[1,11],20:[2,2],22:[1,13],23:[1,14],24:[1,15]},{14:[2,20],15:[2,20],16:[2,20],19:[2,20],22:[2,20],23:[2,20],24:[2,20]},{5:[2,7],14:[2,7],15:[2,7],16:[2,7],19:[2,7],20:[2,7],22:[2,7],23:[2,7],24:[2,7]},{21:54,34:[1,26],36:25},{5:[2,8],14:[2,8],15:[2,8],16:[2,8],19:[2,8],20:[2,8],22:[2,8],23:[2,8],24:[2,8]},{14:[2,14],15:[2,14],16:[2,14],19:[2,14],20:[2,14],22:[2,14],23:[2,14],24:[2,14]},{18:[2,22],21:41,26:55,27:[1,45],28:56,29:[1,42],30:[1,43],31:[1,44],32:40,33:46,34:[1,47],36:25},{18:[2,23]},{18:[2,27],27:[2,27],29:[2,27],30:[2,27],31:[2,27],34:[2,27]},{18:[2,33],33:57,34:[1,58]},{18:[2,28],27:[2,28],29:[2,28],30:[2,28],31:[2,28],34:[2,28]},{18:[2,29],27:[2,29],29:[2,29],30:[2,29],31:[2,29],34:[2,29]},{18:[2,30],27:[2,30],29:[2,30],30:[2,30],31:[2,30],34:[2,30]},{18:[2,31],27:[2,31],29:[2,31],30:[2,31],31:[2,31],34:[2,31]},{18:[2,32],27:[2,32],29:[2,32],30:[2,32],31:[2,32],34:[2,32]},{18:[2,35],34:[2,35]},{18:[2,43],27:[2,43],29:[2,43],30:[2,43],31:[2,43],34:[2,43],35:[1,59],37:[2,43]},{34:[1,60]},{14:[2,13],15:[2,13],16:[2,13],19:[2,13],20:[2,13],22:[2,13],23:[2,13],24:[2,13]},{5:[2,16],14:[2,16],15:[2,16],16:[2,16],19:[2,16],20:[2,16],22:[2,16],23:[2,16],24:[2,16]},{5:[2,17],14:[2,17],15:[2,17],16:[2,17],19:[2,17],20:[2,17],22:[2,17],23:[2,17],24:[2,17]},{5:[2,18],14:[2,18],15:[2,18],16:[2,18],19:[2,18],20:[2,18],22:[2,18],23:[2,18],24:[2,18]},{18:[1,61]},{18:[1,62]},{18:[2,21]},{18:[2,26],27:[2,26],29:[2,26],30:[2,26],31:[2,26],34:[2,26]},{18:[2,34],34:[2,34]},{35:[1,59]},{21:63,27:[1,67],29:[1,64],30:[1,65],31:[1,66],34:[1,26],36:25},{18:[2,42],27:[2,42],29:[2,42],30:[2,42],31:[2,42],34:[2,42],37:[2,42]},{5:[2,19],14:[2,19],15:[2,19],16:[2,19],19:[2,19],20:[2,19],22:[2,19],23:[2,19],24:[2,19]},{5:[2,15],14:[2,15],15:[2,15],16:[2,15],19:[2,15],20:[2,15],22:[2,15],23:[2,15],24:[2,15]},{18:[2,36],34:[2,36]},{18:[2,37],34:[2,37]},{18:[2,38],34:[2,38]},{18:[2,39],34:[2,39]},{18:[2,40],34:[2,40]}],
214
- defaultActions: {16:[2,1],24:[2,25],38:[2,23],55:[2,21]},
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]},
215
305
  parseError: function parseError(str, hash) {
216
306
  throw new Error(str);
217
307
  },
218
308
  parse: function parse(input) {
219
- var self = this,
220
- stack = [0],
221
- vstack = [null], // semantic value stack
222
- lstack = [], // location stack
223
- table = this.table,
224
- yytext = '',
225
- yylineno = 0,
226
- yyleng = 0,
227
- recovering = 0,
228
- TERROR = 2,
229
- EOF = 1;
230
-
231
- //this.reductionCount = this.shiftCount = 0;
232
-
309
+ var self = this, stack = [0], vstack = [null], lstack = [], table = this.table, yytext = "", yylineno = 0, yyleng = 0, recovering = 0, TERROR = 2, EOF = 1;
233
310
  this.lexer.setInput(input);
234
311
  this.lexer.yy = this.yy;
235
312
  this.yy.lexer = this.lexer;
236
- if (typeof this.lexer.yylloc == 'undefined')
313
+ this.yy.parser = this;
314
+ if (typeof this.lexer.yylloc == "undefined")
237
315
  this.lexer.yylloc = {};
238
316
  var yyloc = this.lexer.yylloc;
239
317
  lstack.push(yyloc);
240
-
241
- if (typeof this.yy.parseError === 'function')
318
+ var ranges = this.lexer.options && this.lexer.options.ranges;
319
+ if (typeof this.yy.parseError === "function")
242
320
  this.parseError = this.yy.parseError;
243
-
244
- function popStack (n) {
245
- stack.length = stack.length - 2*n;
321
+ function popStack(n) {
322
+ stack.length = stack.length - 2 * n;
246
323
  vstack.length = vstack.length - n;
247
324
  lstack.length = lstack.length - n;
248
325
  }
249
-
250
326
  function lex() {
251
327
  var token;
252
- token = self.lexer.lex() || 1; // $end = 1
253
- // if token isn't its numeric value, convert
254
- if (typeof token !== 'number') {
328
+ token = self.lexer.lex() || 1;
329
+ if (typeof token !== "number") {
255
330
  token = self.symbols_[token] || token;
256
331
  }
257
332
  return token;
258
333
  }
259
-
260
- var symbol, preErrorSymbol, state, action, a, r, yyval={},p,len,newState, expected;
334
+ var symbol, preErrorSymbol, state, action, a, r, yyval = {}, p, len, newState, expected;
261
335
  while (true) {
262
- // retreive state number from top of stack
263
- state = stack[stack.length-1];
264
-
265
- // use default actions if available
336
+ state = stack[stack.length - 1];
266
337
  if (this.defaultActions[state]) {
267
338
  action = this.defaultActions[state];
268
339
  } else {
269
- if (symbol == null)
340
+ if (symbol === null || typeof symbol == "undefined") {
270
341
  symbol = lex();
271
- // read action for current state and first input
342
+ }
272
343
  action = table[state] && table[state][symbol];
273
344
  }
274
-
275
- // handle parse error
276
- _handle_error:
277
- if (typeof action === 'undefined' || !action.length || !action[0]) {
278
-
345
+ if (typeof action === "undefined" || !action.length || !action[0]) {
346
+ var errStr = "";
279
347
  if (!recovering) {
280
- // Report error
281
348
  expected = [];
282
- for (p in table[state]) if (this.terminals_[p] && p > 2) {
283
- expected.push("'"+this.terminals_[p]+"'");
284
- }
285
- var errStr = '';
349
+ for (p in table[state])
350
+ if (this.terminals_[p] && p > 2) {
351
+ expected.push("'" + this.terminals_[p] + "'");
352
+ }
286
353
  if (this.lexer.showPosition) {
287
- errStr = 'Parse error on line '+(yylineno+1)+":\n"+this.lexer.showPosition()+"\nExpecting "+expected.join(', ') + ", got '" + this.terminals_[symbol]+ "'";
354
+ errStr = "Parse error on line " + (yylineno + 1) + ":\n" + this.lexer.showPosition() + "\nExpecting " + expected.join(", ") + ", got '" + (this.terminals_[symbol] || symbol) + "'";
288
355
  } else {
289
- errStr = 'Parse error on line '+(yylineno+1)+": Unexpected " +
290
- (symbol == 1 /*EOF*/ ? "end of input" :
291
- ("'"+(this.terminals_[symbol] || symbol)+"'"));
356
+ errStr = "Parse error on line " + (yylineno + 1) + ": Unexpected " + (symbol == 1?"end of input":"'" + (this.terminals_[symbol] || symbol) + "'");
292
357
  }
293
- this.parseError(errStr,
294
- {text: this.lexer.match, token: this.terminals_[symbol] || symbol, line: this.lexer.yylineno, loc: yyloc, expected: expected});
358
+ this.parseError(errStr, {text: this.lexer.match, token: this.terminals_[symbol] || symbol, line: this.lexer.yylineno, loc: yyloc, expected: expected});
295
359
  }
296
-
297
- // just recovered from another error
298
- if (recovering == 3) {
299
- if (symbol == EOF) {
300
- throw new Error(errStr || 'Parsing halted.');
301
- }
302
-
303
- // discard current lookahead and grab another
360
+ }
361
+ if (action[0] instanceof Array && action.length > 1) {
362
+ throw new Error("Parse Error: multiple actions possible at state: " + state + ", token: " + symbol);
363
+ }
364
+ switch (action[0]) {
365
+ case 1:
366
+ stack.push(symbol);
367
+ vstack.push(this.lexer.yytext);
368
+ lstack.push(this.lexer.yylloc);
369
+ stack.push(action[1]);
370
+ symbol = null;
371
+ if (!preErrorSymbol) {
304
372
  yyleng = this.lexer.yyleng;
305
373
  yytext = this.lexer.yytext;
306
374
  yylineno = this.lexer.yylineno;
307
375
  yyloc = this.lexer.yylloc;
308
- symbol = lex();
376
+ if (recovering > 0)
377
+ recovering--;
378
+ } else {
379
+ symbol = preErrorSymbol;
380
+ preErrorSymbol = null;
309
381
  }
310
-
311
- // try to recover from error
312
- while (1) {
313
- // check for error recovery rule in this state
314
- if ((TERROR.toString()) in table[state]) {
315
- break;
316
- }
317
- if (state == 0) {
318
- throw new Error(errStr || 'Parsing halted.');
319
- }
320
- popStack(1);
321
- state = stack[stack.length-1];
382
+ break;
383
+ case 2:
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};
387
+ if (ranges) {
388
+ yyval._$.range = [lstack[lstack.length - (len || 1)].range[0], lstack[lstack.length - 1].range[1]];
322
389
  }
323
-
324
- preErrorSymbol = symbol; // save the lookahead token
325
- symbol = TERROR; // insert generic error symbol as new lookahead
326
- state = stack[stack.length-1];
327
- action = table[state] && table[state][TERROR];
328
- recovering = 3; // allow 3 real symbols to be shifted before reporting a new error
329
- }
330
-
331
- // this shouldn't happen, unless resolve defaults are off
332
- if (action[0] instanceof Array && action.length > 1) {
333
- throw new Error('Parse Error: multiple actions possible at state: '+state+', token: '+symbol);
334
- }
335
-
336
- switch (action[0]) {
337
-
338
- case 1: // shift
339
- //this.shiftCount++;
340
-
341
- stack.push(symbol);
342
- vstack.push(this.lexer.yytext);
343
- lstack.push(this.lexer.yylloc);
344
- stack.push(action[1]); // push state
345
- symbol = null;
346
- if (!preErrorSymbol) { // normal execution/no error
347
- yyleng = this.lexer.yyleng;
348
- yytext = this.lexer.yytext;
349
- yylineno = this.lexer.yylineno;
350
- yyloc = this.lexer.yylloc;
351
- if (recovering > 0)
352
- recovering--;
353
- } else { // error just occurred, resume old lookahead f/ before error
354
- symbol = preErrorSymbol;
355
- preErrorSymbol = null;
356
- }
357
- break;
358
-
359
- case 2: // reduce
360
- //this.reductionCount++;
361
-
362
- len = this.productions_[action[1]][1];
363
-
364
- // perform semantic action
365
- yyval.$ = vstack[vstack.length-len]; // default to $$ = $1
366
- // default location, uses first token for firsts, last for lasts
367
- yyval._$ = {
368
- first_line: lstack[lstack.length-(len||1)].first_line,
369
- last_line: lstack[lstack.length-1].last_line,
370
- first_column: lstack[lstack.length-(len||1)].first_column,
371
- last_column: lstack[lstack.length-1].last_column
372
- };
373
- r = this.performAction.call(yyval, yytext, yyleng, yylineno, this.yy, action[1], vstack, lstack);
374
-
375
- if (typeof r !== 'undefined') {
376
- return r;
377
- }
378
-
379
- // pop off stack
380
- if (len) {
381
- stack = stack.slice(0,-1*len*2);
382
- vstack = vstack.slice(0, -1*len);
383
- lstack = lstack.slice(0, -1*len);
384
- }
385
-
386
- stack.push(this.productions_[action[1]][0]); // push nonterminal (reduce)
387
- vstack.push(yyval.$);
388
- lstack.push(yyval._$);
389
- // goto new state = table[STATE][NONTERMINAL]
390
- newState = table[stack[stack.length-2]][stack[stack.length-1]];
391
- stack.push(newState);
392
- break;
393
-
394
- case 3: // accept
395
- return true;
390
+ r = this.performAction.call(yyval, yytext, yyleng, yylineno, this.yy, action[1], vstack, lstack);
391
+ if (typeof r !== "undefined") {
392
+ return r;
393
+ }
394
+ if (len) {
395
+ stack = stack.slice(0, -1 * len * 2);
396
+ vstack = vstack.slice(0, -1 * len);
397
+ lstack = lstack.slice(0, -1 * len);
398
+ }
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);
404
+ break;
405
+ case 3:
406
+ return true;
396
407
  }
397
-
398
408
  }
399
-
400
409
  return true;
401
- }};
410
+ }
411
+ };
402
412
  /* Jison generated lexer */
403
413
  var lexer = (function(){
404
414
  var lexer = ({EOF:1,
405
415
  parseError:function parseError(str, hash) {
406
- if (this.yy.parseError) {
407
- this.yy.parseError(str, hash);
416
+ if (this.yy.parser) {
417
+ this.yy.parser.parseError(str, hash);
408
418
  } else {
409
419
  throw new Error(str);
410
420
  }
@@ -416,21 +426,55 @@ setInput:function (input) {
416
426
  this.yytext = this.matched = this.match = '';
417
427
  this.conditionStack = ['INITIAL'];
418
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];
430
+ this.offset = 0;
419
431
  return this;
420
432
  },
421
433
  input:function () {
422
434
  var ch = this._input[0];
423
- this.yytext+=ch;
435
+ this.yytext += ch;
424
436
  this.yyleng++;
425
- this.match+=ch;
426
- this.matched+=ch;
427
- var lines = ch.match(/\n/);
428
- if (lines) this.yylineno++;
437
+ this.offset++;
438
+ this.match += ch;
439
+ this.matched += ch;
440
+ var lines = ch.match(/(?:\r\n?|\n).*/g);
441
+ if (lines) {
442
+ this.yylineno++;
443
+ this.yylloc.last_line++;
444
+ } else {
445
+ this.yylloc.last_column++;
446
+ }
447
+ if (this.options.ranges) this.yylloc.range[1]++;
448
+
429
449
  this._input = this._input.slice(1);
430
450
  return ch;
431
451
  },
432
452
  unput:function (ch) {
453
+ var len = ch.length;
454
+ var lines = ch.split(/(?:\r\n?|\n)/g);
455
+
433
456
  this._input = ch + this._input;
457
+ this.yytext = this.yytext.substr(0, this.yytext.length-len-1);
458
+ //this.yyleng -= len;
459
+ this.offset -= 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);
463
+
464
+ if (lines.length-1) this.yylineno -= lines.length-1;
465
+ var r = this.yylloc.range;
466
+
467
+ this.yylloc = {first_line: this.yylloc.first_line,
468
+ last_line: this.yylineno+1,
469
+ first_column: this.yylloc.first_column,
470
+ last_column: lines ?
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
473
+ };
474
+
475
+ if (this.options.ranges) {
476
+ this.yylloc.range = [r[0], r[0] + this.yyleng - len];
477
+ }
434
478
  return this;
435
479
  },
436
480
  more:function () {
@@ -438,7 +482,7 @@ more:function () {
438
482
  return this;
439
483
  },
440
484
  less:function (n) {
441
- this._input = this.match.slice(n) + this._input;
485
+ this.unput(this.match.slice(n));
442
486
  },
443
487
  pastInput:function () {
444
488
  var past = this.matched.substr(0, this.matched.length - this.match.length);
@@ -482,26 +526,31 @@ next:function () {
482
526
  }
483
527
  }
484
528
  if (match) {
485
- lines = match[0].match(/\n.*/g);
529
+ lines = match[0].match(/(?:\r\n?|\n).*/g);
486
530
  if (lines) this.yylineno += lines.length;
487
531
  this.yylloc = {first_line: this.yylloc.last_line,
488
532
  last_line: this.yylineno+1,
489
533
  first_column: this.yylloc.last_column,
490
- last_column: lines ? lines[lines.length-1].length-1 : this.yylloc.last_column + match[0].length}
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};
491
535
  this.yytext += match[0];
492
536
  this.match += match[0];
537
+ this.matches = match;
493
538
  this.yyleng = this.yytext.length;
539
+ if (this.options.ranges) {
540
+ this.yylloc.range = [this.offset, this.offset += this.yyleng];
541
+ }
494
542
  this._more = false;
495
543
  this._input = this._input.slice(match[0].length);
496
544
  this.matched += match[0];
497
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;
498
547
  if (token) return token;
499
548
  else return;
500
549
  }
501
550
  if (this._input === "") {
502
551
  return this.EOF;
503
552
  } else {
504
- this.parseError('Lexical error on line '+(this.yylineno+1)+'. Unrecognized text.\n'+this.showPosition(),
553
+ return this.parseError('Lexical error on line '+(this.yylineno+1)+'. Unrecognized text.\n'+this.showPosition(),
505
554
  {text: "", token: null, line: this.yylineno});
506
555
  }
507
556
  },
@@ -533,248 +582,242 @@ lexer.performAction = function anonymous(yy,yy_,$avoiding_name_collisions,YY_STA
533
582
 
534
583
  var YYSTATE=YY_START
535
584
  switch($avoiding_name_collisions) {
536
- case 0:
585
+ case 0: yy_.yytext = "\\"; return 14;
586
+ break;
587
+ case 1:
537
588
  if(yy_.yytext.slice(-1) !== "\\") this.begin("mu");
538
589
  if(yy_.yytext.slice(-1) === "\\") yy_.yytext = yy_.yytext.substr(0,yy_.yyleng-1), this.begin("emu");
539
590
  if(yy_.yytext) return 14;
540
591
 
541
592
  break;
542
- case 1: return 14;
593
+ case 2: return 14;
543
594
  break;
544
- case 2:
595
+ case 3:
545
596
  if(yy_.yytext.slice(-1) !== "\\") this.popState();
546
597
  if(yy_.yytext.slice(-1) === "\\") yy_.yytext = yy_.yytext.substr(0,yy_.yyleng-1);
547
598
  return 14;
548
599
 
549
600
  break;
550
- case 3: return 24;
601
+ case 4: yy_.yytext = yy_.yytext.substr(0, yy_.yyleng-4); this.popState(); return 15;
551
602
  break;
552
- case 4: return 16;
603
+ case 5: return 25;
553
604
  break;
554
- case 5: return 20;
605
+ case 6: return 16;
555
606
  break;
556
- case 6: return 19;
607
+ case 7: return 20;
557
608
  break;
558
- case 7: return 19;
609
+ case 8: return 19;
559
610
  break;
560
- case 8: return 23;
611
+ case 9: return 19;
561
612
  break;
562
- case 9: return 23;
563
- break;
564
- case 10: yy_.yytext = yy_.yytext.substr(3,yy_.yyleng-5); this.popState(); return 15;
613
+ case 10: return 23;
565
614
  break;
566
615
  case 11: return 22;
567
616
  break;
568
- case 12: return 35;
617
+ case 12: this.popState(); this.begin('com');
569
618
  break;
570
- case 13: return 34;
619
+ case 13: yy_.yytext = yy_.yytext.substr(3,yy_.yyleng-5); this.popState(); return 15;
571
620
  break;
572
- case 14: return 34;
621
+ case 14: return 22;
573
622
  break;
574
623
  case 15: return 37;
575
624
  break;
576
- case 16: /*ignore whitespace*/
625
+ case 16: return 36;
626
+ break;
627
+ case 17: return 36;
577
628
  break;
578
- case 17: this.popState(); return 18;
629
+ case 18: return 40;
579
630
  break;
580
- case 18: this.popState(); return 18;
631
+ case 19: /*ignore whitespace*/
581
632
  break;
582
- case 19: yy_.yytext = yy_.yytext.substr(1,yy_.yyleng-2).replace(/\\"/g,'"'); return 29;
633
+ case 20: this.popState(); return 24;
583
634
  break;
584
- case 20: yy_.yytext = yy_.yytext.substr(1,yy_.yyleng-2).replace(/\\"/g,'"'); return 29;
635
+ case 21: this.popState(); return 18;
585
636
  break;
586
- case 21: yy_.yytext = yy_.yytext.substr(1); return 27;
637
+ case 22: yy_.yytext = yy_.yytext.substr(1,yy_.yyleng-2).replace(/\\"/g,'"'); return 31;
587
638
  break;
588
- case 22: return 31;
639
+ case 23: yy_.yytext = yy_.yytext.substr(1,yy_.yyleng-2).replace(/\\'/g,"'"); return 31;
589
640
  break;
590
- case 23: return 31;
641
+ case 24: return 38;
591
642
  break;
592
- case 24: return 30;
643
+ case 25: return 33;
593
644
  break;
594
- case 25: return 34;
645
+ case 26: return 33;
595
646
  break;
596
- case 26: yy_.yytext = yy_.yytext.substr(1, yy_.yyleng-2); return 34;
647
+ case 27: return 32;
597
648
  break;
598
- case 27: return 'INVALID';
649
+ case 28: return 36;
599
650
  break;
600
- case 28: return 5;
651
+ case 29: yy_.yytext = yy_.yytext.substr(1, yy_.yyleng-2); return 36;
652
+ break;
653
+ case 30: return 'INVALID';
654
+ break;
655
+ case 31: return 5;
601
656
  break;
602
657
  }
603
658
  };
604
- lexer.rules = [/^[^\x00]*?(?=(\{\{))/,/^[^\x00]+/,/^[^\x00]{2,}?(?=(\{\{|$))/,/^\{\{>/,/^\{\{#/,/^\{\{\//,/^\{\{\^/,/^\{\{\s*else\b/,/^\{\{\{/,/^\{\{&/,/^\{\{![\s\S]*?\}\}/,/^\{\{/,/^=/,/^\.(?=[} ])/,/^\.\./,/^[\/.]/,/^\s+/,/^\}\}\}/,/^\}\}/,/^"(\\["]|[^"])*"/,/^'(\\[']|[^'])*'/,/^@[a-zA-Z]+/,/^true(?=[}\s])/,/^false(?=[}\s])/,/^[0-9]+(?=[}\s])/,/^[a-zA-Z0-9_$-]+(?=[=}\s\/.])/,/^\[[^\]]*\]/,/^./,/^$/];
605
- lexer.conditions = {"mu":{"rules":[3,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],"inclusive":false},"emu":{"rules":[2],"inclusive":false},"INITIAL":{"rules":[0,1,28],"inclusive":true}};
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}};
606
661
  return lexer;})()
607
662
  parser.lexer = lexer;
608
- return parser;
609
- })();
610
- if (typeof require !== 'undefined' && typeof exports !== 'undefined') {
611
- exports.parser = handlebars;
612
- exports.parse = function () { return handlebars.parse.apply(handlebars, arguments); }
613
- exports.main = function commonjsMain(args) {
614
- if (!args[1])
615
- throw new Error('Usage: '+args[0]+' FILE');
616
- if (typeof process !== 'undefined') {
617
- var source = require('fs').readFileSync(require('path').join(process.cwd(), args[1]), "utf8");
618
- } else {
619
- var cwd = require("file").path(require("file").cwd());
620
- var source = cwd.join(args[1]).read({charset: "utf-8"});
621
- }
622
- return exports.parser.parse(source);
623
- }
624
- if (typeof module !== 'undefined' && require.main === module) {
625
- exports.main(typeof process !== 'undefined' ? process.argv.slice(1) : require("system").args);
626
- }
627
- };
628
- ;
663
+ function Parser () { this.yy = {}; }Parser.prototype = parser;parser.Parser = Parser;
664
+ return new Parser;
665
+ })();;
629
666
  // lib/handlebars/compiler/base.js
630
- Handlebars.Parser = handlebars;
631
667
 
632
- Handlebars.parse = function(string) {
633
- Handlebars.Parser.yy = Handlebars.AST;
634
- return Handlebars.Parser.parse(string);
635
- };
668
+ Handlebars.Parser = handlebars;
636
669
 
637
- Handlebars.print = function(ast) {
638
- return new Handlebars.PrintVisitor().accept(ast);
639
- };
670
+ Handlebars.parse = function(input) {
640
671
 
641
- Handlebars.logger = {
642
- DEBUG: 0, INFO: 1, WARN: 2, ERROR: 3, level: 3,
672
+ // Just return if an already-compile AST was passed in.
673
+ if(input.constructor === Handlebars.AST.ProgramNode) { return input; }
643
674
 
644
- // override in the host environment
645
- log: function(level, str) {}
675
+ Handlebars.Parser.yy = Handlebars.AST;
676
+ return Handlebars.Parser.parse(input);
646
677
  };
647
-
648
- Handlebars.log = function(level, str) { Handlebars.logger.log(level, str); };
649
678
  ;
650
679
  // lib/handlebars/compiler/ast.js
651
- (function() {
652
-
653
- Handlebars.AST = {};
654
-
655
- Handlebars.AST.ProgramNode = function(statements, inverse) {
656
- this.type = "program";
657
- this.statements = statements;
658
- if(inverse) { this.inverse = new Handlebars.AST.ProgramNode(inverse); }
659
- };
660
-
661
- Handlebars.AST.MustacheNode = function(rawParams, hash, unescaped) {
662
- this.type = "mustache";
663
- this.escaped = !unescaped;
664
- this.hash = hash;
680
+ Handlebars.AST = {};
665
681
 
666
- var id = this.id = rawParams[0];
667
- var params = this.params = rawParams.slice(1);
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); }
686
+ };
668
687
 
669
- // a mustache is an eligible helper if:
670
- // * its id is simple (a single part, not `this` or `..`)
671
- var eligibleHelper = this.eligibleHelper = id.isSimple;
688
+ Handlebars.AST.MustacheNode = function(rawParams, hash, unescaped) {
689
+ this.type = "mustache";
690
+ this.escaped = !unescaped;
691
+ this.hash = hash;
672
692
 
673
- // a mustache is definitely a helper if:
674
- // * it is an eligible helper, and
675
- // * it has at least one parameter or hash segment
676
- this.isHelper = eligibleHelper && (params.length || hash);
693
+ var id = this.id = rawParams[0];
694
+ var params = this.params = rawParams.slice(1);
677
695
 
678
- // if a mustache is an eligible helper but not a definite
679
- // helper, it is ambiguous, and will be resolved in a later
680
- // pass or at runtime.
681
- };
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;
682
699
 
683
- Handlebars.AST.PartialNode = function(id, context) {
684
- this.type = "partial";
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);
685
704
 
686
- // TODO: disallow complex IDs
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.
708
+ };
687
709
 
688
- this.id = id;
689
- this.context = context;
690
- };
710
+ Handlebars.AST.PartialNode = function(partialName, context) {
711
+ this.type = "partial";
712
+ this.partialName = partialName;
713
+ this.context = context;
714
+ };
691
715
 
716
+ Handlebars.AST.BlockNode = function(mustache, program, inverse, close) {
692
717
  var verifyMatch = function(open, close) {
693
718
  if(open.original !== close.original) {
694
719
  throw new Handlebars.Exception(open.original + " doesn't match " + close.original);
695
720
  }
696
721
  };
697
722
 
698
- Handlebars.AST.BlockNode = function(mustache, program, inverse, close) {
699
- verifyMatch(mustache.id, close);
700
- this.type = "block";
701
- this.mustache = mustache;
702
- this.program = program;
703
- this.inverse = inverse;
723
+ verifyMatch(mustache.id, close);
724
+ this.type = "block";
725
+ this.mustache = mustache;
726
+ this.program = program;
727
+ this.inverse = inverse;
704
728
 
705
- if (this.inverse && !this.program) {
706
- this.isInverse = true;
707
- }
708
- };
729
+ if (this.inverse && !this.program) {
730
+ this.isInverse = true;
731
+ }
732
+ };
709
733
 
710
- Handlebars.AST.ContentNode = function(string) {
711
- this.type = "content";
712
- this.string = string;
713
- };
734
+ Handlebars.AST.ContentNode = function(string) {
735
+ this.type = "content";
736
+ this.string = string;
737
+ };
714
738
 
715
- Handlebars.AST.HashNode = function(pairs) {
716
- this.type = "hash";
717
- this.pairs = pairs;
718
- };
739
+ Handlebars.AST.HashNode = function(pairs) {
740
+ this.type = "hash";
741
+ this.pairs = pairs;
742
+ };
719
743
 
720
- Handlebars.AST.IdNode = function(parts) {
721
- this.type = "ID";
722
- this.original = parts.join(".");
744
+ Handlebars.AST.IdNode = function(parts) {
745
+ this.type = "ID";
723
746
 
724
- var dig = [], depth = 0;
747
+ var original = "",
748
+ dig = [],
749
+ depth = 0;
725
750
 
726
- for(var i=0,l=parts.length; i<l; i++) {
727
- var part = parts[i];
751
+ for(var i=0,l=parts.length; i<l; i++) {
752
+ var part = parts[i].part;
753
+ original += (parts[i].separator || '') + part;
728
754
 
729
- if(part === "..") { depth++; }
730
- else if(part === "." || part === "this") { this.isScoped = true; }
731
- else { dig.push(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; }
732
759
  }
760
+ else { dig.push(part); }
761
+ }
733
762
 
734
- this.parts = dig;
735
- this.string = dig.join('.');
736
- this.depth = depth;
763
+ this.original = original;
764
+ this.parts = dig;
765
+ this.string = dig.join('.');
766
+ this.depth = depth;
737
767
 
738
- // an ID is simple if it only has one part, and that part is not
739
- // `..` or `this`.
740
- this.isSimple = parts.length === 1 && !this.isScoped && depth === 0;
741
- };
768
+ // an ID is simple if it only has one part, and that part is not
769
+ // `..` or `this`.
770
+ this.isSimple = parts.length === 1 && !this.isScoped && depth === 0;
742
771
 
743
- Handlebars.AST.DataNode = function(id) {
744
- this.type = "DATA";
745
- this.id = id;
746
- };
772
+ this.stringModeValue = this.string;
773
+ };
747
774
 
748
- Handlebars.AST.StringNode = function(string) {
749
- this.type = "STRING";
750
- this.string = string;
751
- };
775
+ Handlebars.AST.PartialNameNode = function(name) {
776
+ this.type = "PARTIAL_NAME";
777
+ this.name = name.original;
778
+ };
752
779
 
753
- Handlebars.AST.IntegerNode = function(integer) {
754
- this.type = "INTEGER";
755
- this.integer = integer;
756
- };
780
+ Handlebars.AST.DataNode = function(id) {
781
+ this.type = "DATA";
782
+ this.id = id;
783
+ };
757
784
 
758
- Handlebars.AST.BooleanNode = function(bool) {
759
- this.type = "BOOLEAN";
760
- this.bool = bool;
761
- };
785
+ Handlebars.AST.StringNode = function(string) {
786
+ this.type = "STRING";
787
+ this.original =
788
+ this.string =
789
+ this.stringModeValue = string;
790
+ };
762
791
 
763
- Handlebars.AST.CommentNode = function(comment) {
764
- this.type = "comment";
765
- this.comment = comment;
766
- };
792
+ Handlebars.AST.IntegerNode = function(integer) {
793
+ this.type = "INTEGER";
794
+ this.original =
795
+ this.integer = integer;
796
+ this.stringModeValue = Number(integer);
797
+ };
767
798
 
768
- })();;
799
+ Handlebars.AST.BooleanNode = function(bool) {
800
+ this.type = "BOOLEAN";
801
+ this.bool = bool;
802
+ this.stringModeValue = bool === "true";
803
+ };
804
+
805
+ Handlebars.AST.CommentNode = function(comment) {
806
+ this.type = "comment";
807
+ this.comment = comment;
808
+ };
809
+ ;
769
810
  // lib/handlebars/utils.js
811
+
812
+ var errorProps = ['description', 'fileName', 'lineNumber', 'message', 'name', 'number', 'stack'];
813
+
770
814
  Handlebars.Exception = function(message) {
771
815
  var tmp = Error.prototype.constructor.apply(this, arguments);
772
816
 
773
- for (var p in tmp) {
774
- if (tmp.hasOwnProperty(p)) { this[p] = tmp[p]; }
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]];
775
820
  }
776
-
777
- this.message = tmp.message;
778
821
  };
779
822
  Handlebars.Exception.prototype = new Error();
780
823
 
@@ -786,1101 +829,1338 @@ Handlebars.SafeString.prototype.toString = function() {
786
829
  return this.string.toString();
787
830
  };
788
831
 
789
- (function() {
790
- var escape = {
791
- "&": "&amp;",
792
- "<": "&lt;",
793
- ">": "&gt;",
794
- '"': "&quot;",
795
- "'": "&#x27;",
796
- "`": "&#x60;"
797
- };
832
+ var escape = {
833
+ "&": "&amp;",
834
+ "<": "&lt;",
835
+ ">": "&gt;",
836
+ '"': "&quot;",
837
+ "'": "&#x27;",
838
+ "`": "&#x60;"
839
+ };
798
840
 
799
- var badChars = /[&<>"'`]/g;
800
- var possible = /[&<>"'`]/;
841
+ var badChars = /[&<>"'`]/g;
842
+ var possible = /[&<>"'`]/;
801
843
 
802
- var escapeChar = function(chr) {
803
- return escape[chr] || "&amp;";
804
- };
844
+ var escapeChar = function(chr) {
845
+ return escape[chr] || "&amp;";
846
+ };
805
847
 
806
- Handlebars.Utils = {
807
- escapeExpression: function(string) {
808
- // don't escape SafeStrings, since they're already safe
809
- if (string instanceof Handlebars.SafeString) {
810
- return string.toString();
811
- } else if (string == null || string === false) {
812
- return "";
848
+ Handlebars.Utils = {
849
+ extend: function(obj, value) {
850
+ for(var key in value) {
851
+ if(value.hasOwnProperty(key)) {
852
+ obj[key] = value[key];
813
853
  }
854
+ }
855
+ },
814
856
 
815
- if(!possible.test(string)) { return string; }
816
- return string.replace(badChars, escapeChar);
817
- },
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) {
862
+ return "";
863
+ }
818
864
 
819
- isEmpty: function(value) {
820
- if (typeof value === "undefined") {
821
- return true;
822
- } else if (value === null) {
823
- return true;
824
- } else if (value === false) {
825
- return true;
826
- } else if(Object.prototype.toString.call(value) === "[object Array]" && value.length === 0) {
827
- return true;
828
- } else {
829
- return false;
830
- }
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();
869
+
870
+ if(!possible.test(string)) { return string; }
871
+ return string.replace(badChars, escapeChar);
872
+ },
873
+
874
+ isEmpty: function(value) {
875
+ if (!value && value !== 0) {
876
+ return true;
877
+ } else if(toString.call(value) === "[object Array]" && value.length === 0) {
878
+ return true;
879
+ } else {
880
+ return false;
831
881
  }
832
- };
833
- })();;
882
+ }
883
+ };
884
+ ;
834
885
  // lib/handlebars/compiler/compiler.js
835
886
 
836
887
  /*jshint eqnull:true*/
837
- Handlebars.Compiler = function() {};
838
- Handlebars.JavaScriptCompiler = function() {};
888
+ var Compiler = Handlebars.Compiler = function() {};
889
+ var JavaScriptCompiler = Handlebars.JavaScriptCompiler = function() {};
839
890
 
840
- (function(Compiler, JavaScriptCompiler) {
841
- // the foundHelper register will disambiguate helper lookup from finding a
842
- // function in a context. This is necessary for mustache compatibility, which
843
- // requires that context functions in blocks are evaluated by blockHelperMissing,
844
- // and then proceed as if the resulting value was provided to blockHelperMissing.
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.
845
895
 
846
- Compiler.prototype = {
847
- compiler: Compiler,
896
+ Compiler.prototype = {
897
+ compiler: Compiler,
848
898
 
849
- disassemble: function() {
850
- var opcodes = this.opcodes, opcode, out = [], params, param;
899
+ disassemble: function() {
900
+ var opcodes = this.opcodes, opcode, out = [], params, param;
851
901
 
852
- for (var i=0, l=opcodes.length; i<l; i++) {
853
- opcode = opcodes[i];
902
+ for (var i=0, l=opcodes.length; i<l; i++) {
903
+ opcode = opcodes[i];
854
904
 
855
- if (opcode.opcode === 'DECLARE') {
856
- out.push("DECLARE " + opcode.name + "=" + opcode.value);
857
- } else {
858
- params = [];
859
- for (var j=0; j<opcode.args.length; j++) {
860
- param = opcode.args[j];
861
- if (typeof param === "string") {
862
- param = "\"" + param.replace("\n", "\\n") + "\"";
863
- }
864
- params.push(param);
905
+ if (opcode.opcode === 'DECLARE') {
906
+ out.push("DECLARE " + opcode.name + "=" + opcode.value);
907
+ } else {
908
+ params = [];
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") + "\"";
865
913
  }
866
- out.push(opcode.opcode + " " + params.join(" "));
914
+ params.push(param);
867
915
  }
916
+ out.push(opcode.opcode + " " + params.join(" "));
868
917
  }
918
+ }
869
919
 
870
- return out.join("\n");
871
- },
920
+ return out.join("\n");
921
+ },
922
+ equals: function(other) {
923
+ var len = this.opcodes.length;
924
+ if (other.opcodes.length !== len) {
925
+ return false;
926
+ }
872
927
 
873
- guid: 0,
874
-
875
- compile: function(program, options) {
876
- this.children = [];
877
- this.depths = {list: []};
878
- this.options = options;
879
-
880
- // These changes will propagate to the other compiler components
881
- var knownHelpers = this.options.knownHelpers;
882
- this.options.knownHelpers = {
883
- 'helperMissing': true,
884
- 'blockHelperMissing': true,
885
- 'each': true,
886
- 'if': true,
887
- 'unless': true,
888
- 'with': true,
889
- 'log': true
890
- };
891
- if (knownHelpers) {
892
- for (var name in knownHelpers) {
893
- this.options.knownHelpers[name] = knownHelpers[name];
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) {
932
+ return false;
933
+ }
934
+ for (var j = 0; j < opcode.args.length; j++) {
935
+ if (opcode.args[j] !== otherOpcode.args[j]) {
936
+ return false;
894
937
  }
895
938
  }
939
+ }
896
940
 
897
- return this.program(program);
898
- },
899
-
900
- accept: function(node) {
901
- return this[node.type](node);
902
- },
941
+ len = this.children.length;
942
+ if (other.children.length !== len) {
943
+ return false;
944
+ }
945
+ for (i = 0; i < len; i++) {
946
+ if (!this.children[i].equals(other.children[i])) {
947
+ return false;
948
+ }
949
+ }
903
950
 
904
- program: function(program) {
905
- var statements = program.statements, statement;
906
- this.opcodes = [];
951
+ return true;
952
+ },
907
953
 
908
- for(var i=0, l=statements.length; i<l; i++) {
909
- statement = statements[i];
910
- this[statement.type](statement);
954
+ guid: 0,
955
+
956
+ compile: function(program, options) {
957
+ this.children = [];
958
+ this.depths = {list: []};
959
+ this.options = options;
960
+
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,
966
+ 'each': true,
967
+ 'if': true,
968
+ 'unless': true,
969
+ 'with': true,
970
+ 'log': true
971
+ };
972
+ if (knownHelpers) {
973
+ for (var name in knownHelpers) {
974
+ this.options.knownHelpers[name] = knownHelpers[name];
911
975
  }
912
- this.isSimple = l === 1;
913
-
914
- this.depths.list = this.depths.list.sort(function(a, b) {
915
- return a - b;
916
- });
976
+ }
917
977
 
918
- return this;
919
- },
978
+ return this.program(program);
979
+ },
920
980
 
921
- compileProgram: function(program) {
922
- var result = new this.compiler().compile(program, this.options);
923
- var guid = this.guid++, depth;
981
+ accept: function(node) {
982
+ return this[node.type](node);
983
+ },
924
984
 
925
- this.usePartial = this.usePartial || result.usePartial;
985
+ program: function(program) {
986
+ var statements = program.statements, statement;
987
+ this.opcodes = [];
926
988
 
927
- this.children[guid] = result;
989
+ for(var i=0, l=statements.length; i<l; i++) {
990
+ statement = statements[i];
991
+ this[statement.type](statement);
992
+ }
993
+ this.isSimple = l === 1;
928
994
 
929
- for(var i=0, l=result.depths.list.length; i<l; i++) {
930
- depth = result.depths.list[i];
995
+ this.depths.list = this.depths.list.sort(function(a, b) {
996
+ return a - b;
997
+ });
931
998
 
932
- if(depth < 2) { continue; }
933
- else { this.addDepth(depth - 1); }
934
- }
999
+ return this;
1000
+ },
935
1001
 
936
- return guid;
937
- },
1002
+ compileProgram: function(program) {
1003
+ var result = new this.compiler().compile(program, this.options);
1004
+ var guid = this.guid++, depth;
938
1005
 
939
- block: function(block) {
940
- var mustache = block.mustache,
941
- program = block.program,
942
- inverse = block.inverse;
1006
+ this.usePartial = this.usePartial || result.usePartial;
943
1007
 
944
- if (program) {
945
- program = this.compileProgram(program);
946
- }
1008
+ this.children[guid] = result;
947
1009
 
948
- if (inverse) {
949
- inverse = this.compileProgram(inverse);
950
- }
1010
+ for(var i=0, l=result.depths.list.length; i<l; i++) {
1011
+ depth = result.depths.list[i];
951
1012
 
952
- var type = this.classifyMustache(mustache);
1013
+ if(depth < 2) { continue; }
1014
+ else { this.addDepth(depth - 1); }
1015
+ }
953
1016
 
954
- if (type === "helper") {
955
- this.helperMustache(mustache, program, inverse);
956
- } else if (type === "simple") {
957
- this.simpleMustache(mustache);
1017
+ return guid;
1018
+ },
958
1019
 
959
- // now that the simple mustache is resolved, we need to
960
- // evaluate it by executing `blockHelperMissing`
961
- this.opcode('pushProgram', program);
962
- this.opcode('pushProgram', inverse);
963
- this.opcode('pushLiteral', '{}');
964
- this.opcode('blockValue');
965
- } else {
966
- this.ambiguousMustache(mustache, program, inverse);
967
-
968
- // now that the simple mustache is resolved, we need to
969
- // evaluate it by executing `blockHelperMissing`
970
- this.opcode('pushProgram', program);
971
- this.opcode('pushProgram', inverse);
972
- this.opcode('pushLiteral', '{}');
973
- this.opcode('ambiguousBlockValue');
974
- }
1020
+ block: function(block) {
1021
+ var mustache = block.mustache,
1022
+ program = block.program,
1023
+ inverse = block.inverse;
975
1024
 
976
- this.opcode('append');
977
- },
1025
+ if (program) {
1026
+ program = this.compileProgram(program);
1027
+ }
978
1028
 
979
- hash: function(hash) {
980
- var pairs = hash.pairs, pair, val;
1029
+ if (inverse) {
1030
+ inverse = this.compileProgram(inverse);
1031
+ }
981
1032
 
982
- this.opcode('push', '{}');
1033
+ var type = this.classifyMustache(mustache);
983
1034
 
984
- for(var i=0, l=pairs.length; i<l; i++) {
985
- pair = pairs[i];
986
- val = pair[1];
1035
+ if (type === "helper") {
1036
+ this.helperMustache(mustache, program, inverse);
1037
+ } else if (type === "simple") {
1038
+ this.simpleMustache(mustache);
987
1039
 
988
- this.accept(val);
989
- this.opcode('assignToHash', pair[0]);
990
- }
991
- },
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');
1046
+ } else {
1047
+ this.ambiguousMustache(mustache, program, inverse);
992
1048
 
993
- partial: function(partial) {
994
- var id = partial.id;
995
- this.usePartial = true;
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');
1055
+ }
996
1056
 
997
- if(partial.context) {
998
- this.ID(partial.context);
999
- } else {
1000
- this.opcode('push', 'depth0');
1001
- }
1057
+ this.opcode('append');
1058
+ },
1002
1059
 
1003
- this.opcode('invokePartial', id.original);
1004
- this.opcode('append');
1005
- },
1060
+ hash: function(hash) {
1061
+ var pairs = hash.pairs, pair, val;
1006
1062
 
1007
- content: function(content) {
1008
- this.opcode('appendContent', content.string);
1009
- },
1063
+ this.opcode('pushHash');
1010
1064
 
1011
- mustache: function(mustache) {
1012
- var options = this.options;
1013
- var type = this.classifyMustache(mustache);
1065
+ for(var i=0, l=pairs.length; i<l; i++) {
1066
+ pair = pairs[i];
1067
+ val = pair[1];
1014
1068
 
1015
- if (type === "simple") {
1016
- this.simpleMustache(mustache);
1017
- } else if (type === "helper") {
1018
- this.helperMustache(mustache);
1069
+ if (this.options.stringParams) {
1070
+ if(val.depth) {
1071
+ this.addDepth(val.depth);
1072
+ }
1073
+ this.opcode('getContext', val.depth || 0);
1074
+ this.opcode('pushStringParam', val.stringModeValue, val.type);
1019
1075
  } else {
1020
- this.ambiguousMustache(mustache);
1076
+ this.accept(val);
1021
1077
  }
1022
1078
 
1023
- if(mustache.escaped && !options.noEscape) {
1024
- this.opcode('appendEscaped');
1025
- } else {
1026
- this.opcode('append');
1027
- }
1028
- },
1079
+ this.opcode('assignToHash', pair[0]);
1080
+ }
1081
+ this.opcode('popHash');
1082
+ },
1029
1083
 
1030
- ambiguousMustache: function(mustache, program, inverse) {
1031
- var id = mustache.id, name = id.parts[0];
1084
+ partial: function(partial) {
1085
+ var partialName = partial.partialName;
1086
+ this.usePartial = true;
1032
1087
 
1033
- this.opcode('getContext', id.depth);
1088
+ if(partial.context) {
1089
+ this.ID(partial.context);
1090
+ } else {
1091
+ this.opcode('push', 'depth0');
1092
+ }
1034
1093
 
1035
- this.opcode('pushProgram', program);
1036
- this.opcode('pushProgram', inverse);
1094
+ this.opcode('invokePartial', partialName.name);
1095
+ this.opcode('append');
1096
+ },
1037
1097
 
1038
- this.opcode('invokeAmbiguous', name);
1039
- },
1098
+ content: function(content) {
1099
+ this.opcode('appendContent', content.string);
1100
+ },
1040
1101
 
1041
- simpleMustache: function(mustache, program, inverse) {
1042
- var id = mustache.id;
1102
+ mustache: function(mustache) {
1103
+ var options = this.options;
1104
+ var type = this.classifyMustache(mustache);
1043
1105
 
1044
- if (id.type === 'DATA') {
1045
- this.DATA(id);
1046
- } else if (id.parts.length) {
1047
- this.ID(id);
1048
- } else {
1049
- // Simplified ID for `this`
1050
- this.addDepth(id.depth);
1051
- this.opcode('getContext', id.depth);
1052
- this.opcode('pushContext');
1053
- }
1106
+ if (type === "simple") {
1107
+ this.simpleMustache(mustache);
1108
+ } else if (type === "helper") {
1109
+ this.helperMustache(mustache);
1110
+ } else {
1111
+ this.ambiguousMustache(mustache);
1112
+ }
1054
1113
 
1055
- this.opcode('resolvePossibleLambda');
1056
- },
1114
+ if(mustache.escaped && !options.noEscape) {
1115
+ this.opcode('appendEscaped');
1116
+ } else {
1117
+ this.opcode('append');
1118
+ }
1119
+ },
1057
1120
 
1058
- helperMustache: function(mustache, program, inverse) {
1059
- var params = this.setupFullMustacheParams(mustache, program, inverse),
1060
- name = mustache.id.parts[0];
1121
+ ambiguousMustache: function(mustache, program, inverse) {
1122
+ var id = mustache.id,
1123
+ name = id.parts[0],
1124
+ isBlock = program != null || inverse != null;
1061
1125
 
1062
- if (this.options.knownHelpers[name]) {
1063
- this.opcode('invokeKnownHelper', params.length, name);
1064
- } else if (this.knownHelpersOnly) {
1065
- throw new Error("You specified knownHelpersOnly, but used the unknown helper " + name);
1066
- } else {
1067
- this.opcode('invokeHelper', params.length, name);
1068
- }
1069
- },
1126
+ this.opcode('getContext', id.depth);
1070
1127
 
1071
- ID: function(id) {
1128
+ this.opcode('pushProgram', program);
1129
+ this.opcode('pushProgram', inverse);
1130
+
1131
+ this.opcode('invokeAmbiguous', name, isBlock);
1132
+ },
1133
+
1134
+ simpleMustache: function(mustache) {
1135
+ var id = mustache.id;
1136
+
1137
+ if (id.type === 'DATA') {
1138
+ this.DATA(id);
1139
+ } else if (id.parts.length) {
1140
+ this.ID(id);
1141
+ } else {
1142
+ // Simplified ID for `this`
1072
1143
  this.addDepth(id.depth);
1073
1144
  this.opcode('getContext', id.depth);
1145
+ this.opcode('pushContext');
1146
+ }
1074
1147
 
1075
- var name = id.parts[0];
1076
- if (!name) {
1077
- this.opcode('pushContext');
1078
- } else {
1079
- this.opcode('lookupOnContext', id.parts[0]);
1080
- }
1148
+ this.opcode('resolvePossibleLambda');
1149
+ },
1081
1150
 
1082
- for(var i=1, l=id.parts.length; i<l; i++) {
1083
- this.opcode('lookup', id.parts[i]);
1084
- }
1085
- },
1151
+ helperMustache: function(mustache, program, inverse) {
1152
+ var params = this.setupFullMustacheParams(mustache, program, inverse),
1153
+ name = mustache.id.parts[0];
1086
1154
 
1087
- DATA: function(data) {
1088
- this.options.data = true;
1089
- this.opcode('lookupData', data.id);
1090
- },
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);
1159
+ } else {
1160
+ this.opcode('invokeHelper', params.length, name);
1161
+ }
1162
+ },
1091
1163
 
1092
- STRING: function(string) {
1093
- this.opcode('pushString', string.string);
1094
- },
1164
+ ID: function(id) {
1165
+ this.addDepth(id.depth);
1166
+ this.opcode('getContext', id.depth);
1095
1167
 
1096
- INTEGER: function(integer) {
1097
- this.opcode('pushLiteral', integer.integer);
1098
- },
1168
+ var name = id.parts[0];
1169
+ if (!name) {
1170
+ this.opcode('pushContext');
1171
+ } else {
1172
+ this.opcode('lookupOnContext', id.parts[0]);
1173
+ }
1099
1174
 
1100
- BOOLEAN: function(bool) {
1101
- this.opcode('pushLiteral', bool.bool);
1102
- },
1175
+ for(var i=1, l=id.parts.length; i<l; i++) {
1176
+ this.opcode('lookup', id.parts[i]);
1177
+ }
1178
+ },
1103
1179
 
1104
- comment: function() {},
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);
1184
+ }
1105
1185
 
1106
- // HELPERS
1107
- opcode: function(name) {
1108
- this.opcodes.push({ opcode: name, args: [].slice.call(arguments, 1) });
1109
- },
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]);
1190
+ }
1191
+ },
1110
1192
 
1111
- declare: function(name, value) {
1112
- this.opcodes.push({ opcode: 'DECLARE', name: name, value: value });
1113
- },
1193
+ STRING: function(string) {
1194
+ this.opcode('pushString', string.string);
1195
+ },
1114
1196
 
1115
- addDepth: function(depth) {
1116
- if(isNaN(depth)) { throw new Error("EWOT"); }
1117
- if(depth === 0) { return; }
1197
+ INTEGER: function(integer) {
1198
+ this.opcode('pushLiteral', integer.integer);
1199
+ },
1118
1200
 
1119
- if(!this.depths[depth]) {
1120
- this.depths[depth] = true;
1121
- this.depths.list.push(depth);
1122
- }
1123
- },
1201
+ BOOLEAN: function(bool) {
1202
+ this.opcode('pushLiteral', bool.bool);
1203
+ },
1124
1204
 
1125
- classifyMustache: function(mustache) {
1126
- var isHelper = mustache.isHelper;
1127
- var isEligible = mustache.eligibleHelper;
1128
- var options = this.options;
1205
+ comment: function() {},
1129
1206
 
1130
- // if ambiguous, we can possibly resolve the ambiguity now
1131
- if (isEligible && !isHelper) {
1132
- var name = mustache.id.parts[0];
1207
+ // HELPERS
1208
+ opcode: function(name) {
1209
+ this.opcodes.push({ opcode: name, args: [].slice.call(arguments, 1) });
1210
+ },
1133
1211
 
1134
- if (options.knownHelpers[name]) {
1135
- isHelper = true;
1136
- } else if (options.knownHelpersOnly) {
1137
- isEligible = false;
1138
- }
1139
- }
1212
+ declare: function(name, value) {
1213
+ this.opcodes.push({ opcode: 'DECLARE', name: name, value: value });
1214
+ },
1140
1215
 
1141
- if (isHelper) { return "helper"; }
1142
- else if (isEligible) { return "ambiguous"; }
1143
- else { return "simple"; }
1144
- },
1216
+ addDepth: function(depth) {
1217
+ if(isNaN(depth)) { throw new Error("EWOT"); }
1218
+ if(depth === 0) { return; }
1145
1219
 
1146
- pushParams: function(params) {
1147
- var i = params.length, param;
1220
+ if(!this.depths[depth]) {
1221
+ this.depths[depth] = true;
1222
+ this.depths.list.push(depth);
1223
+ }
1224
+ },
1148
1225
 
1149
- while(i--) {
1150
- param = params[i];
1226
+ classifyMustache: function(mustache) {
1227
+ var isHelper = mustache.isHelper;
1228
+ var isEligible = mustache.eligibleHelper;
1229
+ var options = this.options;
1151
1230
 
1152
- if(this.options.stringParams) {
1153
- if(param.depth) {
1154
- this.addDepth(param.depth);
1155
- }
1231
+ // if ambiguous, we can possibly resolve the ambiguity now
1232
+ if (isEligible && !isHelper) {
1233
+ var name = mustache.id.parts[0];
1156
1234
 
1157
- this.opcode('getContext', param.depth || 0);
1158
- this.opcode('pushStringParam', param.string);
1159
- } else {
1160
- this[param.type](param);
1161
- }
1235
+ if (options.knownHelpers[name]) {
1236
+ isHelper = true;
1237
+ } else if (options.knownHelpersOnly) {
1238
+ isEligible = false;
1162
1239
  }
1163
- },
1164
-
1165
- setupMustacheParams: function(mustache) {
1166
- var params = mustache.params;
1167
- this.pushParams(params);
1240
+ }
1168
1241
 
1169
- if(mustache.hash) {
1170
- this.hash(mustache.hash);
1171
- } else {
1172
- this.opcode('pushLiteral', '{}');
1173
- }
1242
+ if (isHelper) { return "helper"; }
1243
+ else if (isEligible) { return "ambiguous"; }
1244
+ else { return "simple"; }
1245
+ },
1174
1246
 
1175
- return params;
1176
- },
1247
+ pushParams: function(params) {
1248
+ var i = params.length, param;
1177
1249
 
1178
- // this will replace setupMustacheParams when we're done
1179
- setupFullMustacheParams: function(mustache, program, inverse) {
1180
- var params = mustache.params;
1181
- this.pushParams(params);
1250
+ while(i--) {
1251
+ param = params[i];
1182
1252
 
1183
- this.opcode('pushProgram', program);
1184
- this.opcode('pushProgram', inverse);
1253
+ if(this.options.stringParams) {
1254
+ if(param.depth) {
1255
+ this.addDepth(param.depth);
1256
+ }
1185
1257
 
1186
- if(mustache.hash) {
1187
- this.hash(mustache.hash);
1258
+ this.opcode('getContext', param.depth || 0);
1259
+ this.opcode('pushStringParam', param.stringModeValue, param.type);
1188
1260
  } else {
1189
- this.opcode('pushLiteral', '{}');
1261
+ this[param.type](param);
1190
1262
  }
1263
+ }
1264
+ },
1191
1265
 
1192
- return params;
1266
+ setupMustacheParams: function(mustache) {
1267
+ var params = mustache.params;
1268
+ this.pushParams(params);
1269
+
1270
+ if(mustache.hash) {
1271
+ this.hash(mustache.hash);
1272
+ } else {
1273
+ this.opcode('emptyHash');
1193
1274
  }
1194
- };
1195
1275
 
1196
- var Literal = function(value) {
1197
- this.value = value;
1198
- };
1276
+ return params;
1277
+ },
1199
1278
 
1200
- JavaScriptCompiler.prototype = {
1201
- // PUBLIC API: You can override these methods in a subclass to provide
1202
- // alternative compiled forms for name lookup and buffering semantics
1203
- nameLookup: function(parent, name, type) {
1204
- if (/^[0-9]+$/.test(name)) {
1205
- return parent + "[" + name + "]";
1206
- } else if (JavaScriptCompiler.isValidJavaScriptVariableName(name)) {
1207
- return parent + "." + name;
1208
- }
1209
- else {
1210
- return parent + "['" + name + "']";
1211
- }
1212
- },
1279
+ // this will replace setupMustacheParams when we're done
1280
+ setupFullMustacheParams: function(mustache, program, inverse) {
1281
+ var params = mustache.params;
1282
+ this.pushParams(params);
1213
1283
 
1214
- appendToBuffer: function(string) {
1215
- if (this.environment.isSimple) {
1216
- return "return " + string + ";";
1217
- } else {
1218
- return "buffer += " + string + ";";
1219
- }
1220
- },
1284
+ this.opcode('pushProgram', program);
1285
+ this.opcode('pushProgram', inverse);
1221
1286
 
1222
- initializeBuffer: function() {
1223
- return this.quotedString("");
1224
- },
1287
+ if(mustache.hash) {
1288
+ this.hash(mustache.hash);
1289
+ } else {
1290
+ this.opcode('emptyHash');
1291
+ }
1225
1292
 
1226
- namespace: "Handlebars",
1227
- // END PUBLIC API
1293
+ return params;
1294
+ }
1295
+ };
1228
1296
 
1229
- compile: function(environment, options, context, asObject) {
1230
- this.environment = environment;
1231
- this.options = options || {};
1297
+ var Literal = function(value) {
1298
+ this.value = value;
1299
+ };
1232
1300
 
1233
- Handlebars.log(Handlebars.logger.DEBUG, this.environment.disassemble() + "\n\n");
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;
1309
+ }
1310
+ else {
1311
+ return parent + "['" + name + "']";
1312
+ }
1313
+ },
1234
1314
 
1235
- this.name = this.environment.name;
1236
- this.isChild = !!context;
1237
- this.context = context || {
1238
- programs: [],
1239
- aliases: { }
1315
+ appendToBuffer: function(string) {
1316
+ if (this.environment.isSimple) {
1317
+ return "return " + string + ";";
1318
+ } else {
1319
+ return {
1320
+ appendToBuffer: true,
1321
+ content: string,
1322
+ toString: function() { return "buffer += " + string + ";"; }
1240
1323
  };
1324
+ }
1325
+ },
1241
1326
 
1242
- this.preamble();
1327
+ initializeBuffer: function() {
1328
+ return this.quotedString("");
1329
+ },
1243
1330
 
1244
- this.stackSlot = 0;
1245
- this.stackVars = [];
1246
- this.registers = { list: [] };
1247
- this.compileStack = [];
1331
+ namespace: "Handlebars",
1332
+ // END PUBLIC API
1248
1333
 
1249
- this.compileChildren(environment, options);
1334
+ compile: function(environment, options, context, asObject) {
1335
+ this.environment = environment;
1336
+ this.options = options || {};
1250
1337
 
1251
- var opcodes = environment.opcodes, opcode;
1338
+ Handlebars.log(Handlebars.logger.DEBUG, this.environment.disassemble() + "\n\n");
1252
1339
 
1253
- this.i = 0;
1340
+ this.name = this.environment.name;
1341
+ this.isChild = !!context;
1342
+ this.context = context || {
1343
+ programs: [],
1344
+ environments: [],
1345
+ aliases: { }
1346
+ };
1254
1347
 
1255
- for(l=opcodes.length; this.i<l; this.i++) {
1256
- opcode = opcodes[this.i];
1348
+ this.preamble();
1257
1349
 
1258
- if(opcode.opcode === 'DECLARE') {
1259
- this[opcode.name] = opcode.value;
1260
- } else {
1261
- this[opcode.opcode].apply(this, opcode.args);
1262
- }
1263
- }
1350
+ this.stackSlot = 0;
1351
+ this.stackVars = [];
1352
+ this.registers = { list: [] };
1353
+ this.compileStack = [];
1354
+ this.inlineStack = [];
1264
1355
 
1265
- return this.createFunctionContext(asObject);
1266
- },
1356
+ this.compileChildren(environment, options);
1267
1357
 
1268
- nextOpcode: function() {
1269
- var opcodes = this.environment.opcodes, opcode = opcodes[this.i + 1];
1270
- return opcodes[this.i + 1];
1271
- },
1358
+ var opcodes = environment.opcodes, opcode;
1272
1359
 
1273
- eat: function(opcode) {
1274
- this.i = this.i + 1;
1275
- },
1360
+ this.i = 0;
1276
1361
 
1277
- preamble: function() {
1278
- var out = [];
1362
+ for(l=opcodes.length; this.i<l; this.i++) {
1363
+ opcode = opcodes[this.i];
1279
1364
 
1280
- if (!this.isChild) {
1281
- var namespace = this.namespace;
1282
- var copies = "helpers = helpers || " + namespace + ".helpers;";
1283
- if (this.environment.usePartial) { copies = copies + " partials = partials || " + namespace + ".partials;"; }
1284
- if (this.options.data) { copies = copies + " data = data || {};"; }
1285
- out.push(copies);
1365
+ if(opcode.opcode === 'DECLARE') {
1366
+ this[opcode.name] = opcode.value;
1286
1367
  } else {
1287
- out.push('');
1368
+ this[opcode.opcode].apply(this, opcode.args);
1288
1369
  }
1370
+ }
1289
1371
 
1290
- if (!this.environment.isSimple) {
1291
- out.push(", buffer = " + this.initializeBuffer());
1292
- } else {
1293
- out.push("");
1294
- }
1372
+ return this.createFunctionContext(asObject);
1373
+ },
1295
1374
 
1296
- // track the last context pushed into place to allow skipping the
1297
- // getContext opcode when it would be a noop
1298
- this.lastContext = 0;
1299
- this.source = out;
1300
- },
1375
+ nextOpcode: function() {
1376
+ var opcodes = this.environment.opcodes;
1377
+ return opcodes[this.i + 1];
1378
+ },
1301
1379
 
1302
- createFunctionContext: function(asObject) {
1303
- var locals = this.stackVars.concat(this.registers.list);
1380
+ eat: function() {
1381
+ this.i = this.i + 1;
1382
+ },
1304
1383
 
1305
- if(locals.length > 0) {
1306
- this.source[1] = this.source[1] + ", " + locals.join(", ");
1307
- }
1384
+ preamble: function() {
1385
+ var out = [];
1386
+
1387
+ if (!this.isChild) {
1388
+ var namespace = this.namespace;
1389
+
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 || {};"; }
1393
+ out.push(copies);
1394
+ } else {
1395
+ out.push('');
1396
+ }
1397
+
1398
+ if (!this.environment.isSimple) {
1399
+ out.push(", buffer = " + this.initializeBuffer());
1400
+ } else {
1401
+ out.push("");
1402
+ }
1308
1403
 
1309
- // Generate minimizer alias mappings
1310
- if (!this.isChild) {
1311
- var aliases = [];
1312
- for (var alias in this.context.aliases) {
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;
1407
+ this.source = out;
1408
+ },
1409
+
1410
+ createFunctionContext: function(asObject) {
1411
+ var locals = this.stackVars.concat(this.registers.list);
1412
+
1413
+ if(locals.length > 0) {
1414
+ this.source[1] = this.source[1] + ", " + locals.join(", ");
1415
+ }
1416
+
1417
+ // Generate minimizer alias mappings
1418
+ if (!this.isChild) {
1419
+ for (var alias in this.context.aliases) {
1420
+ if (this.context.aliases.hasOwnProperty(alias)) {
1313
1421
  this.source[1] = this.source[1] + ', ' + alias + '=' + this.context.aliases[alias];
1314
1422
  }
1315
1423
  }
1424
+ }
1316
1425
 
1317
- if (this.source[1]) {
1318
- this.source[1] = "var " + this.source[1].substring(2) + ";";
1319
- }
1426
+ if (this.source[1]) {
1427
+ this.source[1] = "var " + this.source[1].substring(2) + ";";
1428
+ }
1320
1429
 
1321
- // Merge children
1322
- if (!this.isChild) {
1323
- this.source[1] += '\n' + this.context.programs.join('\n') + '\n';
1324
- }
1430
+ // Merge children
1431
+ if (!this.isChild) {
1432
+ this.source[1] += '\n' + this.context.programs.join('\n') + '\n';
1433
+ }
1325
1434
 
1326
- if (!this.environment.isSimple) {
1327
- this.source.push("return buffer;");
1328
- }
1435
+ if (!this.environment.isSimple) {
1436
+ this.source.push("return buffer;");
1437
+ }
1329
1438
 
1330
- var params = this.isChild ? ["depth0", "data"] : ["Handlebars", "depth0", "helpers", "partials", "data"];
1439
+ var params = this.isChild ? ["depth0", "data"] : ["Handlebars", "depth0", "helpers", "partials", "data"];
1331
1440
 
1332
- for(var i=0, l=this.environment.depths.list.length; i<l; i++) {
1333
- params.push("depth" + this.environment.depths.list[i]);
1334
- }
1441
+ for(var i=0, l=this.environment.depths.list.length; i<l; i++) {
1442
+ params.push("depth" + this.environment.depths.list[i]);
1443
+ }
1444
+
1445
+ // Perform a second pass over the output to merge content when possible
1446
+ var source = this.mergeSource();
1335
1447
 
1336
- if (asObject) {
1337
- params.push(this.source.join("\n "));
1448
+ if (!this.isChild) {
1449
+ var revision = Handlebars.COMPILER_REVISION,
1450
+ versions = Handlebars.REVISION_CHANGES[revision];
1451
+ source = "this.compilerInfo = ["+revision+",'"+versions+"'];\n"+source;
1452
+ }
1338
1453
 
1339
- return Function.apply(this, params);
1454
+ if (asObject) {
1455
+ params.push(source);
1456
+
1457
+ return Function.apply(this, params);
1458
+ } else {
1459
+ var functionSource = 'function ' + (this.name || '') + '(' + params.join(',') + ') {\n ' + source + '}';
1460
+ Handlebars.log(Handlebars.logger.DEBUG, functionSource + "\n\n");
1461
+ return functionSource;
1462
+ }
1463
+ },
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.
1467
+ var source = '',
1468
+ buffer;
1469
+ for (var i = 0, len = this.source.length; i < len; i++) {
1470
+ var line = this.source[i];
1471
+ if (line.appendToBuffer) {
1472
+ if (buffer) {
1473
+ buffer = buffer + '\n + ' + line.content;
1474
+ } else {
1475
+ buffer = line.content;
1476
+ }
1340
1477
  } else {
1341
- var functionSource = 'function ' + (this.name || '') + '(' + params.join(',') + ') {\n ' + this.source.join("\n ") + '}';
1342
- Handlebars.log(Handlebars.logger.DEBUG, functionSource + "\n\n");
1343
- return functionSource;
1478
+ if (buffer) {
1479
+ source += 'buffer += ' + buffer + ';\n ';
1480
+ buffer = undefined;
1481
+ }
1482
+ source += line + '\n ';
1344
1483
  }
1345
- },
1484
+ }
1485
+ return source;
1486
+ },
1346
1487
 
1347
- // [blockValue]
1348
- //
1349
- // On stack, before: hash, inverse, program, value
1350
- // On stack, after: return value of blockHelperMissing
1351
- //
1352
- // The purpose of this opcode is to take a block of the form
1353
- // `{{#foo}}...{{/foo}}`, resolve the value of `foo`, and
1354
- // replace it on the stack with the result of properly
1355
- // invoking blockHelperMissing.
1356
- blockValue: function() {
1357
- this.context.aliases.blockHelperMissing = 'helpers.blockHelperMissing';
1358
-
1359
- var params = ["depth0"];
1360
- this.setupParams(0, params);
1361
-
1362
- this.replaceStack(function(current) {
1363
- params.splice(1, 0, current);
1364
- return current + " = blockHelperMissing.call(" + params.join(", ") + ")";
1365
- });
1366
- },
1488
+ // [blockValue]
1489
+ //
1490
+ // On stack, before: hash, inverse, program, value
1491
+ // On stack, after: return value of blockHelperMissing
1492
+ //
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';
1499
+
1500
+ var params = ["depth0"];
1501
+ this.setupParams(0, params);
1502
+
1503
+ this.replaceStack(function(current) {
1504
+ params.splice(1, 0, current);
1505
+ return "blockHelperMissing.call(" + params.join(", ") + ")";
1506
+ });
1507
+ },
1367
1508
 
1368
- // [ambiguousBlockValue]
1369
- //
1370
- // On stack, before: hash, inverse, program, value
1371
- // Compiler value, before: lastHelper=value of last found helper, if any
1372
- // On stack, after, if no lastHelper: same as [blockValue]
1373
- // On stack, after, if lastHelper: value
1374
- ambiguousBlockValue: function() {
1375
- this.context.aliases.blockHelperMissing = 'helpers.blockHelperMissing';
1509
+ // [ambiguousBlockValue]
1510
+ //
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';
1376
1517
 
1377
- var params = ["depth0"];
1378
- this.setupParams(0, params);
1518
+ var params = ["depth0"];
1519
+ this.setupParams(0, params);
1379
1520
 
1380
- var current = this.topStack();
1381
- params.splice(1, 0, current);
1521
+ var current = this.topStack();
1522
+ params.splice(1, 0, current);
1382
1523
 
1383
- this.source.push("if (!" + this.lastHelper + ") { " + current + " = blockHelperMissing.call(" + params.join(", ") + "); }");
1384
- },
1524
+ // Use the options value generated from the invocation
1525
+ params[params.length-1] = 'options';
1385
1526
 
1386
- // [appendContent]
1387
- //
1388
- // On stack, before: ...
1389
- // On stack, after: ...
1390
- //
1391
- // Appends the string value of `content` to the current buffer
1392
- appendContent: function(content) {
1393
- this.source.push(this.appendToBuffer(this.quotedString(content)));
1394
- },
1527
+ this.source.push("if (!" + this.lastHelper + ") { " + current + " = blockHelperMissing.call(" + params.join(", ") + "); }");
1528
+ },
1395
1529
 
1396
- // [append]
1397
- //
1398
- // On stack, before: value, ...
1399
- // On stack, after: ...
1400
- //
1401
- // Coerces `value` to a String and appends it to the current buffer.
1402
- //
1403
- // If `value` is truthy, or 0, it is coerced into a string and appended
1404
- // Otherwise, the empty string is appended
1405
- append: function() {
1406
- var local = this.popStack();
1407
- this.source.push("if(" + local + " || " + local + " === 0) { " + this.appendToBuffer(local) + " }");
1408
- if (this.environment.isSimple) {
1409
- this.source.push("else { " + this.appendToBuffer("''") + " }");
1410
- }
1411
- },
1530
+ // [appendContent]
1531
+ //
1532
+ // On stack, before: ...
1533
+ // On stack, after: ...
1534
+ //
1535
+ // Appends the string value of `content` to the current buffer
1536
+ appendContent: function(content) {
1537
+ this.source.push(this.appendToBuffer(this.quotedString(content)));
1538
+ },
1412
1539
 
1413
- // [appendEscaped]
1414
- //
1415
- // On stack, before: value, ...
1416
- // On stack, after: ...
1417
- //
1418
- // Escape `value` and append it to the buffer
1419
- appendEscaped: function() {
1420
- var opcode = this.nextOpcode(), extra = "";
1421
- this.context.aliases.escapeExpression = 'this.escapeExpression';
1422
-
1423
- if(opcode && opcode.opcode === 'appendContent') {
1424
- extra = " + " + this.quotedString(opcode.args[0]);
1425
- this.eat(opcode);
1426
- }
1540
+ // [append]
1541
+ //
1542
+ // On stack, before: value, ...
1543
+ // On stack, after: ...
1544
+ //
1545
+ // Coerces `value` to a String and appends it to the current buffer.
1546
+ //
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
1552
+ this.flushInline();
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("''") + " }");
1557
+ }
1558
+ },
1427
1559
 
1428
- this.source.push(this.appendToBuffer("escapeExpression(" + this.popStack() + ")" + extra));
1429
- },
1560
+ // [appendEscaped]
1561
+ //
1562
+ // On stack, before: value, ...
1563
+ // On stack, after: ...
1564
+ //
1565
+ // Escape `value` and append it to the buffer
1566
+ appendEscaped: function() {
1567
+ this.context.aliases.escapeExpression = 'this.escapeExpression';
1430
1568
 
1431
- // [getContext]
1432
- //
1433
- // On stack, before: ...
1434
- // On stack, after: ...
1435
- // Compiler value, after: lastContext=depth
1436
- //
1437
- // Set the value of the `lastContext` compiler value to the depth
1438
- getContext: function(depth) {
1439
- if(this.lastContext !== depth) {
1440
- this.lastContext = depth;
1441
- }
1442
- },
1569
+ this.source.push(this.appendToBuffer("escapeExpression(" + this.popStack() + ")"));
1570
+ },
1443
1571
 
1444
- // [lookupOnContext]
1445
- //
1446
- // On stack, before: ...
1447
- // On stack, after: currentContext[name], ...
1448
- //
1449
- // Looks up the value of `name` on the current context and pushes
1450
- // it onto the stack.
1451
- lookupOnContext: function(name) {
1452
- this.pushStack(this.nameLookup('depth' + this.lastContext, name, 'context'));
1453
- },
1572
+ // [getContext]
1573
+ //
1574
+ // On stack, before: ...
1575
+ // On stack, after: ...
1576
+ // Compiler value, after: lastContext=depth
1577
+ //
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;
1582
+ }
1583
+ },
1454
1584
 
1455
- // [pushContext]
1456
- //
1457
- // On stack, before: ...
1458
- // On stack, after: currentContext, ...
1459
- //
1460
- // Pushes the value of the current context onto the stack.
1461
- pushContext: function() {
1462
- this.pushStackLiteral('depth' + this.lastContext);
1463
- },
1585
+ // [lookupOnContext]
1586
+ //
1587
+ // On stack, before: ...
1588
+ // On stack, after: currentContext[name], ...
1589
+ //
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'));
1594
+ },
1464
1595
 
1465
- // [resolvePossibleLambda]
1466
- //
1467
- // On stack, before: value, ...
1468
- // On stack, after: resolved value, ...
1469
- //
1470
- // If the `value` is a lambda, replace it on the stack by
1471
- // the return value of the lambda
1472
- resolvePossibleLambda: function() {
1473
- this.context.aliases.functionType = '"function"';
1474
-
1475
- this.replaceStack(function(current) {
1476
- return "typeof " + current + " === functionType ? " + current + ".apply(depth0) : " + current;
1477
- });
1478
- },
1596
+ // [pushContext]
1597
+ //
1598
+ // On stack, before: ...
1599
+ // On stack, after: currentContext, ...
1600
+ //
1601
+ // Pushes the value of the current context onto the stack.
1602
+ pushContext: function() {
1603
+ this.pushStackLiteral('depth' + this.lastContext);
1604
+ },
1479
1605
 
1480
- // [lookup]
1481
- //
1482
- // On stack, before: value, ...
1483
- // On stack, after: value[name], ...
1484
- //
1485
- // Replace the value on the stack with the result of looking
1486
- // up `name` on `value`
1487
- lookup: function(name) {
1488
- this.replaceStack(function(current) {
1489
- return current + " == null || " + current + " === false ? " + current + " : " + this.nameLookup(current, name, 'context');
1490
- });
1491
- },
1606
+ // [resolvePossibleLambda]
1607
+ //
1608
+ // On stack, before: value, ...
1609
+ // On stack, after: resolved value, ...
1610
+ //
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"';
1615
+
1616
+ this.replaceStack(function(current) {
1617
+ return "typeof " + current + " === functionType ? " + current + ".apply(depth0) : " + current;
1618
+ });
1619
+ },
1492
1620
 
1493
- // [lookupData]
1494
- //
1495
- // On stack, before: ...
1496
- // On stack, after: data[id], ...
1497
- //
1498
- // Push the result of looking up `id` on the current data
1499
- lookupData: function(id) {
1500
- this.pushStack(this.nameLookup('data', id, 'data'));
1501
- },
1621
+ // [lookup]
1622
+ //
1623
+ // On stack, before: value, ...
1624
+ // On stack, after: value[name], ...
1625
+ //
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');
1631
+ });
1632
+ },
1633
+
1634
+ // [lookupData]
1635
+ //
1636
+ // On stack, before: ...
1637
+ // On stack, after: data[id], ...
1638
+ //
1639
+ // Push the result of looking up `id` on the current data
1640
+ lookupData: function(id) {
1641
+ this.push('data');
1642
+ },
1643
+
1644
+ // [pushStringParam]
1645
+ //
1646
+ // On stack, before: ...
1647
+ // On stack, after: string, currentContext, ...
1648
+ //
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);
1502
1654
 
1503
- // [pushStringParam]
1504
- //
1505
- // On stack, before: ...
1506
- // On stack, after: string, currentContext, ...
1507
- //
1508
- // This opcode is designed for use in string mode, which
1509
- // provides the string value of a parameter along with its
1510
- // depth rather than resolving it immediately.
1511
- pushStringParam: function(string) {
1512
- this.pushStackLiteral('depth' + this.lastContext);
1655
+ this.pushString(type);
1656
+
1657
+ if (typeof string === 'string') {
1513
1658
  this.pushString(string);
1514
- },
1659
+ } else {
1660
+ this.pushStackLiteral(string);
1661
+ }
1662
+ },
1515
1663
 
1516
- // [pushString]
1517
- //
1518
- // On stack, before: ...
1519
- // On stack, after: quotedString(string), ...
1520
- //
1521
- // Push a quoted version of `string` onto the stack
1522
- pushString: function(string) {
1523
- this.pushStackLiteral(this.quotedString(string));
1524
- },
1664
+ emptyHash: function() {
1665
+ this.pushStackLiteral('{}');
1525
1666
 
1526
- // [push]
1527
- //
1528
- // On stack, before: ...
1529
- // On stack, after: expr, ...
1530
- //
1531
- // Push an expression onto the stack
1532
- push: function(expr) {
1533
- this.pushStack(expr);
1534
- },
1667
+ if (this.options.stringParams) {
1668
+ this.register('hashTypes', '{}');
1669
+ this.register('hashContexts', '{}');
1670
+ }
1671
+ },
1672
+ pushHash: function() {
1673
+ this.hash = {values: [], types: [], contexts: []};
1674
+ },
1675
+ popHash: function() {
1676
+ var hash = this.hash;
1677
+ this.hash = undefined;
1535
1678
 
1536
- // [pushLiteral]
1537
- //
1538
- // On stack, before: ...
1539
- // On stack, after: value, ...
1540
- //
1541
- // Pushes a value onto the stack. This operation prevents
1542
- // the compiler from creating a temporary variable to hold
1543
- // it.
1544
- pushLiteral: function(value) {
1545
- this.pushStackLiteral(value);
1546
- },
1679
+ if (this.options.stringParams) {
1680
+ this.register('hashContexts', '{' + hash.contexts.join(',') + '}');
1681
+ this.register('hashTypes', '{' + hash.types.join(',') + '}');
1682
+ }
1683
+ this.push('{\n ' + hash.values.join(',\n ') + '\n }');
1684
+ },
1547
1685
 
1548
- // [pushProgram]
1549
- //
1550
- // On stack, before: ...
1551
- // On stack, after: program(guid), ...
1552
- //
1553
- // Push a program expression onto the stack. This takes
1554
- // a compile-time guid and converts it into a runtime-accessible
1555
- // expression.
1556
- pushProgram: function(guid) {
1557
- if (guid != null) {
1558
- this.pushStackLiteral(this.programExpression(guid));
1559
- } else {
1560
- this.pushStackLiteral(null);
1561
- }
1562
- },
1686
+ // [pushString]
1687
+ //
1688
+ // On stack, before: ...
1689
+ // On stack, after: quotedString(string), ...
1690
+ //
1691
+ // Push a quoted version of `string` onto the stack
1692
+ pushString: function(string) {
1693
+ this.pushStackLiteral(this.quotedString(string));
1694
+ },
1563
1695
 
1564
- // [invokeHelper]
1565
- //
1566
- // On stack, before: hash, inverse, program, params..., ...
1567
- // On stack, after: result of helper invocation
1568
- //
1569
- // Pops off the helper's parameters, invokes the helper,
1570
- // and pushes the helper's return value onto the stack.
1571
- //
1572
- // If the helper is not found, `helperMissing` is called.
1573
- invokeHelper: function(paramSize, name) {
1574
- this.context.aliases.helperMissing = 'helpers.helperMissing';
1575
-
1576
- var helper = this.lastHelper = this.setupHelper(paramSize, name);
1577
- this.register('foundHelper', helper.name);
1578
-
1579
- this.pushStack("foundHelper ? foundHelper.call(" +
1580
- helper.callParams + ") " + ": helperMissing.call(" +
1581
- helper.helperMissingParams + ")");
1582
- },
1696
+ // [push]
1697
+ //
1698
+ // On stack, before: ...
1699
+ // On stack, after: expr, ...
1700
+ //
1701
+ // Push an expression onto the stack
1702
+ push: function(expr) {
1703
+ this.inlineStack.push(expr);
1704
+ return expr;
1705
+ },
1583
1706
 
1584
- // [invokeKnownHelper]
1585
- //
1586
- // On stack, before: hash, inverse, program, params..., ...
1587
- // On stack, after: result of helper invocation
1588
- //
1589
- // This operation is used when the helper is known to exist,
1590
- // so a `helperMissing` fallback is not required.
1591
- invokeKnownHelper: function(paramSize, name) {
1592
- var helper = this.setupHelper(paramSize, name);
1593
- this.pushStack(helper.name + ".call(" + helper.callParams + ")");
1594
- },
1707
+ // [pushLiteral]
1708
+ //
1709
+ // On stack, before: ...
1710
+ // On stack, after: value, ...
1711
+ //
1712
+ // Pushes a value onto the stack. This operation prevents
1713
+ // the compiler from creating a temporary variable to hold
1714
+ // it.
1715
+ pushLiteral: function(value) {
1716
+ this.pushStackLiteral(value);
1717
+ },
1595
1718
 
1596
- // [invokeAmbiguous]
1597
- //
1598
- // On stack, before: hash, inverse, program, params..., ...
1599
- // On stack, after: result of disambiguation
1600
- //
1601
- // This operation is used when an expression like `{{foo}}`
1602
- // is provided, but we don't know at compile-time whether it
1603
- // is a helper or a path.
1604
- //
1605
- // This operation emits more code than the other options,
1606
- // and can be avoided by passing the `knownHelpers` and
1607
- // `knownHelpersOnly` flags at compile-time.
1608
- invokeAmbiguous: function(name) {
1609
- this.context.aliases.functionType = '"function"';
1610
-
1611
- this.pushStackLiteral('{}');
1612
- var helper = this.setupHelper(0, name);
1613
-
1614
- var helperName = this.lastHelper = this.nameLookup('helpers', name, 'helper');
1615
- this.register('foundHelper', helperName);
1616
-
1617
- var nonHelper = this.nameLookup('depth' + this.lastContext, name, 'context');
1618
- var nextStack = this.nextStack();
1619
-
1620
- this.source.push('if (foundHelper) { ' + nextStack + ' = foundHelper.call(' + helper.callParams + '); }');
1621
- this.source.push('else { ' + nextStack + ' = ' + nonHelper + '; ' + nextStack + ' = typeof ' + nextStack + ' === functionType ? ' + nextStack + '.apply(depth0) : ' + nextStack + '; }');
1622
- },
1719
+ // [pushProgram]
1720
+ //
1721
+ // On stack, before: ...
1722
+ // On stack, after: program(guid), ...
1723
+ //
1724
+ // Push a program expression onto the stack. This takes
1725
+ // a compile-time guid and converts it into a runtime-accessible
1726
+ // expression.
1727
+ pushProgram: function(guid) {
1728
+ if (guid != null) {
1729
+ this.pushStackLiteral(this.programExpression(guid));
1730
+ } else {
1731
+ this.pushStackLiteral(null);
1732
+ }
1733
+ },
1623
1734
 
1624
- // [invokePartial]
1625
- //
1626
- // On stack, before: context, ...
1627
- // On stack after: result of partial invocation
1628
- //
1629
- // This operation pops off a context, invokes a partial with that context,
1630
- // and pushes the result of the invocation back.
1631
- invokePartial: function(name) {
1632
- var params = [this.nameLookup('partials', name, 'partial'), "'" + name + "'", this.popStack(), "helpers", "partials"];
1633
-
1634
- if (this.options.data) {
1635
- params.push("data");
1636
- }
1735
+ // [invokeHelper]
1736
+ //
1737
+ // On stack, before: hash, inverse, program, params..., ...
1738
+ // On stack, after: result of helper invocation
1739
+ //
1740
+ // Pops off the helper's parameters, invokes the helper,
1741
+ // and pushes the helper's return value onto the stack.
1742
+ //
1743
+ // If the helper is not found, `helperMissing` is called.
1744
+ invokeHelper: function(paramSize, name) {
1745
+ this.context.aliases.helperMissing = 'helpers.helperMissing';
1746
+
1747
+ var helper = this.lastHelper = this.setupHelper(paramSize, name, true);
1748
+ var nonHelper = this.nameLookup('depth' + this.lastContext, name, 'context');
1749
+
1750
+ this.push(helper.name + ' || ' + nonHelper);
1751
+ this.replaceStack(function(name) {
1752
+ return name + ' ? ' + name + '.call(' +
1753
+ helper.callParams + ") " + ": helperMissing.call(" +
1754
+ helper.helperMissingParams + ")";
1755
+ });
1756
+ },
1637
1757
 
1638
- this.context.aliases.self = "this";
1639
- this.pushStack("self.invokePartial(" + params.join(", ") + ");");
1640
- },
1758
+ // [invokeKnownHelper]
1759
+ //
1760
+ // On stack, before: hash, inverse, program, params..., ...
1761
+ // On stack, after: result of helper invocation
1762
+ //
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 + ")");
1768
+ },
1641
1769
 
1642
- // [assignToHash]
1643
- //
1644
- // On stack, before: value, hash, ...
1645
- // On stack, after: hash, ...
1646
- //
1647
- // Pops a value and hash off the stack, assigns `hash[key] = value`
1648
- // and pushes the hash back onto the stack.
1649
- assignToHash: function(key) {
1650
- var value = this.popStack();
1651
- var hash = this.topStack();
1652
-
1653
- this.source.push(hash + "['" + key + "'] = " + value + ";");
1654
- },
1770
+ // [invokeAmbiguous]
1771
+ //
1772
+ // On stack, before: hash, inverse, program, params..., ...
1773
+ // On stack, after: result of disambiguation
1774
+ //
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.
1778
+ //
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"';
1784
+
1785
+ this.pushStackLiteral('{}'); // Hash value
1786
+ var helper = this.setupHelper(0, name, helperCall);
1787
+
1788
+ var helperName = this.lastHelper = this.nameLookup('helpers', name, 'helper');
1789
+
1790
+ var nonHelper = this.nameLookup('depth' + this.lastContext, name, 'context');
1791
+ var nextStack = this.nextStack();
1792
+
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 + '; }');
1795
+ },
1796
+
1797
+ // [invokePartial]
1798
+ //
1799
+ // On stack, before: context, ...
1800
+ // On stack after: result of partial invocation
1801
+ //
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"];
1806
+
1807
+ if (this.options.data) {
1808
+ params.push("data");
1809
+ }
1810
+
1811
+ this.context.aliases.self = "this";
1812
+ this.push("self.invokePartial(" + params.join(", ") + ")");
1813
+ },
1655
1814
 
1656
- // HELPERS
1815
+ // [assignToHash]
1816
+ //
1817
+ // On stack, before: value, hash, ...
1818
+ // On stack, after: hash, ...
1819
+ //
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(),
1824
+ context,
1825
+ type;
1826
+
1827
+ if (this.options.stringParams) {
1828
+ type = this.popStack();
1829
+ context = this.popStack();
1830
+ }
1831
+
1832
+ var hash = this.hash;
1833
+ if (context) {
1834
+ hash.contexts.push("'" + key + "': " + context);
1835
+ }
1836
+ if (type) {
1837
+ hash.types.push("'" + key + "': " + type);
1838
+ }
1839
+ hash.values.push("'" + key + "': (" + value + ")");
1840
+ },
1841
+
1842
+ // HELPERS
1657
1843
 
1658
- compiler: JavaScriptCompiler,
1844
+ compiler: JavaScriptCompiler,
1659
1845
 
1660
- compileChildren: function(environment, options) {
1661
- var children = environment.children, child, compiler;
1846
+ compileChildren: function(environment, options) {
1847
+ var children = environment.children, child, compiler;
1662
1848
 
1663
- for(var i=0, l=children.length; i<l; i++) {
1664
- child = children[i];
1665
- compiler = new this.compiler();
1849
+ for(var i=0, l=children.length; i<l; i++) {
1850
+ child = children[i];
1851
+ compiler = new this.compiler();
1666
1852
 
1853
+ var index = this.matchExistingProgram(child);
1854
+
1855
+ if (index == null) {
1667
1856
  this.context.programs.push(''); // Placeholder to prevent name conflicts for nested children
1668
- var index = this.context.programs.length;
1857
+ index = this.context.programs.length;
1669
1858
  child.index = index;
1670
1859
  child.name = 'program' + index;
1671
1860
  this.context.programs[index] = compiler.compile(child, options, this.context);
1861
+ this.context.environments[index] = child;
1862
+ } else {
1863
+ child.index = index;
1864
+ child.name = 'program' + index;
1672
1865
  }
1673
- },
1674
-
1675
- programExpression: function(guid) {
1676
- this.context.aliases.self = "this";
1677
-
1678
- if(guid == null) {
1679
- return "self.noop";
1866
+ }
1867
+ },
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)) {
1872
+ return i;
1680
1873
  }
1874
+ }
1875
+ },
1681
1876
 
1682
- var child = this.environment.children[guid],
1683
- depths = child.depths.list, depth;
1877
+ programExpression: function(guid) {
1878
+ this.context.aliases.self = "this";
1684
1879
 
1685
- var programParams = [child.index, child.name, "data"];
1880
+ if(guid == null) {
1881
+ return "self.noop";
1882
+ }
1686
1883
 
1687
- for(var i=0, l = depths.length; i<l; i++) {
1688
- depth = depths[i];
1884
+ var child = this.environment.children[guid],
1885
+ depths = child.depths.list, depth;
1689
1886
 
1690
- if(depth === 1) { programParams.push("depth0"); }
1691
- else { programParams.push("depth" + (depth - 1)); }
1692
- }
1887
+ var programParams = [child.index, child.name, "data"];
1693
1888
 
1694
- if(depths.length === 0) {
1695
- return "self.program(" + programParams.join(", ") + ")";
1696
- } else {
1697
- programParams.shift();
1698
- return "self.programWithDepth(" + programParams.join(", ") + ")";
1699
- }
1700
- },
1889
+ for(var i=0, l = depths.length; i<l; i++) {
1890
+ depth = depths[i];
1701
1891
 
1702
- register: function(name, val) {
1703
- this.useRegister(name);
1704
- this.source.push(name + " = " + val + ";");
1705
- },
1892
+ if(depth === 1) { programParams.push("depth0"); }
1893
+ else { programParams.push("depth" + (depth - 1)); }
1894
+ }
1706
1895
 
1707
- useRegister: function(name) {
1708
- if(!this.registers[name]) {
1709
- this.registers[name] = true;
1710
- this.registers.list.push(name);
1711
- }
1712
- },
1896
+ return (depths.length === 0 ? "self.program(" : "self.programWithDepth(") + programParams.join(", ") + ")";
1897
+ },
1713
1898
 
1714
- pushStackLiteral: function(item) {
1715
- this.compileStack.push(new Literal(item));
1716
- return item;
1717
- },
1899
+ register: function(name, val) {
1900
+ this.useRegister(name);
1901
+ this.source.push(name + " = " + val + ";");
1902
+ },
1718
1903
 
1719
- pushStack: function(item) {
1720
- this.source.push(this.incrStack() + " = " + item + ";");
1721
- this.compileStack.push("stack" + this.stackSlot);
1722
- return "stack" + this.stackSlot;
1723
- },
1904
+ useRegister: function(name) {
1905
+ if(!this.registers[name]) {
1906
+ this.registers[name] = true;
1907
+ this.registers.list.push(name);
1908
+ }
1909
+ },
1724
1910
 
1725
- replaceStack: function(callback) {
1726
- var item = callback.call(this, this.topStack());
1911
+ pushStackLiteral: function(item) {
1912
+ return this.push(new Literal(item));
1913
+ },
1727
1914
 
1728
- this.source.push(this.topStack() + " = " + item + ";");
1729
- return "stack" + this.stackSlot;
1730
- },
1915
+ pushStack: function(item) {
1916
+ this.flushInline();
1731
1917
 
1732
- nextStack: function(skipCompileStack) {
1733
- var name = this.incrStack();
1734
- this.compileStack.push("stack" + this.stackSlot);
1735
- return name;
1736
- },
1918
+ var stack = this.incrStack();
1919
+ if (item) {
1920
+ this.source.push(stack + " = " + item + ";");
1921
+ }
1922
+ this.compileStack.push(stack);
1923
+ return stack;
1924
+ },
1737
1925
 
1738
- incrStack: function() {
1739
- this.stackSlot++;
1740
- if(this.stackSlot > this.stackVars.length) { this.stackVars.push("stack" + this.stackSlot); }
1741
- return "stack" + this.stackSlot;
1742
- },
1926
+ replaceStack: function(callback) {
1927
+ var prefix = '',
1928
+ inline = this.isInline(),
1929
+ stack;
1743
1930
 
1744
- popStack: function() {
1745
- var item = this.compileStack.pop();
1931
+ // If we are currently inline then we want to merge the inline statement into the
1932
+ // replacement statement via ','
1933
+ if (inline) {
1934
+ var top = this.popStack(true);
1746
1935
 
1747
- if (item instanceof Literal) {
1748
- return item.value;
1936
+ if (top instanceof Literal) {
1937
+ // Literals do not need to be inlined
1938
+ stack = top.value;
1749
1939
  } else {
1750
- this.stackSlot--;
1751
- return item;
1940
+ // Get or create the current stack name for use by the inline
1941
+ var name = this.stackSlot ? this.topStackName() : this.incrStack();
1942
+
1943
+ prefix = '(' + this.push(name) + ' = ' + top + '),';
1944
+ stack = this.topStack();
1752
1945
  }
1753
- },
1946
+ } else {
1947
+ stack = this.topStack();
1948
+ }
1754
1949
 
1755
- topStack: function() {
1756
- var item = this.compileStack[this.compileStack.length - 1];
1950
+ var item = callback.call(this, stack);
1757
1951
 
1758
- if (item instanceof Literal) {
1759
- return item.value;
1760
- } else {
1761
- return item;
1952
+ if (inline) {
1953
+ if (this.inlineStack.length || this.compileStack.length) {
1954
+ this.popStack();
1955
+ }
1956
+ this.push('(' + prefix + item + ')');
1957
+ } else {
1958
+ // Prevent modification of the context depth variable. Through replaceStack
1959
+ if (!/^stack/.test(stack)) {
1960
+ stack = this.nextStack();
1762
1961
  }
1763
- },
1764
1962
 
1765
- quotedString: function(str) {
1766
- return '"' + str
1767
- .replace(/\\/g, '\\\\')
1768
- .replace(/"/g, '\\"')
1769
- .replace(/\n/g, '\\n')
1770
- .replace(/\r/g, '\\r') + '"';
1771
- },
1963
+ this.source.push(stack + " = (" + prefix + item + ");");
1964
+ }
1965
+ return stack;
1966
+ },
1772
1967
 
1773
- setupHelper: function(paramSize, name) {
1774
- var params = [];
1775
- this.setupParams(paramSize, params);
1776
- var foundHelper = this.nameLookup('helpers', name, 'helper');
1968
+ nextStack: function() {
1969
+ return this.pushStack();
1970
+ },
1777
1971
 
1778
- return {
1779
- params: params,
1780
- name: foundHelper,
1781
- callParams: ["depth0"].concat(params).join(", "),
1782
- helperMissingParams: ["depth0", this.quotedString(name)].concat(params).join(", ")
1783
- };
1784
- },
1972
+ incrStack: function() {
1973
+ this.stackSlot++;
1974
+ if(this.stackSlot > this.stackVars.length) { this.stackVars.push("stack" + this.stackSlot); }
1975
+ return this.topStackName();
1976
+ },
1977
+ topStackName: function() {
1978
+ return "stack" + this.stackSlot;
1979
+ },
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);
1988
+ } else {
1989
+ this.pushStack(entry);
1990
+ }
1991
+ }
1992
+ }
1993
+ },
1994
+ isInline: function() {
1995
+ return this.inlineStack.length;
1996
+ },
1785
1997
 
1786
- // the params and contexts arguments are passed in arrays
1787
- // to fill in
1788
- setupParams: function(paramSize, params) {
1789
- var options = [], contexts = [], param, inverse, program;
1998
+ popStack: function(wrapped) {
1999
+ var inline = this.isInline(),
2000
+ item = (inline ? this.inlineStack : this.compileStack).pop();
1790
2001
 
1791
- options.push("hash:" + this.popStack());
2002
+ if (!wrapped && (item instanceof Literal)) {
2003
+ return item.value;
2004
+ } else {
2005
+ if (!inline) {
2006
+ this.stackSlot--;
2007
+ }
2008
+ return item;
2009
+ }
2010
+ },
1792
2011
 
1793
- inverse = this.popStack();
1794
- program = this.popStack();
2012
+ topStack: function(wrapped) {
2013
+ var stack = (this.isInline() ? this.inlineStack : this.compileStack),
2014
+ item = stack[stack.length - 1];
1795
2015
 
1796
- // Avoid setting fn and inverse if neither are set. This allows
1797
- // helpers to do a check for `if (options.fn)`
1798
- if (program || inverse) {
1799
- if (!program) {
1800
- this.context.aliases.self = "this";
1801
- program = "self.noop";
1802
- }
2016
+ if (!wrapped && (item instanceof Literal)) {
2017
+ return item.value;
2018
+ } else {
2019
+ return item;
2020
+ }
2021
+ },
1803
2022
 
1804
- if (!inverse) {
1805
- this.context.aliases.self = "this";
1806
- inverse = "self.noop";
1807
- }
2023
+ quotedString: function(str) {
2024
+ return '"' + 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') + '"';
2031
+ },
1808
2032
 
1809
- options.push("inverse:" + inverse);
1810
- options.push("fn:" + program);
1811
- }
2033
+ setupHelper: function(paramSize, name, missingParams) {
2034
+ var params = [];
2035
+ this.setupParams(paramSize, params, missingParams);
2036
+ var foundHelper = this.nameLookup('helpers', name, 'helper');
1812
2037
 
1813
- for(var i=0; i<paramSize; i++) {
1814
- param = this.popStack();
1815
- params.push(param);
2038
+ return {
2039
+ params: params,
2040
+ name: foundHelper,
2041
+ callParams: ["depth0"].concat(params).join(", "),
2042
+ helperMissingParams: missingParams && ["depth0", this.quotedString(name)].concat(params).join(", ")
2043
+ };
2044
+ },
1816
2045
 
1817
- if(this.options.stringParams) {
1818
- contexts.push(this.popStack());
1819
- }
1820
- }
2046
+ // the params and contexts arguments are passed in arrays
2047
+ // to fill in
2048
+ setupParams: function(paramSize, params, useRegister) {
2049
+ var options = [], contexts = [], types = [], param, inverse, program;
1821
2050
 
1822
- if (this.options.stringParams) {
1823
- options.push("contexts:[" + contexts.join(",") + "]");
2051
+ options.push("hash:" + this.popStack());
2052
+
2053
+ inverse = this.popStack();
2054
+ program = this.popStack();
2055
+
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) {
2059
+ if (!program) {
2060
+ this.context.aliases.self = "this";
2061
+ program = "self.noop";
1824
2062
  }
1825
2063
 
1826
- if(this.options.data) {
1827
- options.push("data:data");
2064
+ if (!inverse) {
2065
+ this.context.aliases.self = "this";
2066
+ inverse = "self.noop";
1828
2067
  }
1829
2068
 
1830
- params.push("{" + options.join(",") + "}");
1831
- return params.join(", ");
2069
+ options.push("inverse:" + inverse);
2070
+ options.push("fn:" + program);
1832
2071
  }
1833
- };
1834
2072
 
1835
- var reservedWords = (
1836
- "break else new var" +
1837
- " case finally return void" +
1838
- " catch for switch while" +
1839
- " continue function this with" +
1840
- " default if throw" +
1841
- " delete in try" +
1842
- " do instanceof typeof" +
1843
- " abstract enum int short" +
1844
- " boolean export interface static" +
1845
- " byte extends long super" +
1846
- " char final native synchronized" +
1847
- " class float package throws" +
1848
- " const goto private transient" +
1849
- " debugger implements protected volatile" +
1850
- " double import public let yield"
1851
- ).split(" ");
1852
-
1853
- var compilerWords = JavaScriptCompiler.RESERVED_WORDS = {};
1854
-
1855
- for(var i=0, l=reservedWords.length; i<l; i++) {
1856
- compilerWords[reservedWords[i]] = true;
1857
- }
2073
+ for(var i=0; i<paramSize; i++) {
2074
+ param = this.popStack();
2075
+ params.push(param);
1858
2076
 
1859
- JavaScriptCompiler.isValidJavaScriptVariableName = function(name) {
1860
- if(!JavaScriptCompiler.RESERVED_WORDS[name] && /^[a-zA-Z_$][0-9a-zA-Z_$]+$/.test(name)) {
1861
- return true;
2077
+ if(this.options.stringParams) {
2078
+ types.push(this.popStack());
2079
+ contexts.push(this.popStack());
2080
+ }
1862
2081
  }
1863
- return false;
1864
- };
1865
2082
 
1866
- })(Handlebars.Compiler, Handlebars.JavaScriptCompiler);
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");
2088
+ }
1867
2089
 
1868
- Handlebars.precompile = function(string, options) {
1869
- options = options || {};
2090
+ if(this.options.data) {
2091
+ options.push("data:data");
2092
+ }
1870
2093
 
1871
- var ast = Handlebars.parse(string);
1872
- var environment = new Handlebars.Compiler().compile(ast, options);
1873
- return new Handlebars.JavaScriptCompiler().compile(environment, options);
2094
+ options = "{" + options.join(",") + "}";
2095
+ if (useRegister) {
2096
+ this.register('options', options);
2097
+ params.push('options');
2098
+ } else {
2099
+ params.push(options);
2100
+ }
2101
+ return params.join(", ");
2102
+ }
2103
+ };
2104
+
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" +
2111
+ " delete in try" +
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"
2121
+ ).split(" ");
2122
+
2123
+ var compilerWords = JavaScriptCompiler.RESERVED_WORDS = {};
2124
+
2125
+ for(var i=0, l=reservedWords.length; i<l; i++) {
2126
+ compilerWords[reservedWords[i]] = true;
2127
+ }
2128
+
2129
+ JavaScriptCompiler.isValidJavaScriptVariableName = function(name) {
2130
+ if(!JavaScriptCompiler.RESERVED_WORDS[name] && /^[a-zA-Z_$][0-9a-zA-Z_$]+$/.test(name)) {
2131
+ return true;
2132
+ }
2133
+ return false;
1874
2134
  };
1875
2135
 
1876
- Handlebars.compile = function(string, options) {
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);
2139
+ }
2140
+
1877
2141
  options = options || {};
2142
+ if (!('data' in options)) {
2143
+ options.data = true;
2144
+ }
2145
+ var ast = Handlebars.parse(input);
2146
+ var environment = new Compiler().compile(ast, options);
2147
+ return new JavaScriptCompiler().compile(environment, options);
2148
+ };
2149
+
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);
2153
+ }
1878
2154
 
2155
+ options = options || {};
2156
+ if (!('data' in options)) {
2157
+ options.data = true;
2158
+ }
1879
2159
  var compiled;
1880
2160
  function compile() {
1881
- var ast = Handlebars.parse(string);
1882
- var environment = new Handlebars.Compiler().compile(ast, options);
1883
- var templateSpec = new Handlebars.JavaScriptCompiler().compile(environment, options, undefined, true);
2161
+ var ast = Handlebars.parse(input);
2162
+ var environment = new Compiler().compile(ast, options);
2163
+ var templateSpec = new JavaScriptCompiler().compile(environment, options, undefined, true);
1884
2164
  return Handlebars.template(templateSpec);
1885
2165
  }
1886
2166
 
@@ -1892,8 +2172,10 @@ Handlebars.compile = function(string, options) {
1892
2172
  return compiled.call(this, context, options);
1893
2173
  };
1894
2174
  };
2175
+
1895
2176
  ;
1896
2177
  // lib/handlebars/runtime.js
2178
+
1897
2179
  Handlebars.VM = {
1898
2180
  template: function(templateSpec) {
1899
2181
  // Just add water
@@ -1904,39 +2186,73 @@ Handlebars.VM = {
1904
2186
  program: function(i, fn, data) {
1905
2187
  var programWrapper = this.programs[i];
1906
2188
  if(data) {
1907
- return Handlebars.VM.program(fn, data);
1908
- } else if(programWrapper) {
1909
- return programWrapper;
1910
- } else {
1911
- programWrapper = this.programs[i] = Handlebars.VM.program(fn);
1912
- return programWrapper;
2189
+ programWrapper = Handlebars.VM.program(i, fn, data);
2190
+ } else if (!programWrapper) {
2191
+ programWrapper = this.programs[i] = Handlebars.VM.program(i, fn);
1913
2192
  }
2193
+ return programWrapper;
2194
+ },
2195
+ merge: function(param, common) {
2196
+ var ret = param || common;
2197
+
2198
+ if (param && common) {
2199
+ ret = {};
2200
+ Handlebars.Utils.extend(ret, common);
2201
+ Handlebars.Utils.extend(ret, param);
2202
+ }
2203
+ return ret;
1914
2204
  },
1915
2205
  programWithDepth: Handlebars.VM.programWithDepth,
1916
- noop: Handlebars.VM.noop
2206
+ noop: Handlebars.VM.noop,
2207
+ compilerInfo: null
1917
2208
  };
1918
2209
 
1919
2210
  return function(context, options) {
1920
2211
  options = options || {};
1921
- return templateSpec.call(container, Handlebars, context, options.helpers, options.partials, options.data);
2212
+ var result = templateSpec.call(container, Handlebars, context, options.helpers, options.partials, options.data);
2213
+
2214
+ var compilerInfo = container.compilerInfo || [],
2215
+ compilerRevision = compilerInfo[0] || 1,
2216
+ currentRevision = Handlebars.COMPILER_REVISION;
2217
+
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+").";
2224
+ } else {
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]+").";
2228
+ }
2229
+ }
2230
+
2231
+ return result;
1922
2232
  };
1923
2233
  },
1924
2234
 
1925
- programWithDepth: function(fn, data, $depth) {
1926
- var args = Array.prototype.slice.call(arguments, 2);
2235
+ programWithDepth: function(i, fn, data /*, $depth */) {
2236
+ var args = Array.prototype.slice.call(arguments, 3);
1927
2237
 
1928
- return function(context, options) {
2238
+ var program = function(context, options) {
1929
2239
  options = options || {};
1930
2240
 
1931
2241
  return fn.apply(this, [context, options.data || data].concat(args));
1932
2242
  };
2243
+ program.program = i;
2244
+ program.depth = args.length;
2245
+ return program;
1933
2246
  },
1934
- program: function(fn, data) {
1935
- return function(context, options) {
2247
+ program: function(i, fn, data) {
2248
+ var program = function(context, options) {
1936
2249
  options = options || {};
1937
2250
 
1938
2251
  return fn(context, options.data || data);
1939
2252
  };
2253
+ program.program = i;
2254
+ program.depth = 0;
2255
+ return program;
1940
2256
  },
1941
2257
  noop: function() { return ""; },
1942
2258
  invokePartial: function(partial, name, context, helpers, partials, data) {
@@ -1957,3 +2273,6 @@ Handlebars.VM = {
1957
2273
 
1958
2274
  Handlebars.template = Handlebars.VM.template;
1959
2275
  ;
2276
+ // lib/handlebars/browser-suffix.js
2277
+ })(Handlebars);
2278
+ ;