handlebars 0.0.2 → 0.2.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (47) hide show
  1. data/.gitignore +1 -1
  2. data/.gitmodules +3 -0
  3. data/.rspec +1 -0
  4. data/Gemfile +1 -1
  5. data/README.mdown +44 -0
  6. data/Rakefile +3 -0
  7. data/handlebars.gemspec +19 -13
  8. data/lib/handlebars.rb +4 -3
  9. data/lib/handlebars/context.rb +37 -0
  10. data/lib/handlebars/version.rb +1 -1
  11. data/spec/handlebars_spec.rb +40 -0
  12. data/spike.rb +17 -0
  13. data/vendor/handlebars/.gitignore +6 -0
  14. data/vendor/handlebars/.jshintrc +50 -0
  15. data/vendor/handlebars/.npmignore +11 -0
  16. data/vendor/handlebars/Gemfile +5 -0
  17. data/vendor/handlebars/LICENSE +20 -0
  18. data/vendor/handlebars/README.markdown +315 -0
  19. data/vendor/handlebars/Rakefile +116 -0
  20. data/vendor/handlebars/bench/benchwarmer.js +149 -0
  21. data/vendor/handlebars/bench/handlebars.js +163 -0
  22. data/vendor/handlebars/bin/handlebars +139 -0
  23. data/vendor/handlebars/lib/handlebars.js +14 -0
  24. data/vendor/handlebars/lib/handlebars/base.js +101 -0
  25. data/vendor/handlebars/lib/handlebars/compiler/ast.js +103 -0
  26. data/vendor/handlebars/lib/handlebars/compiler/base.js +27 -0
  27. data/vendor/handlebars/lib/handlebars/compiler/compiler.js +808 -0
  28. data/vendor/handlebars/lib/handlebars/compiler/index.js +7 -0
  29. data/vendor/handlebars/lib/handlebars/compiler/printer.js +137 -0
  30. data/vendor/handlebars/lib/handlebars/compiler/visitor.js +13 -0
  31. data/vendor/handlebars/lib/handlebars/runtime.js +68 -0
  32. data/vendor/handlebars/lib/handlebars/utils.js +68 -0
  33. data/vendor/handlebars/package.json +25 -0
  34. data/vendor/handlebars/spec/acceptance_spec.rb +101 -0
  35. data/vendor/handlebars/spec/parser_spec.rb +264 -0
  36. data/vendor/handlebars/spec/qunit_spec.js +1067 -0
  37. data/vendor/handlebars/spec/spec_helper.rb +157 -0
  38. data/vendor/handlebars/spec/tokenizer_spec.rb +254 -0
  39. data/vendor/handlebars/src/handlebars.l +42 -0
  40. data/vendor/handlebars/src/handlebars.yy +99 -0
  41. metadata +93 -77
  42. data/README.md +0 -39
  43. data/lib/handlebars/generator.rb +0 -4
  44. data/lib/handlebars/parser.rb +0 -240
  45. data/spec/generator_spec.rb +0 -5
  46. data/spec/parser_spec.rb +0 -163
  47. data/spec/spec_helper.rb +0 -17
@@ -0,0 +1,14 @@
1
+ var Handlebars = require("./handlebars/base");
2
+ module.exports = Handlebars;
3
+
4
+ // Each of these augment the Handlebars object. No need to setup here.
5
+ // (This is done to easily share code between commonjs and browse envs)
6
+ require("./handlebars/utils");
7
+
8
+ require("./handlebars/compiler");
9
+ require("./handlebars/runtime");
10
+
11
+ // BEGIN(BROWSER)
12
+
13
+ // END(BROWSER)
14
+
@@ -0,0 +1,101 @@
1
+ // BEGIN(BROWSER)
2
+
3
+ /*jshint eqnull:true*/
4
+ var Handlebars = {};
5
+
6
+ Handlebars.VERSION = "1.0.beta.5";
7
+
8
+ Handlebars.helpers = {};
9
+ Handlebars.partials = {};
10
+
11
+ Handlebars.registerHelper = function(name, fn, inverse) {
12
+ if(inverse) { fn.not = inverse; }
13
+ this.helpers[name] = fn;
14
+ };
15
+
16
+ Handlebars.registerPartial = function(name, str) {
17
+ this.partials[name] = str;
18
+ };
19
+
20
+ Handlebars.registerHelper('helperMissing', function(arg) {
21
+ if(arguments.length === 2) {
22
+ return undefined;
23
+ } else {
24
+ throw new Error("Could not find property '" + arg + "'");
25
+ }
26
+ });
27
+
28
+ var toString = Object.prototype.toString, functionType = "[object Function]";
29
+
30
+ Handlebars.registerHelper('blockHelperMissing', function(context, options) {
31
+ var inverse = options.inverse || function() {}, fn = options.fn;
32
+
33
+
34
+ var ret = "";
35
+ var type = toString.call(context);
36
+
37
+ if(type === functionType) { context = context.call(this); }
38
+
39
+ if(context === true) {
40
+ return fn(this);
41
+ } else if(context === false || context == null) {
42
+ return inverse(this);
43
+ } else if(type === "[object Array]") {
44
+ if(context.length > 0) {
45
+ for(var i=0, j=context.length; i<j; i++) {
46
+ ret = ret + fn(context[i]);
47
+ }
48
+ } else {
49
+ ret = inverse(this);
50
+ }
51
+ return ret;
52
+ } else {
53
+ return fn(context);
54
+ }
55
+ });
56
+
57
+ Handlebars.registerHelper('each', function(context, options) {
58
+ var fn = options.fn, inverse = options.inverse;
59
+ var ret = "";
60
+
61
+ if(context && context.length > 0) {
62
+ for(var i=0, j=context.length; i<j; i++) {
63
+ ret = ret + fn(context[i]);
64
+ }
65
+ } else {
66
+ ret = inverse(this);
67
+ }
68
+ return ret;
69
+ });
70
+
71
+ Handlebars.registerHelper('if', function(context, options) {
72
+ var type = toString.call(context);
73
+ if(type === functionType) { context = context.call(this); }
74
+
75
+ if(!context || Handlebars.Utils.isEmpty(context)) {
76
+ return options.inverse(this);
77
+ } else {
78
+ return options.fn(this);
79
+ }
80
+ });
81
+
82
+ Handlebars.registerHelper('unless', function(context, options) {
83
+ var fn = options.fn, inverse = options.inverse;
84
+ options.fn = inverse;
85
+ options.inverse = fn;
86
+
87
+ return Handlebars.helpers['if'].call(this, context, options);
88
+ });
89
+
90
+ Handlebars.registerHelper('with', function(context, options) {
91
+ return options.fn(context);
92
+ });
93
+
94
+ Handlebars.registerHelper('log', function(context) {
95
+ Handlebars.log(context);
96
+ });
97
+
98
+ // END(BROWSER)
99
+
100
+ module.exports = Handlebars;
101
+
@@ -0,0 +1,103 @@
1
+ var Handlebars = require('./base');
2
+
3
+ // BEGIN(BROWSER)
4
+ (function() {
5
+
6
+ Handlebars.AST = {};
7
+
8
+ Handlebars.AST.ProgramNode = function(statements, inverse) {
9
+ this.type = "program";
10
+ this.statements = statements;
11
+ if(inverse) { this.inverse = new Handlebars.AST.ProgramNode(inverse); }
12
+ };
13
+
14
+ Handlebars.AST.MustacheNode = function(params, hash, unescaped) {
15
+ this.type = "mustache";
16
+ this.id = params[0];
17
+ this.params = params.slice(1);
18
+ this.hash = hash;
19
+ this.escaped = !unescaped;
20
+ };
21
+
22
+ Handlebars.AST.PartialNode = function(id, context) {
23
+ this.type = "partial";
24
+
25
+ // TODO: disallow complex IDs
26
+
27
+ this.id = id;
28
+ this.context = context;
29
+ };
30
+
31
+ var verifyMatch = function(open, close) {
32
+ if(open.original !== close.original) {
33
+ throw new Handlebars.Exception(open.original + " doesn't match " + close.original);
34
+ }
35
+ };
36
+
37
+ Handlebars.AST.BlockNode = function(mustache, program, close) {
38
+ verifyMatch(mustache.id, close);
39
+ this.type = "block";
40
+ this.mustache = mustache;
41
+ this.program = program;
42
+ };
43
+
44
+ Handlebars.AST.InverseNode = function(mustache, program, close) {
45
+ verifyMatch(mustache.id, close);
46
+ this.type = "inverse";
47
+ this.mustache = mustache;
48
+ this.program = program;
49
+ };
50
+
51
+ Handlebars.AST.ContentNode = function(string) {
52
+ this.type = "content";
53
+ this.string = string;
54
+ };
55
+
56
+ Handlebars.AST.HashNode = function(pairs) {
57
+ this.type = "hash";
58
+ this.pairs = pairs;
59
+ };
60
+
61
+ Handlebars.AST.IdNode = function(parts) {
62
+ this.type = "ID";
63
+ this.original = parts.join(".");
64
+
65
+ var dig = [], depth = 0;
66
+
67
+ for(var i=0,l=parts.length; i<l; i++) {
68
+ var part = parts[i];
69
+
70
+ if(part === "..") { depth++; }
71
+ else if(part === "." || part === "this") { this.isScoped = true; }
72
+ else { dig.push(part); }
73
+ }
74
+
75
+ this.parts = dig;
76
+ this.string = dig.join('.');
77
+ this.depth = depth;
78
+ this.isSimple = (dig.length === 1) && (depth === 0);
79
+ };
80
+
81
+ Handlebars.AST.StringNode = function(string) {
82
+ this.type = "STRING";
83
+ this.string = string;
84
+ };
85
+
86
+ Handlebars.AST.IntegerNode = function(integer) {
87
+ this.type = "INTEGER";
88
+ this.integer = integer;
89
+ };
90
+
91
+ Handlebars.AST.BooleanNode = function(bool) {
92
+ this.type = "BOOLEAN";
93
+ this.bool = bool;
94
+ };
95
+
96
+ Handlebars.AST.CommentNode = function(comment) {
97
+ this.type = "comment";
98
+ this.comment = comment;
99
+ };
100
+
101
+ })();
102
+ // END(BROWSER)
103
+
@@ -0,0 +1,27 @@
1
+ var handlebars = require("./parser").parser;
2
+ var Handlebars = require("../base");
3
+
4
+ // BEGIN(BROWSER)
5
+ Handlebars.Parser = handlebars;
6
+
7
+ Handlebars.parse = function(string) {
8
+ Handlebars.Parser.yy = Handlebars.AST;
9
+ return Handlebars.Parser.parse(string);
10
+ };
11
+
12
+ Handlebars.print = function(ast) {
13
+ return new Handlebars.PrintVisitor().accept(ast);
14
+ };
15
+
16
+ Handlebars.logger = {
17
+ DEBUG: 0, INFO: 1, WARN: 2, ERROR: 3, level: 3,
18
+
19
+ // override in the host environment
20
+ log: function(level, str) {}
21
+ };
22
+
23
+ Handlebars.log = function(level, str) { Handlebars.logger.log(level, str); };
24
+
25
+ // END(BROWSER)
26
+
27
+ module.exports = Handlebars;
@@ -0,0 +1,808 @@
1
+ var Handlebars = require("./base");
2
+
3
+ // BEGIN(BROWSER)
4
+
5
+ /*jshint eqnull:true*/
6
+ Handlebars.Compiler = function() {};
7
+ Handlebars.JavaScriptCompiler = function() {};
8
+
9
+ (function(Compiler, JavaScriptCompiler) {
10
+ Compiler.OPCODE_MAP = {
11
+ appendContent: 1,
12
+ getContext: 2,
13
+ lookupWithHelpers: 3,
14
+ lookup: 4,
15
+ append: 5,
16
+ invokeMustache: 6,
17
+ appendEscaped: 7,
18
+ pushString: 8,
19
+ truthyOrFallback: 9,
20
+ functionOrFallback: 10,
21
+ invokeProgram: 11,
22
+ invokePartial: 12,
23
+ push: 13,
24
+ assignToHash: 15,
25
+ pushStringParam: 16
26
+ };
27
+
28
+ Compiler.MULTI_PARAM_OPCODES = {
29
+ appendContent: 1,
30
+ getContext: 1,
31
+ lookupWithHelpers: 2,
32
+ lookup: 1,
33
+ invokeMustache: 3,
34
+ pushString: 1,
35
+ truthyOrFallback: 1,
36
+ functionOrFallback: 1,
37
+ invokeProgram: 3,
38
+ invokePartial: 1,
39
+ push: 1,
40
+ assignToHash: 1,
41
+ pushStringParam: 1
42
+ };
43
+
44
+ Compiler.DISASSEMBLE_MAP = {};
45
+
46
+ for(var prop in Compiler.OPCODE_MAP) {
47
+ var value = Compiler.OPCODE_MAP[prop];
48
+ Compiler.DISASSEMBLE_MAP[value] = prop;
49
+ }
50
+
51
+ Compiler.multiParamSize = function(code) {
52
+ return Compiler.MULTI_PARAM_OPCODES[Compiler.DISASSEMBLE_MAP[code]];
53
+ };
54
+
55
+ Compiler.prototype = {
56
+ compiler: Compiler,
57
+
58
+ disassemble: function() {
59
+ var opcodes = this.opcodes, opcode, nextCode;
60
+ var out = [], str, name, value;
61
+
62
+ for(var i=0, l=opcodes.length; i<l; i++) {
63
+ opcode = opcodes[i];
64
+
65
+ if(opcode === 'DECLARE') {
66
+ name = opcodes[++i];
67
+ value = opcodes[++i];
68
+ out.push("DECLARE " + name + " = " + value);
69
+ } else {
70
+ str = Compiler.DISASSEMBLE_MAP[opcode];
71
+
72
+ var extraParams = Compiler.multiParamSize(opcode);
73
+ var codes = [];
74
+
75
+ for(var j=0; j<extraParams; j++) {
76
+ nextCode = opcodes[++i];
77
+
78
+ if(typeof nextCode === "string") {
79
+ nextCode = "\"" + nextCode.replace("\n", "\\n") + "\"";
80
+ }
81
+
82
+ codes.push(nextCode);
83
+ }
84
+
85
+ str = str + " " + codes.join(" ");
86
+
87
+ out.push(str);
88
+ }
89
+ }
90
+
91
+ return out.join("\n");
92
+ },
93
+
94
+ guid: 0,
95
+
96
+ compile: function(program, options) {
97
+ this.children = [];
98
+ this.depths = {list: []};
99
+ this.options = options;
100
+
101
+ // These changes will propagate to the other compiler components
102
+ var knownHelpers = this.options.knownHelpers;
103
+ this.options.knownHelpers = {
104
+ 'helperMissing': true,
105
+ 'blockHelperMissing': true,
106
+ 'each': true,
107
+ 'if': true,
108
+ 'unless': true,
109
+ 'with': true,
110
+ 'log': true
111
+ };
112
+ if (knownHelpers) {
113
+ for (var name in knownHelpers) {
114
+ this.options.knownHelpers[name] = knownHelpers[name];
115
+ }
116
+ }
117
+
118
+ return this.program(program);
119
+ },
120
+
121
+ accept: function(node) {
122
+ return this[node.type](node);
123
+ },
124
+
125
+ program: function(program) {
126
+ var statements = program.statements, statement;
127
+ this.opcodes = [];
128
+
129
+ for(var i=0, l=statements.length; i<l; i++) {
130
+ statement = statements[i];
131
+ this[statement.type](statement);
132
+ }
133
+ this.isSimple = l === 1;
134
+
135
+ this.depths.list = this.depths.list.sort(function(a, b) {
136
+ return a - b;
137
+ });
138
+
139
+ return this;
140
+ },
141
+
142
+ compileProgram: function(program) {
143
+ var result = new this.compiler().compile(program, this.options);
144
+ var guid = this.guid++, depth;
145
+
146
+ this.usePartial = this.usePartial || result.usePartial;
147
+
148
+ this.children[guid] = result;
149
+
150
+ for(var i=0, l=result.depths.list.length; i<l; i++) {
151
+ depth = result.depths.list[i];
152
+
153
+ if(depth < 2) { continue; }
154
+ else { this.addDepth(depth - 1); }
155
+ }
156
+
157
+ return guid;
158
+ },
159
+
160
+ block: function(block) {
161
+ var mustache = block.mustache;
162
+ var depth, child, inverse, inverseGuid;
163
+
164
+ var params = this.setupStackForMustache(mustache);
165
+
166
+ var programGuid = this.compileProgram(block.program);
167
+
168
+ if(block.program.inverse) {
169
+ inverseGuid = this.compileProgram(block.program.inverse);
170
+ this.declare('inverse', inverseGuid);
171
+ }
172
+
173
+ this.opcode('invokeProgram', programGuid, params.length, !!mustache.hash);
174
+ this.declare('inverse', null);
175
+ this.opcode('append');
176
+ },
177
+
178
+ inverse: function(block) {
179
+ var params = this.setupStackForMustache(block.mustache);
180
+
181
+ var programGuid = this.compileProgram(block.program);
182
+
183
+ this.declare('inverse', programGuid);
184
+
185
+ this.opcode('invokeProgram', null, params.length, !!block.mustache.hash);
186
+ this.declare('inverse', null);
187
+ this.opcode('append');
188
+ },
189
+
190
+ hash: function(hash) {
191
+ var pairs = hash.pairs, pair, val;
192
+
193
+ this.opcode('push', '{}');
194
+
195
+ for(var i=0, l=pairs.length; i<l; i++) {
196
+ pair = pairs[i];
197
+ val = pair[1];
198
+
199
+ this.accept(val);
200
+ this.opcode('assignToHash', pair[0]);
201
+ }
202
+ },
203
+
204
+ partial: function(partial) {
205
+ var id = partial.id;
206
+ this.usePartial = true;
207
+
208
+ if(partial.context) {
209
+ this.ID(partial.context);
210
+ } else {
211
+ this.opcode('push', 'depth0');
212
+ }
213
+
214
+ this.opcode('invokePartial', id.original);
215
+ this.opcode('append');
216
+ },
217
+
218
+ content: function(content) {
219
+ this.opcode('appendContent', content.string);
220
+ },
221
+
222
+ mustache: function(mustache) {
223
+ var params = this.setupStackForMustache(mustache);
224
+
225
+ this.opcode('invokeMustache', params.length, mustache.id.original, !!mustache.hash);
226
+
227
+ if(mustache.escaped && !this.options.noEscape) {
228
+ this.opcode('appendEscaped');
229
+ } else {
230
+ this.opcode('append');
231
+ }
232
+ },
233
+
234
+ ID: function(id) {
235
+ this.addDepth(id.depth);
236
+
237
+ this.opcode('getContext', id.depth);
238
+
239
+ this.opcode('lookupWithHelpers', id.parts[0] || null, id.isScoped || false);
240
+
241
+ for(var i=1, l=id.parts.length; i<l; i++) {
242
+ this.opcode('lookup', id.parts[i]);
243
+ }
244
+ },
245
+
246
+ STRING: function(string) {
247
+ this.opcode('pushString', string.string);
248
+ },
249
+
250
+ INTEGER: function(integer) {
251
+ this.opcode('push', integer.integer);
252
+ },
253
+
254
+ BOOLEAN: function(bool) {
255
+ this.opcode('push', bool.bool);
256
+ },
257
+
258
+ comment: function() {},
259
+
260
+ // HELPERS
261
+ pushParams: function(params) {
262
+ var i = params.length, param;
263
+
264
+ while(i--) {
265
+ param = params[i];
266
+
267
+ if(this.options.stringParams) {
268
+ if(param.depth) {
269
+ this.addDepth(param.depth);
270
+ }
271
+
272
+ this.opcode('getContext', param.depth || 0);
273
+ this.opcode('pushStringParam', param.string);
274
+ } else {
275
+ this[param.type](param);
276
+ }
277
+ }
278
+ },
279
+
280
+ opcode: function(name, val1, val2, val3) {
281
+ this.opcodes.push(Compiler.OPCODE_MAP[name]);
282
+ if(val1 !== undefined) { this.opcodes.push(val1); }
283
+ if(val2 !== undefined) { this.opcodes.push(val2); }
284
+ if(val3 !== undefined) { this.opcodes.push(val3); }
285
+ },
286
+
287
+ declare: function(name, value) {
288
+ this.opcodes.push('DECLARE');
289
+ this.opcodes.push(name);
290
+ this.opcodes.push(value);
291
+ },
292
+
293
+ addDepth: function(depth) {
294
+ if(depth === 0) { return; }
295
+
296
+ if(!this.depths[depth]) {
297
+ this.depths[depth] = true;
298
+ this.depths.list.push(depth);
299
+ }
300
+ },
301
+
302
+ setupStackForMustache: function(mustache) {
303
+ var params = mustache.params;
304
+
305
+ this.pushParams(params);
306
+
307
+ if(mustache.hash) {
308
+ this.hash(mustache.hash);
309
+ }
310
+
311
+ this.ID(mustache.id);
312
+
313
+ return params;
314
+ }
315
+ };
316
+
317
+ JavaScriptCompiler.prototype = {
318
+ // PUBLIC API: You can override these methods in a subclass to provide
319
+ // alternative compiled forms for name lookup and buffering semantics
320
+ nameLookup: function(parent, name, type) {
321
+ if (/^[0-9]+$/.test(name)) {
322
+ return parent + "[" + name + "]";
323
+ } else if (JavaScriptCompiler.isValidJavaScriptVariableName(name)) {
324
+ return parent + "." + name;
325
+ }
326
+ else {
327
+ return parent + "['" + name + "']";
328
+ }
329
+ },
330
+
331
+ appendToBuffer: function(string) {
332
+ if (this.environment.isSimple) {
333
+ return "return " + string + ";";
334
+ } else {
335
+ return "buffer += " + string + ";";
336
+ }
337
+ },
338
+
339
+ initializeBuffer: function() {
340
+ return this.quotedString("");
341
+ },
342
+
343
+ namespace: "Handlebars",
344
+ // END PUBLIC API
345
+
346
+ compile: function(environment, options, context, asObject) {
347
+ this.environment = environment;
348
+ this.options = options || {};
349
+
350
+ this.name = this.environment.name;
351
+ this.isChild = !!context;
352
+ this.context = context || {
353
+ programs: [],
354
+ aliases: { self: 'this' },
355
+ registers: {list: []}
356
+ };
357
+
358
+ this.preamble();
359
+
360
+ this.stackSlot = 0;
361
+ this.stackVars = [];
362
+
363
+ this.compileChildren(environment, options);
364
+
365
+ var opcodes = environment.opcodes, opcode;
366
+
367
+ this.i = 0;
368
+
369
+ for(l=opcodes.length; this.i<l; this.i++) {
370
+ opcode = this.nextOpcode(0);
371
+
372
+ if(opcode[0] === 'DECLARE') {
373
+ this.i = this.i + 2;
374
+ this[opcode[1]] = opcode[2];
375
+ } else {
376
+ this.i = this.i + opcode[1].length;
377
+ this[opcode[0]].apply(this, opcode[1]);
378
+ }
379
+ }
380
+
381
+ return this.createFunctionContext(asObject);
382
+ },
383
+
384
+ nextOpcode: function(n) {
385
+ var opcodes = this.environment.opcodes, opcode = opcodes[this.i + n], name, val;
386
+ var extraParams, codes;
387
+
388
+ if(opcode === 'DECLARE') {
389
+ name = opcodes[this.i + 1];
390
+ val = opcodes[this.i + 2];
391
+ return ['DECLARE', name, val];
392
+ } else {
393
+ name = Compiler.DISASSEMBLE_MAP[opcode];
394
+
395
+ extraParams = Compiler.multiParamSize(opcode);
396
+ codes = [];
397
+
398
+ for(var j=0; j<extraParams; j++) {
399
+ codes.push(opcodes[this.i + j + 1 + n]);
400
+ }
401
+
402
+ return [name, codes];
403
+ }
404
+ },
405
+
406
+ eat: function(opcode) {
407
+ this.i = this.i + opcode.length;
408
+ },
409
+
410
+ preamble: function() {
411
+ var out = [];
412
+
413
+ // this register will disambiguate helper lookup from finding a function in
414
+ // a context. This is necessary for mustache compatibility, which requires
415
+ // that context functions in blocks are evaluated by blockHelperMissing, and
416
+ // then proceed as if the resulting value was provided to blockHelperMissing.
417
+ this.useRegister('foundHelper');
418
+
419
+ if (!this.isChild) {
420
+ var namespace = this.namespace;
421
+ var copies = "helpers = helpers || " + namespace + ".helpers;";
422
+ if(this.environment.usePartial) { copies = copies + " partials = partials || " + namespace + ".partials;"; }
423
+ out.push(copies);
424
+ } else {
425
+ out.push('');
426
+ }
427
+
428
+ if (!this.environment.isSimple) {
429
+ out.push(", buffer = " + this.initializeBuffer());
430
+ } else {
431
+ out.push("");
432
+ }
433
+
434
+ // track the last context pushed into place to allow skipping the
435
+ // getContext opcode when it would be a noop
436
+ this.lastContext = 0;
437
+ this.source = out;
438
+ },
439
+
440
+ createFunctionContext: function(asObject) {
441
+ var locals = this.stackVars;
442
+ if (!this.isChild) {
443
+ locals = locals.concat(this.context.registers.list);
444
+ }
445
+
446
+ if(locals.length > 0) {
447
+ this.source[1] = this.source[1] + ", " + locals.join(", ");
448
+ }
449
+
450
+ // Generate minimizer alias mappings
451
+ if (!this.isChild) {
452
+ var aliases = [];
453
+ for (var alias in this.context.aliases) {
454
+ this.source[1] = this.source[1] + ', ' + alias + '=' + this.context.aliases[alias];
455
+ }
456
+ }
457
+
458
+ if (this.source[1]) {
459
+ this.source[1] = "var " + this.source[1].substring(2) + ";";
460
+ }
461
+
462
+ // Merge children
463
+ if (!this.isChild) {
464
+ this.source[1] += '\n' + this.context.programs.join('\n') + '\n';
465
+ }
466
+
467
+ if (!this.environment.isSimple) {
468
+ this.source.push("return buffer;");
469
+ }
470
+
471
+ var params = this.isChild ? ["depth0", "data"] : ["Handlebars", "depth0", "helpers", "partials", "data"];
472
+
473
+ for(var i=0, l=this.environment.depths.list.length; i<l; i++) {
474
+ params.push("depth" + this.environment.depths.list[i]);
475
+ }
476
+
477
+ if (asObject) {
478
+ params.push(this.source.join("\n "));
479
+
480
+ return Function.apply(this, params);
481
+ } else {
482
+ var functionSource = 'function ' + (this.name || '') + '(' + params.join(',') + ') {\n ' + this.source.join("\n ") + '}';
483
+ Handlebars.log(Handlebars.logger.DEBUG, functionSource + "\n\n");
484
+ return functionSource;
485
+ }
486
+ },
487
+
488
+ appendContent: function(content) {
489
+ this.source.push(this.appendToBuffer(this.quotedString(content)));
490
+ },
491
+
492
+ append: function() {
493
+ var local = this.popStack();
494
+ this.source.push("if(" + local + " || " + local + " === 0) { " + this.appendToBuffer(local) + " }");
495
+ if (this.environment.isSimple) {
496
+ this.source.push("else { " + this.appendToBuffer("''") + " }");
497
+ }
498
+ },
499
+
500
+ appendEscaped: function() {
501
+ var opcode = this.nextOpcode(1), extra = "";
502
+ this.context.aliases.escapeExpression = 'this.escapeExpression';
503
+
504
+ if(opcode[0] === 'appendContent') {
505
+ extra = " + " + this.quotedString(opcode[1][0]);
506
+ this.eat(opcode);
507
+ }
508
+
509
+ this.source.push(this.appendToBuffer("escapeExpression(" + this.popStack() + ")" + extra));
510
+ },
511
+
512
+ getContext: function(depth) {
513
+ if(this.lastContext !== depth) {
514
+ this.lastContext = depth;
515
+ }
516
+ },
517
+
518
+ lookupWithHelpers: function(name, isScoped) {
519
+ if(name) {
520
+ var topStack = this.nextStack();
521
+
522
+ this.usingKnownHelper = false;
523
+
524
+ var toPush;
525
+ if (!isScoped && this.options.knownHelpers[name]) {
526
+ toPush = topStack + " = " + this.nameLookup('helpers', name, 'helper');
527
+ this.usingKnownHelper = true;
528
+ } else if (isScoped || this.options.knownHelpersOnly) {
529
+ toPush = topStack + " = " + this.nameLookup('depth' + this.lastContext, name, 'context');
530
+ } else {
531
+ this.register('foundHelper', this.nameLookup('helpers', name, 'helper'));
532
+ toPush = topStack + " = foundHelper || " + this.nameLookup('depth' + this.lastContext, name, 'context');
533
+ }
534
+
535
+ toPush += ';';
536
+ this.source.push(toPush);
537
+ } else {
538
+ this.pushStack('depth' + this.lastContext);
539
+ }
540
+ },
541
+
542
+ lookup: function(name) {
543
+ var topStack = this.topStack();
544
+ this.source.push(topStack + " = (" + topStack + " === null || " + topStack + " === undefined || " + topStack + " === false ? " +
545
+ topStack + " : " + this.nameLookup(topStack, name, 'context') + ");");
546
+ },
547
+
548
+ pushStringParam: function(string) {
549
+ this.pushStack('depth' + this.lastContext);
550
+ this.pushString(string);
551
+ },
552
+
553
+ pushString: function(string) {
554
+ this.pushStack(this.quotedString(string));
555
+ },
556
+
557
+ push: function(name) {
558
+ this.pushStack(name);
559
+ },
560
+
561
+ invokeMustache: function(paramSize, original, hasHash) {
562
+ this.populateParams(paramSize, this.quotedString(original), "{}", null, hasHash, function(nextStack, helperMissingString, id) {
563
+ if (!this.usingKnownHelper) {
564
+ this.context.aliases.helperMissing = 'helpers.helperMissing';
565
+ this.context.aliases.undef = 'void 0';
566
+ this.source.push("else if(" + id + "=== undef) { " + nextStack + " = helperMissing.call(" + helperMissingString + "); }");
567
+ if (nextStack !== id) {
568
+ this.source.push("else { " + nextStack + " = " + id + "; }");
569
+ }
570
+ }
571
+ });
572
+ },
573
+
574
+ invokeProgram: function(guid, paramSize, hasHash) {
575
+ var inverse = this.programExpression(this.inverse);
576
+ var mainProgram = this.programExpression(guid);
577
+
578
+ this.populateParams(paramSize, null, mainProgram, inverse, hasHash, function(nextStack, helperMissingString, id) {
579
+ if (!this.usingKnownHelper) {
580
+ this.context.aliases.blockHelperMissing = 'helpers.blockHelperMissing';
581
+ this.source.push("else { " + nextStack + " = blockHelperMissing.call(" + helperMissingString + "); }");
582
+ }
583
+ });
584
+ },
585
+
586
+ populateParams: function(paramSize, helperId, program, inverse, hasHash, fn) {
587
+ var needsRegister = hasHash || this.options.stringParams || inverse || this.options.data;
588
+ var id = this.popStack(), nextStack;
589
+ var params = [], param, stringParam, stringOptions;
590
+
591
+ if (needsRegister) {
592
+ this.register('tmp1', program);
593
+ stringOptions = 'tmp1';
594
+ } else {
595
+ stringOptions = '{ hash: {} }';
596
+ }
597
+
598
+ if (needsRegister) {
599
+ var hash = (hasHash ? this.popStack() : '{}');
600
+ this.source.push('tmp1.hash = ' + hash + ';');
601
+ }
602
+
603
+ if(this.options.stringParams) {
604
+ this.source.push('tmp1.contexts = [];');
605
+ }
606
+
607
+ for(var i=0; i<paramSize; i++) {
608
+ param = this.popStack();
609
+ params.push(param);
610
+
611
+ if(this.options.stringParams) {
612
+ this.source.push('tmp1.contexts.push(' + this.popStack() + ');');
613
+ }
614
+ }
615
+
616
+ if(inverse) {
617
+ this.source.push('tmp1.fn = tmp1;');
618
+ this.source.push('tmp1.inverse = ' + inverse + ';');
619
+ }
620
+
621
+ if(this.options.data) {
622
+ this.source.push('tmp1.data = data;');
623
+ }
624
+
625
+ params.push(stringOptions);
626
+
627
+ this.populateCall(params, id, helperId || id, fn, program !== '{}');
628
+ },
629
+
630
+ populateCall: function(params, id, helperId, fn, program) {
631
+ var paramString = ["depth0"].concat(params).join(", ");
632
+ var helperMissingString = ["depth0"].concat(helperId).concat(params).join(", ");
633
+
634
+ var nextStack = this.nextStack();
635
+
636
+ if (this.usingKnownHelper) {
637
+ this.source.push(nextStack + " = " + id + ".call(" + paramString + ");");
638
+ } else {
639
+ this.context.aliases.functionType = '"function"';
640
+ var condition = program ? "foundHelper && " : "";
641
+ this.source.push("if(" + condition + "typeof " + id + " === functionType) { " + nextStack + " = " + id + ".call(" + paramString + "); }");
642
+ }
643
+ fn.call(this, nextStack, helperMissingString, id);
644
+ this.usingKnownHelper = false;
645
+ },
646
+
647
+ invokePartial: function(context) {
648
+ var params = [this.nameLookup('partials', context, 'partial'), "'" + context + "'", this.popStack(), "helpers", "partials"];
649
+
650
+ if (this.options.data) {
651
+ params.push("data");
652
+ }
653
+
654
+ this.pushStack("self.invokePartial(" + params.join(", ") + ");");
655
+ },
656
+
657
+ assignToHash: function(key) {
658
+ var value = this.popStack();
659
+ var hash = this.topStack();
660
+
661
+ this.source.push(hash + "['" + key + "'] = " + value + ";");
662
+ },
663
+
664
+ // HELPERS
665
+
666
+ compiler: JavaScriptCompiler,
667
+
668
+ compileChildren: function(environment, options) {
669
+ var children = environment.children, child, compiler;
670
+
671
+ for(var i=0, l=children.length; i<l; i++) {
672
+ child = children[i];
673
+ compiler = new this.compiler();
674
+
675
+ this.context.programs.push(''); // Placeholder to prevent name conflicts for nested children
676
+ var index = this.context.programs.length;
677
+ child.index = index;
678
+ child.name = 'program' + index;
679
+ this.context.programs[index] = compiler.compile(child, options, this.context);
680
+ }
681
+ },
682
+
683
+ programExpression: function(guid) {
684
+ if(guid == null) { return "self.noop"; }
685
+
686
+ var child = this.environment.children[guid],
687
+ depths = child.depths.list, depth;
688
+
689
+ var programParams = [child.index, child.name, "data"];
690
+
691
+ for(var i=0, l = depths.length; i<l; i++) {
692
+ depth = depths[i];
693
+
694
+ if(depth === 1) { programParams.push("depth0"); }
695
+ else { programParams.push("depth" + (depth - 1)); }
696
+ }
697
+
698
+ if(depths.length === 0) {
699
+ return "self.program(" + programParams.join(", ") + ")";
700
+ } else {
701
+ programParams.shift();
702
+ return "self.programWithDepth(" + programParams.join(", ") + ")";
703
+ }
704
+ },
705
+
706
+ register: function(name, val) {
707
+ this.useRegister(name);
708
+ this.source.push(name + " = " + val + ";");
709
+ },
710
+
711
+ useRegister: function(name) {
712
+ if(!this.context.registers[name]) {
713
+ this.context.registers[name] = true;
714
+ this.context.registers.list.push(name);
715
+ }
716
+ },
717
+
718
+ pushStack: function(item) {
719
+ this.source.push(this.nextStack() + " = " + item + ";");
720
+ return "stack" + this.stackSlot;
721
+ },
722
+
723
+ nextStack: function() {
724
+ this.stackSlot++;
725
+ if(this.stackSlot > this.stackVars.length) { this.stackVars.push("stack" + this.stackSlot); }
726
+ return "stack" + this.stackSlot;
727
+ },
728
+
729
+ popStack: function() {
730
+ return "stack" + this.stackSlot--;
731
+ },
732
+
733
+ topStack: function() {
734
+ return "stack" + this.stackSlot;
735
+ },
736
+
737
+ quotedString: function(str) {
738
+ return '"' + str
739
+ .replace(/\\/g, '\\\\')
740
+ .replace(/"/g, '\\"')
741
+ .replace(/\n/g, '\\n')
742
+ .replace(/\r/g, '\\r') + '"';
743
+ }
744
+ };
745
+
746
+ var reservedWords = (
747
+ "break else new var" +
748
+ " case finally return void" +
749
+ " catch for switch while" +
750
+ " continue function this with" +
751
+ " default if throw" +
752
+ " delete in try" +
753
+ " do instanceof typeof" +
754
+ " abstract enum int short" +
755
+ " boolean export interface static" +
756
+ " byte extends long super" +
757
+ " char final native synchronized" +
758
+ " class float package throws" +
759
+ " const goto private transient" +
760
+ " debugger implements protected volatile" +
761
+ " double import public let yield"
762
+ ).split(" ");
763
+
764
+ var compilerWords = JavaScriptCompiler.RESERVED_WORDS = {};
765
+
766
+ for(var i=0, l=reservedWords.length; i<l; i++) {
767
+ compilerWords[reservedWords[i]] = true;
768
+ }
769
+
770
+ JavaScriptCompiler.isValidJavaScriptVariableName = function(name) {
771
+ if(!JavaScriptCompiler.RESERVED_WORDS[name] && /^[a-zA-Z_$][0-9a-zA-Z_$]+$/.test(name)) {
772
+ return true;
773
+ }
774
+ return false;
775
+ };
776
+
777
+ })(Handlebars.Compiler, Handlebars.JavaScriptCompiler);
778
+
779
+ Handlebars.precompile = function(string, options) {
780
+ options = options || {};
781
+
782
+ var ast = Handlebars.parse(string);
783
+ var environment = new Handlebars.Compiler().compile(ast, options);
784
+ return new Handlebars.JavaScriptCompiler().compile(environment, options);
785
+ };
786
+
787
+ Handlebars.compile = function(string, options) {
788
+ options = options || {};
789
+
790
+ var compiled;
791
+ function compile() {
792
+ var ast = Handlebars.parse(string);
793
+ var environment = new Handlebars.Compiler().compile(ast, options);
794
+ var templateSpec = new Handlebars.JavaScriptCompiler().compile(environment, options, undefined, true);
795
+ return Handlebars.template(templateSpec);
796
+ }
797
+
798
+ // Template is only compiled on first use and cached after that point.
799
+ return function(context, options) {
800
+ if (!compiled) {
801
+ compiled = compile();
802
+ }
803
+ return compiled.call(this, context, options);
804
+ };
805
+ };
806
+
807
+ // END(BROWSER)
808
+