1 | // lib/handlebars/base.js
|
---|
2 |
|
---|
3 | /*jshint eqnull:true*/
|
---|
4 | this.Handlebars = {};
|
---|
5 |
|
---|
6 | (function(Handlebars) {
|
---|
7 |
|
---|
8 | Handlebars.VERSION = "1.0.rc.1";
|
---|
9 |
|
---|
10 | Handlebars.helpers = {};
|
---|
11 | Handlebars.partials = {};
|
---|
12 |
|
---|
13 | Handlebars.registerHelper = function(name, fn, inverse) {
|
---|
14 | if(inverse) { fn.not = inverse; }
|
---|
15 | this.helpers[name] = fn;
|
---|
16 | };
|
---|
17 |
|
---|
18 | Handlebars.registerPartial = function(name, str) {
|
---|
19 | this.partials[name] = str;
|
---|
20 | };
|
---|
21 |
|
---|
22 | Handlebars.registerHelper('helperMissing', function(arg) {
|
---|
23 | if(arguments.length === 2) {
|
---|
24 | return undefined;
|
---|
25 | } else {
|
---|
26 | throw new Error("Could not find property '" + arg + "'");
|
---|
27 | }
|
---|
28 | });
|
---|
29 |
|
---|
30 | var toString = Object.prototype.toString, functionType = "[object Function]";
|
---|
31 |
|
---|
32 | Handlebars.registerHelper('blockHelperMissing', function(context, options) {
|
---|
33 | var inverse = options.inverse || function() {}, fn = options.fn;
|
---|
34 |
|
---|
35 |
|
---|
36 | var ret = "";
|
---|
37 | var type = toString.call(context);
|
---|
38 |
|
---|
39 | if(type === functionType) { context = context.call(this); }
|
---|
40 |
|
---|
41 | if(context === true) {
|
---|
42 | return fn(this);
|
---|
43 | } else if(context === false || context == null) {
|
---|
44 | return inverse(this);
|
---|
45 | } else if(type === "[object Array]") {
|
---|
46 | if(context.length > 0) {
|
---|
47 | return Handlebars.helpers.each(context, options);
|
---|
48 | } else {
|
---|
49 | return inverse(this);
|
---|
50 | }
|
---|
51 | } else {
|
---|
52 | return fn(context);
|
---|
53 | }
|
---|
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 |
|
---|
65 | Handlebars.registerHelper('each', function(context, options) {
|
---|
66 | var fn = options.fn, inverse = options.inverse;
|
---|
67 | var ret = "", data;
|
---|
68 |
|
---|
69 | if (options.data) {
|
---|
70 | data = Handlebars.createFrame(options.data);
|
---|
71 | }
|
---|
72 |
|
---|
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 });
|
---|
77 | }
|
---|
78 | } else {
|
---|
79 | ret = inverse(this);
|
---|
80 | }
|
---|
81 | return ret;
|
---|
82 | });
|
---|
83 |
|
---|
84 | Handlebars.registerHelper('if', function(context, options) {
|
---|
85 | var type = toString.call(context);
|
---|
86 | if(type === functionType) { context = context.call(this); }
|
---|
87 |
|
---|
88 | if(!context || Handlebars.Utils.isEmpty(context)) {
|
---|
89 | return options.inverse(this);
|
---|
90 | } else {
|
---|
91 | return options.fn(this);
|
---|
92 | }
|
---|
93 | });
|
---|
94 |
|
---|
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);
|
---|
101 | });
|
---|
102 |
|
---|
103 | Handlebars.registerHelper('with', function(context, options) {
|
---|
104 | return options.fn(context);
|
---|
105 | });
|
---|
106 |
|
---|
107 | Handlebars.registerHelper('log', function(context) {
|
---|
108 | Handlebars.log(context);
|
---|
109 | });
|
---|
110 |
|
---|
111 | }(this.Handlebars));
|
---|
112 | ;
|
---|
113 | // lib/handlebars/compiler/parser.js
|
---|
114 | /* Jison generated parser */
|
---|
115 | var handlebars = (function(){
|
---|
116 | var parser = {trace: function trace() { },
|
---|
117 | 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]],
|
---|
121 | performAction: function anonymous(yytext,yyleng,yylineno,yy,yystate,$$,_$) {
|
---|
122 |
|
---|
123 | var $0 = $$.length - 1;
|
---|
124 | switch (yystate) {
|
---|
125 | case 1: return $$[$0-1];
|
---|
126 | break;
|
---|
127 | case 2: this.$ = new yy.ProgramNode($$[$0-2], $$[$0]);
|
---|
128 | break;
|
---|
129 | case 3: this.$ = new yy.ProgramNode($$[$0]);
|
---|
130 | break;
|
---|
131 | case 4: this.$ = new yy.ProgramNode([]);
|
---|
132 | break;
|
---|
133 | case 5: this.$ = [$$[$0]];
|
---|
134 | break;
|
---|
135 | case 6: $$[$0-1].push($$[$0]); this.$ = $$[$0-1];
|
---|
136 | break;
|
---|
137 | case 7: this.$ = new yy.BlockNode($$[$0-2], $$[$0-1].inverse, $$[$0-1], $$[$0]);
|
---|
138 | break;
|
---|
139 | case 8: this.$ = new yy.BlockNode($$[$0-2], $$[$0-1], $$[$0-1].inverse, $$[$0]);
|
---|
140 | break;
|
---|
141 | case 9: this.$ = $$[$0];
|
---|
142 | break;
|
---|
143 | case 10: this.$ = $$[$0];
|
---|
144 | break;
|
---|
145 | case 11: this.$ = new yy.ContentNode($$[$0]);
|
---|
146 | break;
|
---|
147 | case 12: this.$ = new yy.CommentNode($$[$0]);
|
---|
148 | break;
|
---|
149 | case 13: this.$ = new yy.MustacheNode($$[$0-1][0], $$[$0-1][1]);
|
---|
150 | break;
|
---|
151 | case 14: this.$ = new yy.MustacheNode($$[$0-1][0], $$[$0-1][1]);
|
---|
152 | break;
|
---|
153 | case 15: this.$ = $$[$0-1];
|
---|
154 | break;
|
---|
155 | case 16: this.$ = new yy.MustacheNode($$[$0-1][0], $$[$0-1][1]);
|
---|
156 | break;
|
---|
157 | case 17: this.$ = new yy.MustacheNode($$[$0-1][0], $$[$0-1][1], true);
|
---|
158 | break;
|
---|
159 | case 18: this.$ = new yy.PartialNode($$[$0-1]);
|
---|
160 | break;
|
---|
161 | case 19: this.$ = new yy.PartialNode($$[$0-2], $$[$0-1]);
|
---|
162 | break;
|
---|
163 | case 20:
|
---|
164 | break;
|
---|
165 | case 21: this.$ = [[$$[$0-2]].concat($$[$0-1]), $$[$0]];
|
---|
166 | break;
|
---|
167 | case 22: this.$ = [[$$[$0-1]].concat($$[$0]), null];
|
---|
168 | break;
|
---|
169 | case 23: this.$ = [[$$[$0-1]], $$[$0]];
|
---|
170 | break;
|
---|
171 | case 24: this.$ = [[$$[$0]], null];
|
---|
172 | break;
|
---|
173 | case 25: this.$ = [[new yy.DataNode($$[$0])], null];
|
---|
174 | break;
|
---|
175 | case 26: $$[$0-1].push($$[$0]); this.$ = $$[$0-1];
|
---|
176 | break;
|
---|
177 | case 27: this.$ = [$$[$0]];
|
---|
178 | break;
|
---|
179 | case 28: this.$ = $$[$0];
|
---|
180 | break;
|
---|
181 | case 29: this.$ = new yy.StringNode($$[$0]);
|
---|
182 | break;
|
---|
183 | case 30: this.$ = new yy.IntegerNode($$[$0]);
|
---|
184 | break;
|
---|
185 | case 31: this.$ = new yy.BooleanNode($$[$0]);
|
---|
186 | break;
|
---|
187 | case 32: this.$ = new yy.DataNode($$[$0]);
|
---|
188 | break;
|
---|
189 | case 33: this.$ = new yy.HashNode($$[$0]);
|
---|
190 | break;
|
---|
191 | case 34: $$[$0-1].push($$[$0]); this.$ = $$[$0-1];
|
---|
192 | break;
|
---|
193 | case 35: this.$ = [$$[$0]];
|
---|
194 | break;
|
---|
195 | case 36: this.$ = [$$[$0-2], $$[$0]];
|
---|
196 | break;
|
---|
197 | case 37: this.$ = [$$[$0-2], new yy.StringNode($$[$0])];
|
---|
198 | break;
|
---|
199 | case 38: this.$ = [$$[$0-2], new yy.IntegerNode($$[$0])];
|
---|
200 | break;
|
---|
201 | case 39: this.$ = [$$[$0-2], new yy.BooleanNode($$[$0])];
|
---|
202 | break;
|
---|
203 | case 40: this.$ = [$$[$0-2], new yy.DataNode($$[$0])];
|
---|
204 | break;
|
---|
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]];
|
---|
210 | break;
|
---|
211 | }
|
---|
212 | },
|
---|
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]},
|
---|
215 | parseError: function parseError(str, hash) {
|
---|
216 | throw new Error(str);
|
---|
217 | },
|
---|
218 | parse: function parse(input) {
|
---|
219 | var self = this, stack = [0], vstack = [null], lstack = [], table = this.table, yytext = "", yylineno = 0, yyleng = 0, recovering = 0, TERROR = 2, EOF = 1;
|
---|
220 | this.lexer.setInput(input);
|
---|
221 | this.lexer.yy = this.yy;
|
---|
222 | this.yy.lexer = this.lexer;
|
---|
223 | this.yy.parser = this;
|
---|
224 | if (typeof this.lexer.yylloc == "undefined")
|
---|
225 | this.lexer.yylloc = {};
|
---|
226 | var yyloc = this.lexer.yylloc;
|
---|
227 | lstack.push(yyloc);
|
---|
228 | var ranges = this.lexer.options && this.lexer.options.ranges;
|
---|
229 | if (typeof this.yy.parseError === "function")
|
---|
230 | this.parseError = this.yy.parseError;
|
---|
231 | function popStack(n) {
|
---|
232 | stack.length = stack.length - 2 * n;
|
---|
233 | vstack.length = vstack.length - n;
|
---|
234 | lstack.length = lstack.length - n;
|
---|
235 | }
|
---|
236 | function lex() {
|
---|
237 | var token;
|
---|
238 | token = self.lexer.lex() || 1;
|
---|
239 | if (typeof token !== "number") {
|
---|
240 | token = self.symbols_[token] || token;
|
---|
241 | }
|
---|
242 | return token;
|
---|
243 | }
|
---|
244 | var symbol, preErrorSymbol, state, action, a, r, yyval = {}, p, len, newState, expected;
|
---|
245 | while (true) {
|
---|
246 | state = stack[stack.length - 1];
|
---|
247 | if (this.defaultActions[state]) {
|
---|
248 | action = this.defaultActions[state];
|
---|
249 | } else {
|
---|
250 | if (symbol === null || typeof symbol == "undefined") {
|
---|
251 | symbol = lex();
|
---|
252 | }
|
---|
253 | action = table[state] && table[state][symbol];
|
---|
254 | }
|
---|
255 | if (typeof action === "undefined" || !action.length || !action[0]) {
|
---|
256 | var errStr = "";
|
---|
257 | if (!recovering) {
|
---|
258 | expected = [];
|
---|
259 | for (p in table[state])
|
---|
260 | if (this.terminals_[p] && p > 2) {
|
---|
261 | expected.push("'" + this.terminals_[p] + "'");
|
---|
262 | }
|
---|
263 | if (this.lexer.showPosition) {
|
---|
264 | errStr = "Parse error on line " + (yylineno + 1) + ":\n" + this.lexer.showPosition() + "\nExpecting " + expected.join(", ") + ", got '" + (this.terminals_[symbol] || symbol) + "'";
|
---|
265 | } else {
|
---|
266 | errStr = "Parse error on line " + (yylineno + 1) + ": Unexpected " + (symbol == 1?"end of input":"'" + (this.terminals_[symbol] || symbol) + "'");
|
---|
267 | }
|
---|
268 | this.parseError(errStr, {text: this.lexer.match, token: this.terminals_[symbol] || symbol, line: this.lexer.yylineno, loc: yyloc, expected: expected});
|
---|
269 | }
|
---|
270 | }
|
---|
271 | if (action[0] instanceof Array && action.length > 1) {
|
---|
272 | throw new Error("Parse Error: multiple actions possible at state: " + state + ", token: " + symbol);
|
---|
273 | }
|
---|
274 | switch (action[0]) {
|
---|
275 | case 1:
|
---|
276 | stack.push(symbol);
|
---|
277 | vstack.push(this.lexer.yytext);
|
---|
278 | lstack.push(this.lexer.yylloc);
|
---|
279 | stack.push(action[1]);
|
---|
280 | symbol = null;
|
---|
281 | if (!preErrorSymbol) {
|
---|
282 | yyleng = this.lexer.yyleng;
|
---|
283 | yytext = this.lexer.yytext;
|
---|
284 | yylineno = this.lexer.yylineno;
|
---|
285 | yyloc = this.lexer.yylloc;
|
---|
286 | if (recovering > 0)
|
---|
287 | recovering--;
|
---|
288 | } else {
|
---|
289 | symbol = preErrorSymbol;
|
---|
290 | preErrorSymbol = null;
|
---|
291 | }
|
---|
292 | break;
|
---|
293 | case 2:
|
---|
294 | len = this.productions_[action[1]][1];
|
---|
295 | yyval.$ = vstack[vstack.length - len];
|
---|
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 | }
|
---|
300 | r = this.performAction.call(yyval, yytext, yyleng, yylineno, this.yy, action[1], vstack, lstack);
|
---|
301 | if (typeof r !== "undefined") {
|
---|
302 | return r;
|
---|
303 | }
|
---|
304 | if (len) {
|
---|
305 | stack = stack.slice(0, -1 * len * 2);
|
---|
306 | vstack = vstack.slice(0, -1 * len);
|
---|
307 | lstack = lstack.slice(0, -1 * len);
|
---|
308 | }
|
---|
309 | stack.push(this.productions_[action[1]][0]);
|
---|
310 | vstack.push(yyval.$);
|
---|
311 | lstack.push(yyval._$);
|
---|
312 | newState = table[stack[stack.length - 2]][stack[stack.length - 1]];
|
---|
313 | stack.push(newState);
|
---|
314 | break;
|
---|
315 | case 3:
|
---|
316 | return true;
|
---|
317 | }
|
---|
318 | }
|
---|
319 | return true;
|
---|
320 | }
|
---|
321 | };
|
---|
322 | /* Jison generated lexer */
|
---|
323 | var lexer = (function(){
|
---|
324 | var lexer = ({EOF:1,
|
---|
325 | parseError:function parseError(str, hash) {
|
---|
326 | if (this.yy.parser) {
|
---|
327 | this.yy.parser.parseError(str, hash);
|
---|
328 | } else {
|
---|
329 | throw new Error(str);
|
---|
330 | }
|
---|
331 | },
|
---|
332 | setInput:function (input) {
|
---|
333 | this._input = input;
|
---|
334 | this._more = this._less = this.done = false;
|
---|
335 | this.yylineno = this.yyleng = 0;
|
---|
336 | this.yytext = this.matched = this.match = '';
|
---|
337 | this.conditionStack = ['INITIAL'];
|
---|
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;
|
---|
341 | return this;
|
---|
342 | },
|
---|
343 | input:function () {
|
---|
344 | var ch = this._input[0];
|
---|
345 | this.yytext += ch;
|
---|
346 | this.yyleng++;
|
---|
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 |
|
---|
359 | this._input = this._input.slice(1);
|
---|
360 | return ch;
|
---|
361 | },
|
---|
362 | unput:function (ch) {
|
---|
363 | var len = ch.length;
|
---|
364 | var lines = ch.split(/(?:\r\n?|\n)/g);
|
---|
365 |
|
---|
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 | }
|
---|
388 | return this;
|
---|
389 | },
|
---|
390 | more:function () {
|
---|
391 | this._more = true;
|
---|
392 | return this;
|
---|
393 | },
|
---|
394 | less:function (n) {
|
---|
395 | this.unput(this.match.slice(n));
|
---|
396 | },
|
---|
397 | pastInput:function () {
|
---|
398 | var past = this.matched.substr(0, this.matched.length - this.match.length);
|
---|
399 | return (past.length > 20 ? '...':'') + past.substr(-20).replace(/\n/g, "");
|
---|
400 | },
|
---|
401 | upcomingInput:function () {
|
---|
402 | var next = this.match;
|
---|
403 | if (next.length < 20) {
|
---|
404 | next += this._input.substr(0, 20-next.length);
|
---|
405 | }
|
---|
406 | return (next.substr(0,20)+(next.length > 20 ? '...':'')).replace(/\n/g, "");
|
---|
407 | },
|
---|
408 | showPosition:function () {
|
---|
409 | var pre = this.pastInput();
|
---|
410 | var c = new Array(pre.length + 1).join("-");
|
---|
411 | return pre + this.upcomingInput() + "\n" + c+"^";
|
---|
412 | },
|
---|
413 | next:function () {
|
---|
414 | if (this.done) {
|
---|
415 | return this.EOF;
|
---|
416 | }
|
---|
417 | if (!this._input) this.done = true;
|
---|
418 |
|
---|
419 | var token,
|
---|
420 | match,
|
---|
421 | tempMatch,
|
---|
422 | index,
|
---|
423 | col,
|
---|
424 | lines;
|
---|
425 | if (!this._more) {
|
---|
426 | this.yytext = '';
|
---|
427 | this.match = '';
|
---|
428 | }
|
---|
429 | var rules = this._currentRules();
|
---|
430 | for (var i=0;i < rules.length; i++) {
|
---|
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;
|
---|
436 | }
|
---|
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 | }
|
---|
460 | if (this._input === "") {
|
---|
461 | return this.EOF;
|
---|
462 | } else {
|
---|
463 | return this.parseError('Lexical error on line '+(this.yylineno+1)+'. Unrecognized text.\n'+this.showPosition(),
|
---|
464 | {text: "", token: null, line: this.yylineno});
|
---|
465 | }
|
---|
466 | },
|
---|
467 | lex:function lex() {
|
---|
468 | var r = this.next();
|
---|
469 | if (typeof r !== 'undefined') {
|
---|
470 | return r;
|
---|
471 | } else {
|
---|
472 | return this.lex();
|
---|
473 | }
|
---|
474 | },
|
---|
475 | begin:function begin(condition) {
|
---|
476 | this.conditionStack.push(condition);
|
---|
477 | },
|
---|
478 | popState:function popState() {
|
---|
479 | return this.conditionStack.pop();
|
---|
480 | },
|
---|
481 | _currentRules:function _currentRules() {
|
---|
482 | return this.conditions[this.conditionStack[this.conditionStack.length-1]].rules;
|
---|
483 | },
|
---|
484 | topState:function () {
|
---|
485 | return this.conditionStack[this.conditionStack.length-2];
|
---|
486 | },
|
---|
487 | pushState:function begin(condition) {
|
---|
488 | this.begin(condition);
|
---|
489 | }});
|
---|
490 | lexer.options = {};
|
---|
491 | lexer.performAction = function anonymous(yy,yy_,$avoiding_name_collisions,YY_START) {
|
---|
492 |
|
---|
493 | var YYSTATE=YY_START
|
---|
494 | switch($avoiding_name_collisions) {
|
---|
495 | case 0:
|
---|
496 | if(yy_.yytext.slice(-1) !== "\\") this.begin("mu");
|
---|
497 | if(yy_.yytext.slice(-1) === "\\") yy_.yytext = yy_.yytext.substr(0,yy_.yyleng-1), this.begin("emu");
|
---|
498 | if(yy_.yytext) return 14;
|
---|
499 |
|
---|
500 | break;
|
---|
501 | case 1: return 14;
|
---|
502 | break;
|
---|
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 |
|
---|
508 | break;
|
---|
509 | case 3: return 24;
|
---|
510 | break;
|
---|
511 | case 4: return 16;
|
---|
512 | break;
|
---|
513 | case 5: return 20;
|
---|
514 | break;
|
---|
515 | case 6: return 19;
|
---|
516 | break;
|
---|
517 | case 7: return 19;
|
---|
518 | break;
|
---|
519 | case 8: return 23;
|
---|
520 | break;
|
---|
521 | case 9: return 23;
|
---|
522 | break;
|
---|
523 | case 10: yy_.yytext = yy_.yytext.substr(3,yy_.yyleng-5); this.popState(); return 15;
|
---|
524 | break;
|
---|
525 | case 11: return 22;
|
---|
526 | break;
|
---|
527 | case 12: return 35;
|
---|
528 | break;
|
---|
529 | case 13: return 34;
|
---|
530 | break;
|
---|
531 | case 14: return 34;
|
---|
532 | break;
|
---|
533 | case 15: return 37;
|
---|
534 | break;
|
---|
535 | case 16: /*ignore whitespace*/
|
---|
536 | break;
|
---|
537 | case 17: this.popState(); return 18;
|
---|
538 | break;
|
---|
539 | case 18: this.popState(); return 18;
|
---|
540 | break;
|
---|
541 | case 19: yy_.yytext = yy_.yytext.substr(1,yy_.yyleng-2).replace(/\\"/g,'"'); return 29;
|
---|
542 | break;
|
---|
543 | case 20: yy_.yytext = yy_.yytext.substr(1,yy_.yyleng-2).replace(/\\"/g,'"'); return 29;
|
---|
544 | break;
|
---|
545 | case 21: yy_.yytext = yy_.yytext.substr(1); return 27;
|
---|
546 | break;
|
---|
547 | case 22: return 31;
|
---|
548 | break;
|
---|
549 | case 23: return 31;
|
---|
550 | break;
|
---|
551 | case 24: return 30;
|
---|
552 | break;
|
---|
553 | case 25: return 34;
|
---|
554 | break;
|
---|
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;
|
---|
560 | break;
|
---|
561 | }
|
---|
562 | };
|
---|
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;})()
|
---|
566 | parser.lexer = lexer;
|
---|
567 | function Parser () { this.yy = {}; }Parser.prototype = parser;parser.Parser = Parser;
|
---|
568 | return new Parser;
|
---|
569 | })();
|
---|
570 | if (typeof require !== 'undefined' && typeof exports !== 'undefined') {
|
---|
571 | exports.parser = handlebars;
|
---|
572 | exports.Parser = handlebars.Parser;
|
---|
573 | exports.parse = function () { return handlebars.parse.apply(handlebars, arguments); }
|
---|
574 | exports.main = function commonjsMain(args) {
|
---|
575 | if (!args[1])
|
---|
576 | throw new Error('Usage: '+args[0]+' FILE');
|
---|
577 | var source, cwd;
|
---|
578 | if (typeof process !== 'undefined') {
|
---|
579 | source = require('fs').readFileSync(require('path').resolve(args[1]), "utf8");
|
---|
580 | } else {
|
---|
581 | source = require("file").path(require("file").cwd()).join(args[1]).read({charset: "utf-8"});
|
---|
582 | }
|
---|
583 | return exports.parser.parse(source);
|
---|
584 | }
|
---|
585 | if (typeof module !== 'undefined' && require.main === module) {
|
---|
586 | exports.main(typeof process !== 'undefined' ? process.argv.slice(1) : require("system").args);
|
---|
587 | }
|
---|
588 | };
|
---|
589 | ;
|
---|
590 | // lib/handlebars/compiler/base.js
|
---|
591 | Handlebars.Parser = handlebars;
|
---|
592 |
|
---|
593 | Handlebars.parse = function(string) {
|
---|
594 | Handlebars.Parser.yy = Handlebars.AST;
|
---|
595 | return Handlebars.Parser.parse(string);
|
---|
596 | };
|
---|
597 |
|
---|
598 | Handlebars.print = function(ast) {
|
---|
599 | return new Handlebars.PrintVisitor().accept(ast);
|
---|
600 | };
|
---|
601 |
|
---|
602 | Handlebars.logger = {
|
---|
603 | DEBUG: 0, INFO: 1, WARN: 2, ERROR: 3, level: 3,
|
---|
604 |
|
---|
605 | // override in the host environment
|
---|
606 | log: function(level, str) {}
|
---|
607 | };
|
---|
608 |
|
---|
609 | Handlebars.log = function(level, str) { Handlebars.logger.log(level, str); };
|
---|
610 | ;
|
---|
611 | // lib/handlebars/compiler/ast.js
|
---|
612 | (function() {
|
---|
613 |
|
---|
614 | Handlebars.AST = {};
|
---|
615 |
|
---|
616 | Handlebars.AST.ProgramNode = function(statements, inverse) {
|
---|
617 | this.type = "program";
|
---|
618 | this.statements = statements;
|
---|
619 | if(inverse) { this.inverse = new Handlebars.AST.ProgramNode(inverse); }
|
---|
620 | };
|
---|
621 |
|
---|
622 | Handlebars.AST.MustacheNode = function(rawParams, hash, unescaped) {
|
---|
623 | this.type = "mustache";
|
---|
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.
|
---|
642 | };
|
---|
643 |
|
---|
644 | Handlebars.AST.PartialNode = function(id, context) {
|
---|
645 | this.type = "partial";
|
---|
646 |
|
---|
647 | // TODO: disallow complex IDs
|
---|
648 |
|
---|
649 | this.id = id;
|
---|
650 | this.context = context;
|
---|
651 | };
|
---|
652 |
|
---|
653 | var verifyMatch = function(open, close) {
|
---|
654 | if(open.original !== close.original) {
|
---|
655 | throw new Handlebars.Exception(open.original + " doesn't match " + close.original);
|
---|
656 | }
|
---|
657 | };
|
---|
658 |
|
---|
659 | Handlebars.AST.BlockNode = function(mustache, program, inverse, close) {
|
---|
660 | verifyMatch(mustache.id, close);
|
---|
661 | this.type = "block";
|
---|
662 | this.mustache = mustache;
|
---|
663 | this.program = program;
|
---|
664 | this.inverse = inverse;
|
---|
665 |
|
---|
666 | if (this.inverse && !this.program) {
|
---|
667 | this.isInverse = true;
|
---|
668 | }
|
---|
669 | };
|
---|
670 |
|
---|
671 | Handlebars.AST.ContentNode = function(string) {
|
---|
672 | this.type = "content";
|
---|
673 | this.string = string;
|
---|
674 | };
|
---|
675 |
|
---|
676 | Handlebars.AST.HashNode = function(pairs) {
|
---|
677 | this.type = "hash";
|
---|
678 | this.pairs = pairs;
|
---|
679 | };
|
---|
680 |
|
---|
681 | Handlebars.AST.IdNode = function(parts) {
|
---|
682 | this.type = "ID";
|
---|
683 | this.original = parts.join(".");
|
---|
684 |
|
---|
685 | var dig = [], depth = 0;
|
---|
686 |
|
---|
687 | for(var i=0,l=parts.length; i<l; i++) {
|
---|
688 | var part = parts[i];
|
---|
689 |
|
---|
690 | if(part === "..") { depth++; }
|
---|
691 | else if(part === "." || part === "this") { this.isScoped = true; }
|
---|
692 | else { dig.push(part); }
|
---|
693 | }
|
---|
694 |
|
---|
695 | this.parts = dig;
|
---|
696 | this.string = dig.join('.');
|
---|
697 | this.depth = depth;
|
---|
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;
|
---|
707 | };
|
---|
708 |
|
---|
709 | Handlebars.AST.StringNode = function(string) {
|
---|
710 | this.type = "STRING";
|
---|
711 | this.string = string;
|
---|
712 | };
|
---|
713 |
|
---|
714 | Handlebars.AST.IntegerNode = function(integer) {
|
---|
715 | this.type = "INTEGER";
|
---|
716 | this.integer = integer;
|
---|
717 | };
|
---|
718 |
|
---|
719 | Handlebars.AST.BooleanNode = function(bool) {
|
---|
720 | this.type = "BOOLEAN";
|
---|
721 | this.bool = bool;
|
---|
722 | };
|
---|
723 |
|
---|
724 | Handlebars.AST.CommentNode = function(comment) {
|
---|
725 | this.type = "comment";
|
---|
726 | this.comment = comment;
|
---|
727 | };
|
---|
728 |
|
---|
729 | })();;
|
---|
730 | // lib/handlebars/utils.js
|
---|
731 | Handlebars.Exception = function(message) {
|
---|
732 | var tmp = Error.prototype.constructor.apply(this, arguments);
|
---|
733 |
|
---|
734 | for (var p in tmp) {
|
---|
735 | if (tmp.hasOwnProperty(p)) { this[p] = tmp[p]; }
|
---|
736 | }
|
---|
737 |
|
---|
738 | this.message = tmp.message;
|
---|
739 | };
|
---|
740 | Handlebars.Exception.prototype = new Error();
|
---|
741 |
|
---|
742 | // Build out our basic SafeString type
|
---|
743 | Handlebars.SafeString = function(string) {
|
---|
744 | this.string = string;
|
---|
745 | };
|
---|
746 | Handlebars.SafeString.prototype.toString = function() {
|
---|
747 | return this.string.toString();
|
---|
748 | };
|
---|
749 |
|
---|
750 | (function() {
|
---|
751 | var escape = {
|
---|
752 | "&": "&",
|
---|
753 | "<": "<",
|
---|
754 | ">": ">",
|
---|
755 | '"': """,
|
---|
756 | "'": "'",
|
---|
757 | "`": "`"
|
---|
758 | };
|
---|
759 |
|
---|
760 | var badChars = /[&<>"'`]/g;
|
---|
761 | var possible = /[&<>"'`]/;
|
---|
762 |
|
---|
763 | var escapeChar = function(chr) {
|
---|
764 | return escape[chr] || "&";
|
---|
765 | };
|
---|
766 |
|
---|
767 | Handlebars.Utils = {
|
---|
768 | escapeExpression: function(string) {
|
---|
769 | // don't escape SafeStrings, since they're already safe
|
---|
770 | if (string instanceof Handlebars.SafeString) {
|
---|
771 | return string.toString();
|
---|
772 | } else if (string == null || string === false) {
|
---|
773 | return "";
|
---|
774 | }
|
---|
775 |
|
---|
776 | if(!possible.test(string)) { return string; }
|
---|
777 | return string.replace(badChars, escapeChar);
|
---|
778 | },
|
---|
779 |
|
---|
780 | isEmpty: function(value) {
|
---|
781 | if (typeof value === "undefined") {
|
---|
782 | return true;
|
---|
783 | } else if (value === null) {
|
---|
784 | return true;
|
---|
785 | } else if (value === false) {
|
---|
786 | return true;
|
---|
787 | } else if(Object.prototype.toString.call(value) === "[object Array]" && value.length === 0) {
|
---|
788 | return true;
|
---|
789 | } else {
|
---|
790 | return false;
|
---|
791 | }
|
---|
792 | }
|
---|
793 | };
|
---|
794 | })();;
|
---|
795 | // lib/handlebars/compiler/compiler.js
|
---|
796 |
|
---|
797 | /*jshint eqnull:true*/
|
---|
798 | Handlebars.Compiler = function() {};
|
---|
799 | Handlebars.JavaScriptCompiler = function() {};
|
---|
800 |
|
---|
801 | (function(Compiler, JavaScriptCompiler) {
|
---|
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.
|
---|
806 |
|
---|
807 | Compiler.prototype = {
|
---|
808 | compiler: Compiler,
|
---|
809 |
|
---|
810 | disassemble: function() {
|
---|
811 | var opcodes = this.opcodes, opcode, out = [], params, param;
|
---|
812 |
|
---|
813 | for (var i=0, l=opcodes.length; i<l; i++) {
|
---|
814 | opcode = opcodes[i];
|
---|
815 |
|
---|
816 | if (opcode.opcode === 'DECLARE') {
|
---|
817 | out.push("DECLARE " + opcode.name + "=" + opcode.value);
|
---|
818 | } else {
|
---|
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") + "\"";
|
---|
824 | }
|
---|
825 | params.push(param);
|
---|
826 | }
|
---|
827 | out.push(opcode.opcode + " " + params.join(" "));
|
---|
828 | }
|
---|
829 | }
|
---|
830 |
|
---|
831 | return out.join("\n");
|
---|
832 | },
|
---|
833 |
|
---|
834 | guid: 0,
|
---|
835 |
|
---|
836 | compile: function(program, options) {
|
---|
837 | this.children = [];
|
---|
838 | this.depths = {list: []};
|
---|
839 | this.options = options;
|
---|
840 |
|
---|
841 | // These changes will propagate to the other compiler components
|
---|
842 | var knownHelpers = this.options.knownHelpers;
|
---|
843 | this.options.knownHelpers = {
|
---|
844 | 'helperMissing': true,
|
---|
845 | 'blockHelperMissing': true,
|
---|
846 | 'each': true,
|
---|
847 | 'if': true,
|
---|
848 | 'unless': true,
|
---|
849 | 'with': true,
|
---|
850 | 'log': true
|
---|
851 | };
|
---|
852 | if (knownHelpers) {
|
---|
853 | for (var name in knownHelpers) {
|
---|
854 | this.options.knownHelpers[name] = knownHelpers[name];
|
---|
855 | }
|
---|
856 | }
|
---|
857 |
|
---|
858 | return this.program(program);
|
---|
859 | },
|
---|
860 |
|
---|
861 | accept: function(node) {
|
---|
862 | return this[node.type](node);
|
---|
863 | },
|
---|
864 |
|
---|
865 | program: function(program) {
|
---|
866 | var statements = program.statements, statement;
|
---|
867 | this.opcodes = [];
|
---|
868 |
|
---|
869 | for(var i=0, l=statements.length; i<l; i++) {
|
---|
870 | statement = statements[i];
|
---|
871 | this[statement.type](statement);
|
---|
872 | }
|
---|
873 | this.isSimple = l === 1;
|
---|
874 |
|
---|
875 | this.depths.list = this.depths.list.sort(function(a, b) {
|
---|
876 | return a - b;
|
---|
877 | });
|
---|
878 |
|
---|
879 | return this;
|
---|
880 | },
|
---|
881 |
|
---|
882 | compileProgram: function(program) {
|
---|
883 | var result = new this.compiler().compile(program, this.options);
|
---|
884 | var guid = this.guid++, depth;
|
---|
885 |
|
---|
886 | this.usePartial = this.usePartial || result.usePartial;
|
---|
887 |
|
---|
888 | this.children[guid] = result;
|
---|
889 |
|
---|
890 | for(var i=0, l=result.depths.list.length; i<l; i++) {
|
---|
891 | depth = result.depths.list[i];
|
---|
892 |
|
---|
893 | if(depth < 2) { continue; }
|
---|
894 | else { this.addDepth(depth - 1); }
|
---|
895 | }
|
---|
896 |
|
---|
897 | return guid;
|
---|
898 | },
|
---|
899 |
|
---|
900 | block: function(block) {
|
---|
901 | var mustache = block.mustache,
|
---|
902 | program = block.program,
|
---|
903 | inverse = block.inverse;
|
---|
904 |
|
---|
905 | if (program) {
|
---|
906 | program = this.compileProgram(program);
|
---|
907 | }
|
---|
908 |
|
---|
909 | if (inverse) {
|
---|
910 | inverse = this.compileProgram(inverse);
|
---|
911 | }
|
---|
912 |
|
---|
913 | var type = this.classifyMustache(mustache);
|
---|
914 |
|
---|
915 | if (type === "helper") {
|
---|
916 | this.helperMustache(mustache, program, inverse);
|
---|
917 | } else if (type === "simple") {
|
---|
918 | this.simpleMustache(mustache);
|
---|
919 |
|
---|
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 | }
|
---|
936 |
|
---|
937 | this.opcode('append');
|
---|
938 | },
|
---|
939 |
|
---|
940 | hash: function(hash) {
|
---|
941 | var pairs = hash.pairs, pair, val;
|
---|
942 |
|
---|
943 | this.opcode('push', '{}');
|
---|
944 |
|
---|
945 | for(var i=0, l=pairs.length; i<l; i++) {
|
---|
946 | pair = pairs[i];
|
---|
947 | val = pair[1];
|
---|
948 |
|
---|
949 | this.accept(val);
|
---|
950 | this.opcode('assignToHash', pair[0]);
|
---|
951 | }
|
---|
952 | },
|
---|
953 |
|
---|
954 | partial: function(partial) {
|
---|
955 | var id = partial.id;
|
---|
956 | this.usePartial = true;
|
---|
957 |
|
---|
958 | if(partial.context) {
|
---|
959 | this.ID(partial.context);
|
---|
960 | } else {
|
---|
961 | this.opcode('push', 'depth0');
|
---|
962 | }
|
---|
963 |
|
---|
964 | this.opcode('invokePartial', id.original);
|
---|
965 | this.opcode('append');
|
---|
966 | },
|
---|
967 |
|
---|
968 | content: function(content) {
|
---|
969 | this.opcode('appendContent', content.string);
|
---|
970 | },
|
---|
971 |
|
---|
972 | mustache: function(mustache) {
|
---|
973 | var options = this.options;
|
---|
974 | var type = this.classifyMustache(mustache);
|
---|
975 |
|
---|
976 | if (type === "simple") {
|
---|
977 | this.simpleMustache(mustache);
|
---|
978 | } else if (type === "helper") {
|
---|
979 | this.helperMustache(mustache);
|
---|
980 | } else {
|
---|
981 | this.ambiguousMustache(mustache);
|
---|
982 | }
|
---|
983 |
|
---|
984 | if(mustache.escaped && !options.noEscape) {
|
---|
985 | this.opcode('appendEscaped');
|
---|
986 | } else {
|
---|
987 | this.opcode('append');
|
---|
988 | }
|
---|
989 | },
|
---|
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 |
|
---|
1032 | ID: function(id) {
|
---|
1033 | this.addDepth(id.depth);
|
---|
1034 | this.opcode('getContext', id.depth);
|
---|
1035 |
|
---|
1036 | var name = id.parts[0];
|
---|
1037 | if (!name) {
|
---|
1038 | this.opcode('pushContext');
|
---|
1039 | } else {
|
---|
1040 | this.opcode('lookupOnContext', id.parts[0]);
|
---|
1041 | }
|
---|
1042 |
|
---|
1043 | for(var i=1, l=id.parts.length; i<l; i++) {
|
---|
1044 | this.opcode('lookup', id.parts[i]);
|
---|
1045 | }
|
---|
1046 | },
|
---|
1047 |
|
---|
1048 | DATA: function(data) {
|
---|
1049 | this.options.data = true;
|
---|
1050 | this.opcode('lookupData', data.id);
|
---|
1051 | },
|
---|
1052 |
|
---|
1053 | STRING: function(string) {
|
---|
1054 | this.opcode('pushString', string.string);
|
---|
1055 | },
|
---|
1056 |
|
---|
1057 | INTEGER: function(integer) {
|
---|
1058 | this.opcode('pushLiteral', integer.integer);
|
---|
1059 | },
|
---|
1060 |
|
---|
1061 | BOOLEAN: function(bool) {
|
---|
1062 | this.opcode('pushLiteral', bool.bool);
|
---|
1063 | },
|
---|
1064 |
|
---|
1065 | comment: function() {},
|
---|
1066 |
|
---|
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 |
|
---|
1107 | pushParams: function(params) {
|
---|
1108 | var i = params.length, param;
|
---|
1109 |
|
---|
1110 | while(i--) {
|
---|
1111 | param = params[i];
|
---|
1112 |
|
---|
1113 | if(this.options.stringParams) {
|
---|
1114 | if(param.depth) {
|
---|
1115 | this.addDepth(param.depth);
|
---|
1116 | }
|
---|
1117 |
|
---|
1118 | this.opcode('getContext', param.depth || 0);
|
---|
1119 | this.opcode('pushStringParam', param.string);
|
---|
1120 | } else {
|
---|
1121 | this[param.type](param);
|
---|
1122 | }
|
---|
1123 | }
|
---|
1124 | },
|
---|
1125 |
|
---|
1126 | setupMustacheParams: function(mustache) {
|
---|
1127 | var params = mustache.params;
|
---|
1128 | this.pushParams(params);
|
---|
1129 |
|
---|
1130 | if(mustache.hash) {
|
---|
1131 | this.hash(mustache.hash);
|
---|
1132 | } else {
|
---|
1133 | this.opcode('pushLiteral', '{}');
|
---|
1134 | }
|
---|
1135 |
|
---|
1136 | return params;
|
---|
1137 | },
|
---|
1138 |
|
---|
1139 | // this will replace setupMustacheParams when we're done
|
---|
1140 | setupFullMustacheParams: function(mustache, program, inverse) {
|
---|
1141 | var params = mustache.params;
|
---|
1142 | this.pushParams(params);
|
---|
1143 |
|
---|
1144 | this.opcode('pushProgram', program);
|
---|
1145 | this.opcode('pushProgram', inverse);
|
---|
1146 |
|
---|
1147 | if(mustache.hash) {
|
---|
1148 | this.hash(mustache.hash);
|
---|
1149 | } else {
|
---|
1150 | this.opcode('pushLiteral', '{}');
|
---|
1151 | }
|
---|
1152 |
|
---|
1153 | return params;
|
---|
1154 | }
|
---|
1155 | };
|
---|
1156 |
|
---|
1157 | var Literal = function(value) {
|
---|
1158 | this.value = value;
|
---|
1159 | };
|
---|
1160 |
|
---|
1161 | JavaScriptCompiler.prototype = {
|
---|
1162 | // PUBLIC API: You can override these methods in a subclass to provide
|
---|
1163 | // alternative compiled forms for name lookup and buffering semantics
|
---|
1164 | nameLookup: function(parent, name, type) {
|
---|
1165 | if (/^[0-9]+$/.test(name)) {
|
---|
1166 | return parent + "[" + name + "]";
|
---|
1167 | } else if (JavaScriptCompiler.isValidJavaScriptVariableName(name)) {
|
---|
1168 | return parent + "." + name;
|
---|
1169 | }
|
---|
1170 | else {
|
---|
1171 | return parent + "['" + name + "']";
|
---|
1172 | }
|
---|
1173 | },
|
---|
1174 |
|
---|
1175 | appendToBuffer: function(string) {
|
---|
1176 | if (this.environment.isSimple) {
|
---|
1177 | return "return " + string + ";";
|
---|
1178 | } else {
|
---|
1179 | return "buffer += " + string + ";";
|
---|
1180 | }
|
---|
1181 | },
|
---|
1182 |
|
---|
1183 | initializeBuffer: function() {
|
---|
1184 | return this.quotedString("");
|
---|
1185 | },
|
---|
1186 |
|
---|
1187 | namespace: "Handlebars",
|
---|
1188 | // END PUBLIC API
|
---|
1189 |
|
---|
1190 | compile: function(environment, options, context, asObject) {
|
---|
1191 | this.environment = environment;
|
---|
1192 | this.options = options || {};
|
---|
1193 |
|
---|
1194 | Handlebars.log(Handlebars.logger.DEBUG, this.environment.disassemble() + "\n\n");
|
---|
1195 |
|
---|
1196 | this.name = this.environment.name;
|
---|
1197 | this.isChild = !!context;
|
---|
1198 | this.context = context || {
|
---|
1199 | programs: [],
|
---|
1200 | aliases: { }
|
---|
1201 | };
|
---|
1202 |
|
---|
1203 | this.preamble();
|
---|
1204 |
|
---|
1205 | this.stackSlot = 0;
|
---|
1206 | this.stackVars = [];
|
---|
1207 | this.registers = { list: [] };
|
---|
1208 | this.compileStack = [];
|
---|
1209 |
|
---|
1210 | this.compileChildren(environment, options);
|
---|
1211 |
|
---|
1212 | var opcodes = environment.opcodes, opcode;
|
---|
1213 |
|
---|
1214 | this.i = 0;
|
---|
1215 |
|
---|
1216 | for(l=opcodes.length; this.i<l; this.i++) {
|
---|
1217 | opcode = opcodes[this.i];
|
---|
1218 |
|
---|
1219 | if(opcode.opcode === 'DECLARE') {
|
---|
1220 | this[opcode.name] = opcode.value;
|
---|
1221 | } else {
|
---|
1222 | this[opcode.opcode].apply(this, opcode.args);
|
---|
1223 | }
|
---|
1224 | }
|
---|
1225 |
|
---|
1226 | return this.createFunctionContext(asObject);
|
---|
1227 | },
|
---|
1228 |
|
---|
1229 | nextOpcode: function() {
|
---|
1230 | var opcodes = this.environment.opcodes, opcode = opcodes[this.i + 1];
|
---|
1231 | return opcodes[this.i + 1];
|
---|
1232 | },
|
---|
1233 |
|
---|
1234 | eat: function(opcode) {
|
---|
1235 | this.i = this.i + 1;
|
---|
1236 | },
|
---|
1237 |
|
---|
1238 | preamble: function() {
|
---|
1239 | var out = [];
|
---|
1240 |
|
---|
1241 | if (!this.isChild) {
|
---|
1242 | var namespace = this.namespace;
|
---|
1243 | var copies = "helpers = helpers || " + namespace + ".helpers;";
|
---|
1244 | if (this.environment.usePartial) { copies = copies + " partials = partials || " + namespace + ".partials;"; }
|
---|
1245 | if (this.options.data) { copies = copies + " data = data || {};"; }
|
---|
1246 | out.push(copies);
|
---|
1247 | } else {
|
---|
1248 | out.push('');
|
---|
1249 | }
|
---|
1250 |
|
---|
1251 | if (!this.environment.isSimple) {
|
---|
1252 | out.push(", buffer = " + this.initializeBuffer());
|
---|
1253 | } else {
|
---|
1254 | out.push("");
|
---|
1255 | }
|
---|
1256 |
|
---|
1257 | // track the last context pushed into place to allow skipping the
|
---|
1258 | // getContext opcode when it would be a noop
|
---|
1259 | this.lastContext = 0;
|
---|
1260 | this.source = out;
|
---|
1261 | },
|
---|
1262 |
|
---|
1263 | createFunctionContext: function(asObject) {
|
---|
1264 | var locals = this.stackVars.concat(this.registers.list);
|
---|
1265 |
|
---|
1266 | if(locals.length > 0) {
|
---|
1267 | this.source[1] = this.source[1] + ", " + locals.join(", ");
|
---|
1268 | }
|
---|
1269 |
|
---|
1270 | // Generate minimizer alias mappings
|
---|
1271 | if (!this.isChild) {
|
---|
1272 | var aliases = [];
|
---|
1273 | for (var alias in this.context.aliases) {
|
---|
1274 | this.source[1] = this.source[1] + ', ' + alias + '=' + this.context.aliases[alias];
|
---|
1275 | }
|
---|
1276 | }
|
---|
1277 |
|
---|
1278 | if (this.source[1]) {
|
---|
1279 | this.source[1] = "var " + this.source[1].substring(2) + ";";
|
---|
1280 | }
|
---|
1281 |
|
---|
1282 | // Merge children
|
---|
1283 | if (!this.isChild) {
|
---|
1284 | this.source[1] += '\n' + this.context.programs.join('\n') + '\n';
|
---|
1285 | }
|
---|
1286 |
|
---|
1287 | if (!this.environment.isSimple) {
|
---|
1288 | this.source.push("return buffer;");
|
---|
1289 | }
|
---|
1290 |
|
---|
1291 | var params = this.isChild ? ["depth0", "data"] : ["Handlebars", "depth0", "helpers", "partials", "data"];
|
---|
1292 |
|
---|
1293 | for(var i=0, l=this.environment.depths.list.length; i<l; i++) {
|
---|
1294 | params.push("depth" + this.environment.depths.list[i]);
|
---|
1295 | }
|
---|
1296 |
|
---|
1297 | if (asObject) {
|
---|
1298 | params.push(this.source.join("\n "));
|
---|
1299 |
|
---|
1300 | return Function.apply(this, params);
|
---|
1301 | } else {
|
---|
1302 | var functionSource = 'function ' + (this.name || '') + '(' + params.join(',') + ') {\n ' + this.source.join("\n ") + '}';
|
---|
1303 | Handlebars.log(Handlebars.logger.DEBUG, functionSource + "\n\n");
|
---|
1304 | return functionSource;
|
---|
1305 | }
|
---|
1306 | },
|
---|
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
|
---|
1353 | appendContent: function(content) {
|
---|
1354 | this.source.push(this.appendToBuffer(this.quotedString(content)));
|
---|
1355 | },
|
---|
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
|
---|
1366 | append: function() {
|
---|
1367 | var local = this.popStack();
|
---|
1368 | this.source.push("if(" + local + " || " + local + " === 0) { " + this.appendToBuffer(local) + " }");
|
---|
1369 | if (this.environment.isSimple) {
|
---|
1370 | this.source.push("else { " + this.appendToBuffer("''") + " }");
|
---|
1371 | }
|
---|
1372 | },
|
---|
1373 |
|
---|
1374 | // [appendEscaped]
|
---|
1375 | //
|
---|
1376 | // On stack, before: value, ...
|
---|
1377 | // On stack, after: ...
|
---|
1378 | //
|
---|
1379 | // Escape `value` and append it to the buffer
|
---|
1380 | appendEscaped: function() {
|
---|
1381 | var opcode = this.nextOpcode(), extra = "";
|
---|
1382 | this.context.aliases.escapeExpression = 'this.escapeExpression';
|
---|
1383 |
|
---|
1384 | if(opcode && opcode.opcode === 'appendContent') {
|
---|
1385 | extra = " + " + this.quotedString(opcode.args[0]);
|
---|
1386 | this.eat(opcode);
|
---|
1387 | }
|
---|
1388 |
|
---|
1389 | this.source.push(this.appendToBuffer("escapeExpression(" + this.popStack() + ")" + extra));
|
---|
1390 | },
|
---|
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
|
---|
1399 | getContext: function(depth) {
|
---|
1400 | if(this.lastContext !== depth) {
|
---|
1401 | this.lastContext = depth;
|
---|
1402 | }
|
---|
1403 | },
|
---|
1404 |
|
---|
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 | });
|
---|
1439 | },
|
---|
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`
|
---|
1448 | lookup: function(name) {
|
---|
1449 | this.replaceStack(function(current) {
|
---|
1450 | return current + " == null || " + current + " === false ? " + current + " : " + this.nameLookup(current, name, 'context');
|
---|
1451 | });
|
---|
1452 | },
|
---|
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.
|
---|
1472 | pushStringParam: function(string) {
|
---|
1473 | this.pushStackLiteral('depth' + this.lastContext);
|
---|
1474 | this.pushString(string);
|
---|
1475 | },
|
---|
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
|
---|
1483 | pushString: function(string) {
|
---|
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));
|
---|
1520 | } else {
|
---|
1521 | this.pushStackLiteral(null);
|
---|
1522 | }
|
---|
1523 | },
|
---|
1524 |
|
---|
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');
|
---|
1579 | var nextStack = this.nextStack();
|
---|
1580 |
|
---|
1581 | this.source.push('if (foundHelper) { ' + nextStack + ' = foundHelper.call(' + helper.callParams + '); }');
|
---|
1582 | this.source.push('else { ' + nextStack + ' = ' + nonHelper + '; ' + nextStack + ' = typeof ' + nextStack + ' === functionType ? ' + nextStack + '() : ' + nextStack + '; }');
|
---|
1583 | },
|
---|
1584 |
|
---|
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"];
|
---|
1594 |
|
---|
1595 | if (this.options.data) {
|
---|
1596 | params.push("data");
|
---|
1597 | }
|
---|
1598 |
|
---|
1599 | this.context.aliases.self = "this";
|
---|
1600 | this.pushStack("self.invokePartial(" + params.join(", ") + ");");
|
---|
1601 | },
|
---|
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.
|
---|
1610 | assignToHash: function(key) {
|
---|
1611 | var value = this.popStack();
|
---|
1612 | var hash = this.topStack();
|
---|
1613 |
|
---|
1614 | this.source.push(hash + "['" + key + "'] = " + value + ";");
|
---|
1615 | },
|
---|
1616 |
|
---|
1617 | // HELPERS
|
---|
1618 |
|
---|
1619 | compiler: JavaScriptCompiler,
|
---|
1620 |
|
---|
1621 | compileChildren: function(environment, options) {
|
---|
1622 | var children = environment.children, child, compiler;
|
---|
1623 |
|
---|
1624 | for(var i=0, l=children.length; i<l; i++) {
|
---|
1625 | child = children[i];
|
---|
1626 | compiler = new this.compiler();
|
---|
1627 |
|
---|
1628 | this.context.programs.push(''); // Placeholder to prevent name conflicts for nested children
|
---|
1629 | var index = this.context.programs.length;
|
---|
1630 | child.index = index;
|
---|
1631 | child.name = 'program' + index;
|
---|
1632 | this.context.programs[index] = compiler.compile(child, options, this.context);
|
---|
1633 | }
|
---|
1634 | },
|
---|
1635 |
|
---|
1636 | programExpression: function(guid) {
|
---|
1637 | this.context.aliases.self = "this";
|
---|
1638 |
|
---|
1639 | if(guid == null) {
|
---|
1640 | return "self.noop";
|
---|
1641 | }
|
---|
1642 |
|
---|
1643 | var child = this.environment.children[guid],
|
---|
1644 | depths = child.depths.list, depth;
|
---|
1645 |
|
---|
1646 | var programParams = [child.index, child.name, "data"];
|
---|
1647 |
|
---|
1648 | for(var i=0, l = depths.length; i<l; i++) {
|
---|
1649 | depth = depths[i];
|
---|
1650 |
|
---|
1651 | if(depth === 1) { programParams.push("depth0"); }
|
---|
1652 | else { programParams.push("depth" + (depth - 1)); }
|
---|
1653 | }
|
---|
1654 |
|
---|
1655 | if(depths.length === 0) {
|
---|
1656 | return "self.program(" + programParams.join(", ") + ")";
|
---|
1657 | } else {
|
---|
1658 | programParams.shift();
|
---|
1659 | return "self.programWithDepth(" + programParams.join(", ") + ")";
|
---|
1660 | }
|
---|
1661 | },
|
---|
1662 |
|
---|
1663 | register: function(name, val) {
|
---|
1664 | this.useRegister(name);
|
---|
1665 | this.source.push(name + " = " + val + ";");
|
---|
1666 | },
|
---|
1667 |
|
---|
1668 | useRegister: function(name) {
|
---|
1669 | if(!this.registers[name]) {
|
---|
1670 | this.registers[name] = true;
|
---|
1671 | this.registers.list.push(name);
|
---|
1672 | }
|
---|
1673 | },
|
---|
1674 |
|
---|
1675 | pushStackLiteral: function(item) {
|
---|
1676 | this.compileStack.push(new Literal(item));
|
---|
1677 | return item;
|
---|
1678 | },
|
---|
1679 |
|
---|
1680 | pushStack: function(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 + ";");
|
---|
1690 | return "stack" + this.stackSlot;
|
---|
1691 | },
|
---|
1692 |
|
---|
1693 | nextStack: function(skipCompileStack) {
|
---|
1694 | var name = this.incrStack();
|
---|
1695 | this.compileStack.push("stack" + this.stackSlot);
|
---|
1696 | return name;
|
---|
1697 | },
|
---|
1698 |
|
---|
1699 | incrStack: function() {
|
---|
1700 | this.stackSlot++;
|
---|
1701 | if(this.stackSlot > this.stackVars.length) { this.stackVars.push("stack" + this.stackSlot); }
|
---|
1702 | return "stack" + this.stackSlot;
|
---|
1703 | },
|
---|
1704 |
|
---|
1705 | popStack: function() {
|
---|
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 | }
|
---|
1714 | },
|
---|
1715 |
|
---|
1716 | topStack: function() {
|
---|
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 | }
|
---|
1724 | },
|
---|
1725 |
|
---|
1726 | quotedString: function(str) {
|
---|
1727 | return '"' + str
|
---|
1728 | .replace(/\\/g, '\\\\')
|
---|
1729 | .replace(/"/g, '\\"')
|
---|
1730 | .replace(/\n/g, '\\n')
|
---|
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(", ");
|
---|
1793 | }
|
---|
1794 | };
|
---|
1795 |
|
---|
1796 | var reservedWords = (
|
---|
1797 | "break else new var" +
|
---|
1798 | " case finally return void" +
|
---|
1799 | " catch for switch while" +
|
---|
1800 | " continue function this with" +
|
---|
1801 | " default if throw" +
|
---|
1802 | " delete in try" +
|
---|
1803 | " do instanceof typeof" +
|
---|
1804 | " abstract enum int short" +
|
---|
1805 | " boolean export interface static" +
|
---|
1806 | " byte extends long super" +
|
---|
1807 | " char final native synchronized" +
|
---|
1808 | " class float package throws" +
|
---|
1809 | " const goto private transient" +
|
---|
1810 | " debugger implements protected volatile" +
|
---|
1811 | " double import public let yield"
|
---|
1812 | ).split(" ");
|
---|
1813 |
|
---|
1814 | var compilerWords = JavaScriptCompiler.RESERVED_WORDS = {};
|
---|
1815 |
|
---|
1816 | for(var i=0, l=reservedWords.length; i<l; i++) {
|
---|
1817 | compilerWords[reservedWords[i]] = true;
|
---|
1818 | }
|
---|
1819 |
|
---|
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 | };
|
---|
1826 |
|
---|
1827 | })(Handlebars.Compiler, Handlebars.JavaScriptCompiler);
|
---|
1828 |
|
---|
1829 | Handlebars.precompile = function(string, options) {
|
---|
1830 | options = options || {};
|
---|
1831 |
|
---|
1832 | var ast = Handlebars.parse(string);
|
---|
1833 | var environment = new Handlebars.Compiler().compile(ast, options);
|
---|
1834 | return new Handlebars.JavaScriptCompiler().compile(environment, options);
|
---|
1835 | };
|
---|
1836 |
|
---|
1837 | Handlebars.compile = function(string, options) {
|
---|
1838 | options = options || {};
|
---|
1839 |
|
---|
1840 | var compiled;
|
---|
1841 | function compile() {
|
---|
1842 | var ast = Handlebars.parse(string);
|
---|
1843 | var environment = new Handlebars.Compiler().compile(ast, options);
|
---|
1844 | var templateSpec = new Handlebars.JavaScriptCompiler().compile(environment, options, undefined, true);
|
---|
1845 | return Handlebars.template(templateSpec);
|
---|
1846 | }
|
---|
1847 |
|
---|
1848 | // Template is only compiled on first use and cached after that point.
|
---|
1849 | return function(context, options) {
|
---|
1850 | if (!compiled) {
|
---|
1851 | compiled = compile();
|
---|
1852 | }
|
---|
1853 | return compiled.call(this, context, options);
|
---|
1854 | };
|
---|
1855 | };
|
---|
1856 | ;
|
---|
1857 | // lib/handlebars/runtime.js
|
---|
1858 | Handlebars.VM = {
|
---|
1859 | template: function(templateSpec) {
|
---|
1860 | // Just add water
|
---|
1861 | var container = {
|
---|
1862 | escapeExpression: Handlebars.Utils.escapeExpression,
|
---|
1863 | invokePartial: Handlebars.VM.invokePartial,
|
---|
1864 | programs: [],
|
---|
1865 | program: function(i, fn, data) {
|
---|
1866 | var programWrapper = this.programs[i];
|
---|
1867 | if(data) {
|
---|
1868 | return Handlebars.VM.program(fn, data);
|
---|
1869 | } else if(programWrapper) {
|
---|
1870 | return programWrapper;
|
---|
1871 | } else {
|
---|
1872 | programWrapper = this.programs[i] = Handlebars.VM.program(fn);
|
---|
1873 | return programWrapper;
|
---|
1874 | }
|
---|
1875 | },
|
---|
1876 | programWithDepth: Handlebars.VM.programWithDepth,
|
---|
1877 | noop: Handlebars.VM.noop
|
---|
1878 | };
|
---|
1879 |
|
---|
1880 | return function(context, options) {
|
---|
1881 | options = options || {};
|
---|
1882 | return templateSpec.call(container, Handlebars, context, options.helpers, options.partials, options.data);
|
---|
1883 | };
|
---|
1884 | },
|
---|
1885 |
|
---|
1886 | programWithDepth: function(fn, data, $depth) {
|
---|
1887 | var args = Array.prototype.slice.call(arguments, 2);
|
---|
1888 |
|
---|
1889 | return function(context, options) {
|
---|
1890 | options = options || {};
|
---|
1891 |
|
---|
1892 | return fn.apply(this, [context, options.data || data].concat(args));
|
---|
1893 | };
|
---|
1894 | },
|
---|
1895 | program: function(fn, data) {
|
---|
1896 | return function(context, options) {
|
---|
1897 | options = options || {};
|
---|
1898 |
|
---|
1899 | return fn(context, options.data || data);
|
---|
1900 | };
|
---|
1901 | },
|
---|
1902 | noop: function() { return ""; },
|
---|
1903 | invokePartial: function(partial, name, context, helpers, partials, data) {
|
---|
1904 | var options = { helpers: helpers, partials: partials, data: data };
|
---|
1905 |
|
---|
1906 | if(partial === undefined) {
|
---|
1907 | throw new Handlebars.Exception("The partial " + name + " could not be found");
|
---|
1908 | } else if(partial instanceof Function) {
|
---|
1909 | return partial(context, options);
|
---|
1910 | } else if (!Handlebars.compile) {
|
---|
1911 | throw new Handlebars.Exception("The partial " + name + " could not be compiled when running in runtime-only mode");
|
---|
1912 | } else {
|
---|
1913 | partials[name] = Handlebars.compile(partial, {data: data !== undefined});
|
---|
1914 | return partials[name](context, options);
|
---|
1915 | }
|
---|
1916 | }
|
---|
1917 | };
|
---|
1918 |
|
---|
1919 | Handlebars.template = Handlebars.VM.template;
|
---|
1920 | ;
|
---|