handlebars 0.0.2 → 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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
+