steering-rails 1.0.0 → 1.1.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.
data/README.md CHANGED
@@ -20,4 +20,38 @@ Or install it yourself as:
20
20
 
21
21
  TODO: write usage instructions
22
22
 
23
+ ## Changelog
24
+
25
+ ### 1.1.0
26
+
27
+ * Bumped steering version to 1.1.1
28
+ * Updated Handlbars assets to 1.0.rc.1
29
+
30
+ ### 1.0.0
31
+
32
+ Initial version.
33
+
34
+ ## License (MIT)
35
+
36
+ Copyright (c) 2012 Andrew White <andyw@pixeltrix.co.uk>
37
+
38
+ Permission is hereby granted, free of charge, to any person obtaining
39
+ a copy of this software and associated documentation files (the
40
+ "Software"), to deal in the Software without restriction, including
41
+ without limitation the rights to use, copy, modify, merge, publish,
42
+ distribute, sublicense, and/or sell copies of the Software, and to
43
+ permit persons to whom the Software is furnished to do so, subject to
44
+ the following conditions:
45
+
46
+ The above copyright notice and this permission notice shall be
47
+ included in all copies or substantial portions of the Software.
48
+
49
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
50
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
51
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
52
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
53
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
54
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
55
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
56
+
23
57
  [1]: https://github.com/wycats/handlebars.js
@@ -1,5 +1,5 @@
1
1
  module Steering
2
2
  module Rails
3
- VERSION = "1.0.0"
3
+ VERSION = "1.1.0"
4
4
  end
5
5
  end
@@ -20,5 +20,5 @@ Gem::Specification.new do |gem|
20
20
 
21
21
  gem.add_runtime_dependency 'sprockets', '>= 2.1'
22
22
  gem.add_runtime_dependency 'railties', '>= 3.1'
23
- gem.add_runtime_dependency 'steering', '~> 1.0'
23
+ gem.add_runtime_dependency 'steering', '~> 1.1.1'
24
24
  end
@@ -1,7 +1,11 @@
1
1
  // lib/handlebars/base.js
2
- var Handlebars = {};
3
2
 
4
- Handlebars.VERSION = "1.0.beta.6";
3
+ /*jshint eqnull:true*/
4
+ this.Handlebars = {};
5
+
6
+ (function(Handlebars) {
7
+
8
+ Handlebars.VERSION = "1.0.rc.1";
5
9
 
6
10
  Handlebars.helpers = {};
7
11
  Handlebars.partials = {};
@@ -40,25 +44,36 @@ Handlebars.registerHelper('blockHelperMissing', function(context, options) {
40
44
  return inverse(this);
41
45
  } else if(type === "[object Array]") {
42
46
  if(context.length > 0) {
43
- for(var i=0, j=context.length; i<j; i++) {
44
- ret = ret + fn(context[i]);
45
- }
47
+ return Handlebars.helpers.each(context, options);
46
48
  } else {
47
- ret = inverse(this);
49
+ return inverse(this);
48
50
  }
49
- return ret;
50
51
  } else {
51
52
  return fn(context);
52
53
  }
53
54
  });
54
55
 
56
+ Handlebars.K = function() {};
57
+
58
+ Handlebars.createFrame = Object.create || function(object) {
59
+ Handlebars.K.prototype = object;
60
+ var obj = new Handlebars.K();
61
+ Handlebars.K.prototype = null;
62
+ return obj;
63
+ };
64
+
55
65
  Handlebars.registerHelper('each', function(context, options) {
56
66
  var fn = options.fn, inverse = options.inverse;
57
- var ret = "";
67
+ var ret = "", data;
68
+
69
+ if (options.data) {
70
+ data = Handlebars.createFrame(options.data);
71
+ }
58
72
 
59
73
  if(context && context.length > 0) {
60
74
  for(var i=0, j=context.length; i<j; i++) {
61
- ret = ret + fn(context[i]);
75
+ if (data) { data.index = i; }
76
+ ret = ret + fn(context[i], { data: data });
62
77
  }
63
78
  } else {
64
79
  ret = inverse(this);
@@ -92,104 +107,111 @@ Handlebars.registerHelper('with', function(context, options) {
92
107
  Handlebars.registerHelper('log', function(context) {
93
108
  Handlebars.log(context);
94
109
  });
110
+
111
+ }(this.Handlebars));
95
112
  ;
96
113
  // lib/handlebars/compiler/parser.js
97
114
  /* Jison generated parser */
98
115
  var handlebars = (function(){
99
-
100
116
  var parser = {trace: function trace() { },
101
117
  yy: {},
102
- 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,"param":27,"STRING":28,"INTEGER":29,"BOOLEAN":30,"hashSegments":31,"hashSegment":32,"ID":33,"EQUALS":34,"pathSegments":35,"SEP":36,"$accept":0,"$end":1},
103
- 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",28:"STRING",29:"INTEGER",30:"BOOLEAN",33:"ID",34:"EQUALS",36:"SEP"},
104
- 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],[25,2],[25,1],[27,1],[27,1],[27,1],[27,1],[26,1],[31,2],[31,1],[32,3],[32,3],[32,3],[32,3],[21,1],[35,3],[35,1]],
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]],
105
121
  performAction: function anonymous(yytext,yyleng,yylineno,yy,yystate,$$,_$) {
106
122
 
107
123
  var $0 = $$.length - 1;
108
124
  switch (yystate) {
109
- case 1: return $$[$0-1]
125
+ case 1: return $$[$0-1];
126
+ break;
127
+ case 2: this.$ = new yy.ProgramNode($$[$0-2], $$[$0]);
110
128
  break;
111
- case 2: this.$ = new yy.ProgramNode($$[$0-2], $$[$0])
129
+ case 3: this.$ = new yy.ProgramNode($$[$0]);
112
130
  break;
113
- case 3: this.$ = new yy.ProgramNode($$[$0])
131
+ case 4: this.$ = new yy.ProgramNode([]);
114
132
  break;
115
- case 4: this.$ = new yy.ProgramNode([])
133
+ case 5: this.$ = [$$[$0]];
116
134
  break;
117
- case 5: this.$ = [$$[$0]]
135
+ case 6: $$[$0-1].push($$[$0]); this.$ = $$[$0-1];
118
136
  break;
119
- case 6: $$[$0-1].push($$[$0]); this.$ = $$[$0-1]
137
+ case 7: this.$ = new yy.BlockNode($$[$0-2], $$[$0-1].inverse, $$[$0-1], $$[$0]);
120
138
  break;
121
- case 7: this.$ = new yy.InverseNode($$[$0-2], $$[$0-1], $$[$0])
139
+ case 8: this.$ = new yy.BlockNode($$[$0-2], $$[$0-1], $$[$0-1].inverse, $$[$0]);
122
140
  break;
123
- case 8: this.$ = new yy.BlockNode($$[$0-2], $$[$0-1], $$[$0])
141
+ case 9: this.$ = $$[$0];
124
142
  break;
125
- case 9: this.$ = $$[$0]
143
+ case 10: this.$ = $$[$0];
126
144
  break;
127
- case 10: this.$ = $$[$0]
145
+ case 11: this.$ = new yy.ContentNode($$[$0]);
128
146
  break;
129
- case 11: this.$ = new yy.ContentNode($$[$0])
147
+ case 12: this.$ = new yy.CommentNode($$[$0]);
130
148
  break;
131
- case 12: this.$ = new yy.CommentNode($$[$0])
149
+ case 13: this.$ = new yy.MustacheNode($$[$0-1][0], $$[$0-1][1]);
132
150
  break;
133
- case 13: this.$ = new yy.MustacheNode($$[$0-1][0], $$[$0-1][1])
151
+ case 14: this.$ = new yy.MustacheNode($$[$0-1][0], $$[$0-1][1]);
134
152
  break;
135
- case 14: this.$ = new yy.MustacheNode($$[$0-1][0], $$[$0-1][1])
153
+ case 15: this.$ = $$[$0-1];
136
154
  break;
137
- case 15: this.$ = $$[$0-1]
155
+ case 16: this.$ = new yy.MustacheNode($$[$0-1][0], $$[$0-1][1]);
138
156
  break;
139
- case 16: this.$ = new yy.MustacheNode($$[$0-1][0], $$[$0-1][1])
157
+ case 17: this.$ = new yy.MustacheNode($$[$0-1][0], $$[$0-1][1], true);
140
158
  break;
141
- case 17: this.$ = new yy.MustacheNode($$[$0-1][0], $$[$0-1][1], true)
159
+ case 18: this.$ = new yy.PartialNode($$[$0-1]);
142
160
  break;
143
- case 18: this.$ = new yy.PartialNode($$[$0-1])
161
+ case 19: this.$ = new yy.PartialNode($$[$0-2], $$[$0-1]);
144
162
  break;
145
- case 19: this.$ = new yy.PartialNode($$[$0-2], $$[$0-1])
163
+ case 20:
146
164
  break;
147
- case 20:
165
+ case 21: this.$ = [[$$[$0-2]].concat($$[$0-1]), $$[$0]];
148
166
  break;
149
- case 21: this.$ = [[$$[$0-2]].concat($$[$0-1]), $$[$0]]
167
+ case 22: this.$ = [[$$[$0-1]].concat($$[$0]), null];
150
168
  break;
151
- case 22: this.$ = [[$$[$0-1]].concat($$[$0]), null]
169
+ case 23: this.$ = [[$$[$0-1]], $$[$0]];
152
170
  break;
153
- case 23: this.$ = [[$$[$0-1]], $$[$0]]
171
+ case 24: this.$ = [[$$[$0]], null];
154
172
  break;
155
- case 24: this.$ = [[$$[$0]], null]
173
+ case 25: this.$ = [[new yy.DataNode($$[$0])], null];
156
174
  break;
157
- case 25: $$[$0-1].push($$[$0]); this.$ = $$[$0-1];
175
+ case 26: $$[$0-1].push($$[$0]); this.$ = $$[$0-1];
158
176
  break;
159
- case 26: this.$ = [$$[$0]]
177
+ case 27: this.$ = [$$[$0]];
160
178
  break;
161
- case 27: this.$ = $$[$0]
179
+ case 28: this.$ = $$[$0];
162
180
  break;
163
- case 28: this.$ = new yy.StringNode($$[$0])
181
+ case 29: this.$ = new yy.StringNode($$[$0]);
164
182
  break;
165
- case 29: this.$ = new yy.IntegerNode($$[$0])
183
+ case 30: this.$ = new yy.IntegerNode($$[$0]);
166
184
  break;
167
- case 30: this.$ = new yy.BooleanNode($$[$0])
185
+ case 31: this.$ = new yy.BooleanNode($$[$0]);
168
186
  break;
169
- case 31: this.$ = new yy.HashNode($$[$0])
187
+ case 32: this.$ = new yy.DataNode($$[$0]);
170
188
  break;
171
- case 32: $$[$0-1].push($$[$0]); this.$ = $$[$0-1]
189
+ case 33: this.$ = new yy.HashNode($$[$0]);
172
190
  break;
173
- case 33: this.$ = [$$[$0]]
191
+ case 34: $$[$0-1].push($$[$0]); this.$ = $$[$0-1];
174
192
  break;
175
- case 34: this.$ = [$$[$0-2], $$[$0]]
193
+ case 35: this.$ = [$$[$0]];
176
194
  break;
177
- case 35: this.$ = [$$[$0-2], new yy.StringNode($$[$0])]
195
+ case 36: this.$ = [$$[$0-2], $$[$0]];
178
196
  break;
179
- case 36: this.$ = [$$[$0-2], new yy.IntegerNode($$[$0])]
197
+ case 37: this.$ = [$$[$0-2], new yy.StringNode($$[$0])];
180
198
  break;
181
- case 37: this.$ = [$$[$0-2], new yy.BooleanNode($$[$0])]
199
+ case 38: this.$ = [$$[$0-2], new yy.IntegerNode($$[$0])];
182
200
  break;
183
- case 38: this.$ = new yy.IdNode($$[$0])
201
+ case 39: this.$ = [$$[$0-2], new yy.BooleanNode($$[$0])];
184
202
  break;
185
- case 39: $$[$0-2].push($$[$0]); this.$ = $$[$0-2];
203
+ case 40: this.$ = [$$[$0-2], new yy.DataNode($$[$0])];
186
204
  break;
187
- case 40: this.$ = [$$[$0]]
205
+ case 41: this.$ = new yy.IdNode($$[$0]);
206
+ break;
207
+ case 42: $$[$0-2].push($$[$0]); this.$ = $$[$0-2];
208
+ break;
209
+ case 43: this.$ = [$$[$0]];
188
210
  break;
189
211
  }
190
212
  },
191
- 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,33:[1,25],35:24},{17:26,21:23,33:[1,25],35:24},{17:27,21:23,33:[1,25],35:24},{17:28,21:23,33:[1,25],35:24},{21:29,33:[1,25],35:24},{1:[2,1]},{6:30,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,31],21:23,33:[1,25],35:24},{10:32,20:[1,33]},{10:34,20:[1,33]},{18:[1,35]},{18:[2,24],21:40,25:36,26:37,27:38,28:[1,41],29:[1,42],30:[1,43],31:39,32:44,33:[1,45],35:24},{18:[2,38],28:[2,38],29:[2,38],30:[2,38],33:[2,38],36:[1,46]},{18:[2,40],28:[2,40],29:[2,40],30:[2,40],33:[2,40],36:[2,40]},{18:[1,47]},{18:[1,48]},{18:[1,49]},{18:[1,50],21:51,33:[1,25],35:24},{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:52,33:[1,25],35:24},{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:40,26:53,27:54,28:[1,41],29:[1,42],30:[1,43],31:39,32:44,33:[1,45],35:24},{18:[2,23]},{18:[2,26],28:[2,26],29:[2,26],30:[2,26],33:[2,26]},{18:[2,31],32:55,33:[1,56]},{18:[2,27],28:[2,27],29:[2,27],30:[2,27],33:[2,27]},{18:[2,28],28:[2,28],29:[2,28],30:[2,28],33:[2,28]},{18:[2,29],28:[2,29],29:[2,29],30:[2,29],33:[2,29]},{18:[2,30],28:[2,30],29:[2,30],30:[2,30],33:[2,30]},{18:[2,33],33:[2,33]},{18:[2,40],28:[2,40],29:[2,40],30:[2,40],33:[2,40],34:[1,57],36:[2,40]},{33:[1,58]},{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,59]},{18:[1,60]},{18:[2,21]},{18:[2,25],28:[2,25],29:[2,25],30:[2,25],33:[2,25]},{18:[2,32],33:[2,32]},{34:[1,57]},{21:61,28:[1,62],29:[1,63],30:[1,64],33:[1,25],35:24},{18:[2,39],28:[2,39],29:[2,39],30:[2,39],33:[2,39],36:[2,39]},{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,34],33:[2,34]},{18:[2,35],33:[2,35]},{18:[2,36],33:[2,36]},{18:[2,37],33:[2,37]}],
192
- defaultActions: {16:[2,1],37:[2,23],53:[2,21]},
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]},
193
215
  parseError: function parseError(str, hash) {
194
216
  throw new Error(str);
195
217
  },
@@ -198,10 +220,12 @@ parse: function parse(input) {
198
220
  this.lexer.setInput(input);
199
221
  this.lexer.yy = this.yy;
200
222
  this.yy.lexer = this.lexer;
223
+ this.yy.parser = this;
201
224
  if (typeof this.lexer.yylloc == "undefined")
202
225
  this.lexer.yylloc = {};
203
226
  var yyloc = this.lexer.yylloc;
204
227
  lstack.push(yyloc);
228
+ var ranges = this.lexer.options && this.lexer.options.ranges;
205
229
  if (typeof this.yy.parseError === "function")
206
230
  this.parseError = this.yy.parseError;
207
231
  function popStack(n) {
@@ -223,20 +247,21 @@ parse: function parse(input) {
223
247
  if (this.defaultActions[state]) {
224
248
  action = this.defaultActions[state];
225
249
  } else {
226
- if (symbol == null)
250
+ if (symbol === null || typeof symbol == "undefined") {
227
251
  symbol = lex();
252
+ }
228
253
  action = table[state] && table[state][symbol];
229
254
  }
230
255
  if (typeof action === "undefined" || !action.length || !action[0]) {
256
+ var errStr = "";
231
257
  if (!recovering) {
232
258
  expected = [];
233
259
  for (p in table[state])
234
260
  if (this.terminals_[p] && p > 2) {
235
261
  expected.push("'" + this.terminals_[p] + "'");
236
262
  }
237
- var errStr = "";
238
263
  if (this.lexer.showPosition) {
239
- errStr = "Parse error on line " + (yylineno + 1) + ":\n" + this.lexer.showPosition() + "\nExpecting " + expected.join(", ") + ", got '" + this.terminals_[symbol] + "'";
264
+ errStr = "Parse error on line " + (yylineno + 1) + ":\n" + this.lexer.showPosition() + "\nExpecting " + expected.join(", ") + ", got '" + (this.terminals_[symbol] || symbol) + "'";
240
265
  } else {
241
266
  errStr = "Parse error on line " + (yylineno + 1) + ": Unexpected " + (symbol == 1?"end of input":"'" + (this.terminals_[symbol] || symbol) + "'");
242
267
  }
@@ -269,6 +294,9 @@ parse: function parse(input) {
269
294
  len = this.productions_[action[1]][1];
270
295
  yyval.$ = vstack[vstack.length - len];
271
296
  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};
297
+ if (ranges) {
298
+ yyval._$.range = [lstack[lstack.length - (len || 1)].range[0], lstack[lstack.length - 1].range[1]];
299
+ }
272
300
  r = this.performAction.call(yyval, yytext, yyleng, yylineno, this.yy, action[1], vstack, lstack);
273
301
  if (typeof r !== "undefined") {
274
302
  return r;
@@ -290,13 +318,13 @@ parse: function parse(input) {
290
318
  }
291
319
  return true;
292
320
  }
293
- };/* Jison generated lexer */
321
+ };
322
+ /* Jison generated lexer */
294
323
  var lexer = (function(){
295
-
296
324
  var lexer = ({EOF:1,
297
325
  parseError:function parseError(str, hash) {
298
- if (this.yy.parseError) {
299
- this.yy.parseError(str, hash);
326
+ if (this.yy.parser) {
327
+ this.yy.parser.parseError(str, hash);
300
328
  } else {
301
329
  throw new Error(str);
302
330
  }
@@ -308,27 +336,64 @@ setInput:function (input) {
308
336
  this.yytext = this.matched = this.match = '';
309
337
  this.conditionStack = ['INITIAL'];
310
338
  this.yylloc = {first_line:1,first_column:0,last_line:1,last_column:0};
339
+ if (this.options.ranges) this.yylloc.range = [0,0];
340
+ this.offset = 0;
311
341
  return this;
312
342
  },
313
343
  input:function () {
314
344
  var ch = this._input[0];
315
- this.yytext+=ch;
345
+ this.yytext += ch;
316
346
  this.yyleng++;
317
- this.match+=ch;
318
- this.matched+=ch;
319
- var lines = ch.match(/\n/);
320
- if (lines) this.yylineno++;
347
+ this.offset++;
348
+ this.match += ch;
349
+ this.matched += ch;
350
+ var lines = ch.match(/(?:\r\n?|\n).*/g);
351
+ if (lines) {
352
+ this.yylineno++;
353
+ this.yylloc.last_line++;
354
+ } else {
355
+ this.yylloc.last_column++;
356
+ }
357
+ if (this.options.ranges) this.yylloc.range[1]++;
358
+
321
359
  this._input = this._input.slice(1);
322
360
  return ch;
323
361
  },
324
362
  unput:function (ch) {
363
+ var len = ch.length;
364
+ var lines = ch.split(/(?:\r\n?|\n)/g);
365
+
325
366
  this._input = ch + this._input;
367
+ this.yytext = this.yytext.substr(0, this.yytext.length-len-1);
368
+ //this.yyleng -= len;
369
+ this.offset -= len;
370
+ var oldLines = this.match.split(/(?:\r\n?|\n)/g);
371
+ this.match = this.match.substr(0, this.match.length-1);
372
+ this.matched = this.matched.substr(0, this.matched.length-1);
373
+
374
+ if (lines.length-1) this.yylineno -= lines.length-1;
375
+ var r = this.yylloc.range;
376
+
377
+ this.yylloc = {first_line: this.yylloc.first_line,
378
+ last_line: this.yylineno+1,
379
+ first_column: this.yylloc.first_column,
380
+ last_column: lines ?
381
+ (lines.length === oldLines.length ? this.yylloc.first_column : 0) + oldLines[oldLines.length - lines.length].length - lines[0].length:
382
+ this.yylloc.first_column - len
383
+ };
384
+
385
+ if (this.options.ranges) {
386
+ this.yylloc.range = [r[0], r[0] + this.yyleng - len];
387
+ }
326
388
  return this;
327
389
  },
328
390
  more:function () {
329
391
  this._more = true;
330
392
  return this;
331
393
  },
394
+ less:function (n) {
395
+ this.unput(this.match.slice(n));
396
+ },
332
397
  pastInput:function () {
333
398
  var past = this.matched.substr(0, this.matched.length - this.match.length);
334
399
  return (past.length > 20 ? '...':'') + past.substr(-20).replace(/\n/g, "");
@@ -353,6 +418,8 @@ next:function () {
353
418
 
354
419
  var token,
355
420
  match,
421
+ tempMatch,
422
+ index,
356
423
  col,
357
424
  lines;
358
425
  if (!this._more) {
@@ -361,30 +428,39 @@ next:function () {
361
428
  }
362
429
  var rules = this._currentRules();
363
430
  for (var i=0;i < rules.length; i++) {
364
- match = this._input.match(this.rules[rules[i]]);
365
- if (match) {
366
- lines = match[0].match(/\n.*/g);
367
- if (lines) this.yylineno += lines.length;
368
- this.yylloc = {first_line: this.yylloc.last_line,
369
- last_line: this.yylineno+1,
370
- first_column: this.yylloc.last_column,
371
- last_column: lines ? lines[lines.length-1].length-1 : this.yylloc.last_column + match[0].length}
372
- this.yytext += match[0];
373
- this.match += match[0];
374
- this.matches = match;
375
- this.yyleng = this.yytext.length;
376
- this._more = false;
377
- this._input = this._input.slice(match[0].length);
378
- this.matched += match[0];
379
- token = this.performAction.call(this, this.yy, this, rules[i],this.conditionStack[this.conditionStack.length-1]);
380
- if (token) return token;
381
- else return;
431
+ tempMatch = this._input.match(this.rules[rules[i]]);
432
+ if (tempMatch && (!match || tempMatch[0].length > match[0].length)) {
433
+ match = tempMatch;
434
+ index = i;
435
+ if (!this.options.flex) break;
382
436
  }
383
437
  }
438
+ if (match) {
439
+ lines = match[0].match(/(?:\r\n?|\n).*/g);
440
+ if (lines) this.yylineno += lines.length;
441
+ this.yylloc = {first_line: this.yylloc.last_line,
442
+ last_line: this.yylineno+1,
443
+ first_column: this.yylloc.last_column,
444
+ last_column: lines ? lines[lines.length-1].length-lines[lines.length-1].match(/\r?\n?/)[0].length : this.yylloc.last_column + match[0].length};
445
+ this.yytext += match[0];
446
+ this.match += match[0];
447
+ this.matches = match;
448
+ this.yyleng = this.yytext.length;
449
+ if (this.options.ranges) {
450
+ this.yylloc.range = [this.offset, this.offset += this.yyleng];
451
+ }
452
+ this._more = false;
453
+ this._input = this._input.slice(match[0].length);
454
+ this.matched += match[0];
455
+ token = this.performAction.call(this, this.yy, this, rules[index],this.conditionStack[this.conditionStack.length-1]);
456
+ if (this.done && this._input) this.done = false;
457
+ if (token) return token;
458
+ else return;
459
+ }
384
460
  if (this._input === "") {
385
461
  return this.EOF;
386
462
  } else {
387
- this.parseError('Lexical error on line '+(this.yylineno+1)+'. Unrecognized text.\n'+this.showPosition(),
463
+ return this.parseError('Lexical error on line '+(this.yylineno+1)+'. Unrecognized text.\n'+this.showPosition(),
388
464
  {text: "", token: null, line: this.yylineno});
389
465
  }
390
466
  },
@@ -411,6 +487,7 @@ topState:function () {
411
487
  pushState:function begin(condition) {
412
488
  this.begin(condition);
413
489
  }});
490
+ lexer.options = {};
414
491
  lexer.performAction = function anonymous(yy,yy_,$avoiding_name_collisions,YY_START) {
415
492
 
416
493
  var YYSTATE=YY_START
@@ -419,78 +496,89 @@ case 0:
419
496
  if(yy_.yytext.slice(-1) !== "\\") this.begin("mu");
420
497
  if(yy_.yytext.slice(-1) === "\\") yy_.yytext = yy_.yytext.substr(0,yy_.yyleng-1), this.begin("emu");
421
498
  if(yy_.yytext) return 14;
422
-
499
+
423
500
  break;
424
- case 1: return 14;
501
+ case 1: return 14;
425
502
  break;
426
- case 2: this.popState(); return 14;
503
+ case 2:
504
+ if(yy_.yytext.slice(-1) !== "\\") this.popState();
505
+ if(yy_.yytext.slice(-1) === "\\") yy_.yytext = yy_.yytext.substr(0,yy_.yyleng-1);
506
+ return 14;
507
+
427
508
  break;
428
- case 3: return 24;
509
+ case 3: return 24;
429
510
  break;
430
- case 4: return 16;
511
+ case 4: return 16;
431
512
  break;
432
- case 5: return 20;
513
+ case 5: return 20;
433
514
  break;
434
- case 6: return 19;
515
+ case 6: return 19;
435
516
  break;
436
- case 7: return 19;
517
+ case 7: return 19;
437
518
  break;
438
- case 8: return 23;
519
+ case 8: return 23;
439
520
  break;
440
- case 9: return 23;
521
+ case 9: return 23;
441
522
  break;
442
- case 10: yy_.yytext = yy_.yytext.substr(3,yy_.yyleng-5); this.popState(); return 15;
523
+ case 10: yy_.yytext = yy_.yytext.substr(3,yy_.yyleng-5); this.popState(); return 15;
443
524
  break;
444
- case 11: return 22;
525
+ case 11: return 22;
445
526
  break;
446
- case 12: return 34;
527
+ case 12: return 35;
447
528
  break;
448
- case 13: return 33;
529
+ case 13: return 34;
449
530
  break;
450
- case 14: return 33;
531
+ case 14: return 34;
451
532
  break;
452
- case 15: return 36;
533
+ case 15: return 37;
453
534
  break;
454
- case 16: /*ignore whitespace*/
535
+ case 16: /*ignore whitespace*/
455
536
  break;
456
- case 17: this.popState(); return 18;
537
+ case 17: this.popState(); return 18;
457
538
  break;
458
- case 18: this.popState(); return 18;
539
+ case 18: this.popState(); return 18;
459
540
  break;
460
- case 19: yy_.yytext = yy_.yytext.substr(1,yy_.yyleng-2).replace(/\\"/g,'"'); return 28;
541
+ case 19: yy_.yytext = yy_.yytext.substr(1,yy_.yyleng-2).replace(/\\"/g,'"'); return 29;
461
542
  break;
462
- case 20: return 30;
543
+ case 20: yy_.yytext = yy_.yytext.substr(1,yy_.yyleng-2).replace(/\\"/g,'"'); return 29;
463
544
  break;
464
- case 21: return 30;
545
+ case 21: yy_.yytext = yy_.yytext.substr(1); return 27;
465
546
  break;
466
- case 22: return 29;
547
+ case 22: return 31;
467
548
  break;
468
- case 23: return 33;
549
+ case 23: return 31;
469
550
  break;
470
- case 24: yy_.yytext = yy_.yytext.substr(1, yy_.yyleng-2); return 33;
551
+ case 24: return 30;
471
552
  break;
472
- case 25: return 'INVALID';
553
+ case 25: return 34;
473
554
  break;
474
- case 26: return 5;
555
+ case 26: yy_.yytext = yy_.yytext.substr(1, yy_.yyleng-2); return 34;
556
+ break;
557
+ case 27: return 'INVALID';
558
+ break;
559
+ case 28: return 5;
475
560
  break;
476
561
  }
477
562
  };
478
- lexer.rules = [/^[^\x00]*?(?=(\{\{))/,/^[^\x00]+/,/^[^\x00]{2,}?(?=(\{\{))/,/^\{\{>/,/^\{\{#/,/^\{\{\//,/^\{\{\^/,/^\{\{\s*else\b/,/^\{\{\{/,/^\{\{&/,/^\{\{![\s\S]*?\}\}/,/^\{\{/,/^=/,/^\.(?=[} ])/,/^\.\./,/^[\/.]/,/^\s+/,/^\}\}\}/,/^\}\}/,/^"(\\["]|[^"])*"/,/^true(?=[}\s])/,/^false(?=[}\s])/,/^[0-9]+(?=[}\s])/,/^[a-zA-Z0-9_$-]+(?=[=}\s\/.])/,/^\[[^\]]*\]/,/^./,/^$/];
479
- 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],"inclusive":false},"emu":{"rules":[2],"inclusive":false},"INITIAL":{"rules":[0,1,26],"inclusive":true}};return lexer;})()
563
+ 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\/.]))/,/^(?:\[[^\]]*\])/,/^(?:.)/,/^(?:$)/];
564
+ 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}};
565
+ return lexer;})()
480
566
  parser.lexer = lexer;
481
- return parser;
567
+ function Parser () { this.yy = {}; }Parser.prototype = parser;parser.Parser = Parser;
568
+ return new Parser;
482
569
  })();
483
570
  if (typeof require !== 'undefined' && typeof exports !== 'undefined') {
484
571
  exports.parser = handlebars;
572
+ exports.Parser = handlebars.Parser;
485
573
  exports.parse = function () { return handlebars.parse.apply(handlebars, arguments); }
486
574
  exports.main = function commonjsMain(args) {
487
575
  if (!args[1])
488
576
  throw new Error('Usage: '+args[0]+' FILE');
577
+ var source, cwd;
489
578
  if (typeof process !== 'undefined') {
490
- var source = require('fs').readFileSync(require('path').join(process.cwd(), args[1]), "utf8");
579
+ source = require('fs').readFileSync(require('path').resolve(args[1]), "utf8");
491
580
  } else {
492
- var cwd = require("file").path(require("file").cwd());
493
- var source = cwd.join(args[1]).read({charset: "utf-8"});
581
+ source = require("file").path(require("file").cwd()).join(args[1]).read({charset: "utf-8"});
494
582
  }
495
583
  return exports.parser.parse(source);
496
584
  }
@@ -531,12 +619,26 @@ Handlebars.log = function(level, str) { Handlebars.logger.log(level, str); };
531
619
  if(inverse) { this.inverse = new Handlebars.AST.ProgramNode(inverse); }
532
620
  };
533
621
 
534
- Handlebars.AST.MustacheNode = function(params, hash, unescaped) {
622
+ Handlebars.AST.MustacheNode = function(rawParams, hash, unescaped) {
535
623
  this.type = "mustache";
536
- this.id = params[0];
537
- this.params = params.slice(1);
538
- this.hash = hash;
539
624
  this.escaped = !unescaped;
625
+ this.hash = hash;
626
+
627
+ var id = this.id = rawParams[0];
628
+ var params = this.params = rawParams.slice(1);
629
+
630
+ // a mustache is an eligible helper if:
631
+ // * its id is simple (a single part, not `this` or `..`)
632
+ var eligibleHelper = this.eligibleHelper = id.isSimple;
633
+
634
+ // a mustache is definitely a helper if:
635
+ // * it is an eligible helper, and
636
+ // * it has at least one parameter or hash segment
637
+ this.isHelper = eligibleHelper && (params.length || hash);
638
+
639
+ // if a mustache is an eligible helper but not a definite
640
+ // helper, it is ambiguous, and will be resolved in a later
641
+ // pass or at runtime.
540
642
  };
541
643
 
542
644
  Handlebars.AST.PartialNode = function(id, context) {
@@ -554,18 +656,16 @@ Handlebars.log = function(level, str) { Handlebars.logger.log(level, str); };
554
656
  }
555
657
  };
556
658
 
557
- Handlebars.AST.BlockNode = function(mustache, program, close) {
659
+ Handlebars.AST.BlockNode = function(mustache, program, inverse, close) {
558
660
  verifyMatch(mustache.id, close);
559
661
  this.type = "block";
560
662
  this.mustache = mustache;
561
663
  this.program = program;
562
- };
664
+ this.inverse = inverse;
563
665
 
564
- Handlebars.AST.InverseNode = function(mustache, program, close) {
565
- verifyMatch(mustache.id, close);
566
- this.type = "inverse";
567
- this.mustache = mustache;
568
- this.program = program;
666
+ if (this.inverse && !this.program) {
667
+ this.isInverse = true;
668
+ }
569
669
  };
570
670
 
571
671
  Handlebars.AST.ContentNode = function(string) {
@@ -595,7 +695,15 @@ Handlebars.log = function(level, str) { Handlebars.logger.log(level, str); };
595
695
  this.parts = dig;
596
696
  this.string = dig.join('.');
597
697
  this.depth = depth;
598
- this.isSimple = (dig.length === 1) && (depth === 0);
698
+
699
+ // an ID is simple if it only has one part, and that part is not
700
+ // `..` or `this`.
701
+ this.isSimple = parts.length === 1 && !this.isScoped && depth === 0;
702
+ };
703
+
704
+ Handlebars.AST.DataNode = function(id) {
705
+ this.type = "DATA";
706
+ this.id = id;
599
707
  };
600
708
 
601
709
  Handlebars.AST.StringNode = function(string) {
@@ -629,7 +737,7 @@ Handlebars.Exception = function(message) {
629
737
 
630
738
  this.message = tmp.message;
631
739
  };
632
- Handlebars.Exception.prototype = new Error;
740
+ Handlebars.Exception.prototype = new Error();
633
741
 
634
742
  // Build out our basic SafeString type
635
743
  Handlebars.SafeString = function(string) {
@@ -641,6 +749,7 @@ Handlebars.SafeString.prototype.toString = function() {
641
749
 
642
750
  (function() {
643
751
  var escape = {
752
+ "&": "&amp;",
644
753
  "<": "&lt;",
645
754
  ">": "&gt;",
646
755
  '"': "&quot;",
@@ -648,7 +757,7 @@ Handlebars.SafeString.prototype.toString = function() {
648
757
  "`": "&#x60;"
649
758
  };
650
759
 
651
- var badChars = /&(?!\w+;)|[<>"'`]/g;
760
+ var badChars = /[&<>"'`]/g;
652
761
  var possible = /[&<>"'`]/;
653
762
 
654
763
  var escapeChar = function(chr) {
@@ -684,88 +793,38 @@ Handlebars.SafeString.prototype.toString = function() {
684
793
  };
685
794
  })();;
686
795
  // lib/handlebars/compiler/compiler.js
796
+
797
+ /*jshint eqnull:true*/
687
798
  Handlebars.Compiler = function() {};
688
799
  Handlebars.JavaScriptCompiler = function() {};
689
800
 
690
801
  (function(Compiler, JavaScriptCompiler) {
691
- Compiler.OPCODE_MAP = {
692
- appendContent: 1,
693
- getContext: 2,
694
- lookupWithHelpers: 3,
695
- lookup: 4,
696
- append: 5,
697
- invokeMustache: 6,
698
- appendEscaped: 7,
699
- pushString: 8,
700
- truthyOrFallback: 9,
701
- functionOrFallback: 10,
702
- invokeProgram: 11,
703
- invokePartial: 12,
704
- push: 13,
705
- assignToHash: 15,
706
- pushStringParam: 16
707
- };
708
-
709
- Compiler.MULTI_PARAM_OPCODES = {
710
- appendContent: 1,
711
- getContext: 1,
712
- lookupWithHelpers: 2,
713
- lookup: 1,
714
- invokeMustache: 3,
715
- pushString: 1,
716
- truthyOrFallback: 1,
717
- functionOrFallback: 1,
718
- invokeProgram: 3,
719
- invokePartial: 1,
720
- push: 1,
721
- assignToHash: 1,
722
- pushStringParam: 1
723
- };
724
-
725
- Compiler.DISASSEMBLE_MAP = {};
726
-
727
- for(var prop in Compiler.OPCODE_MAP) {
728
- var value = Compiler.OPCODE_MAP[prop];
729
- Compiler.DISASSEMBLE_MAP[value] = prop;
730
- }
731
-
732
- Compiler.multiParamSize = function(code) {
733
- return Compiler.MULTI_PARAM_OPCODES[Compiler.DISASSEMBLE_MAP[code]];
734
- };
802
+ // the foundHelper register will disambiguate helper lookup from finding a
803
+ // function in a context. This is necessary for mustache compatibility, which
804
+ // requires that context functions in blocks are evaluated by blockHelperMissing,
805
+ // and then proceed as if the resulting value was provided to blockHelperMissing.
735
806
 
736
807
  Compiler.prototype = {
737
808
  compiler: Compiler,
738
809
 
739
810
  disassemble: function() {
740
- var opcodes = this.opcodes, opcode, nextCode;
741
- var out = [], str, name, value;
811
+ var opcodes = this.opcodes, opcode, out = [], params, param;
742
812
 
743
- for(var i=0, l=opcodes.length; i<l; i++) {
813
+ for (var i=0, l=opcodes.length; i<l; i++) {
744
814
  opcode = opcodes[i];
745
815
 
746
- if(opcode === 'DECLARE') {
747
- name = opcodes[++i];
748
- value = opcodes[++i];
749
- out.push("DECLARE " + name + " = " + value);
816
+ if (opcode.opcode === 'DECLARE') {
817
+ out.push("DECLARE " + opcode.name + "=" + opcode.value);
750
818
  } else {
751
- str = Compiler.DISASSEMBLE_MAP[opcode];
752
-
753
- var extraParams = Compiler.multiParamSize(opcode);
754
- var codes = [];
755
-
756
- for(var j=0; j<extraParams; j++) {
757
- nextCode = opcodes[++i];
758
-
759
- if(typeof nextCode === "string") {
760
- nextCode = "\"" + nextCode.replace("\n", "\\n") + "\"";
819
+ params = [];
820
+ for (var j=0; j<opcode.args.length; j++) {
821
+ param = opcode.args[j];
822
+ if (typeof param === "string") {
823
+ param = "\"" + param.replace("\n", "\\n") + "\"";
761
824
  }
762
-
763
- codes.push(nextCode);
825
+ params.push(param);
764
826
  }
765
-
766
- str = str + " " + codes.join(" ");
767
-
768
- out.push(str);
827
+ out.push(opcode.opcode + " " + params.join(" "));
769
828
  }
770
829
  }
771
830
 
@@ -822,7 +881,7 @@ Handlebars.JavaScriptCompiler = function() {};
822
881
 
823
882
  compileProgram: function(program) {
824
883
  var result = new this.compiler().compile(program, this.options);
825
- var guid = this.guid++;
884
+ var guid = this.guid++, depth;
826
885
 
827
886
  this.usePartial = this.usePartial || result.usePartial;
828
887
 
@@ -839,32 +898,42 @@ Handlebars.JavaScriptCompiler = function() {};
839
898
  },
840
899
 
841
900
  block: function(block) {
842
- var mustache = block.mustache;
843
- var depth, child, inverse, inverseGuid;
901
+ var mustache = block.mustache,
902
+ program = block.program,
903
+ inverse = block.inverse;
844
904
 
845
- var params = this.setupStackForMustache(mustache);
846
-
847
- var programGuid = this.compileProgram(block.program);
848
-
849
- if(block.program.inverse) {
850
- inverseGuid = this.compileProgram(block.program.inverse);
851
- this.declare('inverse', inverseGuid);
905
+ if (program) {
906
+ program = this.compileProgram(program);
852
907
  }
853
908
 
854
- this.opcode('invokeProgram', programGuid, params.length, !!mustache.hash);
855
- this.declare('inverse', null);
856
- this.opcode('append');
857
- },
909
+ if (inverse) {
910
+ inverse = this.compileProgram(inverse);
911
+ }
858
912
 
859
- inverse: function(block) {
860
- var params = this.setupStackForMustache(block.mustache);
913
+ var type = this.classifyMustache(mustache);
861
914
 
862
- var programGuid = this.compileProgram(block.program);
915
+ if (type === "helper") {
916
+ this.helperMustache(mustache, program, inverse);
917
+ } else if (type === "simple") {
918
+ this.simpleMustache(mustache);
863
919
 
864
- this.declare('inverse', programGuid);
920
+ // now that the simple mustache is resolved, we need to
921
+ // evaluate it by executing `blockHelperMissing`
922
+ this.opcode('pushProgram', program);
923
+ this.opcode('pushProgram', inverse);
924
+ this.opcode('pushLiteral', '{}');
925
+ this.opcode('blockValue');
926
+ } else {
927
+ this.ambiguousMustache(mustache, program, inverse);
928
+
929
+ // now that the simple mustache is resolved, we need to
930
+ // evaluate it by executing `blockHelperMissing`
931
+ this.opcode('pushProgram', program);
932
+ this.opcode('pushProgram', inverse);
933
+ this.opcode('pushLiteral', '{}');
934
+ this.opcode('ambiguousBlockValue');
935
+ }
865
936
 
866
- this.opcode('invokeProgram', null, params.length, !!block.mustache.hash);
867
- this.declare('inverse', null);
868
937
  this.opcode('append');
869
938
  },
870
939
 
@@ -901,44 +970,140 @@ Handlebars.JavaScriptCompiler = function() {};
901
970
  },
902
971
 
903
972
  mustache: function(mustache) {
904
- var params = this.setupStackForMustache(mustache);
973
+ var options = this.options;
974
+ var type = this.classifyMustache(mustache);
905
975
 
906
- this.opcode('invokeMustache', params.length, mustache.id.original, !!mustache.hash);
976
+ if (type === "simple") {
977
+ this.simpleMustache(mustache);
978
+ } else if (type === "helper") {
979
+ this.helperMustache(mustache);
980
+ } else {
981
+ this.ambiguousMustache(mustache);
982
+ }
907
983
 
908
- if(mustache.escaped && !this.options.noEscape) {
984
+ if(mustache.escaped && !options.noEscape) {
909
985
  this.opcode('appendEscaped');
910
986
  } else {
911
987
  this.opcode('append');
912
988
  }
913
989
  },
914
990
 
991
+ ambiguousMustache: function(mustache, program, inverse) {
992
+ var id = mustache.id, name = id.parts[0];
993
+
994
+ this.opcode('getContext', id.depth);
995
+
996
+ this.opcode('pushProgram', program);
997
+ this.opcode('pushProgram', inverse);
998
+
999
+ this.opcode('invokeAmbiguous', name);
1000
+ },
1001
+
1002
+ simpleMustache: function(mustache, program, inverse) {
1003
+ var id = mustache.id;
1004
+
1005
+ if (id.type === 'DATA') {
1006
+ this.DATA(id);
1007
+ } else if (id.parts.length) {
1008
+ this.ID(id);
1009
+ } else {
1010
+ // Simplified ID for `this`
1011
+ this.addDepth(id.depth);
1012
+ this.opcode('getContext', id.depth);
1013
+ this.opcode('pushContext');
1014
+ }
1015
+
1016
+ this.opcode('resolvePossibleLambda');
1017
+ },
1018
+
1019
+ helperMustache: function(mustache, program, inverse) {
1020
+ var params = this.setupFullMustacheParams(mustache, program, inverse),
1021
+ name = mustache.id.parts[0];
1022
+
1023
+ if (this.options.knownHelpers[name]) {
1024
+ this.opcode('invokeKnownHelper', params.length, name);
1025
+ } else if (this.knownHelpersOnly) {
1026
+ throw new Error("You specified knownHelpersOnly, but used the unknown helper " + name);
1027
+ } else {
1028
+ this.opcode('invokeHelper', params.length, name);
1029
+ }
1030
+ },
1031
+
915
1032
  ID: function(id) {
916
1033
  this.addDepth(id.depth);
917
-
918
1034
  this.opcode('getContext', id.depth);
919
1035
 
920
- this.opcode('lookupWithHelpers', id.parts[0] || null, id.isScoped || false);
1036
+ var name = id.parts[0];
1037
+ if (!name) {
1038
+ this.opcode('pushContext');
1039
+ } else {
1040
+ this.opcode('lookupOnContext', id.parts[0]);
1041
+ }
921
1042
 
922
1043
  for(var i=1, l=id.parts.length; i<l; i++) {
923
1044
  this.opcode('lookup', id.parts[i]);
924
1045
  }
925
1046
  },
926
1047
 
1048
+ DATA: function(data) {
1049
+ this.options.data = true;
1050
+ this.opcode('lookupData', data.id);
1051
+ },
1052
+
927
1053
  STRING: function(string) {
928
1054
  this.opcode('pushString', string.string);
929
1055
  },
930
1056
 
931
1057
  INTEGER: function(integer) {
932
- this.opcode('push', integer.integer);
1058
+ this.opcode('pushLiteral', integer.integer);
933
1059
  },
934
1060
 
935
1061
  BOOLEAN: function(bool) {
936
- this.opcode('push', bool.bool);
1062
+ this.opcode('pushLiteral', bool.bool);
937
1063
  },
938
1064
 
939
1065
  comment: function() {},
940
1066
 
941
1067
  // HELPERS
1068
+ opcode: function(name) {
1069
+ this.opcodes.push({ opcode: name, args: [].slice.call(arguments, 1) });
1070
+ },
1071
+
1072
+ declare: function(name, value) {
1073
+ this.opcodes.push({ opcode: 'DECLARE', name: name, value: value });
1074
+ },
1075
+
1076
+ addDepth: function(depth) {
1077
+ if(isNaN(depth)) { throw new Error("EWOT"); }
1078
+ if(depth === 0) { return; }
1079
+
1080
+ if(!this.depths[depth]) {
1081
+ this.depths[depth] = true;
1082
+ this.depths.list.push(depth);
1083
+ }
1084
+ },
1085
+
1086
+ classifyMustache: function(mustache) {
1087
+ var isHelper = mustache.isHelper;
1088
+ var isEligible = mustache.eligibleHelper;
1089
+ var options = this.options;
1090
+
1091
+ // if ambiguous, we can possibly resolve the ambiguity now
1092
+ if (isEligible && !isHelper) {
1093
+ var name = mustache.id.parts[0];
1094
+
1095
+ if (options.knownHelpers[name]) {
1096
+ isHelper = true;
1097
+ } else if (options.knownHelpersOnly) {
1098
+ isEligible = false;
1099
+ }
1100
+ }
1101
+
1102
+ if (isHelper) { return "helper"; }
1103
+ else if (isEligible) { return "ambiguous"; }
1104
+ else { return "simple"; }
1105
+ },
1106
+
942
1107
  pushParams: function(params) {
943
1108
  var i = params.length, param;
944
1109
 
@@ -958,54 +1123,52 @@ Handlebars.JavaScriptCompiler = function() {};
958
1123
  }
959
1124
  },
960
1125
 
961
- opcode: function(name, val1, val2, val3) {
962
- this.opcodes.push(Compiler.OPCODE_MAP[name]);
963
- if(val1 !== undefined) { this.opcodes.push(val1); }
964
- if(val2 !== undefined) { this.opcodes.push(val2); }
965
- if(val3 !== undefined) { this.opcodes.push(val3); }
966
- },
967
-
968
- declare: function(name, value) {
969
- this.opcodes.push('DECLARE');
970
- this.opcodes.push(name);
971
- this.opcodes.push(value);
972
- },
973
-
974
- addDepth: function(depth) {
975
- if(depth === 0) { return; }
1126
+ setupMustacheParams: function(mustache) {
1127
+ var params = mustache.params;
1128
+ this.pushParams(params);
976
1129
 
977
- if(!this.depths[depth]) {
978
- this.depths[depth] = true;
979
- this.depths.list.push(depth);
1130
+ if(mustache.hash) {
1131
+ this.hash(mustache.hash);
1132
+ } else {
1133
+ this.opcode('pushLiteral', '{}');
980
1134
  }
1135
+
1136
+ return params;
981
1137
  },
982
1138
 
983
- setupStackForMustache: function(mustache) {
1139
+ // this will replace setupMustacheParams when we're done
1140
+ setupFullMustacheParams: function(mustache, program, inverse) {
984
1141
  var params = mustache.params;
985
-
986
1142
  this.pushParams(params);
987
1143
 
1144
+ this.opcode('pushProgram', program);
1145
+ this.opcode('pushProgram', inverse);
1146
+
988
1147
  if(mustache.hash) {
989
1148
  this.hash(mustache.hash);
1149
+ } else {
1150
+ this.opcode('pushLiteral', '{}');
990
1151
  }
991
1152
 
992
- this.ID(mustache.id);
993
-
994
1153
  return params;
995
1154
  }
996
1155
  };
997
1156
 
1157
+ var Literal = function(value) {
1158
+ this.value = value;
1159
+ };
1160
+
998
1161
  JavaScriptCompiler.prototype = {
999
1162
  // PUBLIC API: You can override these methods in a subclass to provide
1000
1163
  // alternative compiled forms for name lookup and buffering semantics
1001
1164
  nameLookup: function(parent, name, type) {
1002
- if (/^[0-9]+$/.test(name)) {
1165
+ if (/^[0-9]+$/.test(name)) {
1003
1166
  return parent + "[" + name + "]";
1004
1167
  } else if (JavaScriptCompiler.isValidJavaScriptVariableName(name)) {
1005
- return parent + "." + name;
1006
- }
1007
- else {
1008
- return parent + "['" + name + "']";
1168
+ return parent + "." + name;
1169
+ }
1170
+ else {
1171
+ return parent + "['" + name + "']";
1009
1172
  }
1010
1173
  },
1011
1174
 
@@ -1028,18 +1191,21 @@ Handlebars.JavaScriptCompiler = function() {};
1028
1191
  this.environment = environment;
1029
1192
  this.options = options || {};
1030
1193
 
1194
+ Handlebars.log(Handlebars.logger.DEBUG, this.environment.disassemble() + "\n\n");
1195
+
1031
1196
  this.name = this.environment.name;
1032
1197
  this.isChild = !!context;
1033
1198
  this.context = context || {
1034
1199
  programs: [],
1035
- aliases: { self: 'this' },
1036
- registers: {list: []}
1200
+ aliases: { }
1037
1201
  };
1038
1202
 
1039
1203
  this.preamble();
1040
1204
 
1041
1205
  this.stackSlot = 0;
1042
1206
  this.stackVars = [];
1207
+ this.registers = { list: [] };
1208
+ this.compileStack = [];
1043
1209
 
1044
1210
  this.compileChildren(environment, options);
1045
1211
 
@@ -1048,59 +1214,35 @@ Handlebars.JavaScriptCompiler = function() {};
1048
1214
  this.i = 0;
1049
1215
 
1050
1216
  for(l=opcodes.length; this.i<l; this.i++) {
1051
- opcode = this.nextOpcode(0);
1217
+ opcode = opcodes[this.i];
1052
1218
 
1053
- if(opcode[0] === 'DECLARE') {
1054
- this.i = this.i + 2;
1055
- this[opcode[1]] = opcode[2];
1219
+ if(opcode.opcode === 'DECLARE') {
1220
+ this[opcode.name] = opcode.value;
1056
1221
  } else {
1057
- this.i = this.i + opcode[1].length;
1058
- this[opcode[0]].apply(this, opcode[1]);
1222
+ this[opcode.opcode].apply(this, opcode.args);
1059
1223
  }
1060
1224
  }
1061
1225
 
1062
1226
  return this.createFunctionContext(asObject);
1063
1227
  },
1064
1228
 
1065
- nextOpcode: function(n) {
1066
- var opcodes = this.environment.opcodes, opcode = opcodes[this.i + n], name, val;
1067
- var extraParams, codes;
1068
-
1069
- if(opcode === 'DECLARE') {
1070
- name = opcodes[this.i + 1];
1071
- val = opcodes[this.i + 2];
1072
- return ['DECLARE', name, val];
1073
- } else {
1074
- name = Compiler.DISASSEMBLE_MAP[opcode];
1075
-
1076
- extraParams = Compiler.multiParamSize(opcode);
1077
- codes = [];
1078
-
1079
- for(var j=0; j<extraParams; j++) {
1080
- codes.push(opcodes[this.i + j + 1 + n]);
1081
- }
1082
-
1083
- return [name, codes];
1084
- }
1229
+ nextOpcode: function() {
1230
+ var opcodes = this.environment.opcodes, opcode = opcodes[this.i + 1];
1231
+ return opcodes[this.i + 1];
1085
1232
  },
1086
1233
 
1087
1234
  eat: function(opcode) {
1088
- this.i = this.i + opcode.length;
1235
+ this.i = this.i + 1;
1089
1236
  },
1090
1237
 
1091
1238
  preamble: function() {
1092
1239
  var out = [];
1093
1240
 
1094
- // this register will disambiguate helper lookup from finding a function in
1095
- // a context. This is necessary for mustache compatibility, which requires
1096
- // that context functions in blocks are evaluated by blockHelperMissing, and
1097
- // then proceed as if the resulting value was provided to blockHelperMissing.
1098
- this.useRegister('foundHelper');
1099
-
1100
1241
  if (!this.isChild) {
1101
1242
  var namespace = this.namespace;
1102
1243
  var copies = "helpers = helpers || " + namespace + ".helpers;";
1103
- if(this.environment.usePartial) { copies = copies + " partials = partials || " + namespace + ".partials;"; }
1244
+ if (this.environment.usePartial) { copies = copies + " partials = partials || " + namespace + ".partials;"; }
1245
+ if (this.options.data) { copies = copies + " data = data || {};"; }
1104
1246
  out.push(copies);
1105
1247
  } else {
1106
1248
  out.push('');
@@ -1119,10 +1261,7 @@ Handlebars.JavaScriptCompiler = function() {};
1119
1261
  },
1120
1262
 
1121
1263
  createFunctionContext: function(asObject) {
1122
- var locals = this.stackVars;
1123
- if (!this.isChild) {
1124
- locals = locals.concat(this.context.registers.list);
1125
- }
1264
+ var locals = this.stackVars.concat(this.registers.list);
1126
1265
 
1127
1266
  if(locals.length > 0) {
1128
1267
  this.source[1] = this.source[1] + ", " + locals.join(", ");
@@ -1130,7 +1269,7 @@ Handlebars.JavaScriptCompiler = function() {};
1130
1269
 
1131
1270
  // Generate minimizer alias mappings
1132
1271
  if (!this.isChild) {
1133
- var aliases = []
1272
+ var aliases = [];
1134
1273
  for (var alias in this.context.aliases) {
1135
1274
  this.source[1] = this.source[1] + ', ' + alias + '=' + this.context.aliases[alias];
1136
1275
  }
@@ -1166,10 +1305,64 @@ Handlebars.JavaScriptCompiler = function() {};
1166
1305
  }
1167
1306
  },
1168
1307
 
1308
+ // [blockValue]
1309
+ //
1310
+ // On stack, before: hash, inverse, program, value
1311
+ // On stack, after: return value of blockHelperMissing
1312
+ //
1313
+ // The purpose of this opcode is to take a block of the form
1314
+ // `{{#foo}}...{{/foo}}`, resolve the value of `foo`, and
1315
+ // replace it on the stack with the result of properly
1316
+ // invoking blockHelperMissing.
1317
+ blockValue: function() {
1318
+ this.context.aliases.blockHelperMissing = 'helpers.blockHelperMissing';
1319
+
1320
+ var params = ["depth0"];
1321
+ this.setupParams(0, params);
1322
+
1323
+ this.replaceStack(function(current) {
1324
+ params.splice(1, 0, current);
1325
+ return current + " = blockHelperMissing.call(" + params.join(", ") + ")";
1326
+ });
1327
+ },
1328
+
1329
+ // [ambiguousBlockValue]
1330
+ //
1331
+ // On stack, before: hash, inverse, program, value
1332
+ // Compiler value, before: lastHelper=value of last found helper, if any
1333
+ // On stack, after, if no lastHelper: same as [blockValue]
1334
+ // On stack, after, if lastHelper: value
1335
+ ambiguousBlockValue: function() {
1336
+ this.context.aliases.blockHelperMissing = 'helpers.blockHelperMissing';
1337
+
1338
+ var params = ["depth0"];
1339
+ this.setupParams(0, params);
1340
+
1341
+ var current = this.topStack();
1342
+ params.splice(1, 0, current);
1343
+
1344
+ this.source.push("if (!" + this.lastHelper + ") { " + current + " = blockHelperMissing.call(" + params.join(", ") + "); }");
1345
+ },
1346
+
1347
+ // [appendContent]
1348
+ //
1349
+ // On stack, before: ...
1350
+ // On stack, after: ...
1351
+ //
1352
+ // Appends the string value of `content` to the current buffer
1169
1353
  appendContent: function(content) {
1170
1354
  this.source.push(this.appendToBuffer(this.quotedString(content)));
1171
1355
  },
1172
1356
 
1357
+ // [append]
1358
+ //
1359
+ // On stack, before: value, ...
1360
+ // On stack, after: ...
1361
+ //
1362
+ // Coerces `value` to a String and appends it to the current buffer.
1363
+ //
1364
+ // If `value` is truthy, or 0, it is coerced into a string and appended
1365
+ // Otherwise, the empty string is appended
1173
1366
  append: function() {
1174
1367
  var local = this.popStack();
1175
1368
  this.source.push("if(" + local + " || " + local + " === 0) { " + this.appendToBuffer(local) + " }");
@@ -1178,163 +1371,242 @@ Handlebars.JavaScriptCompiler = function() {};
1178
1371
  }
1179
1372
  },
1180
1373
 
1374
+ // [appendEscaped]
1375
+ //
1376
+ // On stack, before: value, ...
1377
+ // On stack, after: ...
1378
+ //
1379
+ // Escape `value` and append it to the buffer
1181
1380
  appendEscaped: function() {
1182
- var opcode = this.nextOpcode(1), extra = "";
1381
+ var opcode = this.nextOpcode(), extra = "";
1183
1382
  this.context.aliases.escapeExpression = 'this.escapeExpression';
1184
1383
 
1185
- if(opcode[0] === 'appendContent') {
1186
- extra = " + " + this.quotedString(opcode[1][0]);
1384
+ if(opcode && opcode.opcode === 'appendContent') {
1385
+ extra = " + " + this.quotedString(opcode.args[0]);
1187
1386
  this.eat(opcode);
1188
1387
  }
1189
1388
 
1190
1389
  this.source.push(this.appendToBuffer("escapeExpression(" + this.popStack() + ")" + extra));
1191
1390
  },
1192
1391
 
1392
+ // [getContext]
1393
+ //
1394
+ // On stack, before: ...
1395
+ // On stack, after: ...
1396
+ // Compiler value, after: lastContext=depth
1397
+ //
1398
+ // Set the value of the `lastContext` compiler value to the depth
1193
1399
  getContext: function(depth) {
1194
1400
  if(this.lastContext !== depth) {
1195
1401
  this.lastContext = depth;
1196
1402
  }
1197
1403
  },
1198
1404
 
1199
- lookupWithHelpers: function(name, isScoped) {
1200
- if(name) {
1201
- var topStack = this.nextStack();
1202
-
1203
- this.usingKnownHelper = false;
1204
-
1205
- var toPush;
1206
- if (!isScoped && this.options.knownHelpers[name]) {
1207
- toPush = topStack + " = " + this.nameLookup('helpers', name, 'helper');
1208
- this.usingKnownHelper = true;
1209
- } else if (isScoped || this.options.knownHelpersOnly) {
1210
- toPush = topStack + " = " + this.nameLookup('depth' + this.lastContext, name, 'context');
1211
- } else {
1212
- this.register('foundHelper', this.nameLookup('helpers', name, 'helper'));
1213
- toPush = topStack + " = foundHelper || " + this.nameLookup('depth' + this.lastContext, name, 'context');
1214
- }
1215
-
1216
- toPush += ';';
1217
- this.source.push(toPush);
1218
- } else {
1219
- this.pushStack('depth' + this.lastContext);
1220
- }
1405
+ // [lookupOnContext]
1406
+ //
1407
+ // On stack, before: ...
1408
+ // On stack, after: currentContext[name], ...
1409
+ //
1410
+ // Looks up the value of `name` on the current context and pushes
1411
+ // it onto the stack.
1412
+ lookupOnContext: function(name) {
1413
+ this.pushStack(this.nameLookup('depth' + this.lastContext, name, 'context'));
1414
+ },
1415
+
1416
+ // [pushContext]
1417
+ //
1418
+ // On stack, before: ...
1419
+ // On stack, after: currentContext, ...
1420
+ //
1421
+ // Pushes the value of the current context onto the stack.
1422
+ pushContext: function() {
1423
+ this.pushStackLiteral('depth' + this.lastContext);
1424
+ },
1425
+
1426
+ // [resolvePossibleLambda]
1427
+ //
1428
+ // On stack, before: value, ...
1429
+ // On stack, after: resolved value, ...
1430
+ //
1431
+ // If the `value` is a lambda, replace it on the stack by
1432
+ // the return value of the lambda
1433
+ resolvePossibleLambda: function() {
1434
+ this.context.aliases.functionType = '"function"';
1435
+
1436
+ this.replaceStack(function(current) {
1437
+ return "typeof " + current + " === functionType ? " + current + "() : " + current;
1438
+ });
1221
1439
  },
1222
1440
 
1441
+ // [lookup]
1442
+ //
1443
+ // On stack, before: value, ...
1444
+ // On stack, after: value[name], ...
1445
+ //
1446
+ // Replace the value on the stack with the result of looking
1447
+ // up `name` on `value`
1223
1448
  lookup: function(name) {
1224
- var topStack = this.topStack();
1225
- this.source.push(topStack + " = (" + topStack + " === null || " + topStack + " === undefined || " + topStack + " === false ? " +
1226
- topStack + " : " + this.nameLookup(topStack, name, 'context') + ");");
1449
+ this.replaceStack(function(current) {
1450
+ return current + " == null || " + current + " === false ? " + current + " : " + this.nameLookup(current, name, 'context');
1451
+ });
1227
1452
  },
1228
1453
 
1454
+ // [lookupData]
1455
+ //
1456
+ // On stack, before: ...
1457
+ // On stack, after: data[id], ...
1458
+ //
1459
+ // Push the result of looking up `id` on the current data
1460
+ lookupData: function(id) {
1461
+ this.pushStack(this.nameLookup('data', id, 'data'));
1462
+ },
1463
+
1464
+ // [pushStringParam]
1465
+ //
1466
+ // On stack, before: ...
1467
+ // On stack, after: string, currentContext, ...
1468
+ //
1469
+ // This opcode is designed for use in string mode, which
1470
+ // provides the string value of a parameter along with its
1471
+ // depth rather than resolving it immediately.
1229
1472
  pushStringParam: function(string) {
1230
- this.pushStack('depth' + this.lastContext);
1473
+ this.pushStackLiteral('depth' + this.lastContext);
1231
1474
  this.pushString(string);
1232
1475
  },
1233
1476
 
1477
+ // [pushString]
1478
+ //
1479
+ // On stack, before: ...
1480
+ // On stack, after: quotedString(string), ...
1481
+ //
1482
+ // Push a quoted version of `string` onto the stack
1234
1483
  pushString: function(string) {
1235
- this.pushStack(this.quotedString(string));
1236
- },
1237
-
1238
- push: function(name) {
1239
- this.pushStack(name);
1240
- },
1241
-
1242
- invokeMustache: function(paramSize, original, hasHash) {
1243
- this.populateParams(paramSize, this.quotedString(original), "{}", null, hasHash, function(nextStack, helperMissingString, id) {
1244
- if (!this.usingKnownHelper) {
1245
- this.context.aliases.helperMissing = 'helpers.helperMissing';
1246
- this.context.aliases.undef = 'void 0';
1247
- this.source.push("else if(" + id + "=== undef) { " + nextStack + " = helperMissing.call(" + helperMissingString + "); }");
1248
- if (nextStack !== id) {
1249
- this.source.push("else { " + nextStack + " = " + id + "; }");
1250
- }
1251
- }
1252
- });
1253
- },
1254
-
1255
- invokeProgram: function(guid, paramSize, hasHash) {
1256
- var inverse = this.programExpression(this.inverse);
1257
- var mainProgram = this.programExpression(guid);
1258
-
1259
- this.populateParams(paramSize, null, mainProgram, inverse, hasHash, function(nextStack, helperMissingString, id) {
1260
- if (!this.usingKnownHelper) {
1261
- this.context.aliases.blockHelperMissing = 'helpers.blockHelperMissing';
1262
- this.source.push("else { " + nextStack + " = blockHelperMissing.call(" + helperMissingString + "); }");
1263
- }
1264
- });
1265
- },
1266
-
1267
- populateParams: function(paramSize, helperId, program, inverse, hasHash, fn) {
1268
- var needsRegister = hasHash || this.options.stringParams || inverse || this.options.data;
1269
- var id = this.popStack(), nextStack;
1270
- var params = [], param, stringParam, stringOptions;
1271
-
1272
- if (needsRegister) {
1273
- this.register('tmp1', program);
1274
- stringOptions = 'tmp1';
1484
+ this.pushStackLiteral(this.quotedString(string));
1485
+ },
1486
+
1487
+ // [push]
1488
+ //
1489
+ // On stack, before: ...
1490
+ // On stack, after: expr, ...
1491
+ //
1492
+ // Push an expression onto the stack
1493
+ push: function(expr) {
1494
+ this.pushStack(expr);
1495
+ },
1496
+
1497
+ // [pushLiteral]
1498
+ //
1499
+ // On stack, before: ...
1500
+ // On stack, after: value, ...
1501
+ //
1502
+ // Pushes a value onto the stack. This operation prevents
1503
+ // the compiler from creating a temporary variable to hold
1504
+ // it.
1505
+ pushLiteral: function(value) {
1506
+ this.pushStackLiteral(value);
1507
+ },
1508
+
1509
+ // [pushProgram]
1510
+ //
1511
+ // On stack, before: ...
1512
+ // On stack, after: program(guid), ...
1513
+ //
1514
+ // Push a program expression onto the stack. This takes
1515
+ // a compile-time guid and converts it into a runtime-accessible
1516
+ // expression.
1517
+ pushProgram: function(guid) {
1518
+ if (guid != null) {
1519
+ this.pushStackLiteral(this.programExpression(guid));
1275
1520
  } else {
1276
- stringOptions = '{ hash: {} }';
1277
- }
1278
-
1279
- if (needsRegister) {
1280
- var hash = (hasHash ? this.popStack() : '{}');
1281
- this.source.push('tmp1.hash = ' + hash + ';');
1282
- }
1283
-
1284
- if(this.options.stringParams) {
1285
- this.source.push('tmp1.contexts = [];');
1286
- }
1287
-
1288
- for(var i=0; i<paramSize; i++) {
1289
- param = this.popStack();
1290
- params.push(param);
1291
-
1292
- if(this.options.stringParams) {
1293
- this.source.push('tmp1.contexts.push(' + this.popStack() + ');');
1294
- }
1295
- }
1296
-
1297
- if(inverse) {
1298
- this.source.push('tmp1.fn = tmp1;');
1299
- this.source.push('tmp1.inverse = ' + inverse + ';');
1300
- }
1301
-
1302
- if(this.options.data) {
1303
- this.source.push('tmp1.data = data;');
1521
+ this.pushStackLiteral(null);
1304
1522
  }
1305
-
1306
- params.push(stringOptions);
1307
-
1308
- this.populateCall(params, id, helperId || id, fn, program !== '{}');
1309
1523
  },
1310
1524
 
1311
- populateCall: function(params, id, helperId, fn, program) {
1312
- var paramString = ["depth0"].concat(params).join(", ");
1313
- var helperMissingString = ["depth0"].concat(helperId).concat(params).join(", ");
1314
-
1525
+ // [invokeHelper]
1526
+ //
1527
+ // On stack, before: hash, inverse, program, params..., ...
1528
+ // On stack, after: result of helper invocation
1529
+ //
1530
+ // Pops off the helper's parameters, invokes the helper,
1531
+ // and pushes the helper's return value onto the stack.
1532
+ //
1533
+ // If the helper is not found, `helperMissing` is called.
1534
+ invokeHelper: function(paramSize, name) {
1535
+ this.context.aliases.helperMissing = 'helpers.helperMissing';
1536
+
1537
+ var helper = this.lastHelper = this.setupHelper(paramSize, name);
1538
+ this.register('foundHelper', helper.name);
1539
+
1540
+ this.pushStack("foundHelper ? foundHelper.call(" +
1541
+ helper.callParams + ") " + ": helperMissing.call(" +
1542
+ helper.helperMissingParams + ")");
1543
+ },
1544
+
1545
+ // [invokeKnownHelper]
1546
+ //
1547
+ // On stack, before: hash, inverse, program, params..., ...
1548
+ // On stack, after: result of helper invocation
1549
+ //
1550
+ // This operation is used when the helper is known to exist,
1551
+ // so a `helperMissing` fallback is not required.
1552
+ invokeKnownHelper: function(paramSize, name) {
1553
+ var helper = this.setupHelper(paramSize, name);
1554
+ this.pushStack(helper.name + ".call(" + helper.callParams + ")");
1555
+ },
1556
+
1557
+ // [invokeAmbiguous]
1558
+ //
1559
+ // On stack, before: hash, inverse, program, params..., ...
1560
+ // On stack, after: result of disambiguation
1561
+ //
1562
+ // This operation is used when an expression like `{{foo}}`
1563
+ // is provided, but we don't know at compile-time whether it
1564
+ // is a helper or a path.
1565
+ //
1566
+ // This operation emits more code than the other options,
1567
+ // and can be avoided by passing the `knownHelpers` and
1568
+ // `knownHelpersOnly` flags at compile-time.
1569
+ invokeAmbiguous: function(name) {
1570
+ this.context.aliases.functionType = '"function"';
1571
+
1572
+ this.pushStackLiteral('{}');
1573
+ var helper = this.setupHelper(0, name);
1574
+
1575
+ var helperName = this.lastHelper = this.nameLookup('helpers', name, 'helper');
1576
+ this.register('foundHelper', helperName);
1577
+
1578
+ var nonHelper = this.nameLookup('depth' + this.lastContext, name, 'context');
1315
1579
  var nextStack = this.nextStack();
1316
1580
 
1317
- if (this.usingKnownHelper) {
1318
- this.source.push(nextStack + " = " + id + ".call(" + paramString + ");");
1319
- } else {
1320
- this.context.aliases.functionType = '"function"';
1321
- var condition = program ? "foundHelper && " : ""
1322
- this.source.push("if(" + condition + "typeof " + id + " === functionType) { " + nextStack + " = " + id + ".call(" + paramString + "); }");
1323
- }
1324
- fn.call(this, nextStack, helperMissingString, id);
1325
- this.usingKnownHelper = false;
1581
+ this.source.push('if (foundHelper) { ' + nextStack + ' = foundHelper.call(' + helper.callParams + '); }');
1582
+ this.source.push('else { ' + nextStack + ' = ' + nonHelper + '; ' + nextStack + ' = typeof ' + nextStack + ' === functionType ? ' + nextStack + '() : ' + nextStack + '; }');
1326
1583
  },
1327
1584
 
1328
- invokePartial: function(context) {
1329
- params = [this.nameLookup('partials', context, 'partial'), "'" + context + "'", this.popStack(), "helpers", "partials"];
1585
+ // [invokePartial]
1586
+ //
1587
+ // On stack, before: context, ...
1588
+ // On stack after: result of partial invocation
1589
+ //
1590
+ // This operation pops off a context, invokes a partial with that context,
1591
+ // and pushes the result of the invocation back.
1592
+ invokePartial: function(name) {
1593
+ var params = [this.nameLookup('partials', name, 'partial'), "'" + name + "'", this.popStack(), "helpers", "partials"];
1330
1594
 
1331
1595
  if (this.options.data) {
1332
1596
  params.push("data");
1333
1597
  }
1334
1598
 
1599
+ this.context.aliases.self = "this";
1335
1600
  this.pushStack("self.invokePartial(" + params.join(", ") + ");");
1336
1601
  },
1337
1602
 
1603
+ // [assignToHash]
1604
+ //
1605
+ // On stack, before: value, hash, ...
1606
+ // On stack, after: hash, ...
1607
+ //
1608
+ // Pops a value and hash off the stack, assigns `hash[key] = value`
1609
+ // and pushes the hash back onto the stack.
1338
1610
  assignToHash: function(key) {
1339
1611
  var value = this.popStack();
1340
1612
  var hash = this.topStack();
@@ -1362,10 +1634,15 @@ Handlebars.JavaScriptCompiler = function() {};
1362
1634
  },
1363
1635
 
1364
1636
  programExpression: function(guid) {
1365
- if(guid == null) { return "self.noop"; }
1637
+ this.context.aliases.self = "this";
1638
+
1639
+ if(guid == null) {
1640
+ return "self.noop";
1641
+ }
1366
1642
 
1367
1643
  var child = this.environment.children[guid],
1368
- depths = child.depths.list;
1644
+ depths = child.depths.list, depth;
1645
+
1369
1646
  var programParams = [child.index, child.name, "data"];
1370
1647
 
1371
1648
  for(var i=0, l = depths.length; i<l; i++) {
@@ -1389,29 +1666,61 @@ Handlebars.JavaScriptCompiler = function() {};
1389
1666
  },
1390
1667
 
1391
1668
  useRegister: function(name) {
1392
- if(!this.context.registers[name]) {
1393
- this.context.registers[name] = true;
1394
- this.context.registers.list.push(name);
1669
+ if(!this.registers[name]) {
1670
+ this.registers[name] = true;
1671
+ this.registers.list.push(name);
1395
1672
  }
1396
1673
  },
1397
1674
 
1675
+ pushStackLiteral: function(item) {
1676
+ this.compileStack.push(new Literal(item));
1677
+ return item;
1678
+ },
1679
+
1398
1680
  pushStack: function(item) {
1399
- this.source.push(this.nextStack() + " = " + item + ";");
1681
+ this.source.push(this.incrStack() + " = " + item + ";");
1682
+ this.compileStack.push("stack" + this.stackSlot);
1683
+ return "stack" + this.stackSlot;
1684
+ },
1685
+
1686
+ replaceStack: function(callback) {
1687
+ var item = callback.call(this, this.topStack());
1688
+
1689
+ this.source.push(this.topStack() + " = " + item + ";");
1400
1690
  return "stack" + this.stackSlot;
1401
1691
  },
1402
1692
 
1403
- nextStack: function() {
1693
+ nextStack: function(skipCompileStack) {
1694
+ var name = this.incrStack();
1695
+ this.compileStack.push("stack" + this.stackSlot);
1696
+ return name;
1697
+ },
1698
+
1699
+ incrStack: function() {
1404
1700
  this.stackSlot++;
1405
1701
  if(this.stackSlot > this.stackVars.length) { this.stackVars.push("stack" + this.stackSlot); }
1406
1702
  return "stack" + this.stackSlot;
1407
1703
  },
1408
1704
 
1409
1705
  popStack: function() {
1410
- return "stack" + this.stackSlot--;
1706
+ var item = this.compileStack.pop();
1707
+
1708
+ if (item instanceof Literal) {
1709
+ return item.value;
1710
+ } else {
1711
+ this.stackSlot--;
1712
+ return item;
1713
+ }
1411
1714
  },
1412
1715
 
1413
1716
  topStack: function() {
1414
- return "stack" + this.stackSlot;
1717
+ var item = this.compileStack[this.compileStack.length - 1];
1718
+
1719
+ if (item instanceof Literal) {
1720
+ return item.value;
1721
+ } else {
1722
+ return item;
1723
+ }
1415
1724
  },
1416
1725
 
1417
1726
  quotedString: function(str) {
@@ -1420,6 +1729,67 @@ Handlebars.JavaScriptCompiler = function() {};
1420
1729
  .replace(/"/g, '\\"')
1421
1730
  .replace(/\n/g, '\\n')
1422
1731
  .replace(/\r/g, '\\r') + '"';
1732
+ },
1733
+
1734
+ setupHelper: function(paramSize, name) {
1735
+ var params = [];
1736
+ this.setupParams(paramSize, params);
1737
+ var foundHelper = this.nameLookup('helpers', name, 'helper');
1738
+
1739
+ return {
1740
+ params: params,
1741
+ name: foundHelper,
1742
+ callParams: ["depth0"].concat(params).join(", "),
1743
+ helperMissingParams: ["depth0", this.quotedString(name)].concat(params).join(", ")
1744
+ };
1745
+ },
1746
+
1747
+ // the params and contexts arguments are passed in arrays
1748
+ // to fill in
1749
+ setupParams: function(paramSize, params) {
1750
+ var options = [], contexts = [], param, inverse, program;
1751
+
1752
+ options.push("hash:" + this.popStack());
1753
+
1754
+ inverse = this.popStack();
1755
+ program = this.popStack();
1756
+
1757
+ // Avoid setting fn and inverse if neither are set. This allows
1758
+ // helpers to do a check for `if (options.fn)`
1759
+ if (program || inverse) {
1760
+ if (!program) {
1761
+ this.context.aliases.self = "this";
1762
+ program = "self.noop";
1763
+ }
1764
+
1765
+ if (!inverse) {
1766
+ this.context.aliases.self = "this";
1767
+ inverse = "self.noop";
1768
+ }
1769
+
1770
+ options.push("inverse:" + inverse);
1771
+ options.push("fn:" + program);
1772
+ }
1773
+
1774
+ for(var i=0; i<paramSize; i++) {
1775
+ param = this.popStack();
1776
+ params.push(param);
1777
+
1778
+ if(this.options.stringParams) {
1779
+ contexts.push(this.popStack());
1780
+ }
1781
+ }
1782
+
1783
+ if (this.options.stringParams) {
1784
+ options.push("contexts:[" + contexts.join(",") + "]");
1785
+ }
1786
+
1787
+ if(this.options.data) {
1788
+ options.push("data:data");
1789
+ }
1790
+
1791
+ params.push("{" + options.join(",") + "}");
1792
+ return params.join(", ");
1423
1793
  }
1424
1794
  };
1425
1795
 
@@ -1447,12 +1817,12 @@ Handlebars.JavaScriptCompiler = function() {};
1447
1817
  compilerWords[reservedWords[i]] = true;
1448
1818
  }
1449
1819
 
1450
- JavaScriptCompiler.isValidJavaScriptVariableName = function(name) {
1451
- if(!JavaScriptCompiler.RESERVED_WORDS[name] && /^[a-zA-Z_$][0-9a-zA-Z_$]+$/.test(name)) {
1452
- return true;
1453
- }
1454
- return false;
1455
- }
1820
+ JavaScriptCompiler.isValidJavaScriptVariableName = function(name) {
1821
+ if(!JavaScriptCompiler.RESERVED_WORDS[name] && /^[a-zA-Z_$][0-9a-zA-Z_$]+$/.test(name)) {
1822
+ return true;
1823
+ }
1824
+ return false;
1825
+ };
1456
1826
 
1457
1827
  })(Handlebars.Compiler, Handlebars.JavaScriptCompiler);
1458
1828
 
@@ -1531,7 +1901,7 @@ Handlebars.VM = {
1531
1901
  },
1532
1902
  noop: function() { return ""; },
1533
1903
  invokePartial: function(partial, name, context, helpers, partials, data) {
1534
- options = { helpers: helpers, partials: partials, data: data };
1904
+ var options = { helpers: helpers, partials: partials, data: data };
1535
1905
 
1536
1906
  if(partial === undefined) {
1537
1907
  throw new Handlebars.Exception("The partial " + name + " could not be found");
@@ -1540,7 +1910,7 @@ Handlebars.VM = {
1540
1910
  } else if (!Handlebars.compile) {
1541
1911
  throw new Handlebars.Exception("The partial " + name + " could not be compiled when running in runtime-only mode");
1542
1912
  } else {
1543
- partials[name] = Handlebars.compile(partial);
1913
+ partials[name] = Handlebars.compile(partial, {data: data !== undefined});
1544
1914
  return partials[name](context, options);
1545
1915
  }
1546
1916
  }