handlebars_assets 0.15 → 0.16

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: d256320ba8600f2735ac4006de0ef9568de10837
4
- data.tar.gz: 047bfc66136a8fde214b7f081183ff8eb9f615a5
3
+ metadata.gz: db80ec041bb1a30fa034a7191da617510877dd1a
4
+ data.tar.gz: 591a444cbb3bdb7f71ee728c085b7912bf7769ce
5
5
  SHA512:
6
- metadata.gz: fabda19de11168da2423a3aba95420a67695e0749d50c1791f3b5208b3a048b2b8006a2e6bc27959752efab09b2e5b48915d79bb7e1e2bad1dab2e35ba715c1c
7
- data.tar.gz: e8118dc25fe68a2a32d0ad80169673e7724f624ebf24677069cc6d6a6e753b14cfdf8e6299c2b5be7f21455a025c0bd1a094e9c9028e68c1dc414e63ea921010
6
+ metadata.gz: b79943a6ffe7c085e29907aeeb0ee2187710d379453cae0bbbe3dfb25aef0b61470b6e170ebb9f413a9d03d34f1f6f00984b6bc4b53592173aa53cdb262611bb
7
+ data.tar.gz: 0c91048ddb22913fccc4334d1f0a2c4d92b851b550710cb0d3660d2cd4366cda16f4a1038894b46ecf1196edfc3e709783b12c38e756cbf216706f1bd7f5f5df
data/CHANGELOG.md CHANGED
@@ -1,3 +1,8 @@
1
+ ## 0.16 (2014-05-27)
2
+
3
+ * README clarification - @jithugopal
4
+ * Upgrade `handlebars` to 1.1.3 - @neodude
5
+
1
6
  ## 0.15 (2013-12-06)
2
7
 
3
8
  * Allow use of ember and other handlebars simultaneously - @rbhitchcock
data/README.md CHANGED
@@ -136,6 +136,8 @@ If your file is `templates/contacts/new.hbs`, the asset pipeline will generate J
136
136
 
137
137
  You can then invoke the resulting template in your application's JavaScript
138
138
 
139
+ NOTE: There will be no javascript object `HandlebarsTemplates` unless at least ONE template is included.
140
+
139
141
  ```javascript
140
142
  HandlebarsTemplates['contacts/new'](context);
141
143
  ```
@@ -1,3 +1,3 @@
1
1
  module HandlebarsAssets
2
- VERSION = "0.15"
2
+ VERSION = "0.16"
3
3
  end
@@ -1,6 +1,6 @@
1
1
  /*!
2
2
 
3
- handlebars v1.1.2
3
+ handlebars v1.3.0
4
4
 
5
5
  Copyright (C) 2011 by Yehuda Katz
6
6
 
@@ -24,6 +24,7 @@ THE SOFTWARE.
24
24
 
25
25
  @license
26
26
  */
27
+ /* exported Handlebars */
27
28
  var Handlebars = (function() {
28
29
  // handlebars/safe-string.js
29
30
  var __module4__ = (function() {
@@ -46,6 +47,7 @@ var __module4__ = (function() {
46
47
  var __module3__ = (function(__dependency1__) {
47
48
  "use strict";
48
49
  var __exports__ = {};
50
+ /*jshint -W004 */
49
51
  var SafeString = __dependency1__;
50
52
 
51
53
  var escape = {
@@ -66,7 +68,7 @@ var __module3__ = (function(__dependency1__) {
66
68
 
67
69
  function extend(obj, value) {
68
70
  for(var key in value) {
69
- if(value.hasOwnProperty(key)) {
71
+ if(Object.prototype.hasOwnProperty.call(value, key)) {
70
72
  obj[key] = value[key];
71
73
  }
72
74
  }
@@ -130,13 +132,25 @@ var __module5__ = (function() {
130
132
 
131
133
  var errorProps = ['description', 'fileName', 'lineNumber', 'message', 'name', 'number', 'stack'];
132
134
 
133
- function Exception(/* message */) {
134
- var tmp = Error.prototype.constructor.apply(this, arguments);
135
+ function Exception(message, node) {
136
+ var line;
137
+ if (node && node.firstLine) {
138
+ line = node.firstLine;
139
+
140
+ message += ' - ' + line + ':' + node.firstColumn;
141
+ }
142
+
143
+ var tmp = Error.prototype.constructor.call(this, message);
135
144
 
136
145
  // Unfortunately errors are not enumerable in Chrome (at least), so `for prop in tmp` doesn't work.
137
146
  for (var idx = 0; idx < errorProps.length; idx++) {
138
147
  this[errorProps[idx]] = tmp[errorProps[idx]];
139
148
  }
149
+
150
+ if (line) {
151
+ this.lineNumber = line;
152
+ this.column = node.firstColumn;
153
+ }
140
154
  }
141
155
 
142
156
  Exception.prototype = new Error();
@@ -149,11 +163,10 @@ var __module5__ = (function() {
149
163
  var __module2__ = (function(__dependency1__, __dependency2__) {
150
164
  "use strict";
151
165
  var __exports__ = {};
152
- /*globals Exception, Utils */
153
166
  var Utils = __dependency1__;
154
167
  var Exception = __dependency2__;
155
168
 
156
- var VERSION = "1.1.2";
169
+ var VERSION = "1.3.0";
157
170
  __exports__.VERSION = VERSION;var COMPILER_REVISION = 4;
158
171
  __exports__.COMPILER_REVISION = COMPILER_REVISION;
159
172
  var REVISION_CHANGES = {
@@ -205,7 +218,7 @@ var __module2__ = (function(__dependency1__, __dependency2__) {
205
218
  if(arguments.length === 2) {
206
219
  return undefined;
207
220
  } else {
208
- throw new Error("Missing helper: '" + arg + "'");
221
+ throw new Exception("Missing helper: '" + arg + "'");
209
222
  }
210
223
  });
211
224
 
@@ -244,7 +257,7 @@ var __module2__ = (function(__dependency1__, __dependency2__) {
244
257
  for(var j = context.length; i<j; i++) {
245
258
  if (data) {
246
259
  data.index = i;
247
- data.first = (i === 0)
260
+ data.first = (i === 0);
248
261
  data.last = (i === (context.length-1));
249
262
  }
250
263
  ret = ret + fn(context[i], { data: data });
@@ -252,7 +265,11 @@ var __module2__ = (function(__dependency1__, __dependency2__) {
252
265
  } else {
253
266
  for(var key in context) {
254
267
  if(context.hasOwnProperty(key)) {
255
- if(data) { data.key = key; }
268
+ if(data) {
269
+ data.key = key;
270
+ data.index = i;
271
+ data.first = (i === 0);
272
+ }
256
273
  ret = ret + fn(context[key], {data: data});
257
274
  i++;
258
275
  }
@@ -332,7 +349,6 @@ var __module2__ = (function(__dependency1__, __dependency2__) {
332
349
  var __module6__ = (function(__dependency1__, __dependency2__, __dependency3__) {
333
350
  "use strict";
334
351
  var __exports__ = {};
335
- /*global Utils */
336
352
  var Utils = __dependency1__;
337
353
  var Exception = __dependency2__;
338
354
  var COMPILER_REVISION = __dependency3__.COMPILER_REVISION;
@@ -346,42 +362,37 @@ var __module6__ = (function(__dependency1__, __dependency2__, __dependency3__) {
346
362
  if (compilerRevision < currentRevision) {
347
363
  var runtimeVersions = REVISION_CHANGES[currentRevision],
348
364
  compilerVersions = REVISION_CHANGES[compilerRevision];
349
- throw new Error("Template was precompiled with an older version of Handlebars than the current runtime. "+
365
+ throw new Exception("Template was precompiled with an older version of Handlebars than the current runtime. "+
350
366
  "Please update your precompiler to a newer version ("+runtimeVersions+") or downgrade your runtime to an older version ("+compilerVersions+").");
351
367
  } else {
352
368
  // Use the embedded version info since the runtime doesn't know about this revision yet
353
- throw new Error("Template was precompiled with a newer version of Handlebars than the current runtime. "+
369
+ throw new Exception("Template was precompiled with a newer version of Handlebars than the current runtime. "+
354
370
  "Please update your runtime to a newer version ("+compilerInfo[1]+").");
355
371
  }
356
372
  }
357
373
  }
358
374
 
359
- // TODO: Remove this line and break up compilePartial
375
+ __exports__.checkRevision = checkRevision;// TODO: Remove this line and break up compilePartial
360
376
 
361
377
  function template(templateSpec, env) {
362
378
  if (!env) {
363
- throw new Error("No environment passed to template");
379
+ throw new Exception("No environment passed to template");
364
380
  }
365
381
 
366
- var invokePartialWrapper;
367
- if (env.compile) {
368
- invokePartialWrapper = function(partial, name, context, helpers, partials, data) {
369
- // TODO : Check this for all inputs and the options handling (partial flag, etc). This feels
370
- // like there should be a common exec path
371
- var result = invokePartial.apply(this, arguments);
372
- if (result) { return result; }
382
+ // Note: Using env.VM references rather than local var references throughout this section to allow
383
+ // for external users to override these as psuedo-supported APIs.
384
+ var invokePartialWrapper = function(partial, name, context, helpers, partials, data) {
385
+ var result = env.VM.invokePartial.apply(this, arguments);
386
+ if (result != null) { return result; }
373
387
 
388
+ if (env.compile) {
374
389
  var options = { helpers: helpers, partials: partials, data: data };
375
390
  partials[name] = env.compile(partial, { data: data !== undefined }, env);
376
391
  return partials[name](context, options);
377
- };
378
- } else {
379
- invokePartialWrapper = function(partial, name /* , context, helpers, partials, data */) {
380
- var result = invokePartial.apply(this, arguments);
381
- if (result) { return result; }
392
+ } else {
382
393
  throw new Exception("The partial " + name + " could not be compiled when running in runtime-only mode");
383
- };
384
- }
394
+ }
395
+ };
385
396
 
386
397
  // Just add water
387
398
  var container = {
@@ -407,8 +418,8 @@ var __module6__ = (function(__dependency1__, __dependency2__, __dependency3__) {
407
418
  }
408
419
  return ret;
409
420
  },
410
- programWithDepth: programWithDepth,
411
- noop: noop,
421
+ programWithDepth: env.VM.programWithDepth,
422
+ noop: env.VM.noop,
412
423
  compilerInfo: null
413
424
  };
414
425
 
@@ -430,7 +441,7 @@ var __module6__ = (function(__dependency1__, __dependency2__, __dependency3__) {
430
441
  options.data);
431
442
 
432
443
  if (!options.partial) {
433
- checkRevision(container.compilerInfo);
444
+ env.VM.checkRevision(container.compilerInfo);
434
445
  }
435
446
 
436
447
  return result;
@@ -481,6 +492,7 @@ var __module6__ = (function(__dependency1__, __dependency2__, __dependency3__) {
481
492
  var __module1__ = (function(__dependency1__, __dependency2__, __dependency3__, __dependency4__, __dependency5__) {
482
493
  "use strict";
483
494
  var __exports__;
495
+ /*globals Handlebars: true */
484
496
  var base = __dependency1__;
485
497
 
486
498
  // Each of these augment the Handlebars object. No need to setup here.
@@ -517,154 +529,233 @@ var __module1__ = (function(__dependency1__, __dependency2__, __dependency3__, _
517
529
  // handlebars/compiler/ast.js
518
530
  var __module7__ = (function(__dependency1__) {
519
531
  "use strict";
520
- var __exports__ = {};
532
+ var __exports__;
521
533
  var Exception = __dependency1__;
522
534
 
523
- function ProgramNode(statements, inverseStrip, inverse) {
524
- this.type = "program";
525
- this.statements = statements;
526
- this.strip = {};
527
-
528
- if(inverse) {
529
- this.inverse = new ProgramNode(inverse, inverseStrip);
530
- this.strip.right = inverseStrip.left;
531
- } else if (inverseStrip) {
532
- this.strip.left = inverseStrip.right;
533
- }
535
+ function LocationInfo(locInfo){
536
+ locInfo = locInfo || {};
537
+ this.firstLine = locInfo.first_line;
538
+ this.firstColumn = locInfo.first_column;
539
+ this.lastColumn = locInfo.last_column;
540
+ this.lastLine = locInfo.last_line;
534
541
  }
535
542
 
536
- __exports__.ProgramNode = ProgramNode;function MustacheNode(rawParams, hash, open, strip) {
537
- this.type = "mustache";
538
- this.hash = hash;
539
- this.strip = strip;
543
+ var AST = {
544
+ ProgramNode: function(statements, inverseStrip, inverse, locInfo) {
545
+ var inverseLocationInfo, firstInverseNode;
546
+ if (arguments.length === 3) {
547
+ locInfo = inverse;
548
+ inverse = null;
549
+ } else if (arguments.length === 2) {
550
+ locInfo = inverseStrip;
551
+ inverseStrip = null;
552
+ }
553
+
554
+ LocationInfo.call(this, locInfo);
555
+ this.type = "program";
556
+ this.statements = statements;
557
+ this.strip = {};
558
+
559
+ if(inverse) {
560
+ firstInverseNode = inverse[0];
561
+ if (firstInverseNode) {
562
+ inverseLocationInfo = {
563
+ first_line: firstInverseNode.firstLine,
564
+ last_line: firstInverseNode.lastLine,
565
+ last_column: firstInverseNode.lastColumn,
566
+ first_column: firstInverseNode.firstColumn
567
+ };
568
+ this.inverse = new AST.ProgramNode(inverse, inverseStrip, inverseLocationInfo);
569
+ } else {
570
+ this.inverse = new AST.ProgramNode(inverse, inverseStrip);
571
+ }
572
+ this.strip.right = inverseStrip.left;
573
+ } else if (inverseStrip) {
574
+ this.strip.left = inverseStrip.right;
575
+ }
576
+ },
540
577
 
541
- var escapeFlag = open[3] || open[2];
542
- this.escaped = escapeFlag !== '{' && escapeFlag !== '&';
578
+ MustacheNode: function(rawParams, hash, open, strip, locInfo) {
579
+ LocationInfo.call(this, locInfo);
580
+ this.type = "mustache";
581
+ this.strip = strip;
543
582
 
544
- var id = this.id = rawParams[0];
545
- var params = this.params = rawParams.slice(1);
583
+ // Open may be a string parsed from the parser or a passed boolean flag
584
+ if (open != null && open.charAt) {
585
+ // Must use charAt to support IE pre-10
586
+ var escapeFlag = open.charAt(3) || open.charAt(2);
587
+ this.escaped = escapeFlag !== '{' && escapeFlag !== '&';
588
+ } else {
589
+ this.escaped = !!open;
590
+ }
546
591
 
547
- // a mustache is an eligible helper if:
548
- // * its id is simple (a single part, not `this` or `..`)
549
- var eligibleHelper = this.eligibleHelper = id.isSimple;
592
+ if (rawParams instanceof AST.SexprNode) {
593
+ this.sexpr = rawParams;
594
+ } else {
595
+ // Support old AST API
596
+ this.sexpr = new AST.SexprNode(rawParams, hash);
597
+ }
550
598
 
551
- // a mustache is definitely a helper if:
552
- // * it is an eligible helper, and
553
- // * it has at least one parameter or hash segment
554
- this.isHelper = eligibleHelper && (params.length || hash);
599
+ this.sexpr.isRoot = true;
555
600
 
556
- // if a mustache is an eligible helper but not a definite
557
- // helper, it is ambiguous, and will be resolved in a later
558
- // pass or at runtime.
559
- }
601
+ // Support old AST API that stored this info in MustacheNode
602
+ this.id = this.sexpr.id;
603
+ this.params = this.sexpr.params;
604
+ this.hash = this.sexpr.hash;
605
+ this.eligibleHelper = this.sexpr.eligibleHelper;
606
+ this.isHelper = this.sexpr.isHelper;
607
+ },
560
608
 
561
- __exports__.MustacheNode = MustacheNode;function PartialNode(partialName, context, strip) {
562
- this.type = "partial";
563
- this.partialName = partialName;
564
- this.context = context;
565
- this.strip = strip;
566
- }
609
+ SexprNode: function(rawParams, hash, locInfo) {
610
+ LocationInfo.call(this, locInfo);
567
611
 
568
- __exports__.PartialNode = PartialNode;function BlockNode(mustache, program, inverse, close) {
569
- if(mustache.id.original !== close.path.original) {
570
- throw new Exception(mustache.id.original + " doesn't match " + close.path.original);
571
- }
612
+ this.type = "sexpr";
613
+ this.hash = hash;
572
614
 
573
- this.type = "block";
574
- this.mustache = mustache;
575
- this.program = program;
576
- this.inverse = inverse;
615
+ var id = this.id = rawParams[0];
616
+ var params = this.params = rawParams.slice(1);
577
617
 
578
- this.strip = {
579
- left: mustache.strip.left,
580
- right: close.strip.right
581
- };
618
+ // a mustache is an eligible helper if:
619
+ // * its id is simple (a single part, not `this` or `..`)
620
+ var eligibleHelper = this.eligibleHelper = id.isSimple;
582
621
 
583
- (program || inverse).strip.left = mustache.strip.right;
584
- (inverse || program).strip.right = close.strip.left;
622
+ // a mustache is definitely a helper if:
623
+ // * it is an eligible helper, and
624
+ // * it has at least one parameter or hash segment
625
+ this.isHelper = eligibleHelper && (params.length || hash);
585
626
 
586
- if (inverse && !program) {
587
- this.isInverse = true;
588
- }
589
- }
627
+ // if a mustache is an eligible helper but not a definite
628
+ // helper, it is ambiguous, and will be resolved in a later
629
+ // pass or at runtime.
630
+ },
590
631
 
591
- __exports__.BlockNode = BlockNode;function ContentNode(string) {
592
- this.type = "content";
593
- this.string = string;
594
- }
632
+ PartialNode: function(partialName, context, strip, locInfo) {
633
+ LocationInfo.call(this, locInfo);
634
+ this.type = "partial";
635
+ this.partialName = partialName;
636
+ this.context = context;
637
+ this.strip = strip;
638
+ },
595
639
 
596
- __exports__.ContentNode = ContentNode;function HashNode(pairs) {
597
- this.type = "hash";
598
- this.pairs = pairs;
599
- }
640
+ BlockNode: function(mustache, program, inverse, close, locInfo) {
641
+ LocationInfo.call(this, locInfo);
642
+
643
+ if(mustache.sexpr.id.original !== close.path.original) {
644
+ throw new Exception(mustache.sexpr.id.original + " doesn't match " + close.path.original, this);
645
+ }
600
646
 
601
- __exports__.HashNode = HashNode;function IdNode(parts) {
602
- this.type = "ID";
647
+ this.type = 'block';
648
+ this.mustache = mustache;
649
+ this.program = program;
650
+ this.inverse = inverse;
603
651
 
604
- var original = "",
605
- dig = [],
606
- depth = 0;
652
+ this.strip = {
653
+ left: mustache.strip.left,
654
+ right: close.strip.right
655
+ };
607
656
 
608
- for(var i=0,l=parts.length; i<l; i++) {
609
- var part = parts[i].part;
610
- original += (parts[i].separator || '') + part;
657
+ (program || inverse).strip.left = mustache.strip.right;
658
+ (inverse || program).strip.right = close.strip.left;
611
659
 
612
- if (part === ".." || part === "." || part === "this") {
613
- if (dig.length > 0) { throw new Exception("Invalid path: " + original); }
614
- else if (part === "..") { depth++; }
615
- else { this.isScoped = true; }
660
+ if (inverse && !program) {
661
+ this.isInverse = true;
616
662
  }
617
- else { dig.push(part); }
618
- }
663
+ },
619
664
 
620
- this.original = original;
621
- this.parts = dig;
622
- this.string = dig.join('.');
623
- this.depth = depth;
665
+ ContentNode: function(string, locInfo) {
666
+ LocationInfo.call(this, locInfo);
667
+ this.type = "content";
668
+ this.string = string;
669
+ },
624
670
 
625
- // an ID is simple if it only has one part, and that part is not
626
- // `..` or `this`.
627
- this.isSimple = parts.length === 1 && !this.isScoped && depth === 0;
671
+ HashNode: function(pairs, locInfo) {
672
+ LocationInfo.call(this, locInfo);
673
+ this.type = "hash";
674
+ this.pairs = pairs;
675
+ },
628
676
 
629
- this.stringModeValue = this.string;
630
- }
677
+ IdNode: function(parts, locInfo) {
678
+ LocationInfo.call(this, locInfo);
679
+ this.type = "ID";
631
680
 
632
- __exports__.IdNode = IdNode;function PartialNameNode(name) {
633
- this.type = "PARTIAL_NAME";
634
- this.name = name.original;
635
- }
681
+ var original = "",
682
+ dig = [],
683
+ depth = 0;
636
684
 
637
- __exports__.PartialNameNode = PartialNameNode;function DataNode(id) {
638
- this.type = "DATA";
639
- this.id = id;
640
- }
685
+ for(var i=0,l=parts.length; i<l; i++) {
686
+ var part = parts[i].part;
687
+ original += (parts[i].separator || '') + part;
641
688
 
642
- __exports__.DataNode = DataNode;function StringNode(string) {
643
- this.type = "STRING";
644
- this.original =
645
- this.string =
646
- this.stringModeValue = string;
647
- }
689
+ if (part === ".." || part === "." || part === "this") {
690
+ if (dig.length > 0) {
691
+ throw new Exception("Invalid path: " + original, this);
692
+ } else if (part === "..") {
693
+ depth++;
694
+ } else {
695
+ this.isScoped = true;
696
+ }
697
+ } else {
698
+ dig.push(part);
699
+ }
700
+ }
648
701
 
649
- __exports__.StringNode = StringNode;function IntegerNode(integer) {
650
- this.type = "INTEGER";
651
- this.original =
652
- this.integer = integer;
653
- this.stringModeValue = Number(integer);
654
- }
702
+ this.original = original;
703
+ this.parts = dig;
704
+ this.string = dig.join('.');
705
+ this.depth = depth;
655
706
 
656
- __exports__.IntegerNode = IntegerNode;function BooleanNode(bool) {
657
- this.type = "BOOLEAN";
658
- this.bool = bool;
659
- this.stringModeValue = bool === "true";
660
- }
707
+ // an ID is simple if it only has one part, and that part is not
708
+ // `..` or `this`.
709
+ this.isSimple = parts.length === 1 && !this.isScoped && depth === 0;
661
710
 
662
- __exports__.BooleanNode = BooleanNode;function CommentNode(comment) {
663
- this.type = "comment";
664
- this.comment = comment;
665
- }
711
+ this.stringModeValue = this.string;
712
+ },
713
+
714
+ PartialNameNode: function(name, locInfo) {
715
+ LocationInfo.call(this, locInfo);
716
+ this.type = "PARTIAL_NAME";
717
+ this.name = name.original;
718
+ },
719
+
720
+ DataNode: function(id, locInfo) {
721
+ LocationInfo.call(this, locInfo);
722
+ this.type = "DATA";
723
+ this.id = id;
724
+ },
725
+
726
+ StringNode: function(string, locInfo) {
727
+ LocationInfo.call(this, locInfo);
728
+ this.type = "STRING";
729
+ this.original =
730
+ this.string =
731
+ this.stringModeValue = string;
732
+ },
733
+
734
+ IntegerNode: function(integer, locInfo) {
735
+ LocationInfo.call(this, locInfo);
736
+ this.type = "INTEGER";
737
+ this.original =
738
+ this.integer = integer;
739
+ this.stringModeValue = Number(integer);
740
+ },
741
+
742
+ BooleanNode: function(bool, locInfo) {
743
+ LocationInfo.call(this, locInfo);
744
+ this.type = "BOOLEAN";
745
+ this.bool = bool;
746
+ this.stringModeValue = bool === "true";
747
+ },
748
+
749
+ CommentNode: function(comment, locInfo) {
750
+ LocationInfo.call(this, locInfo);
751
+ this.type = "comment";
752
+ this.comment = comment;
753
+ }
754
+ };
666
755
 
667
- __exports__.CommentNode = CommentNode;
756
+ // Must be exported as an object rather than the root of the module as the jison lexer
757
+ // most modify the object to operate properly.
758
+ __exports__ = AST;
668
759
  return __exports__;
669
760
  })(__module5__);
670
761
 
@@ -672,107 +763,110 @@ var __module7__ = (function(__dependency1__) {
672
763
  var __module9__ = (function() {
673
764
  "use strict";
674
765
  var __exports__;
766
+ /* jshint ignore:start */
675
767
  /* Jison generated parser */
676
768
  var handlebars = (function(){
677
769
  var parser = {trace: function trace() { },
678
770
  yy: {},
679
- symbols_: {"error":2,"root":3,"statements":4,"EOF":5,"program":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,"CLOSE_UNESCAPED":24,"OPEN_PARTIAL":25,"partialName":26,"partial_option0":27,"inMustache_repetition0":28,"inMustache_option0":29,"dataName":30,"param":31,"STRING":32,"INTEGER":33,"BOOLEAN":34,"hash":35,"hash_repetition_plus0":36,"hashSegment":37,"ID":38,"EQUALS":39,"DATA":40,"pathSegments":41,"SEP":42,"$accept":0,"$end":1},
680
- terminals_: {2:"error",5:"EOF",14:"CONTENT",15:"COMMENT",16:"OPEN_BLOCK",18:"CLOSE",19:"OPEN_INVERSE",20:"OPEN_ENDBLOCK",22:"OPEN",23:"OPEN_UNESCAPED",24:"CLOSE_UNESCAPED",25:"OPEN_PARTIAL",32:"STRING",33:"INTEGER",34:"BOOLEAN",38:"ID",39:"EQUALS",40:"DATA",42:"SEP"},
681
- productions_: [0,[3,2],[3,1],[6,2],[6,3],[6,2],[6,1],[6,1],[6,0],[4,1],[4,2],[8,3],[8,3],[8,1],[8,1],[8,1],[8,1],[11,3],[9,3],[10,3],[12,3],[12,3],[13,4],[7,2],[17,3],[17,1],[31,1],[31,1],[31,1],[31,1],[31,1],[35,1],[37,3],[26,1],[26,1],[26,1],[30,2],[21,1],[41,3],[41,1],[27,0],[27,1],[28,0],[28,2],[29,0],[29,1],[36,1],[36,2]],
771
+ symbols_: {"error":2,"root":3,"statements":4,"EOF":5,"program":6,"simpleInverse":7,"statement":8,"openInverse":9,"closeBlock":10,"openBlock":11,"mustache":12,"partial":13,"CONTENT":14,"COMMENT":15,"OPEN_BLOCK":16,"sexpr":17,"CLOSE":18,"OPEN_INVERSE":19,"OPEN_ENDBLOCK":20,"path":21,"OPEN":22,"OPEN_UNESCAPED":23,"CLOSE_UNESCAPED":24,"OPEN_PARTIAL":25,"partialName":26,"partial_option0":27,"sexpr_repetition0":28,"sexpr_option0":29,"dataName":30,"param":31,"STRING":32,"INTEGER":33,"BOOLEAN":34,"OPEN_SEXPR":35,"CLOSE_SEXPR":36,"hash":37,"hash_repetition_plus0":38,"hashSegment":39,"ID":40,"EQUALS":41,"DATA":42,"pathSegments":43,"SEP":44,"$accept":0,"$end":1},
772
+ terminals_: {2:"error",5:"EOF",14:"CONTENT",15:"COMMENT",16:"OPEN_BLOCK",18:"CLOSE",19:"OPEN_INVERSE",20:"OPEN_ENDBLOCK",22:"OPEN",23:"OPEN_UNESCAPED",24:"CLOSE_UNESCAPED",25:"OPEN_PARTIAL",32:"STRING",33:"INTEGER",34:"BOOLEAN",35:"OPEN_SEXPR",36:"CLOSE_SEXPR",40:"ID",41:"EQUALS",42:"DATA",44:"SEP"},
773
+ productions_: [0,[3,2],[3,1],[6,2],[6,3],[6,2],[6,1],[6,1],[6,0],[4,1],[4,2],[8,3],[8,3],[8,1],[8,1],[8,1],[8,1],[11,3],[9,3],[10,3],[12,3],[12,3],[13,4],[7,2],[17,3],[17,1],[31,1],[31,1],[31,1],[31,1],[31,1],[31,3],[37,1],[39,3],[26,1],[26,1],[26,1],[30,2],[21,1],[43,3],[43,1],[27,0],[27,1],[28,0],[28,2],[29,0],[29,1],[38,1],[38,2]],
682
774
  performAction: function anonymous(yytext,yyleng,yylineno,yy,yystate,$$,_$) {
683
775
 
684
776
  var $0 = $$.length - 1;
685
777
  switch (yystate) {
686
- case 1: return new yy.ProgramNode($$[$0-1]);
778
+ case 1: return new yy.ProgramNode($$[$0-1], this._$);
687
779
  break;
688
- case 2: return new yy.ProgramNode([]);
780
+ case 2: return new yy.ProgramNode([], this._$);
689
781
  break;
690
- case 3:this.$ = new yy.ProgramNode([], $$[$0-1], $$[$0]);
782
+ case 3:this.$ = new yy.ProgramNode([], $$[$0-1], $$[$0], this._$);
691
783
  break;
692
- case 4:this.$ = new yy.ProgramNode($$[$0-2], $$[$0-1], $$[$0]);
784
+ case 4:this.$ = new yy.ProgramNode($$[$0-2], $$[$0-1], $$[$0], this._$);
693
785
  break;
694
- case 5:this.$ = new yy.ProgramNode($$[$0-1], $$[$0], []);
786
+ case 5:this.$ = new yy.ProgramNode($$[$0-1], $$[$0], [], this._$);
695
787
  break;
696
- case 6:this.$ = new yy.ProgramNode($$[$0]);
788
+ case 6:this.$ = new yy.ProgramNode($$[$0], this._$);
697
789
  break;
698
- case 7:this.$ = new yy.ProgramNode([]);
790
+ case 7:this.$ = new yy.ProgramNode([], this._$);
699
791
  break;
700
- case 8:this.$ = new yy.ProgramNode([]);
792
+ case 8:this.$ = new yy.ProgramNode([], this._$);
701
793
  break;
702
794
  case 9:this.$ = [$$[$0]];
703
795
  break;
704
796
  case 10: $$[$0-1].push($$[$0]); this.$ = $$[$0-1];
705
797
  break;
706
- case 11:this.$ = new yy.BlockNode($$[$0-2], $$[$0-1].inverse, $$[$0-1], $$[$0]);
798
+ case 11:this.$ = new yy.BlockNode($$[$0-2], $$[$0-1].inverse, $$[$0-1], $$[$0], this._$);
707
799
  break;
708
- case 12:this.$ = new yy.BlockNode($$[$0-2], $$[$0-1], $$[$0-1].inverse, $$[$0]);
800
+ case 12:this.$ = new yy.BlockNode($$[$0-2], $$[$0-1], $$[$0-1].inverse, $$[$0], this._$);
709
801
  break;
710
802
  case 13:this.$ = $$[$0];
711
803
  break;
712
804
  case 14:this.$ = $$[$0];
713
805
  break;
714
- case 15:this.$ = new yy.ContentNode($$[$0]);
806
+ case 15:this.$ = new yy.ContentNode($$[$0], this._$);
715
807
  break;
716
- case 16:this.$ = new yy.CommentNode($$[$0]);
808
+ case 16:this.$ = new yy.CommentNode($$[$0], this._$);
717
809
  break;
718
- case 17:this.$ = new yy.MustacheNode($$[$0-1][0], $$[$0-1][1], $$[$0-2], stripFlags($$[$0-2], $$[$0]));
810
+ case 17:this.$ = new yy.MustacheNode($$[$0-1], null, $$[$0-2], stripFlags($$[$0-2], $$[$0]), this._$);
719
811
  break;
720
- case 18:this.$ = new yy.MustacheNode($$[$0-1][0], $$[$0-1][1], $$[$0-2], stripFlags($$[$0-2], $$[$0]));
812
+ case 18:this.$ = new yy.MustacheNode($$[$0-1], null, $$[$0-2], stripFlags($$[$0-2], $$[$0]), this._$);
721
813
  break;
722
814
  case 19:this.$ = {path: $$[$0-1], strip: stripFlags($$[$0-2], $$[$0])};
723
815
  break;
724
- case 20:this.$ = new yy.MustacheNode($$[$0-1][0], $$[$0-1][1], $$[$0-2], stripFlags($$[$0-2], $$[$0]));
816
+ case 20:this.$ = new yy.MustacheNode($$[$0-1], null, $$[$0-2], stripFlags($$[$0-2], $$[$0]), this._$);
725
817
  break;
726
- case 21:this.$ = new yy.MustacheNode($$[$0-1][0], $$[$0-1][1], $$[$0-2], stripFlags($$[$0-2], $$[$0]));
818
+ case 21:this.$ = new yy.MustacheNode($$[$0-1], null, $$[$0-2], stripFlags($$[$0-2], $$[$0]), this._$);
727
819
  break;
728
- case 22:this.$ = new yy.PartialNode($$[$0-2], $$[$0-1], stripFlags($$[$0-3], $$[$0]));
820
+ case 22:this.$ = new yy.PartialNode($$[$0-2], $$[$0-1], stripFlags($$[$0-3], $$[$0]), this._$);
729
821
  break;
730
822
  case 23:this.$ = stripFlags($$[$0-1], $$[$0]);
731
823
  break;
732
- case 24:this.$ = [[$$[$0-2]].concat($$[$0-1]), $$[$0]];
824
+ case 24:this.$ = new yy.SexprNode([$$[$0-2]].concat($$[$0-1]), $$[$0], this._$);
733
825
  break;
734
- case 25:this.$ = [[$$[$0]], null];
826
+ case 25:this.$ = new yy.SexprNode([$$[$0]], null, this._$);
735
827
  break;
736
828
  case 26:this.$ = $$[$0];
737
829
  break;
738
- case 27:this.$ = new yy.StringNode($$[$0]);
830
+ case 27:this.$ = new yy.StringNode($$[$0], this._$);
739
831
  break;
740
- case 28:this.$ = new yy.IntegerNode($$[$0]);
832
+ case 28:this.$ = new yy.IntegerNode($$[$0], this._$);
741
833
  break;
742
- case 29:this.$ = new yy.BooleanNode($$[$0]);
834
+ case 29:this.$ = new yy.BooleanNode($$[$0], this._$);
743
835
  break;
744
836
  case 30:this.$ = $$[$0];
745
837
  break;
746
- case 31:this.$ = new yy.HashNode($$[$0]);
838
+ case 31:$$[$0-1].isHelper = true; this.$ = $$[$0-1];
747
839
  break;
748
- case 32:this.$ = [$$[$0-2], $$[$0]];
840
+ case 32:this.$ = new yy.HashNode($$[$0], this._$);
749
841
  break;
750
- case 33:this.$ = new yy.PartialNameNode($$[$0]);
842
+ case 33:this.$ = [$$[$0-2], $$[$0]];
751
843
  break;
752
- case 34:this.$ = new yy.PartialNameNode(new yy.StringNode($$[$0]));
844
+ case 34:this.$ = new yy.PartialNameNode($$[$0], this._$);
753
845
  break;
754
- case 35:this.$ = new yy.PartialNameNode(new yy.IntegerNode($$[$0]));
846
+ case 35:this.$ = new yy.PartialNameNode(new yy.StringNode($$[$0], this._$), this._$);
755
847
  break;
756
- case 36:this.$ = new yy.DataNode($$[$0]);
848
+ case 36:this.$ = new yy.PartialNameNode(new yy.IntegerNode($$[$0], this._$));
757
849
  break;
758
- case 37:this.$ = new yy.IdNode($$[$0]);
850
+ case 37:this.$ = new yy.DataNode($$[$0], this._$);
759
851
  break;
760
- case 38: $$[$0-2].push({part: $$[$0], separator: $$[$0-1]}); this.$ = $$[$0-2];
852
+ case 38:this.$ = new yy.IdNode($$[$0], this._$);
761
853
  break;
762
- case 39:this.$ = [{part: $$[$0]}];
854
+ case 39: $$[$0-2].push({part: $$[$0], separator: $$[$0-1]}); this.$ = $$[$0-2];
763
855
  break;
764
- case 42:this.$ = [];
856
+ case 40:this.$ = [{part: $$[$0]}];
765
857
  break;
766
- case 43:$$[$0-1].push($$[$0]);
858
+ case 43:this.$ = [];
767
859
  break;
768
- case 46:this.$ = [$$[$0]];
860
+ case 44:$$[$0-1].push($$[$0]);
769
861
  break;
770
- case 47:$$[$0-1].push($$[$0]);
862
+ case 47:this.$ = [$$[$0]];
863
+ break;
864
+ case 48:$$[$0-1].push($$[$0]);
771
865
  break;
772
866
  }
773
867
  },
774
- table: [{3:1,4:2,5:[1,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],25:[1,15]},{1:[3]},{5:[1,16],8:17,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],25:[1,15]},{1:[2,2]},{5:[2,9],14:[2,9],15:[2,9],16:[2,9],19:[2,9],20:[2,9],22:[2,9],23:[2,9],25:[2,9]},{4:20,6:18,7:19,8:4,9:5,11:6,12:7,13:8,14:[1,9],15:[1,10],16:[1,12],19:[1,21],20:[2,8],22:[1,13],23:[1,14],25:[1,15]},{4:20,6:22,7:19,8:4,9:5,11:6,12:7,13:8,14:[1,9],15:[1,10],16:[1,12],19:[1,21],20:[2,8],22:[1,13],23:[1,14],25:[1,15]},{5:[2,13],14:[2,13],15:[2,13],16:[2,13],19:[2,13],20:[2,13],22:[2,13],23:[2,13],25:[2,13]},{5:[2,14],14:[2,14],15:[2,14],16:[2,14],19:[2,14],20:[2,14],22:[2,14],23:[2,14],25:[2,14]},{5:[2,15],14:[2,15],15:[2,15],16:[2,15],19:[2,15],20:[2,15],22:[2,15],23:[2,15],25:[2,15]},{5:[2,16],14:[2,16],15:[2,16],16:[2,16],19:[2,16],20:[2,16],22:[2,16],23:[2,16],25:[2,16]},{17:23,21:24,30:25,38:[1,28],40:[1,27],41:26},{17:29,21:24,30:25,38:[1,28],40:[1,27],41:26},{17:30,21:24,30:25,38:[1,28],40:[1,27],41:26},{17:31,21:24,30:25,38:[1,28],40:[1,27],41:26},{21:33,26:32,32:[1,34],33:[1,35],38:[1,28],41:26},{1:[2,1]},{5:[2,10],14:[2,10],15:[2,10],16:[2,10],19:[2,10],20:[2,10],22:[2,10],23:[2,10],25:[2,10]},{10:36,20:[1,37]},{4:38,8:4,9:5,11:6,12:7,13:8,14:[1,9],15:[1,10],16:[1,12],19:[1,11],20:[2,7],22:[1,13],23:[1,14],25:[1,15]},{7:39,8:17,9:5,11:6,12:7,13:8,14:[1,9],15:[1,10],16:[1,12],19:[1,21],20:[2,6],22:[1,13],23:[1,14],25:[1,15]},{17:23,18:[1,40],21:24,30:25,38:[1,28],40:[1,27],41:26},{10:41,20:[1,37]},{18:[1,42]},{18:[2,42],24:[2,42],28:43,32:[2,42],33:[2,42],34:[2,42],38:[2,42],40:[2,42]},{18:[2,25],24:[2,25]},{18:[2,37],24:[2,37],32:[2,37],33:[2,37],34:[2,37],38:[2,37],40:[2,37],42:[1,44]},{21:45,38:[1,28],41:26},{18:[2,39],24:[2,39],32:[2,39],33:[2,39],34:[2,39],38:[2,39],40:[2,39],42:[2,39]},{18:[1,46]},{18:[1,47]},{24:[1,48]},{18:[2,40],21:50,27:49,38:[1,28],41:26},{18:[2,33],38:[2,33]},{18:[2,34],38:[2,34]},{18:[2,35],38:[2,35]},{5:[2,11],14:[2,11],15:[2,11],16:[2,11],19:[2,11],20:[2,11],22:[2,11],23:[2,11],25:[2,11]},{21:51,38:[1,28],41:26},{8:17,9:5,11:6,12:7,13:8,14:[1,9],15:[1,10],16:[1,12],19:[1,11],20:[2,3],22:[1,13],23:[1,14],25:[1,15]},{4:52,8:4,9:5,11:6,12:7,13:8,14:[1,9],15:[1,10],16:[1,12],19:[1,11],20:[2,5],22:[1,13],23:[1,14],25:[1,15]},{14:[2,23],15:[2,23],16:[2,23],19:[2,23],20:[2,23],22:[2,23],23:[2,23],25:[2,23]},{5:[2,12],14:[2,12],15:[2,12],16:[2,12],19:[2,12],20:[2,12],22:[2,12],23:[2,12],25:[2,12]},{14:[2,18],15:[2,18],16:[2,18],19:[2,18],20:[2,18],22:[2,18],23:[2,18],25:[2,18]},{18:[2,44],21:56,24:[2,44],29:53,30:60,31:54,32:[1,57],33:[1,58],34:[1,59],35:55,36:61,37:62,38:[1,63],40:[1,27],41:26},{38:[1,64]},{18:[2,36],24:[2,36],32:[2,36],33:[2,36],34:[2,36],38:[2,36],40:[2,36]},{14:[2,17],15:[2,17],16:[2,17],19:[2,17],20:[2,17],22:[2,17],23:[2,17],25:[2,17]},{5:[2,20],14:[2,20],15:[2,20],16:[2,20],19:[2,20],20:[2,20],22:[2,20],23:[2,20],25:[2,20]},{5:[2,21],14:[2,21],15:[2,21],16:[2,21],19:[2,21],20:[2,21],22:[2,21],23:[2,21],25:[2,21]},{18:[1,65]},{18:[2,41]},{18:[1,66]},{8:17,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],25:[1,15]},{18:[2,24],24:[2,24]},{18:[2,43],24:[2,43],32:[2,43],33:[2,43],34:[2,43],38:[2,43],40:[2,43]},{18:[2,45],24:[2,45]},{18:[2,26],24:[2,26],32:[2,26],33:[2,26],34:[2,26],38:[2,26],40:[2,26]},{18:[2,27],24:[2,27],32:[2,27],33:[2,27],34:[2,27],38:[2,27],40:[2,27]},{18:[2,28],24:[2,28],32:[2,28],33:[2,28],34:[2,28],38:[2,28],40:[2,28]},{18:[2,29],24:[2,29],32:[2,29],33:[2,29],34:[2,29],38:[2,29],40:[2,29]},{18:[2,30],24:[2,30],32:[2,30],33:[2,30],34:[2,30],38:[2,30],40:[2,30]},{18:[2,31],24:[2,31],37:67,38:[1,68]},{18:[2,46],24:[2,46],38:[2,46]},{18:[2,39],24:[2,39],32:[2,39],33:[2,39],34:[2,39],38:[2,39],39:[1,69],40:[2,39],42:[2,39]},{18:[2,38],24:[2,38],32:[2,38],33:[2,38],34:[2,38],38:[2,38],40:[2,38],42:[2,38]},{5:[2,22],14:[2,22],15:[2,22],16:[2,22],19:[2,22],20:[2,22],22:[2,22],23:[2,22],25:[2,22]},{5:[2,19],14:[2,19],15:[2,19],16:[2,19],19:[2,19],20:[2,19],22:[2,19],23:[2,19],25:[2,19]},{18:[2,47],24:[2,47],38:[2,47]},{39:[1,69]},{21:56,30:60,31:70,32:[1,57],33:[1,58],34:[1,59],38:[1,28],40:[1,27],41:26},{18:[2,32],24:[2,32],38:[2,32]}],
775
- defaultActions: {3:[2,2],16:[2,1],50:[2,41]},
868
+ table: [{3:1,4:2,5:[1,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],25:[1,15]},{1:[3]},{5:[1,16],8:17,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],25:[1,15]},{1:[2,2]},{5:[2,9],14:[2,9],15:[2,9],16:[2,9],19:[2,9],20:[2,9],22:[2,9],23:[2,9],25:[2,9]},{4:20,6:18,7:19,8:4,9:5,11:6,12:7,13:8,14:[1,9],15:[1,10],16:[1,12],19:[1,21],20:[2,8],22:[1,13],23:[1,14],25:[1,15]},{4:20,6:22,7:19,8:4,9:5,11:6,12:7,13:8,14:[1,9],15:[1,10],16:[1,12],19:[1,21],20:[2,8],22:[1,13],23:[1,14],25:[1,15]},{5:[2,13],14:[2,13],15:[2,13],16:[2,13],19:[2,13],20:[2,13],22:[2,13],23:[2,13],25:[2,13]},{5:[2,14],14:[2,14],15:[2,14],16:[2,14],19:[2,14],20:[2,14],22:[2,14],23:[2,14],25:[2,14]},{5:[2,15],14:[2,15],15:[2,15],16:[2,15],19:[2,15],20:[2,15],22:[2,15],23:[2,15],25:[2,15]},{5:[2,16],14:[2,16],15:[2,16],16:[2,16],19:[2,16],20:[2,16],22:[2,16],23:[2,16],25:[2,16]},{17:23,21:24,30:25,40:[1,28],42:[1,27],43:26},{17:29,21:24,30:25,40:[1,28],42:[1,27],43:26},{17:30,21:24,30:25,40:[1,28],42:[1,27],43:26},{17:31,21:24,30:25,40:[1,28],42:[1,27],43:26},{21:33,26:32,32:[1,34],33:[1,35],40:[1,28],43:26},{1:[2,1]},{5:[2,10],14:[2,10],15:[2,10],16:[2,10],19:[2,10],20:[2,10],22:[2,10],23:[2,10],25:[2,10]},{10:36,20:[1,37]},{4:38,8:4,9:5,11:6,12:7,13:8,14:[1,9],15:[1,10],16:[1,12],19:[1,11],20:[2,7],22:[1,13],23:[1,14],25:[1,15]},{7:39,8:17,9:5,11:6,12:7,13:8,14:[1,9],15:[1,10],16:[1,12],19:[1,21],20:[2,6],22:[1,13],23:[1,14],25:[1,15]},{17:23,18:[1,40],21:24,30:25,40:[1,28],42:[1,27],43:26},{10:41,20:[1,37]},{18:[1,42]},{18:[2,43],24:[2,43],28:43,32:[2,43],33:[2,43],34:[2,43],35:[2,43],36:[2,43],40:[2,43],42:[2,43]},{18:[2,25],24:[2,25],36:[2,25]},{18:[2,38],24:[2,38],32:[2,38],33:[2,38],34:[2,38],35:[2,38],36:[2,38],40:[2,38],42:[2,38],44:[1,44]},{21:45,40:[1,28],43:26},{18:[2,40],24:[2,40],32:[2,40],33:[2,40],34:[2,40],35:[2,40],36:[2,40],40:[2,40],42:[2,40],44:[2,40]},{18:[1,46]},{18:[1,47]},{24:[1,48]},{18:[2,41],21:50,27:49,40:[1,28],43:26},{18:[2,34],40:[2,34]},{18:[2,35],40:[2,35]},{18:[2,36],40:[2,36]},{5:[2,11],14:[2,11],15:[2,11],16:[2,11],19:[2,11],20:[2,11],22:[2,11],23:[2,11],25:[2,11]},{21:51,40:[1,28],43:26},{8:17,9:5,11:6,12:7,13:8,14:[1,9],15:[1,10],16:[1,12],19:[1,11],20:[2,3],22:[1,13],23:[1,14],25:[1,15]},{4:52,8:4,9:5,11:6,12:7,13:8,14:[1,9],15:[1,10],16:[1,12],19:[1,11],20:[2,5],22:[1,13],23:[1,14],25:[1,15]},{14:[2,23],15:[2,23],16:[2,23],19:[2,23],20:[2,23],22:[2,23],23:[2,23],25:[2,23]},{5:[2,12],14:[2,12],15:[2,12],16:[2,12],19:[2,12],20:[2,12],22:[2,12],23:[2,12],25:[2,12]},{14:[2,18],15:[2,18],16:[2,18],19:[2,18],20:[2,18],22:[2,18],23:[2,18],25:[2,18]},{18:[2,45],21:56,24:[2,45],29:53,30:60,31:54,32:[1,57],33:[1,58],34:[1,59],35:[1,61],36:[2,45],37:55,38:62,39:63,40:[1,64],42:[1,27],43:26},{40:[1,65]},{18:[2,37],24:[2,37],32:[2,37],33:[2,37],34:[2,37],35:[2,37],36:[2,37],40:[2,37],42:[2,37]},{14:[2,17],15:[2,17],16:[2,17],19:[2,17],20:[2,17],22:[2,17],23:[2,17],25:[2,17]},{5:[2,20],14:[2,20],15:[2,20],16:[2,20],19:[2,20],20:[2,20],22:[2,20],23:[2,20],25:[2,20]},{5:[2,21],14:[2,21],15:[2,21],16:[2,21],19:[2,21],20:[2,21],22:[2,21],23:[2,21],25:[2,21]},{18:[1,66]},{18:[2,42]},{18:[1,67]},{8:17,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],25:[1,15]},{18:[2,24],24:[2,24],36:[2,24]},{18:[2,44],24:[2,44],32:[2,44],33:[2,44],34:[2,44],35:[2,44],36:[2,44],40:[2,44],42:[2,44]},{18:[2,46],24:[2,46],36:[2,46]},{18:[2,26],24:[2,26],32:[2,26],33:[2,26],34:[2,26],35:[2,26],36:[2,26],40:[2,26],42:[2,26]},{18:[2,27],24:[2,27],32:[2,27],33:[2,27],34:[2,27],35:[2,27],36:[2,27],40:[2,27],42:[2,27]},{18:[2,28],24:[2,28],32:[2,28],33:[2,28],34:[2,28],35:[2,28],36:[2,28],40:[2,28],42:[2,28]},{18:[2,29],24:[2,29],32:[2,29],33:[2,29],34:[2,29],35:[2,29],36:[2,29],40:[2,29],42:[2,29]},{18:[2,30],24:[2,30],32:[2,30],33:[2,30],34:[2,30],35:[2,30],36:[2,30],40:[2,30],42:[2,30]},{17:68,21:24,30:25,40:[1,28],42:[1,27],43:26},{18:[2,32],24:[2,32],36:[2,32],39:69,40:[1,70]},{18:[2,47],24:[2,47],36:[2,47],40:[2,47]},{18:[2,40],24:[2,40],32:[2,40],33:[2,40],34:[2,40],35:[2,40],36:[2,40],40:[2,40],41:[1,71],42:[2,40],44:[2,40]},{18:[2,39],24:[2,39],32:[2,39],33:[2,39],34:[2,39],35:[2,39],36:[2,39],40:[2,39],42:[2,39],44:[2,39]},{5:[2,22],14:[2,22],15:[2,22],16:[2,22],19:[2,22],20:[2,22],22:[2,22],23:[2,22],25:[2,22]},{5:[2,19],14:[2,19],15:[2,19],16:[2,19],19:[2,19],20:[2,19],22:[2,19],23:[2,19],25:[2,19]},{36:[1,72]},{18:[2,48],24:[2,48],36:[2,48],40:[2,48]},{41:[1,71]},{21:56,30:60,31:73,32:[1,57],33:[1,58],34:[1,59],35:[1,61],40:[1,28],42:[1,27],43:26},{18:[2,31],24:[2,31],32:[2,31],33:[2,31],34:[2,31],35:[2,31],36:[2,31],40:[2,31],42:[2,31]},{18:[2,33],24:[2,33],36:[2,33],40:[2,33]}],
869
+ defaultActions: {3:[2,2],16:[2,1],50:[2,42]},
776
870
  parseError: function parseError(str, hash) {
777
871
  throw new Error(str);
778
872
  },
@@ -884,8 +978,8 @@ var __module9__ = (function() {
884
978
 
885
979
  function stripFlags(open, close) {
886
980
  return {
887
- left: open[2] === '~',
888
- right: close[0] === '~' || close[1] === '~'
981
+ left: open.charAt(2) === '~',
982
+ right: close.charAt(0) === '~' || close.charAt(1) === '~'
889
983
  };
890
984
  }
891
985
 
@@ -1084,76 +1178,80 @@ var __module9__ = (function() {
1084
1178
  case 1:return 14;
1085
1179
  break;
1086
1180
  case 2:
1087
- if(yy_.yytext.slice(-1) !== "\\") this.popState();
1088
- if(yy_.yytext.slice(-1) === "\\") strip(0,1);
1181
+ this.popState();
1089
1182
  return 14;
1090
1183
 
1091
1184
  break;
1092
1185
  case 3:strip(0,4); this.popState(); return 15;
1093
1186
  break;
1094
- case 4:return 25;
1187
+ case 4:return 35;
1188
+ break;
1189
+ case 5:return 36;
1095
1190
  break;
1096
- case 5:return 16;
1191
+ case 6:return 25;
1097
1192
  break;
1098
- case 6:return 20;
1193
+ case 7:return 16;
1099
1194
  break;
1100
- case 7:return 19;
1195
+ case 8:return 20;
1101
1196
  break;
1102
- case 8:return 19;
1197
+ case 9:return 19;
1103
1198
  break;
1104
- case 9:return 23;
1199
+ case 10:return 19;
1105
1200
  break;
1106
- case 10:return 22;
1201
+ case 11:return 23;
1107
1202
  break;
1108
- case 11:this.popState(); this.begin('com');
1203
+ case 12:return 22;
1109
1204
  break;
1110
- case 12:strip(3,5); this.popState(); return 15;
1205
+ case 13:this.popState(); this.begin('com');
1111
1206
  break;
1112
- case 13:return 22;
1207
+ case 14:strip(3,5); this.popState(); return 15;
1113
1208
  break;
1114
- case 14:return 39;
1209
+ case 15:return 22;
1115
1210
  break;
1116
- case 15:return 38;
1211
+ case 16:return 41;
1117
1212
  break;
1118
- case 16:return 38;
1213
+ case 17:return 40;
1119
1214
  break;
1120
- case 17:return 42;
1215
+ case 18:return 40;
1121
1216
  break;
1122
- case 18:/*ignore whitespace*/
1217
+ case 19:return 44;
1123
1218
  break;
1124
- case 19:this.popState(); return 24;
1219
+ case 20:// ignore whitespace
1125
1220
  break;
1126
- case 20:this.popState(); return 18;
1221
+ case 21:this.popState(); return 24;
1127
1222
  break;
1128
- case 21:yy_.yytext = strip(1,2).replace(/\\"/g,'"'); return 32;
1223
+ case 22:this.popState(); return 18;
1129
1224
  break;
1130
- case 22:yy_.yytext = strip(1,2).replace(/\\'/g,"'"); return 32;
1225
+ case 23:yy_.yytext = strip(1,2).replace(/\\"/g,'"'); return 32;
1131
1226
  break;
1132
- case 23:return 40;
1227
+ case 24:yy_.yytext = strip(1,2).replace(/\\'/g,"'"); return 32;
1133
1228
  break;
1134
- case 24:return 34;
1229
+ case 25:return 42;
1135
1230
  break;
1136
- case 25:return 34;
1231
+ case 26:return 34;
1137
1232
  break;
1138
- case 26:return 33;
1233
+ case 27:return 34;
1139
1234
  break;
1140
- case 27:return 38;
1235
+ case 28:return 33;
1141
1236
  break;
1142
- case 28:yy_.yytext = strip(1,2); return 38;
1237
+ case 29:return 40;
1143
1238
  break;
1144
- case 29:return 'INVALID';
1239
+ case 30:yy_.yytext = strip(1,2); return 40;
1145
1240
  break;
1146
- case 30:return 5;
1241
+ case 31:return 'INVALID';
1242
+ break;
1243
+ case 32:return 5;
1147
1244
  break;
1148
1245
  }
1149
1246
  };
1150
- lexer.rules = [/^(?:[^\x00]*?(?=(\{\{)))/,/^(?:[^\x00]+)/,/^(?:[^\x00]{2,}?(?=(\{\{|$)))/,/^(?:[\s\S]*?--\}\})/,/^(?:\{\{(~)?>)/,/^(?:\{\{(~)?#)/,/^(?:\{\{(~)?\/)/,/^(?:\{\{(~)?\^)/,/^(?:\{\{(~)?\s*else\b)/,/^(?:\{\{(~)?\{)/,/^(?:\{\{(~)?&)/,/^(?:\{\{!--)/,/^(?:\{\{![\s\S]*?\}\})/,/^(?:\{\{(~)?)/,/^(?:=)/,/^(?:\.\.)/,/^(?:\.(?=([=~}\s\/.])))/,/^(?:[\/.])/,/^(?:\s+)/,/^(?:\}(~)?\}\})/,/^(?:(~)?\}\})/,/^(?:"(\\["]|[^"])*")/,/^(?:'(\\[']|[^'])*')/,/^(?:@)/,/^(?:true(?=([~}\s])))/,/^(?:false(?=([~}\s])))/,/^(?:-?[0-9]+(?=([~}\s])))/,/^(?:([^\s!"#%-,\.\/;->@\[-\^`\{-~]+(?=([=~}\s\/.]))))/,/^(?:\[[^\]]*\])/,/^(?:.)/,/^(?:$)/];
1151
- lexer.conditions = {"mu":{"rules":[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,29,30],"inclusive":false},"emu":{"rules":[2],"inclusive":false},"com":{"rules":[3],"inclusive":false},"INITIAL":{"rules":[0,1,30],"inclusive":true}};
1247
+ lexer.rules = [/^(?:[^\x00]*?(?=(\{\{)))/,/^(?:[^\x00]+)/,/^(?:[^\x00]{2,}?(?=(\{\{|\\\{\{|\\\\\{\{|$)))/,/^(?:[\s\S]*?--\}\})/,/^(?:\()/,/^(?:\))/,/^(?:\{\{(~)?>)/,/^(?:\{\{(~)?#)/,/^(?:\{\{(~)?\/)/,/^(?:\{\{(~)?\^)/,/^(?:\{\{(~)?\s*else\b)/,/^(?:\{\{(~)?\{)/,/^(?:\{\{(~)?&)/,/^(?:\{\{!--)/,/^(?:\{\{![\s\S]*?\}\})/,/^(?:\{\{(~)?)/,/^(?:=)/,/^(?:\.\.)/,/^(?:\.(?=([=~}\s\/.)])))/,/^(?:[\/.])/,/^(?:\s+)/,/^(?:\}(~)?\}\})/,/^(?:(~)?\}\})/,/^(?:"(\\["]|[^"])*")/,/^(?:'(\\[']|[^'])*')/,/^(?:@)/,/^(?:true(?=([~}\s)])))/,/^(?:false(?=([~}\s)])))/,/^(?:-?[0-9]+(?=([~}\s)])))/,/^(?:([^\s!"#%-,\.\/;->@\[-\^`\{-~]+(?=([=~}\s\/.)]))))/,/^(?:\[[^\]]*\])/,/^(?:.)/,/^(?:$)/];
1248
+ lexer.conditions = {"mu":{"rules":[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,29,30,31,32],"inclusive":false},"emu":{"rules":[2],"inclusive":false},"com":{"rules":[3],"inclusive":false},"INITIAL":{"rules":[0,1,32],"inclusive":true}};
1152
1249
  return lexer;})()
1153
1250
  parser.lexer = lexer;
1154
1251
  function Parser () { this.yy = {}; }Parser.prototype = parser;parser.Parser = Parser;
1155
1252
  return new Parser;
1156
1253
  })();__exports__ = handlebars;
1254
+ /* jshint ignore:end */
1157
1255
  return __exports__;
1158
1256
  })();
1159
1257
 
@@ -1178,1383 +1276,1434 @@ var __module8__ = (function(__dependency1__, __dependency2__) {
1178
1276
  return __exports__;
1179
1277
  })(__module9__, __module7__);
1180
1278
 
1181
- // handlebars/compiler/javascript-compiler.js
1182
- var __module11__ = (function(__dependency1__) {
1279
+ // handlebars/compiler/compiler.js
1280
+ var __module10__ = (function(__dependency1__) {
1183
1281
  "use strict";
1184
- var __exports__;
1185
- var COMPILER_REVISION = __dependency1__.COMPILER_REVISION;
1186
- var REVISION_CHANGES = __dependency1__.REVISION_CHANGES;
1187
- var log = __dependency1__.log;
1282
+ var __exports__ = {};
1283
+ var Exception = __dependency1__;
1188
1284
 
1189
- function Literal(value) {
1190
- this.value = value;
1191
- }
1285
+ function Compiler() {}
1192
1286
 
1193
- function JavaScriptCompiler() {}
1287
+ __exports__.Compiler = Compiler;// the foundHelper register will disambiguate helper lookup from finding a
1288
+ // function in a context. This is necessary for mustache compatibility, which
1289
+ // requires that context functions in blocks are evaluated by blockHelperMissing,
1290
+ // and then proceed as if the resulting value was provided to blockHelperMissing.
1194
1291
 
1195
- JavaScriptCompiler.prototype = {
1196
- // PUBLIC API: You can override these methods in a subclass to provide
1197
- // alternative compiled forms for name lookup and buffering semantics
1198
- nameLookup: function(parent, name /* , type*/) {
1199
- var wrap,
1200
- ret;
1201
- if (parent.indexOf('depth') === 0) {
1202
- wrap = true;
1203
- }
1292
+ Compiler.prototype = {
1293
+ compiler: Compiler,
1204
1294
 
1205
- if (/^[0-9]+$/.test(name)) {
1206
- ret = parent + "[" + name + "]";
1207
- } else if (JavaScriptCompiler.isValidJavaScriptVariableName(name)) {
1208
- ret = parent + "." + name;
1295
+ disassemble: function() {
1296
+ var opcodes = this.opcodes, opcode, out = [], params, param;
1297
+
1298
+ for (var i=0, l=opcodes.length; i<l; i++) {
1299
+ opcode = opcodes[i];
1300
+
1301
+ if (opcode.opcode === 'DECLARE') {
1302
+ out.push("DECLARE " + opcode.name + "=" + opcode.value);
1303
+ } else {
1304
+ params = [];
1305
+ for (var j=0; j<opcode.args.length; j++) {
1306
+ param = opcode.args[j];
1307
+ if (typeof param === "string") {
1308
+ param = "\"" + param.replace("\n", "\\n") + "\"";
1309
+ }
1310
+ params.push(param);
1311
+ }
1312
+ out.push(opcode.opcode + " " + params.join(" "));
1313
+ }
1209
1314
  }
1210
- else {
1211
- ret = parent + "['" + name + "']";
1315
+
1316
+ return out.join("\n");
1317
+ },
1318
+
1319
+ equals: function(other) {
1320
+ var len = this.opcodes.length;
1321
+ if (other.opcodes.length !== len) {
1322
+ return false;
1212
1323
  }
1213
1324
 
1214
- if (wrap) {
1215
- return '(' + parent + ' && ' + ret + ')';
1216
- } else {
1217
- return ret;
1325
+ for (var i = 0; i < len; i++) {
1326
+ var opcode = this.opcodes[i],
1327
+ otherOpcode = other.opcodes[i];
1328
+ if (opcode.opcode !== otherOpcode.opcode || opcode.args.length !== otherOpcode.args.length) {
1329
+ return false;
1330
+ }
1331
+ for (var j = 0; j < opcode.args.length; j++) {
1332
+ if (opcode.args[j] !== otherOpcode.args[j]) {
1333
+ return false;
1334
+ }
1335
+ }
1218
1336
  }
1219
- },
1220
1337
 
1221
- appendToBuffer: function(string) {
1222
- if (this.environment.isSimple) {
1223
- return "return " + string + ";";
1224
- } else {
1225
- return {
1226
- appendToBuffer: true,
1227
- content: string,
1228
- toString: function() { return "buffer += " + string + ";"; }
1229
- };
1338
+ len = this.children.length;
1339
+ if (other.children.length !== len) {
1340
+ return false;
1341
+ }
1342
+ for (i = 0; i < len; i++) {
1343
+ if (!this.children[i].equals(other.children[i])) {
1344
+ return false;
1345
+ }
1230
1346
  }
1231
- },
1232
1347
 
1233
- initializeBuffer: function() {
1234
- return this.quotedString("");
1348
+ return true;
1235
1349
  },
1236
1350
 
1237
- namespace: "Handlebars",
1238
- // END PUBLIC API
1239
-
1240
- compile: function(environment, options, context, asObject) {
1241
- this.environment = environment;
1242
- this.options = options || {};
1351
+ guid: 0,
1243
1352
 
1244
- log('debug', this.environment.disassemble() + "\n\n");
1353
+ compile: function(program, options) {
1354
+ this.opcodes = [];
1355
+ this.children = [];
1356
+ this.depths = {list: []};
1357
+ this.options = options;
1245
1358
 
1246
- this.name = this.environment.name;
1247
- this.isChild = !!context;
1248
- this.context = context || {
1249
- programs: [],
1250
- environments: [],
1251
- aliases: { }
1359
+ // These changes will propagate to the other compiler components
1360
+ var knownHelpers = this.options.knownHelpers;
1361
+ this.options.knownHelpers = {
1362
+ 'helperMissing': true,
1363
+ 'blockHelperMissing': true,
1364
+ 'each': true,
1365
+ 'if': true,
1366
+ 'unless': true,
1367
+ 'with': true,
1368
+ 'log': true
1252
1369
  };
1370
+ if (knownHelpers) {
1371
+ for (var name in knownHelpers) {
1372
+ this.options.knownHelpers[name] = knownHelpers[name];
1373
+ }
1374
+ }
1253
1375
 
1254
- this.preamble();
1255
-
1256
- this.stackSlot = 0;
1257
- this.stackVars = [];
1258
- this.registers = { list: [] };
1259
- this.compileStack = [];
1260
- this.inlineStack = [];
1376
+ return this.accept(program);
1377
+ },
1261
1378
 
1262
- this.compileChildren(environment, options);
1379
+ accept: function(node) {
1380
+ var strip = node.strip || {},
1381
+ ret;
1382
+ if (strip.left) {
1383
+ this.opcode('strip');
1384
+ }
1263
1385
 
1264
- var opcodes = environment.opcodes, opcode;
1386
+ ret = this[node.type](node);
1265
1387
 
1266
- this.i = 0;
1388
+ if (strip.right) {
1389
+ this.opcode('strip');
1390
+ }
1267
1391
 
1268
- for(var l=opcodes.length; this.i<l; this.i++) {
1269
- opcode = opcodes[this.i];
1392
+ return ret;
1393
+ },
1270
1394
 
1271
- if(opcode.opcode === 'DECLARE') {
1272
- this[opcode.name] = opcode.value;
1273
- } else {
1274
- this[opcode.opcode].apply(this, opcode.args);
1275
- }
1395
+ program: function(program) {
1396
+ var statements = program.statements;
1276
1397
 
1277
- // Reset the stripNext flag if it was not set by this operation.
1278
- if (opcode.opcode !== this.stripNext) {
1279
- this.stripNext = false;
1280
- }
1398
+ for(var i=0, l=statements.length; i<l; i++) {
1399
+ this.accept(statements[i]);
1281
1400
  }
1401
+ this.isSimple = l === 1;
1282
1402
 
1283
- // Flush any trailing content that might be pending.
1284
- this.pushSource('');
1403
+ this.depths.list = this.depths.list.sort(function(a, b) {
1404
+ return a - b;
1405
+ });
1285
1406
 
1286
- return this.createFunctionContext(asObject);
1407
+ return this;
1287
1408
  },
1288
1409
 
1289
- preamble: function() {
1290
- var out = [];
1291
-
1292
- if (!this.isChild) {
1293
- var namespace = this.namespace;
1410
+ compileProgram: function(program) {
1411
+ var result = new this.compiler().compile(program, this.options);
1412
+ var guid = this.guid++, depth;
1294
1413
 
1295
- var copies = "helpers = this.merge(helpers, " + namespace + ".helpers);";
1296
- if (this.environment.usePartial) { copies = copies + " partials = this.merge(partials, " + namespace + ".partials);"; }
1297
- if (this.options.data) { copies = copies + " data = data || {};"; }
1298
- out.push(copies);
1299
- } else {
1300
- out.push('');
1301
- }
1414
+ this.usePartial = this.usePartial || result.usePartial;
1302
1415
 
1303
- if (!this.environment.isSimple) {
1304
- out.push(", buffer = " + this.initializeBuffer());
1305
- } else {
1306
- out.push("");
1416
+ this.children[guid] = result;
1417
+
1418
+ for(var i=0, l=result.depths.list.length; i<l; i++) {
1419
+ depth = result.depths.list[i];
1420
+
1421
+ if(depth < 2) { continue; }
1422
+ else { this.addDepth(depth - 1); }
1307
1423
  }
1308
1424
 
1309
- // track the last context pushed into place to allow skipping the
1310
- // getContext opcode when it would be a noop
1311
- this.lastContext = 0;
1312
- this.source = out;
1425
+ return guid;
1313
1426
  },
1314
1427
 
1315
- createFunctionContext: function(asObject) {
1316
- var locals = this.stackVars.concat(this.registers.list);
1428
+ block: function(block) {
1429
+ var mustache = block.mustache,
1430
+ program = block.program,
1431
+ inverse = block.inverse;
1317
1432
 
1318
- if(locals.length > 0) {
1319
- this.source[1] = this.source[1] + ", " + locals.join(", ");
1433
+ if (program) {
1434
+ program = this.compileProgram(program);
1320
1435
  }
1321
1436
 
1322
- // Generate minimizer alias mappings
1323
- if (!this.isChild) {
1324
- for (var alias in this.context.aliases) {
1325
- if (this.context.aliases.hasOwnProperty(alias)) {
1326
- this.source[1] = this.source[1] + ', ' + alias + '=' + this.context.aliases[alias];
1327
- }
1328
- }
1437
+ if (inverse) {
1438
+ inverse = this.compileProgram(inverse);
1329
1439
  }
1330
1440
 
1331
- if (this.source[1]) {
1332
- this.source[1] = "var " + this.source[1].substring(2) + ";";
1333
- }
1441
+ var sexpr = mustache.sexpr;
1442
+ var type = this.classifySexpr(sexpr);
1334
1443
 
1335
- // Merge children
1336
- if (!this.isChild) {
1337
- this.source[1] += '\n' + this.context.programs.join('\n') + '\n';
1338
- }
1444
+ if (type === "helper") {
1445
+ this.helperSexpr(sexpr, program, inverse);
1446
+ } else if (type === "simple") {
1447
+ this.simpleSexpr(sexpr);
1339
1448
 
1340
- if (!this.environment.isSimple) {
1341
- this.pushSource("return buffer;");
1449
+ // now that the simple mustache is resolved, we need to
1450
+ // evaluate it by executing `blockHelperMissing`
1451
+ this.opcode('pushProgram', program);
1452
+ this.opcode('pushProgram', inverse);
1453
+ this.opcode('emptyHash');
1454
+ this.opcode('blockValue');
1455
+ } else {
1456
+ this.ambiguousSexpr(sexpr, program, inverse);
1457
+
1458
+ // now that the simple mustache is resolved, we need to
1459
+ // evaluate it by executing `blockHelperMissing`
1460
+ this.opcode('pushProgram', program);
1461
+ this.opcode('pushProgram', inverse);
1462
+ this.opcode('emptyHash');
1463
+ this.opcode('ambiguousBlockValue');
1342
1464
  }
1343
1465
 
1344
- var params = this.isChild ? ["depth0", "data"] : ["Handlebars", "depth0", "helpers", "partials", "data"];
1466
+ this.opcode('append');
1467
+ },
1345
1468
 
1346
- for(var i=0, l=this.environment.depths.list.length; i<l; i++) {
1347
- params.push("depth" + this.environment.depths.list[i]);
1348
- }
1469
+ hash: function(hash) {
1470
+ var pairs = hash.pairs, pair, val;
1349
1471
 
1350
- // Perform a second pass over the output to merge content when possible
1351
- var source = this.mergeSource();
1472
+ this.opcode('pushHash');
1352
1473
 
1353
- if (!this.isChild) {
1354
- var revision = COMPILER_REVISION,
1355
- versions = REVISION_CHANGES[revision];
1356
- source = "this.compilerInfo = ["+revision+",'"+versions+"'];\n"+source;
1357
- }
1474
+ for(var i=0, l=pairs.length; i<l; i++) {
1475
+ pair = pairs[i];
1476
+ val = pair[1];
1358
1477
 
1359
- if (asObject) {
1360
- params.push(source);
1478
+ if (this.options.stringParams) {
1479
+ if(val.depth) {
1480
+ this.addDepth(val.depth);
1481
+ }
1482
+ this.opcode('getContext', val.depth || 0);
1483
+ this.opcode('pushStringParam', val.stringModeValue, val.type);
1361
1484
 
1362
- return Function.apply(this, params);
1363
- } else {
1364
- var functionSource = 'function ' + (this.name || '') + '(' + params.join(',') + ') {\n ' + source + '}';
1365
- log('debug', functionSource + "\n\n");
1366
- return functionSource;
1367
- }
1368
- },
1369
- mergeSource: function() {
1370
- // WARN: We are not handling the case where buffer is still populated as the source should
1371
- // not have buffer append operations as their final action.
1372
- var source = '',
1373
- buffer;
1374
- for (var i = 0, len = this.source.length; i < len; i++) {
1375
- var line = this.source[i];
1376
- if (line.appendToBuffer) {
1377
- if (buffer) {
1378
- buffer = buffer + '\n + ' + line.content;
1379
- } else {
1380
- buffer = line.content;
1485
+ if (val.type === 'sexpr') {
1486
+ // Subexpressions get evaluated and passed in
1487
+ // in string params mode.
1488
+ this.sexpr(val);
1381
1489
  }
1382
1490
  } else {
1383
- if (buffer) {
1384
- source += 'buffer += ' + buffer + ';\n ';
1385
- buffer = undefined;
1386
- }
1387
- source += line + '\n ';
1491
+ this.accept(val);
1388
1492
  }
1493
+
1494
+ this.opcode('assignToHash', pair[0]);
1389
1495
  }
1390
- return source;
1496
+ this.opcode('popHash');
1391
1497
  },
1392
1498
 
1393
- // [blockValue]
1394
- //
1395
- // On stack, before: hash, inverse, program, value
1396
- // On stack, after: return value of blockHelperMissing
1397
- //
1398
- // The purpose of this opcode is to take a block of the form
1399
- // `{{#foo}}...{{/foo}}`, resolve the value of `foo`, and
1400
- // replace it on the stack with the result of properly
1401
- // invoking blockHelperMissing.
1402
- blockValue: function() {
1403
- this.context.aliases.blockHelperMissing = 'helpers.blockHelperMissing';
1499
+ partial: function(partial) {
1500
+ var partialName = partial.partialName;
1501
+ this.usePartial = true;
1404
1502
 
1405
- var params = ["depth0"];
1406
- this.setupParams(0, params);
1503
+ if(partial.context) {
1504
+ this.ID(partial.context);
1505
+ } else {
1506
+ this.opcode('push', 'depth0');
1507
+ }
1407
1508
 
1408
- this.replaceStack(function(current) {
1409
- params.splice(1, 0, current);
1410
- return "blockHelperMissing.call(" + params.join(", ") + ")";
1411
- });
1509
+ this.opcode('invokePartial', partialName.name);
1510
+ this.opcode('append');
1412
1511
  },
1413
1512
 
1414
- // [ambiguousBlockValue]
1415
- //
1416
- // On stack, before: hash, inverse, program, value
1417
- // Compiler value, before: lastHelper=value of last found helper, if any
1418
- // On stack, after, if no lastHelper: same as [blockValue]
1419
- // On stack, after, if lastHelper: value
1420
- ambiguousBlockValue: function() {
1421
- this.context.aliases.blockHelperMissing = 'helpers.blockHelperMissing';
1513
+ content: function(content) {
1514
+ this.opcode('appendContent', content.string);
1515
+ },
1422
1516
 
1423
- var params = ["depth0"];
1424
- this.setupParams(0, params);
1517
+ mustache: function(mustache) {
1518
+ this.sexpr(mustache.sexpr);
1425
1519
 
1426
- var current = this.topStack();
1427
- params.splice(1, 0, current);
1520
+ if(mustache.escaped && !this.options.noEscape) {
1521
+ this.opcode('appendEscaped');
1522
+ } else {
1523
+ this.opcode('append');
1524
+ }
1525
+ },
1428
1526
 
1429
- // Use the options value generated from the invocation
1430
- params[params.length-1] = 'options';
1527
+ ambiguousSexpr: function(sexpr, program, inverse) {
1528
+ var id = sexpr.id,
1529
+ name = id.parts[0],
1530
+ isBlock = program != null || inverse != null;
1431
1531
 
1432
- this.pushSource("if (!" + this.lastHelper + ") { " + current + " = blockHelperMissing.call(" + params.join(", ") + "); }");
1433
- },
1532
+ this.opcode('getContext', id.depth);
1434
1533
 
1435
- // [appendContent]
1436
- //
1437
- // On stack, before: ...
1438
- // On stack, after: ...
1439
- //
1440
- // Appends the string value of `content` to the current buffer
1441
- appendContent: function(content) {
1442
- if (this.pendingContent) {
1443
- content = this.pendingContent + content;
1444
- }
1445
- if (this.stripNext) {
1446
- content = content.replace(/^\s+/, '');
1447
- }
1534
+ this.opcode('pushProgram', program);
1535
+ this.opcode('pushProgram', inverse);
1448
1536
 
1449
- this.pendingContent = content;
1537
+ this.opcode('invokeAmbiguous', name, isBlock);
1450
1538
  },
1451
1539
 
1452
- // [strip]
1453
- //
1454
- // On stack, before: ...
1455
- // On stack, after: ...
1456
- //
1457
- // Removes any trailing whitespace from the prior content node and flags
1458
- // the next operation for stripping if it is a content node.
1459
- strip: function() {
1460
- if (this.pendingContent) {
1461
- this.pendingContent = this.pendingContent.replace(/\s+$/, '');
1540
+ simpleSexpr: function(sexpr) {
1541
+ var id = sexpr.id;
1542
+
1543
+ if (id.type === 'DATA') {
1544
+ this.DATA(id);
1545
+ } else if (id.parts.length) {
1546
+ this.ID(id);
1547
+ } else {
1548
+ // Simplified ID for `this`
1549
+ this.addDepth(id.depth);
1550
+ this.opcode('getContext', id.depth);
1551
+ this.opcode('pushContext');
1462
1552
  }
1463
- this.stripNext = 'strip';
1553
+
1554
+ this.opcode('resolvePossibleLambda');
1464
1555
  },
1465
1556
 
1466
- // [append]
1467
- //
1468
- // On stack, before: value, ...
1469
- // On stack, after: ...
1470
- //
1471
- // Coerces `value` to a String and appends it to the current buffer.
1472
- //
1473
- // If `value` is truthy, or 0, it is coerced into a string and appended
1474
- // Otherwise, the empty string is appended
1475
- append: function() {
1476
- // Force anything that is inlined onto the stack so we don't have duplication
1477
- // when we examine local
1478
- this.flushInline();
1479
- var local = this.popStack();
1480
- this.pushSource("if(" + local + " || " + local + " === 0) { " + this.appendToBuffer(local) + " }");
1481
- if (this.environment.isSimple) {
1482
- this.pushSource("else { " + this.appendToBuffer("''") + " }");
1557
+ helperSexpr: function(sexpr, program, inverse) {
1558
+ var params = this.setupFullMustacheParams(sexpr, program, inverse),
1559
+ name = sexpr.id.parts[0];
1560
+
1561
+ if (this.options.knownHelpers[name]) {
1562
+ this.opcode('invokeKnownHelper', params.length, name);
1563
+ } else if (this.options.knownHelpersOnly) {
1564
+ throw new Exception("You specified knownHelpersOnly, but used the unknown helper " + name, sexpr);
1565
+ } else {
1566
+ this.opcode('invokeHelper', params.length, name, sexpr.isRoot);
1483
1567
  }
1484
1568
  },
1485
1569
 
1486
- // [appendEscaped]
1487
- //
1488
- // On stack, before: value, ...
1489
- // On stack, after: ...
1490
- //
1491
- // Escape `value` and append it to the buffer
1492
- appendEscaped: function() {
1493
- this.context.aliases.escapeExpression = 'this.escapeExpression';
1570
+ sexpr: function(sexpr) {
1571
+ var type = this.classifySexpr(sexpr);
1494
1572
 
1495
- this.pushSource(this.appendToBuffer("escapeExpression(" + this.popStack() + ")"));
1573
+ if (type === "simple") {
1574
+ this.simpleSexpr(sexpr);
1575
+ } else if (type === "helper") {
1576
+ this.helperSexpr(sexpr);
1577
+ } else {
1578
+ this.ambiguousSexpr(sexpr);
1579
+ }
1496
1580
  },
1497
1581
 
1498
- // [getContext]
1499
- //
1500
- // On stack, before: ...
1501
- // On stack, after: ...
1502
- // Compiler value, after: lastContext=depth
1503
- //
1504
- // Set the value of the `lastContext` compiler value to the depth
1505
- getContext: function(depth) {
1506
- if(this.lastContext !== depth) {
1507
- this.lastContext = depth;
1582
+ ID: function(id) {
1583
+ this.addDepth(id.depth);
1584
+ this.opcode('getContext', id.depth);
1585
+
1586
+ var name = id.parts[0];
1587
+ if (!name) {
1588
+ this.opcode('pushContext');
1589
+ } else {
1590
+ this.opcode('lookupOnContext', id.parts[0]);
1508
1591
  }
1509
- },
1510
1592
 
1511
- // [lookupOnContext]
1512
- //
1513
- // On stack, before: ...
1514
- // On stack, after: currentContext[name], ...
1515
- //
1516
- // Looks up the value of `name` on the current context and pushes
1517
- // it onto the stack.
1518
- lookupOnContext: function(name) {
1519
- this.push(this.nameLookup('depth' + this.lastContext, name, 'context'));
1593
+ for(var i=1, l=id.parts.length; i<l; i++) {
1594
+ this.opcode('lookup', id.parts[i]);
1595
+ }
1520
1596
  },
1521
1597
 
1522
- // [pushContext]
1523
- //
1524
- // On stack, before: ...
1525
- // On stack, after: currentContext, ...
1526
- //
1527
- // Pushes the value of the current context onto the stack.
1528
- pushContext: function() {
1529
- this.pushStackLiteral('depth' + this.lastContext);
1530
- },
1598
+ DATA: function(data) {
1599
+ this.options.data = true;
1600
+ if (data.id.isScoped || data.id.depth) {
1601
+ throw new Exception('Scoped data references are not supported: ' + data.original, data);
1602
+ }
1531
1603
 
1532
- // [resolvePossibleLambda]
1533
- //
1534
- // On stack, before: value, ...
1535
- // On stack, after: resolved value, ...
1536
- //
1537
- // If the `value` is a lambda, replace it on the stack by
1538
- // the return value of the lambda
1539
- resolvePossibleLambda: function() {
1540
- this.context.aliases.functionType = '"function"';
1604
+ this.opcode('lookupData');
1605
+ var parts = data.id.parts;
1606
+ for(var i=0, l=parts.length; i<l; i++) {
1607
+ this.opcode('lookup', parts[i]);
1608
+ }
1609
+ },
1541
1610
 
1542
- this.replaceStack(function(current) {
1543
- return "typeof " + current + " === functionType ? " + current + ".apply(depth0) : " + current;
1544
- });
1611
+ STRING: function(string) {
1612
+ this.opcode('pushString', string.string);
1545
1613
  },
1546
1614
 
1547
- // [lookup]
1548
- //
1549
- // On stack, before: value, ...
1550
- // On stack, after: value[name], ...
1551
- //
1552
- // Replace the value on the stack with the result of looking
1553
- // up `name` on `value`
1554
- lookup: function(name) {
1555
- this.replaceStack(function(current) {
1556
- return current + " == null || " + current + " === false ? " + current + " : " + this.nameLookup(current, name, 'context');
1557
- });
1615
+ INTEGER: function(integer) {
1616
+ this.opcode('pushLiteral', integer.integer);
1558
1617
  },
1559
1618
 
1560
- // [lookupData]
1561
- //
1562
- // On stack, before: ...
1563
- // On stack, after: data, ...
1564
- //
1565
- // Push the data lookup operator
1566
- lookupData: function() {
1567
- this.push('data');
1619
+ BOOLEAN: function(bool) {
1620
+ this.opcode('pushLiteral', bool.bool);
1568
1621
  },
1569
1622
 
1570
- // [pushStringParam]
1571
- //
1572
- // On stack, before: ...
1573
- // On stack, after: string, currentContext, ...
1574
- //
1575
- // This opcode is designed for use in string mode, which
1576
- // provides the string value of a parameter along with its
1577
- // depth rather than resolving it immediately.
1578
- pushStringParam: function(string, type) {
1579
- this.pushStackLiteral('depth' + this.lastContext);
1623
+ comment: function() {},
1580
1624
 
1581
- this.pushString(type);
1625
+ // HELPERS
1626
+ opcode: function(name) {
1627
+ this.opcodes.push({ opcode: name, args: [].slice.call(arguments, 1) });
1628
+ },
1582
1629
 
1583
- if (typeof string === 'string') {
1584
- this.pushString(string);
1585
- } else {
1586
- this.pushStackLiteral(string);
1587
- }
1630
+ declare: function(name, value) {
1631
+ this.opcodes.push({ opcode: 'DECLARE', name: name, value: value });
1588
1632
  },
1589
1633
 
1590
- emptyHash: function() {
1591
- this.pushStackLiteral('{}');
1634
+ addDepth: function(depth) {
1635
+ if(depth === 0) { return; }
1592
1636
 
1593
- if (this.options.stringParams) {
1594
- this.register('hashTypes', '{}');
1595
- this.register('hashContexts', '{}');
1637
+ if(!this.depths[depth]) {
1638
+ this.depths[depth] = true;
1639
+ this.depths.list.push(depth);
1596
1640
  }
1597
1641
  },
1598
- pushHash: function() {
1599
- this.hash = {values: [], types: [], contexts: []};
1600
- },
1601
- popHash: function() {
1602
- var hash = this.hash;
1603
- this.hash = undefined;
1604
1642
 
1605
- if (this.options.stringParams) {
1606
- this.register('hashContexts', '{' + hash.contexts.join(',') + '}');
1607
- this.register('hashTypes', '{' + hash.types.join(',') + '}');
1643
+ classifySexpr: function(sexpr) {
1644
+ var isHelper = sexpr.isHelper;
1645
+ var isEligible = sexpr.eligibleHelper;
1646
+ var options = this.options;
1647
+
1648
+ // if ambiguous, we can possibly resolve the ambiguity now
1649
+ if (isEligible && !isHelper) {
1650
+ var name = sexpr.id.parts[0];
1651
+
1652
+ if (options.knownHelpers[name]) {
1653
+ isHelper = true;
1654
+ } else if (options.knownHelpersOnly) {
1655
+ isEligible = false;
1656
+ }
1608
1657
  }
1609
- this.push('{\n ' + hash.values.join(',\n ') + '\n }');
1610
- },
1611
1658
 
1612
- // [pushString]
1613
- //
1614
- // On stack, before: ...
1615
- // On stack, after: quotedString(string), ...
1616
- //
1617
- // Push a quoted version of `string` onto the stack
1618
- pushString: function(string) {
1619
- this.pushStackLiteral(this.quotedString(string));
1659
+ if (isHelper) { return "helper"; }
1660
+ else if (isEligible) { return "ambiguous"; }
1661
+ else { return "simple"; }
1620
1662
  },
1621
1663
 
1622
- // [push]
1623
- //
1624
- // On stack, before: ...
1625
- // On stack, after: expr, ...
1626
- //
1627
- // Push an expression onto the stack
1628
- push: function(expr) {
1629
- this.inlineStack.push(expr);
1630
- return expr;
1631
- },
1664
+ pushParams: function(params) {
1665
+ var i = params.length, param;
1632
1666
 
1633
- // [pushLiteral]
1634
- //
1635
- // On stack, before: ...
1636
- // On stack, after: value, ...
1637
- //
1638
- // Pushes a value onto the stack. This operation prevents
1639
- // the compiler from creating a temporary variable to hold
1640
- // it.
1641
- pushLiteral: function(value) {
1642
- this.pushStackLiteral(value);
1643
- },
1667
+ while(i--) {
1668
+ param = params[i];
1644
1669
 
1645
- // [pushProgram]
1646
- //
1647
- // On stack, before: ...
1648
- // On stack, after: program(guid), ...
1649
- //
1650
- // Push a program expression onto the stack. This takes
1651
- // a compile-time guid and converts it into a runtime-accessible
1652
- // expression.
1653
- pushProgram: function(guid) {
1654
- if (guid != null) {
1655
- this.pushStackLiteral(this.programExpression(guid));
1656
- } else {
1657
- this.pushStackLiteral(null);
1670
+ if(this.options.stringParams) {
1671
+ if(param.depth) {
1672
+ this.addDepth(param.depth);
1673
+ }
1674
+
1675
+ this.opcode('getContext', param.depth || 0);
1676
+ this.opcode('pushStringParam', param.stringModeValue, param.type);
1677
+
1678
+ if (param.type === 'sexpr') {
1679
+ // Subexpressions get evaluated and passed in
1680
+ // in string params mode.
1681
+ this.sexpr(param);
1682
+ }
1683
+ } else {
1684
+ this[param.type](param);
1685
+ }
1658
1686
  }
1659
1687
  },
1660
1688
 
1661
- // [invokeHelper]
1662
- //
1663
- // On stack, before: hash, inverse, program, params..., ...
1664
- // On stack, after: result of helper invocation
1665
- //
1666
- // Pops off the helper's parameters, invokes the helper,
1667
- // and pushes the helper's return value onto the stack.
1668
- //
1669
- // If the helper is not found, `helperMissing` is called.
1670
- invokeHelper: function(paramSize, name) {
1671
- this.context.aliases.helperMissing = 'helpers.helperMissing';
1689
+ setupFullMustacheParams: function(sexpr, program, inverse) {
1690
+ var params = sexpr.params;
1691
+ this.pushParams(params);
1672
1692
 
1673
- var helper = this.lastHelper = this.setupHelper(paramSize, name, true);
1674
- var nonHelper = this.nameLookup('depth' + this.lastContext, name, 'context');
1693
+ this.opcode('pushProgram', program);
1694
+ this.opcode('pushProgram', inverse);
1675
1695
 
1676
- this.push(helper.name + ' || ' + nonHelper);
1677
- this.replaceStack(function(name) {
1678
- return name + ' ? ' + name + '.call(' +
1679
- helper.callParams + ") " + ": helperMissing.call(" +
1680
- helper.helperMissingParams + ")";
1681
- });
1682
- },
1696
+ if (sexpr.hash) {
1697
+ this.hash(sexpr.hash);
1698
+ } else {
1699
+ this.opcode('emptyHash');
1700
+ }
1683
1701
 
1684
- // [invokeKnownHelper]
1685
- //
1686
- // On stack, before: hash, inverse, program, params..., ...
1687
- // On stack, after: result of helper invocation
1688
- //
1689
- // This operation is used when the helper is known to exist,
1690
- // so a `helperMissing` fallback is not required.
1691
- invokeKnownHelper: function(paramSize, name) {
1692
- var helper = this.setupHelper(paramSize, name);
1693
- this.push(helper.name + ".call(" + helper.callParams + ")");
1694
- },
1702
+ return params;
1703
+ }
1704
+ };
1695
1705
 
1696
- // [invokeAmbiguous]
1697
- //
1698
- // On stack, before: hash, inverse, program, params..., ...
1699
- // On stack, after: result of disambiguation
1700
- //
1701
- // This operation is used when an expression like `{{foo}}`
1702
- // is provided, but we don't know at compile-time whether it
1703
- // is a helper or a path.
1704
- //
1705
- // This operation emits more code than the other options,
1706
- // and can be avoided by passing the `knownHelpers` and
1707
- // `knownHelpersOnly` flags at compile-time.
1708
- invokeAmbiguous: function(name, helperCall) {
1709
- this.context.aliases.functionType = '"function"';
1706
+ function precompile(input, options, env) {
1707
+ if (input == null || (typeof input !== 'string' && input.constructor !== env.AST.ProgramNode)) {
1708
+ throw new Exception("You must pass a string or Handlebars AST to Handlebars.precompile. You passed " + input);
1709
+ }
1710
1710
 
1711
- this.pushStackLiteral('{}'); // Hash value
1712
- var helper = this.setupHelper(0, name, helperCall);
1711
+ options = options || {};
1712
+ if (!('data' in options)) {
1713
+ options.data = true;
1714
+ }
1713
1715
 
1714
- var helperName = this.lastHelper = this.nameLookup('helpers', name, 'helper');
1716
+ var ast = env.parse(input);
1717
+ var environment = new env.Compiler().compile(ast, options);
1718
+ return new env.JavaScriptCompiler().compile(environment, options);
1719
+ }
1715
1720
 
1716
- var nonHelper = this.nameLookup('depth' + this.lastContext, name, 'context');
1717
- var nextStack = this.nextStack();
1721
+ __exports__.precompile = precompile;function compile(input, options, env) {
1722
+ if (input == null || (typeof input !== 'string' && input.constructor !== env.AST.ProgramNode)) {
1723
+ throw new Exception("You must pass a string or Handlebars AST to Handlebars.compile. You passed " + input);
1724
+ }
1718
1725
 
1719
- this.pushSource('if (' + nextStack + ' = ' + helperName + ') { ' + nextStack + ' = ' + nextStack + '.call(' + helper.callParams + '); }');
1720
- this.pushSource('else { ' + nextStack + ' = ' + nonHelper + '; ' + nextStack + ' = typeof ' + nextStack + ' === functionType ? ' + nextStack + '.call(' + helper.callParams + ') : ' + nextStack + '; }');
1721
- },
1726
+ options = options || {};
1722
1727
 
1723
- // [invokePartial]
1724
- //
1725
- // On stack, before: context, ...
1726
- // On stack after: result of partial invocation
1727
- //
1728
- // This operation pops off a context, invokes a partial with that context,
1729
- // and pushes the result of the invocation back.
1730
- invokePartial: function(name) {
1731
- var params = [this.nameLookup('partials', name, 'partial'), "'" + name + "'", this.popStack(), "helpers", "partials"];
1728
+ if (!('data' in options)) {
1729
+ options.data = true;
1730
+ }
1732
1731
 
1733
- if (this.options.data) {
1734
- params.push("data");
1732
+ var compiled;
1733
+
1734
+ function compileInput() {
1735
+ var ast = env.parse(input);
1736
+ var environment = new env.Compiler().compile(ast, options);
1737
+ var templateSpec = new env.JavaScriptCompiler().compile(environment, options, undefined, true);
1738
+ return env.template(templateSpec);
1739
+ }
1740
+
1741
+ // Template is only compiled on first use and cached after that point.
1742
+ return function(context, options) {
1743
+ if (!compiled) {
1744
+ compiled = compileInput();
1735
1745
  }
1746
+ return compiled.call(this, context, options);
1747
+ };
1748
+ }
1736
1749
 
1737
- this.context.aliases.self = "this";
1738
- this.push("self.invokePartial(" + params.join(", ") + ")");
1739
- },
1750
+ __exports__.compile = compile;
1751
+ return __exports__;
1752
+ })(__module5__);
1740
1753
 
1741
- // [assignToHash]
1742
- //
1743
- // On stack, before: value, hash, ...
1744
- // On stack, after: hash, ...
1745
- //
1746
- // Pops a value and hash off the stack, assigns `hash[key] = value`
1747
- // and pushes the hash back onto the stack.
1748
- assignToHash: function(key) {
1749
- var value = this.popStack(),
1750
- context,
1751
- type;
1754
+ // handlebars/compiler/javascript-compiler.js
1755
+ var __module11__ = (function(__dependency1__, __dependency2__) {
1756
+ "use strict";
1757
+ var __exports__;
1758
+ var COMPILER_REVISION = __dependency1__.COMPILER_REVISION;
1759
+ var REVISION_CHANGES = __dependency1__.REVISION_CHANGES;
1760
+ var log = __dependency1__.log;
1761
+ var Exception = __dependency2__;
1752
1762
 
1753
- if (this.options.stringParams) {
1754
- type = this.popStack();
1755
- context = this.popStack();
1763
+ function Literal(value) {
1764
+ this.value = value;
1765
+ }
1766
+
1767
+ function JavaScriptCompiler() {}
1768
+
1769
+ JavaScriptCompiler.prototype = {
1770
+ // PUBLIC API: You can override these methods in a subclass to provide
1771
+ // alternative compiled forms for name lookup and buffering semantics
1772
+ nameLookup: function(parent, name /* , type*/) {
1773
+ var wrap,
1774
+ ret;
1775
+ if (parent.indexOf('depth') === 0) {
1776
+ wrap = true;
1756
1777
  }
1757
1778
 
1758
- var hash = this.hash;
1759
- if (context) {
1760
- hash.contexts.push("'" + key + "': " + context);
1779
+ if (/^[0-9]+$/.test(name)) {
1780
+ ret = parent + "[" + name + "]";
1781
+ } else if (JavaScriptCompiler.isValidJavaScriptVariableName(name)) {
1782
+ ret = parent + "." + name;
1761
1783
  }
1762
- if (type) {
1763
- hash.types.push("'" + key + "': " + type);
1784
+ else {
1785
+ ret = parent + "['" + name + "']";
1786
+ }
1787
+
1788
+ if (wrap) {
1789
+ return '(' + parent + ' && ' + ret + ')';
1790
+ } else {
1791
+ return ret;
1764
1792
  }
1765
- hash.values.push("'" + key + "': (" + value + ")");
1766
1793
  },
1767
1794
 
1768
- // HELPERS
1795
+ compilerInfo: function() {
1796
+ var revision = COMPILER_REVISION,
1797
+ versions = REVISION_CHANGES[revision];
1798
+ return "this.compilerInfo = ["+revision+",'"+versions+"'];\n";
1799
+ },
1769
1800
 
1770
- compiler: JavaScriptCompiler,
1801
+ appendToBuffer: function(string) {
1802
+ if (this.environment.isSimple) {
1803
+ return "return " + string + ";";
1804
+ } else {
1805
+ return {
1806
+ appendToBuffer: true,
1807
+ content: string,
1808
+ toString: function() { return "buffer += " + string + ";"; }
1809
+ };
1810
+ }
1811
+ },
1771
1812
 
1772
- compileChildren: function(environment, options) {
1773
- var children = environment.children, child, compiler;
1813
+ initializeBuffer: function() {
1814
+ return this.quotedString("");
1815
+ },
1774
1816
 
1775
- for(var i=0, l=children.length; i<l; i++) {
1776
- child = children[i];
1777
- compiler = new this.compiler();
1817
+ namespace: "Handlebars",
1818
+ // END PUBLIC API
1778
1819
 
1779
- var index = this.matchExistingProgram(child);
1820
+ compile: function(environment, options, context, asObject) {
1821
+ this.environment = environment;
1822
+ this.options = options || {};
1780
1823
 
1781
- if (index == null) {
1782
- this.context.programs.push(''); // Placeholder to prevent name conflicts for nested children
1783
- index = this.context.programs.length;
1784
- child.index = index;
1785
- child.name = 'program' + index;
1786
- this.context.programs[index] = compiler.compile(child, options, this.context);
1787
- this.context.environments[index] = child;
1824
+ log('debug', this.environment.disassemble() + "\n\n");
1825
+
1826
+ this.name = this.environment.name;
1827
+ this.isChild = !!context;
1828
+ this.context = context || {
1829
+ programs: [],
1830
+ environments: [],
1831
+ aliases: { }
1832
+ };
1833
+
1834
+ this.preamble();
1835
+
1836
+ this.stackSlot = 0;
1837
+ this.stackVars = [];
1838
+ this.registers = { list: [] };
1839
+ this.hashes = [];
1840
+ this.compileStack = [];
1841
+ this.inlineStack = [];
1842
+
1843
+ this.compileChildren(environment, options);
1844
+
1845
+ var opcodes = environment.opcodes, opcode;
1846
+
1847
+ this.i = 0;
1848
+
1849
+ for(var l=opcodes.length; this.i<l; this.i++) {
1850
+ opcode = opcodes[this.i];
1851
+
1852
+ if(opcode.opcode === 'DECLARE') {
1853
+ this[opcode.name] = opcode.value;
1788
1854
  } else {
1789
- child.index = index;
1790
- child.name = 'program' + index;
1855
+ this[opcode.opcode].apply(this, opcode.args);
1791
1856
  }
1792
- }
1793
- },
1794
- matchExistingProgram: function(child) {
1795
- for (var i = 0, len = this.context.environments.length; i < len; i++) {
1796
- var environment = this.context.environments[i];
1797
- if (environment && environment.equals(child)) {
1798
- return i;
1857
+
1858
+ // Reset the stripNext flag if it was not set by this operation.
1859
+ if (opcode.opcode !== this.stripNext) {
1860
+ this.stripNext = false;
1799
1861
  }
1800
1862
  }
1801
- },
1802
1863
 
1803
- programExpression: function(guid) {
1804
- this.context.aliases.self = "this";
1864
+ // Flush any trailing content that might be pending.
1865
+ this.pushSource('');
1805
1866
 
1806
- if(guid == null) {
1807
- return "self.noop";
1867
+ if (this.stackSlot || this.inlineStack.length || this.compileStack.length) {
1868
+ throw new Exception('Compile completed with content left on stack');
1808
1869
  }
1809
1870
 
1810
- var child = this.environment.children[guid],
1811
- depths = child.depths.list, depth;
1871
+ return this.createFunctionContext(asObject);
1872
+ },
1812
1873
 
1813
- var programParams = [child.index, child.name, "data"];
1874
+ preamble: function() {
1875
+ var out = [];
1814
1876
 
1815
- for(var i=0, l = depths.length; i<l; i++) {
1816
- depth = depths[i];
1877
+ if (!this.isChild) {
1878
+ var namespace = this.namespace;
1817
1879
 
1818
- if(depth === 1) { programParams.push("depth0"); }
1819
- else { programParams.push("depth" + (depth - 1)); }
1880
+ var copies = "helpers = this.merge(helpers, " + namespace + ".helpers);";
1881
+ if (this.environment.usePartial) { copies = copies + " partials = this.merge(partials, " + namespace + ".partials);"; }
1882
+ if (this.options.data) { copies = copies + " data = data || {};"; }
1883
+ out.push(copies);
1884
+ } else {
1885
+ out.push('');
1820
1886
  }
1821
1887
 
1822
- return (depths.length === 0 ? "self.program(" : "self.programWithDepth(") + programParams.join(", ") + ")";
1823
- },
1888
+ if (!this.environment.isSimple) {
1889
+ out.push(", buffer = " + this.initializeBuffer());
1890
+ } else {
1891
+ out.push("");
1892
+ }
1824
1893
 
1825
- register: function(name, val) {
1826
- this.useRegister(name);
1827
- this.pushSource(name + " = " + val + ";");
1894
+ // track the last context pushed into place to allow skipping the
1895
+ // getContext opcode when it would be a noop
1896
+ this.lastContext = 0;
1897
+ this.source = out;
1828
1898
  },
1829
1899
 
1830
- useRegister: function(name) {
1831
- if(!this.registers[name]) {
1832
- this.registers[name] = true;
1833
- this.registers.list.push(name);
1900
+ createFunctionContext: function(asObject) {
1901
+ var locals = this.stackVars.concat(this.registers.list);
1902
+
1903
+ if(locals.length > 0) {
1904
+ this.source[1] = this.source[1] + ", " + locals.join(", ");
1834
1905
  }
1835
- },
1836
1906
 
1837
- pushStackLiteral: function(item) {
1838
- return this.push(new Literal(item));
1839
- },
1907
+ // Generate minimizer alias mappings
1908
+ if (!this.isChild) {
1909
+ for (var alias in this.context.aliases) {
1910
+ if (this.context.aliases.hasOwnProperty(alias)) {
1911
+ this.source[1] = this.source[1] + ', ' + alias + '=' + this.context.aliases[alias];
1912
+ }
1913
+ }
1914
+ }
1840
1915
 
1841
- pushSource: function(source) {
1842
- if (this.pendingContent) {
1843
- this.source.push(this.appendToBuffer(this.quotedString(this.pendingContent)));
1844
- this.pendingContent = undefined;
1916
+ if (this.source[1]) {
1917
+ this.source[1] = "var " + this.source[1].substring(2) + ";";
1845
1918
  }
1846
1919
 
1847
- if (source) {
1848
- this.source.push(source);
1920
+ // Merge children
1921
+ if (!this.isChild) {
1922
+ this.source[1] += '\n' + this.context.programs.join('\n') + '\n';
1849
1923
  }
1850
- },
1851
1924
 
1852
- pushStack: function(item) {
1853
- this.flushInline();
1925
+ if (!this.environment.isSimple) {
1926
+ this.pushSource("return buffer;");
1927
+ }
1854
1928
 
1855
- var stack = this.incrStack();
1856
- if (item) {
1857
- this.pushSource(stack + " = " + item + ";");
1929
+ var params = this.isChild ? ["depth0", "data"] : ["Handlebars", "depth0", "helpers", "partials", "data"];
1930
+
1931
+ for(var i=0, l=this.environment.depths.list.length; i<l; i++) {
1932
+ params.push("depth" + this.environment.depths.list[i]);
1858
1933
  }
1859
- this.compileStack.push(stack);
1860
- return stack;
1861
- },
1862
1934
 
1863
- replaceStack: function(callback) {
1864
- var prefix = '',
1865
- inline = this.isInline(),
1866
- stack;
1935
+ // Perform a second pass over the output to merge content when possible
1936
+ var source = this.mergeSource();
1867
1937
 
1868
- // If we are currently inline then we want to merge the inline statement into the
1869
- // replacement statement via ','
1870
- if (inline) {
1871
- var top = this.popStack(true);
1872
-
1873
- if (top instanceof Literal) {
1874
- // Literals do not need to be inlined
1875
- stack = top.value;
1876
- } else {
1877
- // Get or create the current stack name for use by the inline
1878
- var name = this.stackSlot ? this.topStackName() : this.incrStack();
1879
-
1880
- prefix = '(' + this.push(name) + ' = ' + top + '),';
1881
- stack = this.topStack();
1882
- }
1883
- } else {
1884
- stack = this.topStack();
1938
+ if (!this.isChild) {
1939
+ source = this.compilerInfo()+source;
1885
1940
  }
1886
1941
 
1887
- var item = callback.call(this, stack);
1942
+ if (asObject) {
1943
+ params.push(source);
1888
1944
 
1889
- if (inline) {
1890
- if (this.inlineStack.length || this.compileStack.length) {
1891
- this.popStack();
1892
- }
1893
- this.push('(' + prefix + item + ')');
1945
+ return Function.apply(this, params);
1894
1946
  } else {
1895
- // Prevent modification of the context depth variable. Through replaceStack
1896
- if (!/^stack/.test(stack)) {
1897
- stack = this.nextStack();
1898
- }
1899
-
1900
- this.pushSource(stack + " = (" + prefix + item + ");");
1947
+ var functionSource = 'function ' + (this.name || '') + '(' + params.join(',') + ') {\n ' + source + '}';
1948
+ log('debug', functionSource + "\n\n");
1949
+ return functionSource;
1901
1950
  }
1902
- return stack;
1903
- },
1904
-
1905
- nextStack: function() {
1906
- return this.pushStack();
1907
1951
  },
1908
-
1909
- incrStack: function() {
1910
- this.stackSlot++;
1911
- if(this.stackSlot > this.stackVars.length) { this.stackVars.push("stack" + this.stackSlot); }
1912
- return this.topStackName();
1913
- },
1914
- topStackName: function() {
1915
- return "stack" + this.stackSlot;
1916
- },
1917
- flushInline: function() {
1918
- var inlineStack = this.inlineStack;
1919
- if (inlineStack.length) {
1920
- this.inlineStack = [];
1921
- for (var i = 0, len = inlineStack.length; i < len; i++) {
1922
- var entry = inlineStack[i];
1923
- if (entry instanceof Literal) {
1924
- this.compileStack.push(entry);
1952
+ mergeSource: function() {
1953
+ // WARN: We are not handling the case where buffer is still populated as the source should
1954
+ // not have buffer append operations as their final action.
1955
+ var source = '',
1956
+ buffer;
1957
+ for (var i = 0, len = this.source.length; i < len; i++) {
1958
+ var line = this.source[i];
1959
+ if (line.appendToBuffer) {
1960
+ if (buffer) {
1961
+ buffer = buffer + '\n + ' + line.content;
1925
1962
  } else {
1926
- this.pushStack(entry);
1963
+ buffer = line.content;
1927
1964
  }
1965
+ } else {
1966
+ if (buffer) {
1967
+ source += 'buffer += ' + buffer + ';\n ';
1968
+ buffer = undefined;
1969
+ }
1970
+ source += line + '\n ';
1928
1971
  }
1929
1972
  }
1930
- },
1931
- isInline: function() {
1932
- return this.inlineStack.length;
1933
- },
1934
-
1935
- popStack: function(wrapped) {
1936
- var inline = this.isInline(),
1937
- item = (inline ? this.inlineStack : this.compileStack).pop();
1938
-
1939
- if (!wrapped && (item instanceof Literal)) {
1940
- return item.value;
1941
- } else {
1942
- if (!inline) {
1943
- this.stackSlot--;
1944
- }
1945
- return item;
1946
- }
1947
- },
1948
-
1949
- topStack: function(wrapped) {
1950
- var stack = (this.isInline() ? this.inlineStack : this.compileStack),
1951
- item = stack[stack.length - 1];
1952
-
1953
- if (!wrapped && (item instanceof Literal)) {
1954
- return item.value;
1955
- } else {
1956
- return item;
1957
- }
1973
+ return source;
1958
1974
  },
1959
1975
 
1960
- quotedString: function(str) {
1961
- return '"' + str
1962
- .replace(/\\/g, '\\\\')
1963
- .replace(/"/g, '\\"')
1964
- .replace(/\n/g, '\\n')
1965
- .replace(/\r/g, '\\r')
1966
- .replace(/\u2028/g, '\\u2028') // Per Ecma-262 7.3 + 7.8.4
1967
- .replace(/\u2029/g, '\\u2029') + '"';
1968
- },
1976
+ // [blockValue]
1977
+ //
1978
+ // On stack, before: hash, inverse, program, value
1979
+ // On stack, after: return value of blockHelperMissing
1980
+ //
1981
+ // The purpose of this opcode is to take a block of the form
1982
+ // `{{#foo}}...{{/foo}}`, resolve the value of `foo`, and
1983
+ // replace it on the stack with the result of properly
1984
+ // invoking blockHelperMissing.
1985
+ blockValue: function() {
1986
+ this.context.aliases.blockHelperMissing = 'helpers.blockHelperMissing';
1969
1987
 
1970
- setupHelper: function(paramSize, name, missingParams) {
1971
- var params = [];
1972
- this.setupParams(paramSize, params, missingParams);
1973
- var foundHelper = this.nameLookup('helpers', name, 'helper');
1988
+ var params = ["depth0"];
1989
+ this.setupParams(0, params);
1974
1990
 
1975
- return {
1976
- params: params,
1977
- name: foundHelper,
1978
- callParams: ["depth0"].concat(params).join(", "),
1979
- helperMissingParams: missingParams && ["depth0", this.quotedString(name)].concat(params).join(", ")
1980
- };
1991
+ this.replaceStack(function(current) {
1992
+ params.splice(1, 0, current);
1993
+ return "blockHelperMissing.call(" + params.join(", ") + ")";
1994
+ });
1981
1995
  },
1982
1996
 
1983
- // the params and contexts arguments are passed in arrays
1984
- // to fill in
1985
- setupParams: function(paramSize, params, useRegister) {
1986
- var options = [], contexts = [], types = [], param, inverse, program;
1987
-
1988
- options.push("hash:" + this.popStack());
1997
+ // [ambiguousBlockValue]
1998
+ //
1999
+ // On stack, before: hash, inverse, program, value
2000
+ // Compiler value, before: lastHelper=value of last found helper, if any
2001
+ // On stack, after, if no lastHelper: same as [blockValue]
2002
+ // On stack, after, if lastHelper: value
2003
+ ambiguousBlockValue: function() {
2004
+ this.context.aliases.blockHelperMissing = 'helpers.blockHelperMissing';
1989
2005
 
1990
- inverse = this.popStack();
1991
- program = this.popStack();
2006
+ var params = ["depth0"];
2007
+ this.setupParams(0, params);
1992
2008
 
1993
- // Avoid setting fn and inverse if neither are set. This allows
1994
- // helpers to do a check for `if (options.fn)`
1995
- if (program || inverse) {
1996
- if (!program) {
1997
- this.context.aliases.self = "this";
1998
- program = "self.noop";
1999
- }
2009
+ var current = this.topStack();
2010
+ params.splice(1, 0, current);
2000
2011
 
2001
- if (!inverse) {
2002
- this.context.aliases.self = "this";
2003
- inverse = "self.noop";
2004
- }
2012
+ this.pushSource("if (!" + this.lastHelper + ") { " + current + " = blockHelperMissing.call(" + params.join(", ") + "); }");
2013
+ },
2005
2014
 
2006
- options.push("inverse:" + inverse);
2007
- options.push("fn:" + program);
2015
+ // [appendContent]
2016
+ //
2017
+ // On stack, before: ...
2018
+ // On stack, after: ...
2019
+ //
2020
+ // Appends the string value of `content` to the current buffer
2021
+ appendContent: function(content) {
2022
+ if (this.pendingContent) {
2023
+ content = this.pendingContent + content;
2008
2024
  }
2009
-
2010
- for(var i=0; i<paramSize; i++) {
2011
- param = this.popStack();
2012
- params.push(param);
2013
-
2014
- if(this.options.stringParams) {
2015
- types.push(this.popStack());
2016
- contexts.push(this.popStack());
2017
- }
2025
+ if (this.stripNext) {
2026
+ content = content.replace(/^\s+/, '');
2018
2027
  }
2019
2028
 
2020
- if (this.options.stringParams) {
2021
- options.push("contexts:[" + contexts.join(",") + "]");
2022
- options.push("types:[" + types.join(",") + "]");
2023
- options.push("hashContexts:hashContexts");
2024
- options.push("hashTypes:hashTypes");
2025
- }
2029
+ this.pendingContent = content;
2030
+ },
2026
2031
 
2027
- if(this.options.data) {
2028
- options.push("data:data");
2032
+ // [strip]
2033
+ //
2034
+ // On stack, before: ...
2035
+ // On stack, after: ...
2036
+ //
2037
+ // Removes any trailing whitespace from the prior content node and flags
2038
+ // the next operation for stripping if it is a content node.
2039
+ strip: function() {
2040
+ if (this.pendingContent) {
2041
+ this.pendingContent = this.pendingContent.replace(/\s+$/, '');
2029
2042
  }
2043
+ this.stripNext = 'strip';
2044
+ },
2030
2045
 
2031
- options = "{" + options.join(",") + "}";
2032
- if (useRegister) {
2033
- this.register('options', options);
2034
- params.push('options');
2035
- } else {
2036
- params.push(options);
2046
+ // [append]
2047
+ //
2048
+ // On stack, before: value, ...
2049
+ // On stack, after: ...
2050
+ //
2051
+ // Coerces `value` to a String and appends it to the current buffer.
2052
+ //
2053
+ // If `value` is truthy, or 0, it is coerced into a string and appended
2054
+ // Otherwise, the empty string is appended
2055
+ append: function() {
2056
+ // Force anything that is inlined onto the stack so we don't have duplication
2057
+ // when we examine local
2058
+ this.flushInline();
2059
+ var local = this.popStack();
2060
+ this.pushSource("if(" + local + " || " + local + " === 0) { " + this.appendToBuffer(local) + " }");
2061
+ if (this.environment.isSimple) {
2062
+ this.pushSource("else { " + this.appendToBuffer("''") + " }");
2037
2063
  }
2038
- return params.join(", ");
2039
- }
2040
- };
2064
+ },
2041
2065
 
2042
- var reservedWords = (
2043
- "break else new var" +
2044
- " case finally return void" +
2045
- " catch for switch while" +
2046
- " continue function this with" +
2047
- " default if throw" +
2048
- " delete in try" +
2049
- " do instanceof typeof" +
2050
- " abstract enum int short" +
2051
- " boolean export interface static" +
2052
- " byte extends long super" +
2053
- " char final native synchronized" +
2054
- " class float package throws" +
2055
- " const goto private transient" +
2056
- " debugger implements protected volatile" +
2057
- " double import public let yield"
2058
- ).split(" ");
2066
+ // [appendEscaped]
2067
+ //
2068
+ // On stack, before: value, ...
2069
+ // On stack, after: ...
2070
+ //
2071
+ // Escape `value` and append it to the buffer
2072
+ appendEscaped: function() {
2073
+ this.context.aliases.escapeExpression = 'this.escapeExpression';
2059
2074
 
2060
- var compilerWords = JavaScriptCompiler.RESERVED_WORDS = {};
2075
+ this.pushSource(this.appendToBuffer("escapeExpression(" + this.popStack() + ")"));
2076
+ },
2061
2077
 
2062
- for(var i=0, l=reservedWords.length; i<l; i++) {
2063
- compilerWords[reservedWords[i]] = true;
2064
- }
2078
+ // [getContext]
2079
+ //
2080
+ // On stack, before: ...
2081
+ // On stack, after: ...
2082
+ // Compiler value, after: lastContext=depth
2083
+ //
2084
+ // Set the value of the `lastContext` compiler value to the depth
2085
+ getContext: function(depth) {
2086
+ if(this.lastContext !== depth) {
2087
+ this.lastContext = depth;
2088
+ }
2089
+ },
2065
2090
 
2066
- JavaScriptCompiler.isValidJavaScriptVariableName = function(name) {
2067
- if(!JavaScriptCompiler.RESERVED_WORDS[name] && /^[a-zA-Z_$][0-9a-zA-Z_$]+$/.test(name)) {
2068
- return true;
2069
- }
2070
- return false;
2071
- };
2091
+ // [lookupOnContext]
2092
+ //
2093
+ // On stack, before: ...
2094
+ // On stack, after: currentContext[name], ...
2095
+ //
2096
+ // Looks up the value of `name` on the current context and pushes
2097
+ // it onto the stack.
2098
+ lookupOnContext: function(name) {
2099
+ this.push(this.nameLookup('depth' + this.lastContext, name, 'context'));
2100
+ },
2072
2101
 
2073
- __exports__ = JavaScriptCompiler;
2074
- return __exports__;
2075
- })(__module2__);
2102
+ // [pushContext]
2103
+ //
2104
+ // On stack, before: ...
2105
+ // On stack, after: currentContext, ...
2106
+ //
2107
+ // Pushes the value of the current context onto the stack.
2108
+ pushContext: function() {
2109
+ this.pushStackLiteral('depth' + this.lastContext);
2110
+ },
2076
2111
 
2077
- // handlebars/compiler/compiler.js
2078
- var __module10__ = (function(__dependency1__, __dependency2__, __dependency3__, __dependency4__) {
2079
- "use strict";
2080
- var __exports__ = {};
2081
- var Exception = __dependency1__;
2082
- var parse = __dependency2__.parse;
2083
- var JavaScriptCompiler = __dependency3__;
2084
- var AST = __dependency4__;
2112
+ // [resolvePossibleLambda]
2113
+ //
2114
+ // On stack, before: value, ...
2115
+ // On stack, after: resolved value, ...
2116
+ //
2117
+ // If the `value` is a lambda, replace it on the stack by
2118
+ // the return value of the lambda
2119
+ resolvePossibleLambda: function() {
2120
+ this.context.aliases.functionType = '"function"';
2085
2121
 
2086
- function Compiler() {}
2122
+ this.replaceStack(function(current) {
2123
+ return "typeof " + current + " === functionType ? " + current + ".apply(depth0) : " + current;
2124
+ });
2125
+ },
2087
2126
 
2088
- __exports__.Compiler = Compiler;// the foundHelper register will disambiguate helper lookup from finding a
2089
- // function in a context. This is necessary for mustache compatibility, which
2090
- // requires that context functions in blocks are evaluated by blockHelperMissing,
2091
- // and then proceed as if the resulting value was provided to blockHelperMissing.
2127
+ // [lookup]
2128
+ //
2129
+ // On stack, before: value, ...
2130
+ // On stack, after: value[name], ...
2131
+ //
2132
+ // Replace the value on the stack with the result of looking
2133
+ // up `name` on `value`
2134
+ lookup: function(name) {
2135
+ this.replaceStack(function(current) {
2136
+ return current + " == null || " + current + " === false ? " + current + " : " + this.nameLookup(current, name, 'context');
2137
+ });
2138
+ },
2092
2139
 
2093
- Compiler.prototype = {
2094
- compiler: Compiler,
2140
+ // [lookupData]
2141
+ //
2142
+ // On stack, before: ...
2143
+ // On stack, after: data, ...
2144
+ //
2145
+ // Push the data lookup operator
2146
+ lookupData: function() {
2147
+ this.pushStackLiteral('data');
2148
+ },
2095
2149
 
2096
- disassemble: function() {
2097
- var opcodes = this.opcodes, opcode, out = [], params, param;
2150
+ // [pushStringParam]
2151
+ //
2152
+ // On stack, before: ...
2153
+ // On stack, after: string, currentContext, ...
2154
+ //
2155
+ // This opcode is designed for use in string mode, which
2156
+ // provides the string value of a parameter along with its
2157
+ // depth rather than resolving it immediately.
2158
+ pushStringParam: function(string, type) {
2159
+ this.pushStackLiteral('depth' + this.lastContext);
2098
2160
 
2099
- for (var i=0, l=opcodes.length; i<l; i++) {
2100
- opcode = opcodes[i];
2161
+ this.pushString(type);
2101
2162
 
2102
- if (opcode.opcode === 'DECLARE') {
2103
- out.push("DECLARE " + opcode.name + "=" + opcode.value);
2163
+ // If it's a subexpression, the string result
2164
+ // will be pushed after this opcode.
2165
+ if (type !== 'sexpr') {
2166
+ if (typeof string === 'string') {
2167
+ this.pushString(string);
2104
2168
  } else {
2105
- params = [];
2106
- for (var j=0; j<opcode.args.length; j++) {
2107
- param = opcode.args[j];
2108
- if (typeof param === "string") {
2109
- param = "\"" + param.replace("\n", "\\n") + "\"";
2110
- }
2111
- params.push(param);
2112
- }
2113
- out.push(opcode.opcode + " " + params.join(" "));
2169
+ this.pushStackLiteral(string);
2114
2170
  }
2115
2171
  }
2116
-
2117
- return out.join("\n");
2118
2172
  },
2119
2173
 
2120
- equals: function(other) {
2121
- var len = this.opcodes.length;
2122
- if (other.opcodes.length !== len) {
2123
- return false;
2124
- }
2174
+ emptyHash: function() {
2175
+ this.pushStackLiteral('{}');
2125
2176
 
2126
- for (var i = 0; i < len; i++) {
2127
- var opcode = this.opcodes[i],
2128
- otherOpcode = other.opcodes[i];
2129
- if (opcode.opcode !== otherOpcode.opcode || opcode.args.length !== otherOpcode.args.length) {
2130
- return false;
2131
- }
2132
- for (var j = 0; j < opcode.args.length; j++) {
2133
- if (opcode.args[j] !== otherOpcode.args[j]) {
2134
- return false;
2135
- }
2136
- }
2177
+ if (this.options.stringParams) {
2178
+ this.push('{}'); // hashContexts
2179
+ this.push('{}'); // hashTypes
2137
2180
  }
2138
-
2139
- len = this.children.length;
2140
- if (other.children.length !== len) {
2141
- return false;
2181
+ },
2182
+ pushHash: function() {
2183
+ if (this.hash) {
2184
+ this.hashes.push(this.hash);
2142
2185
  }
2143
- for (i = 0; i < len; i++) {
2144
- if (!this.children[i].equals(other.children[i])) {
2145
- return false;
2146
- }
2186
+ this.hash = {values: [], types: [], contexts: []};
2187
+ },
2188
+ popHash: function() {
2189
+ var hash = this.hash;
2190
+ this.hash = this.hashes.pop();
2191
+
2192
+ if (this.options.stringParams) {
2193
+ this.push('{' + hash.contexts.join(',') + '}');
2194
+ this.push('{' + hash.types.join(',') + '}');
2147
2195
  }
2148
2196
 
2149
- return true;
2197
+ this.push('{\n ' + hash.values.join(',\n ') + '\n }');
2150
2198
  },
2151
2199
 
2152
- guid: 0,
2153
-
2154
- compile: function(program, options) {
2155
- this.opcodes = [];
2156
- this.children = [];
2157
- this.depths = {list: []};
2158
- this.options = options;
2200
+ // [pushString]
2201
+ //
2202
+ // On stack, before: ...
2203
+ // On stack, after: quotedString(string), ...
2204
+ //
2205
+ // Push a quoted version of `string` onto the stack
2206
+ pushString: function(string) {
2207
+ this.pushStackLiteral(this.quotedString(string));
2208
+ },
2159
2209
 
2160
- // These changes will propagate to the other compiler components
2161
- var knownHelpers = this.options.knownHelpers;
2162
- this.options.knownHelpers = {
2163
- 'helperMissing': true,
2164
- 'blockHelperMissing': true,
2165
- 'each': true,
2166
- 'if': true,
2167
- 'unless': true,
2168
- 'with': true,
2169
- 'log': true
2170
- };
2171
- if (knownHelpers) {
2172
- for (var name in knownHelpers) {
2173
- this.options.knownHelpers[name] = knownHelpers[name];
2174
- }
2175
- }
2210
+ // [push]
2211
+ //
2212
+ // On stack, before: ...
2213
+ // On stack, after: expr, ...
2214
+ //
2215
+ // Push an expression onto the stack
2216
+ push: function(expr) {
2217
+ this.inlineStack.push(expr);
2218
+ return expr;
2219
+ },
2176
2220
 
2177
- return this.accept(program);
2221
+ // [pushLiteral]
2222
+ //
2223
+ // On stack, before: ...
2224
+ // On stack, after: value, ...
2225
+ //
2226
+ // Pushes a value onto the stack. This operation prevents
2227
+ // the compiler from creating a temporary variable to hold
2228
+ // it.
2229
+ pushLiteral: function(value) {
2230
+ this.pushStackLiteral(value);
2178
2231
  },
2179
2232
 
2180
- accept: function(node) {
2181
- var strip = node.strip || {},
2182
- ret;
2183
- if (strip.left) {
2184
- this.opcode('strip');
2233
+ // [pushProgram]
2234
+ //
2235
+ // On stack, before: ...
2236
+ // On stack, after: program(guid), ...
2237
+ //
2238
+ // Push a program expression onto the stack. This takes
2239
+ // a compile-time guid and converts it into a runtime-accessible
2240
+ // expression.
2241
+ pushProgram: function(guid) {
2242
+ if (guid != null) {
2243
+ this.pushStackLiteral(this.programExpression(guid));
2244
+ } else {
2245
+ this.pushStackLiteral(null);
2185
2246
  }
2247
+ },
2248
+
2249
+ // [invokeHelper]
2250
+ //
2251
+ // On stack, before: hash, inverse, program, params..., ...
2252
+ // On stack, after: result of helper invocation
2253
+ //
2254
+ // Pops off the helper's parameters, invokes the helper,
2255
+ // and pushes the helper's return value onto the stack.
2256
+ //
2257
+ // If the helper is not found, `helperMissing` is called.
2258
+ invokeHelper: function(paramSize, name, isRoot) {
2259
+ this.context.aliases.helperMissing = 'helpers.helperMissing';
2260
+ this.useRegister('helper');
2186
2261
 
2187
- ret = this[node.type](node);
2262
+ var helper = this.lastHelper = this.setupHelper(paramSize, name, true);
2263
+ var nonHelper = this.nameLookup('depth' + this.lastContext, name, 'context');
2188
2264
 
2189
- if (strip.right) {
2190
- this.opcode('strip');
2265
+ var lookup = 'helper = ' + helper.name + ' || ' + nonHelper;
2266
+ if (helper.paramsInit) {
2267
+ lookup += ',' + helper.paramsInit;
2191
2268
  }
2192
2269
 
2193
- return ret;
2194
- },
2195
-
2196
- program: function(program) {
2197
- var statements = program.statements;
2270
+ this.push(
2271
+ '('
2272
+ + lookup
2273
+ + ',helper '
2274
+ + '? helper.call(' + helper.callParams + ') '
2275
+ + ': helperMissing.call(' + helper.helperMissingParams + '))');
2198
2276
 
2199
- for(var i=0, l=statements.length; i<l; i++) {
2200
- this.accept(statements[i]);
2277
+ // Always flush subexpressions. This is both to prevent the compounding size issue that
2278
+ // occurs when the code has to be duplicated for inlining and also to prevent errors
2279
+ // due to the incorrect options object being passed due to the shared register.
2280
+ if (!isRoot) {
2281
+ this.flushInline();
2201
2282
  }
2202
- this.isSimple = l === 1;
2203
-
2204
- this.depths.list = this.depths.list.sort(function(a, b) {
2205
- return a - b;
2206
- });
2283
+ },
2207
2284
 
2208
- return this;
2285
+ // [invokeKnownHelper]
2286
+ //
2287
+ // On stack, before: hash, inverse, program, params..., ...
2288
+ // On stack, after: result of helper invocation
2289
+ //
2290
+ // This operation is used when the helper is known to exist,
2291
+ // so a `helperMissing` fallback is not required.
2292
+ invokeKnownHelper: function(paramSize, name) {
2293
+ var helper = this.setupHelper(paramSize, name);
2294
+ this.push(helper.name + ".call(" + helper.callParams + ")");
2209
2295
  },
2210
2296
 
2211
- compileProgram: function(program) {
2212
- var result = new this.compiler().compile(program, this.options);
2213
- var guid = this.guid++, depth;
2297
+ // [invokeAmbiguous]
2298
+ //
2299
+ // On stack, before: hash, inverse, program, params..., ...
2300
+ // On stack, after: result of disambiguation
2301
+ //
2302
+ // This operation is used when an expression like `{{foo}}`
2303
+ // is provided, but we don't know at compile-time whether it
2304
+ // is a helper or a path.
2305
+ //
2306
+ // This operation emits more code than the other options,
2307
+ // and can be avoided by passing the `knownHelpers` and
2308
+ // `knownHelpersOnly` flags at compile-time.
2309
+ invokeAmbiguous: function(name, helperCall) {
2310
+ this.context.aliases.functionType = '"function"';
2311
+ this.useRegister('helper');
2214
2312
 
2215
- this.usePartial = this.usePartial || result.usePartial;
2313
+ this.emptyHash();
2314
+ var helper = this.setupHelper(0, name, helperCall);
2216
2315
 
2217
- this.children[guid] = result;
2316
+ var helperName = this.lastHelper = this.nameLookup('helpers', name, 'helper');
2218
2317
 
2219
- for(var i=0, l=result.depths.list.length; i<l; i++) {
2220
- depth = result.depths.list[i];
2318
+ var nonHelper = this.nameLookup('depth' + this.lastContext, name, 'context');
2319
+ var nextStack = this.nextStack();
2221
2320
 
2222
- if(depth < 2) { continue; }
2223
- else { this.addDepth(depth - 1); }
2321
+ if (helper.paramsInit) {
2322
+ this.pushSource(helper.paramsInit);
2224
2323
  }
2225
-
2226
- return guid;
2324
+ this.pushSource('if (helper = ' + helperName + ') { ' + nextStack + ' = helper.call(' + helper.callParams + '); }');
2325
+ this.pushSource('else { helper = ' + nonHelper + '; ' + nextStack + ' = typeof helper === functionType ? helper.call(' + helper.callParams + ') : helper; }');
2227
2326
  },
2228
2327
 
2229
- block: function(block) {
2230
- var mustache = block.mustache,
2231
- program = block.program,
2232
- inverse = block.inverse;
2233
-
2234
- if (program) {
2235
- program = this.compileProgram(program);
2236
- }
2328
+ // [invokePartial]
2329
+ //
2330
+ // On stack, before: context, ...
2331
+ // On stack after: result of partial invocation
2332
+ //
2333
+ // This operation pops off a context, invokes a partial with that context,
2334
+ // and pushes the result of the invocation back.
2335
+ invokePartial: function(name) {
2336
+ var params = [this.nameLookup('partials', name, 'partial'), "'" + name + "'", this.popStack(), "helpers", "partials"];
2237
2337
 
2238
- if (inverse) {
2239
- inverse = this.compileProgram(inverse);
2338
+ if (this.options.data) {
2339
+ params.push("data");
2240
2340
  }
2241
2341
 
2242
- var type = this.classifyMustache(mustache);
2243
-
2244
- if (type === "helper") {
2245
- this.helperMustache(mustache, program, inverse);
2246
- } else if (type === "simple") {
2247
- this.simpleMustache(mustache);
2342
+ this.context.aliases.self = "this";
2343
+ this.push("self.invokePartial(" + params.join(", ") + ")");
2344
+ },
2248
2345
 
2249
- // now that the simple mustache is resolved, we need to
2250
- // evaluate it by executing `blockHelperMissing`
2251
- this.opcode('pushProgram', program);
2252
- this.opcode('pushProgram', inverse);
2253
- this.opcode('emptyHash');
2254
- this.opcode('blockValue');
2255
- } else {
2256
- this.ambiguousMustache(mustache, program, inverse);
2346
+ // [assignToHash]
2347
+ //
2348
+ // On stack, before: value, hash, ...
2349
+ // On stack, after: hash, ...
2350
+ //
2351
+ // Pops a value and hash off the stack, assigns `hash[key] = value`
2352
+ // and pushes the hash back onto the stack.
2353
+ assignToHash: function(key) {
2354
+ var value = this.popStack(),
2355
+ context,
2356
+ type;
2257
2357
 
2258
- // now that the simple mustache is resolved, we need to
2259
- // evaluate it by executing `blockHelperMissing`
2260
- this.opcode('pushProgram', program);
2261
- this.opcode('pushProgram', inverse);
2262
- this.opcode('emptyHash');
2263
- this.opcode('ambiguousBlockValue');
2358
+ if (this.options.stringParams) {
2359
+ type = this.popStack();
2360
+ context = this.popStack();
2264
2361
  }
2265
2362
 
2266
- this.opcode('append');
2363
+ var hash = this.hash;
2364
+ if (context) {
2365
+ hash.contexts.push("'" + key + "': " + context);
2366
+ }
2367
+ if (type) {
2368
+ hash.types.push("'" + key + "': " + type);
2369
+ }
2370
+ hash.values.push("'" + key + "': (" + value + ")");
2267
2371
  },
2268
2372
 
2269
- hash: function(hash) {
2270
- var pairs = hash.pairs, pair, val;
2373
+ // HELPERS
2271
2374
 
2272
- this.opcode('pushHash');
2375
+ compiler: JavaScriptCompiler,
2273
2376
 
2274
- for(var i=0, l=pairs.length; i<l; i++) {
2275
- pair = pairs[i];
2276
- val = pair[1];
2377
+ compileChildren: function(environment, options) {
2378
+ var children = environment.children, child, compiler;
2277
2379
 
2278
- if (this.options.stringParams) {
2279
- if(val.depth) {
2280
- this.addDepth(val.depth);
2281
- }
2282
- this.opcode('getContext', val.depth || 0);
2283
- this.opcode('pushStringParam', val.stringModeValue, val.type);
2380
+ for(var i=0, l=children.length; i<l; i++) {
2381
+ child = children[i];
2382
+ compiler = new this.compiler();
2383
+
2384
+ var index = this.matchExistingProgram(child);
2385
+
2386
+ if (index == null) {
2387
+ this.context.programs.push(''); // Placeholder to prevent name conflicts for nested children
2388
+ index = this.context.programs.length;
2389
+ child.index = index;
2390
+ child.name = 'program' + index;
2391
+ this.context.programs[index] = compiler.compile(child, options, this.context);
2392
+ this.context.environments[index] = child;
2284
2393
  } else {
2285
- this.accept(val);
2394
+ child.index = index;
2395
+ child.name = 'program' + index;
2396
+ }
2397
+ }
2398
+ },
2399
+ matchExistingProgram: function(child) {
2400
+ for (var i = 0, len = this.context.environments.length; i < len; i++) {
2401
+ var environment = this.context.environments[i];
2402
+ if (environment && environment.equals(child)) {
2403
+ return i;
2286
2404
  }
2287
-
2288
- this.opcode('assignToHash', pair[0]);
2289
2405
  }
2290
- this.opcode('popHash');
2291
2406
  },
2292
2407
 
2293
- partial: function(partial) {
2294
- var partialName = partial.partialName;
2295
- this.usePartial = true;
2408
+ programExpression: function(guid) {
2409
+ this.context.aliases.self = "this";
2296
2410
 
2297
- if(partial.context) {
2298
- this.ID(partial.context);
2299
- } else {
2300
- this.opcode('push', 'depth0');
2411
+ if(guid == null) {
2412
+ return "self.noop";
2301
2413
  }
2302
2414
 
2303
- this.opcode('invokePartial', partialName.name);
2304
- this.opcode('append');
2305
- },
2415
+ var child = this.environment.children[guid],
2416
+ depths = child.depths.list, depth;
2306
2417
 
2307
- content: function(content) {
2308
- this.opcode('appendContent', content.string);
2309
- },
2418
+ var programParams = [child.index, child.name, "data"];
2310
2419
 
2311
- mustache: function(mustache) {
2312
- var options = this.options;
2313
- var type = this.classifyMustache(mustache);
2420
+ for(var i=0, l = depths.length; i<l; i++) {
2421
+ depth = depths[i];
2314
2422
 
2315
- if (type === "simple") {
2316
- this.simpleMustache(mustache);
2317
- } else if (type === "helper") {
2318
- this.helperMustache(mustache);
2319
- } else {
2320
- this.ambiguousMustache(mustache);
2423
+ if(depth === 1) { programParams.push("depth0"); }
2424
+ else { programParams.push("depth" + (depth - 1)); }
2321
2425
  }
2322
2426
 
2323
- if(mustache.escaped && !options.noEscape) {
2324
- this.opcode('appendEscaped');
2325
- } else {
2326
- this.opcode('append');
2327
- }
2427
+ return (depths.length === 0 ? "self.program(" : "self.programWithDepth(") + programParams.join(", ") + ")";
2328
2428
  },
2329
2429
 
2330
- ambiguousMustache: function(mustache, program, inverse) {
2331
- var id = mustache.id,
2332
- name = id.parts[0],
2333
- isBlock = program != null || inverse != null;
2334
-
2335
- this.opcode('getContext', id.depth);
2430
+ register: function(name, val) {
2431
+ this.useRegister(name);
2432
+ this.pushSource(name + " = " + val + ";");
2433
+ },
2336
2434
 
2337
- this.opcode('pushProgram', program);
2338
- this.opcode('pushProgram', inverse);
2435
+ useRegister: function(name) {
2436
+ if(!this.registers[name]) {
2437
+ this.registers[name] = true;
2438
+ this.registers.list.push(name);
2439
+ }
2440
+ },
2339
2441
 
2340
- this.opcode('invokeAmbiguous', name, isBlock);
2442
+ pushStackLiteral: function(item) {
2443
+ return this.push(new Literal(item));
2341
2444
  },
2342
2445
 
2343
- simpleMustache: function(mustache) {
2344
- var id = mustache.id;
2446
+ pushSource: function(source) {
2447
+ if (this.pendingContent) {
2448
+ this.source.push(this.appendToBuffer(this.quotedString(this.pendingContent)));
2449
+ this.pendingContent = undefined;
2450
+ }
2345
2451
 
2346
- if (id.type === 'DATA') {
2347
- this.DATA(id);
2348
- } else if (id.parts.length) {
2349
- this.ID(id);
2350
- } else {
2351
- // Simplified ID for `this`
2352
- this.addDepth(id.depth);
2353
- this.opcode('getContext', id.depth);
2354
- this.opcode('pushContext');
2452
+ if (source) {
2453
+ this.source.push(source);
2355
2454
  }
2455
+ },
2356
2456
 
2357
- this.opcode('resolvePossibleLambda');
2457
+ pushStack: function(item) {
2458
+ this.flushInline();
2459
+
2460
+ var stack = this.incrStack();
2461
+ if (item) {
2462
+ this.pushSource(stack + " = " + item + ";");
2463
+ }
2464
+ this.compileStack.push(stack);
2465
+ return stack;
2358
2466
  },
2359
2467
 
2360
- helperMustache: function(mustache, program, inverse) {
2361
- var params = this.setupFullMustacheParams(mustache, program, inverse),
2362
- name = mustache.id.parts[0];
2468
+ replaceStack: function(callback) {
2469
+ var prefix = '',
2470
+ inline = this.isInline(),
2471
+ stack,
2472
+ createdStack,
2473
+ usedLiteral;
2474
+
2475
+ // If we are currently inline then we want to merge the inline statement into the
2476
+ // replacement statement via ','
2477
+ if (inline) {
2478
+ var top = this.popStack(true);
2479
+
2480
+ if (top instanceof Literal) {
2481
+ // Literals do not need to be inlined
2482
+ stack = top.value;
2483
+ usedLiteral = true;
2484
+ } else {
2485
+ // Get or create the current stack name for use by the inline
2486
+ createdStack = !this.stackSlot;
2487
+ var name = !createdStack ? this.topStackName() : this.incrStack();
2363
2488
 
2364
- if (this.options.knownHelpers[name]) {
2365
- this.opcode('invokeKnownHelper', params.length, name);
2366
- } else if (this.options.knownHelpersOnly) {
2367
- throw new Error("You specified knownHelpersOnly, but used the unknown helper " + name);
2489
+ prefix = '(' + this.push(name) + ' = ' + top + '),';
2490
+ stack = this.topStack();
2491
+ }
2368
2492
  } else {
2369
- this.opcode('invokeHelper', params.length, name);
2493
+ stack = this.topStack();
2370
2494
  }
2371
- },
2372
2495
 
2373
- ID: function(id) {
2374
- this.addDepth(id.depth);
2375
- this.opcode('getContext', id.depth);
2496
+ var item = callback.call(this, stack);
2376
2497
 
2377
- var name = id.parts[0];
2378
- if (!name) {
2379
- this.opcode('pushContext');
2498
+ if (inline) {
2499
+ if (!usedLiteral) {
2500
+ this.popStack();
2501
+ }
2502
+ if (createdStack) {
2503
+ this.stackSlot--;
2504
+ }
2505
+ this.push('(' + prefix + item + ')');
2380
2506
  } else {
2381
- this.opcode('lookupOnContext', id.parts[0]);
2382
- }
2507
+ // Prevent modification of the context depth variable. Through replaceStack
2508
+ if (!/^stack/.test(stack)) {
2509
+ stack = this.nextStack();
2510
+ }
2383
2511
 
2384
- for(var i=1, l=id.parts.length; i<l; i++) {
2385
- this.opcode('lookup', id.parts[i]);
2512
+ this.pushSource(stack + " = (" + prefix + item + ");");
2386
2513
  }
2514
+ return stack;
2387
2515
  },
2388
2516
 
2389
- DATA: function(data) {
2390
- this.options.data = true;
2391
- if (data.id.isScoped || data.id.depth) {
2392
- throw new Exception('Scoped data references are not supported: ' + data.original);
2393
- }
2517
+ nextStack: function() {
2518
+ return this.pushStack();
2519
+ },
2394
2520
 
2395
- this.opcode('lookupData');
2396
- var parts = data.id.parts;
2397
- for(var i=0, l=parts.length; i<l; i++) {
2398
- this.opcode('lookup', parts[i]);
2521
+ incrStack: function() {
2522
+ this.stackSlot++;
2523
+ if(this.stackSlot > this.stackVars.length) { this.stackVars.push("stack" + this.stackSlot); }
2524
+ return this.topStackName();
2525
+ },
2526
+ topStackName: function() {
2527
+ return "stack" + this.stackSlot;
2528
+ },
2529
+ flushInline: function() {
2530
+ var inlineStack = this.inlineStack;
2531
+ if (inlineStack.length) {
2532
+ this.inlineStack = [];
2533
+ for (var i = 0, len = inlineStack.length; i < len; i++) {
2534
+ var entry = inlineStack[i];
2535
+ if (entry instanceof Literal) {
2536
+ this.compileStack.push(entry);
2537
+ } else {
2538
+ this.pushStack(entry);
2539
+ }
2540
+ }
2399
2541
  }
2400
2542
  },
2401
-
2402
- STRING: function(string) {
2403
- this.opcode('pushString', string.string);
2543
+ isInline: function() {
2544
+ return this.inlineStack.length;
2404
2545
  },
2405
2546
 
2406
- INTEGER: function(integer) {
2407
- this.opcode('pushLiteral', integer.integer);
2408
- },
2547
+ popStack: function(wrapped) {
2548
+ var inline = this.isInline(),
2549
+ item = (inline ? this.inlineStack : this.compileStack).pop();
2409
2550
 
2410
- BOOLEAN: function(bool) {
2411
- this.opcode('pushLiteral', bool.bool);
2551
+ if (!wrapped && (item instanceof Literal)) {
2552
+ return item.value;
2553
+ } else {
2554
+ if (!inline) {
2555
+ if (!this.stackSlot) {
2556
+ throw new Exception('Invalid stack pop');
2557
+ }
2558
+ this.stackSlot--;
2559
+ }
2560
+ return item;
2561
+ }
2412
2562
  },
2413
2563
 
2414
- comment: function() {},
2564
+ topStack: function(wrapped) {
2565
+ var stack = (this.isInline() ? this.inlineStack : this.compileStack),
2566
+ item = stack[stack.length - 1];
2415
2567
 
2416
- // HELPERS
2417
- opcode: function(name) {
2418
- this.opcodes.push({ opcode: name, args: [].slice.call(arguments, 1) });
2568
+ if (!wrapped && (item instanceof Literal)) {
2569
+ return item.value;
2570
+ } else {
2571
+ return item;
2572
+ }
2419
2573
  },
2420
2574
 
2421
- declare: function(name, value) {
2422
- this.opcodes.push({ opcode: 'DECLARE', name: name, value: value });
2575
+ quotedString: function(str) {
2576
+ return '"' + str
2577
+ .replace(/\\/g, '\\\\')
2578
+ .replace(/"/g, '\\"')
2579
+ .replace(/\n/g, '\\n')
2580
+ .replace(/\r/g, '\\r')
2581
+ .replace(/\u2028/g, '\\u2028') // Per Ecma-262 7.3 + 7.8.4
2582
+ .replace(/\u2029/g, '\\u2029') + '"';
2423
2583
  },
2424
2584
 
2425
- addDepth: function(depth) {
2426
- if(isNaN(depth)) { throw new Error("EWOT"); }
2427
- if(depth === 0) { return; }
2585
+ setupHelper: function(paramSize, name, missingParams) {
2586
+ var params = [],
2587
+ paramsInit = this.setupParams(paramSize, params, missingParams);
2588
+ var foundHelper = this.nameLookup('helpers', name, 'helper');
2428
2589
 
2429
- if(!this.depths[depth]) {
2430
- this.depths[depth] = true;
2431
- this.depths.list.push(depth);
2432
- }
2590
+ return {
2591
+ params: params,
2592
+ paramsInit: paramsInit,
2593
+ name: foundHelper,
2594
+ callParams: ["depth0"].concat(params).join(", "),
2595
+ helperMissingParams: missingParams && ["depth0", this.quotedString(name)].concat(params).join(", ")
2596
+ };
2433
2597
  },
2434
2598
 
2435
- classifyMustache: function(mustache) {
2436
- var isHelper = mustache.isHelper;
2437
- var isEligible = mustache.eligibleHelper;
2438
- var options = this.options;
2599
+ setupOptions: function(paramSize, params) {
2600
+ var options = [], contexts = [], types = [], param, inverse, program;
2439
2601
 
2440
- // if ambiguous, we can possibly resolve the ambiguity now
2441
- if (isEligible && !isHelper) {
2442
- var name = mustache.id.parts[0];
2602
+ options.push("hash:" + this.popStack());
2443
2603
 
2444
- if (options.knownHelpers[name]) {
2445
- isHelper = true;
2446
- } else if (options.knownHelpersOnly) {
2447
- isEligible = false;
2448
- }
2604
+ if (this.options.stringParams) {
2605
+ options.push("hashTypes:" + this.popStack());
2606
+ options.push("hashContexts:" + this.popStack());
2449
2607
  }
2450
2608
 
2451
- if (isHelper) { return "helper"; }
2452
- else if (isEligible) { return "ambiguous"; }
2453
- else { return "simple"; }
2454
- },
2609
+ inverse = this.popStack();
2610
+ program = this.popStack();
2455
2611
 
2456
- pushParams: function(params) {
2457
- var i = params.length, param;
2612
+ // Avoid setting fn and inverse if neither are set. This allows
2613
+ // helpers to do a check for `if (options.fn)`
2614
+ if (program || inverse) {
2615
+ if (!program) {
2616
+ this.context.aliases.self = "this";
2617
+ program = "self.noop";
2618
+ }
2458
2619
 
2459
- while(i--) {
2460
- param = params[i];
2620
+ if (!inverse) {
2621
+ this.context.aliases.self = "this";
2622
+ inverse = "self.noop";
2623
+ }
2461
2624
 
2462
- if(this.options.stringParams) {
2463
- if(param.depth) {
2464
- this.addDepth(param.depth);
2465
- }
2625
+ options.push("inverse:" + inverse);
2626
+ options.push("fn:" + program);
2627
+ }
2466
2628
 
2467
- this.opcode('getContext', param.depth || 0);
2468
- this.opcode('pushStringParam', param.stringModeValue, param.type);
2469
- } else {
2470
- this[param.type](param);
2629
+ for(var i=0; i<paramSize; i++) {
2630
+ param = this.popStack();
2631
+ params.push(param);
2632
+
2633
+ if(this.options.stringParams) {
2634
+ types.push(this.popStack());
2635
+ contexts.push(this.popStack());
2471
2636
  }
2472
2637
  }
2473
- },
2474
2638
 
2475
- setupMustacheParams: function(mustache) {
2476
- var params = mustache.params;
2477
- this.pushParams(params);
2639
+ if (this.options.stringParams) {
2640
+ options.push("contexts:[" + contexts.join(",") + "]");
2641
+ options.push("types:[" + types.join(",") + "]");
2642
+ }
2478
2643
 
2479
- if(mustache.hash) {
2480
- this.hash(mustache.hash);
2481
- } else {
2482
- this.opcode('emptyHash');
2644
+ if(this.options.data) {
2645
+ options.push("data:data");
2483
2646
  }
2484
2647
 
2485
- return params;
2648
+ return options;
2486
2649
  },
2487
2650
 
2488
- // this will replace setupMustacheParams when we're done
2489
- setupFullMustacheParams: function(mustache, program, inverse) {
2490
- var params = mustache.params;
2491
- this.pushParams(params);
2492
-
2493
- this.opcode('pushProgram', program);
2494
- this.opcode('pushProgram', inverse);
2651
+ // the params and contexts arguments are passed in arrays
2652
+ // to fill in
2653
+ setupParams: function(paramSize, params, useRegister) {
2654
+ var options = '{' + this.setupOptions(paramSize, params).join(',') + '}';
2495
2655
 
2496
- if(mustache.hash) {
2497
- this.hash(mustache.hash);
2656
+ if (useRegister) {
2657
+ this.useRegister('options');
2658
+ params.push('options');
2659
+ return 'options=' + options;
2498
2660
  } else {
2499
- this.opcode('emptyHash');
2661
+ params.push(options);
2662
+ return '';
2500
2663
  }
2501
-
2502
- return params;
2503
2664
  }
2504
2665
  };
2505
2666
 
2506
- function precompile(input, options) {
2507
- if (input == null || (typeof input !== 'string' && input.constructor !== AST.ProgramNode)) {
2508
- throw new Exception("You must pass a string or Handlebars AST to Handlebars.precompile. You passed " + input);
2509
- }
2667
+ var reservedWords = (
2668
+ "break else new var" +
2669
+ " case finally return void" +
2670
+ " catch for switch while" +
2671
+ " continue function this with" +
2672
+ " default if throw" +
2673
+ " delete in try" +
2674
+ " do instanceof typeof" +
2675
+ " abstract enum int short" +
2676
+ " boolean export interface static" +
2677
+ " byte extends long super" +
2678
+ " char final native synchronized" +
2679
+ " class float package throws" +
2680
+ " const goto private transient" +
2681
+ " debugger implements protected volatile" +
2682
+ " double import public let yield"
2683
+ ).split(" ");
2510
2684
 
2511
- options = options || {};
2512
- if (!('data' in options)) {
2513
- options.data = true;
2514
- }
2685
+ var compilerWords = JavaScriptCompiler.RESERVED_WORDS = {};
2515
2686
 
2516
- var ast = parse(input);
2517
- var environment = new Compiler().compile(ast, options);
2518
- return new JavaScriptCompiler().compile(environment, options);
2687
+ for(var i=0, l=reservedWords.length; i<l; i++) {
2688
+ compilerWords[reservedWords[i]] = true;
2519
2689
  }
2520
2690
 
2521
- __exports__.precompile = precompile;function compile(input, options, env) {
2522
- if (input == null || (typeof input !== 'string' && input.constructor !== AST.ProgramNode)) {
2523
- throw new Exception("You must pass a string or Handlebars AST to Handlebars.compile. You passed " + input);
2524
- }
2525
-
2526
- options = options || {};
2527
-
2528
- if (!('data' in options)) {
2529
- options.data = true;
2530
- }
2531
-
2532
- var compiled;
2533
-
2534
- function compileInput() {
2535
- var ast = parse(input);
2536
- var environment = new Compiler().compile(ast, options);
2537
- var templateSpec = new JavaScriptCompiler().compile(environment, options, undefined, true);
2538
- return env.template(templateSpec);
2691
+ JavaScriptCompiler.isValidJavaScriptVariableName = function(name) {
2692
+ if(!JavaScriptCompiler.RESERVED_WORDS[name] && /^[a-zA-Z_$][0-9a-zA-Z_$]*$/.test(name)) {
2693
+ return true;
2539
2694
  }
2695
+ return false;
2696
+ };
2540
2697
 
2541
- // Template is only compiled on first use and cached after that point.
2542
- return function(context, options) {
2543
- if (!compiled) {
2544
- compiled = compileInput();
2545
- }
2546
- return compiled.call(this, context, options);
2547
- };
2548
- }
2549
-
2550
- __exports__.compile = compile;
2698
+ __exports__ = JavaScriptCompiler;
2551
2699
  return __exports__;
2552
- })(__module5__, __module8__, __module11__, __module7__);
2700
+ })(__module2__, __module5__);
2553
2701
 
2554
2702
  // handlebars.js
2555
2703
  var __module0__ = (function(__dependency1__, __dependency2__, __dependency3__, __dependency4__, __dependency5__) {
2556
2704
  "use strict";
2557
2705
  var __exports__;
2706
+ /*globals Handlebars: true */
2558
2707
  var Handlebars = __dependency1__;
2559
2708
 
2560
2709
  // Compiler imports
@@ -2573,7 +2722,9 @@ var __module0__ = (function(__dependency1__, __dependency2__, __dependency3__, _
2573
2722
  hb.compile = function(input, options) {
2574
2723
  return compile(input, options, hb);
2575
2724
  };
2576
- hb.precompile = precompile;
2725
+ hb.precompile = function (input, options) {
2726
+ return precompile(input, options, hb);
2727
+ };
2577
2728
 
2578
2729
  hb.AST = AST;
2579
2730
  hb.Compiler = Compiler;