handlebars-source 1.0.7 → 1.0.9
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of handlebars-source might be problematic. Click here for more details.
- data/dist/handlebars.js +1232 -1024
- data/dist/handlebars.runtime.js +100 -55
- metadata +1 -1
data/dist/handlebars.js
CHANGED
@@ -1,11 +1,41 @@
|
|
1
|
-
|
1
|
+
/*
|
2
2
|
|
3
|
-
|
4
|
-
|
3
|
+
Copyright (C) 2011 by Yehuda Katz
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
7
|
+
in the Software without restriction, including without limitation the rights
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
10
|
+
furnished to do so, subject to the following conditions:
|
11
|
+
|
12
|
+
The above copyright notice and this permission notice shall be included in
|
13
|
+
all copies or substantial portions of the Software.
|
5
14
|
|
6
|
-
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
21
|
+
THE SOFTWARE.
|
7
22
|
|
8
|
-
|
23
|
+
*/
|
24
|
+
|
25
|
+
// lib/handlebars/browser-prefix.js
|
26
|
+
var Handlebars = {};
|
27
|
+
|
28
|
+
(function(Handlebars, undefined) {
|
29
|
+
;
|
30
|
+
// lib/handlebars/base.js
|
31
|
+
|
32
|
+
Handlebars.VERSION = "1.0.0-rc.3";
|
33
|
+
Handlebars.COMPILER_REVISION = 2;
|
34
|
+
|
35
|
+
Handlebars.REVISION_CHANGES = {
|
36
|
+
1: '<= 1.0.rc.2', // 1.0.rc.2 is actually rev2 but doesn't report it
|
37
|
+
2: '>= 1.0.0-rc.3'
|
38
|
+
};
|
9
39
|
|
10
40
|
Handlebars.helpers = {};
|
11
41
|
Handlebars.partials = {};
|
@@ -32,8 +62,6 @@ var toString = Object.prototype.toString, functionType = "[object Function]";
|
|
32
62
|
Handlebars.registerHelper('blockHelperMissing', function(context, options) {
|
33
63
|
var inverse = options.inverse || function() {}, fn = options.fn;
|
34
64
|
|
35
|
-
|
36
|
-
var ret = "";
|
37
65
|
var type = toString.call(context);
|
38
66
|
|
39
67
|
if(type === functionType) { context = context.call(this); }
|
@@ -124,11 +152,7 @@ Handlebars.registerHelper('if', function(context, options) {
|
|
124
152
|
});
|
125
153
|
|
126
154
|
Handlebars.registerHelper('unless', function(context, options) {
|
127
|
-
|
128
|
-
options.fn = inverse;
|
129
|
-
options.inverse = fn;
|
130
|
-
|
131
|
-
return Handlebars.helpers['if'].call(this, context, options);
|
155
|
+
return Handlebars.helpers['if'].call(this, context, {fn: options.inverse, inverse: options.fn});
|
132
156
|
});
|
133
157
|
|
134
158
|
Handlebars.registerHelper('with', function(context, options) {
|
@@ -139,8 +163,6 @@ Handlebars.registerHelper('log', function(context, options) {
|
|
139
163
|
var level = options.data && options.data.level != null ? parseInt(options.data.level, 10) : 1;
|
140
164
|
Handlebars.log(level, context);
|
141
165
|
});
|
142
|
-
|
143
|
-
}(this.Handlebars));
|
144
166
|
;
|
145
167
|
// lib/handlebars/compiler/parser.js
|
146
168
|
/* Jison generated parser */
|
@@ -530,7 +552,7 @@ pushState:function begin(condition) {
|
|
530
552
|
lexer.options = {};
|
531
553
|
lexer.performAction = function anonymous(yy,yy_,$avoiding_name_collisions,YY_START) {
|
532
554
|
|
533
|
-
var YYSTATE=YY_START
|
555
|
+
var YYSTATE=YY_START
|
534
556
|
switch($avoiding_name_collisions) {
|
535
557
|
case 0:
|
536
558
|
if(yy_.yytext.slice(-1) !== "\\") this.begin("mu");
|
@@ -608,150 +630,152 @@ case 32: return 5;
|
|
608
630
|
break;
|
609
631
|
}
|
610
632
|
};
|
611
|
-
lexer.rules = [/^(?:[^\x00]*?(?=(\{\{)))/,/^(?:[^\x00]+)/,/^(?:[^\x00]{2,}?(?=(\{\{|$)))/,/^(?:[\s\S]*?--\}\})/,/^(?:\{\{>)/,/^(?:\{\{#)/,/^(?:\{\{\/)/,/^(?:\{\{\^)/,/^(?:\{\{\s*else\b)/,/^(?:\{\{\{)/,/^(?:\{\{&)/,/^(?:\{\{!--)/,/^(?:\{\{![\s\S]*?\}\})/,/^(?:\{\{)/,/^(?:=)/,/^(?:\.(?=[} ]))/,/^(?:\.\.)/,/^(?:[\/.])/,/^(?:\s+)/,/^(?:\}\}\})/,/^(?:\}\})/,/^(?:"(\\["]|[^"])*")/,/^(?:'(\\[']|[^'])*')/,/^(?:@[a-zA-Z]+)/,/^(?:true(?=[}\s]))/,/^(?:false(?=[}\s]))/,/^(
|
633
|
+
lexer.rules = [/^(?:[^\x00]*?(?=(\{\{)))/,/^(?:[^\x00]+)/,/^(?:[^\x00]{2,}?(?=(\{\{|$)))/,/^(?:[\s\S]*?--\}\})/,/^(?:\{\{>)/,/^(?:\{\{#)/,/^(?:\{\{\/)/,/^(?:\{\{\^)/,/^(?:\{\{\s*else\b)/,/^(?:\{\{\{)/,/^(?:\{\{&)/,/^(?:\{\{!--)/,/^(?:\{\{![\s\S]*?\}\})/,/^(?:\{\{)/,/^(?:=)/,/^(?:\.(?=[} ]))/,/^(?:\.\.)/,/^(?:[\/.])/,/^(?:\s+)/,/^(?:\}\}\})/,/^(?:\}\})/,/^(?:"(\\["]|[^"])*")/,/^(?:'(\\[']|[^'])*')/,/^(?:@[a-zA-Z]+)/,/^(?:true(?=[}\s]))/,/^(?:false(?=[}\s]))/,/^(?:-?[0-9]+(?=[}\s]))/,/^(?:[a-zA-Z0-9_$-]+(?=[=}\s\/.]))/,/^(?:\[[^\]]*\])/,/^(?:.)/,/^(?:\s+)/,/^(?:[a-zA-Z0-9_$-/]+)/,/^(?:$)/];
|
612
634
|
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,32],"inclusive":false},"emu":{"rules":[2],"inclusive":false},"com":{"rules":[3],"inclusive":false},"par":{"rules":[30,31],"inclusive":false},"INITIAL":{"rules":[0,1,32],"inclusive":true}};
|
613
|
-
return lexer;})()
|
635
|
+
return lexer;})()
|
614
636
|
parser.lexer = lexer;
|
615
637
|
function Parser () { this.yy = {}; }Parser.prototype = parser;parser.Parser = Parser;
|
616
638
|
return new Parser;
|
617
639
|
})();;
|
618
640
|
// lib/handlebars/compiler/base.js
|
641
|
+
|
619
642
|
Handlebars.Parser = handlebars;
|
620
643
|
|
621
|
-
Handlebars.parse = function(
|
644
|
+
Handlebars.parse = function(input) {
|
645
|
+
|
646
|
+
// Just return if an already-compile AST was passed in.
|
647
|
+
if(input.constructor === Handlebars.AST.ProgramNode) { return input; }
|
648
|
+
|
622
649
|
Handlebars.Parser.yy = Handlebars.AST;
|
623
|
-
return Handlebars.Parser.parse(
|
650
|
+
return Handlebars.Parser.parse(input);
|
624
651
|
};
|
625
|
-
|
626
|
-
Handlebars.print = function(ast) {
|
627
|
-
return new Handlebars.PrintVisitor().accept(ast);
|
628
|
-
};;
|
652
|
+
;
|
629
653
|
// lib/handlebars/compiler/ast.js
|
630
|
-
|
631
|
-
|
632
|
-
Handlebars.AST = {};
|
654
|
+
Handlebars.AST = {};
|
633
655
|
|
634
|
-
|
635
|
-
|
636
|
-
|
637
|
-
|
638
|
-
|
656
|
+
Handlebars.AST.ProgramNode = function(statements, inverse) {
|
657
|
+
this.type = "program";
|
658
|
+
this.statements = statements;
|
659
|
+
if(inverse) { this.inverse = new Handlebars.AST.ProgramNode(inverse); }
|
660
|
+
};
|
639
661
|
|
640
|
-
|
641
|
-
|
642
|
-
|
643
|
-
|
662
|
+
Handlebars.AST.MustacheNode = function(rawParams, hash, unescaped) {
|
663
|
+
this.type = "mustache";
|
664
|
+
this.escaped = !unescaped;
|
665
|
+
this.hash = hash;
|
644
666
|
|
645
|
-
|
646
|
-
|
667
|
+
var id = this.id = rawParams[0];
|
668
|
+
var params = this.params = rawParams.slice(1);
|
647
669
|
|
648
|
-
|
649
|
-
|
650
|
-
|
670
|
+
// a mustache is an eligible helper if:
|
671
|
+
// * its id is simple (a single part, not `this` or `..`)
|
672
|
+
var eligibleHelper = this.eligibleHelper = id.isSimple;
|
651
673
|
|
652
|
-
|
653
|
-
|
654
|
-
|
655
|
-
|
674
|
+
// a mustache is definitely a helper if:
|
675
|
+
// * it is an eligible helper, and
|
676
|
+
// * it has at least one parameter or hash segment
|
677
|
+
this.isHelper = eligibleHelper && (params.length || hash);
|
656
678
|
|
657
|
-
|
658
|
-
|
659
|
-
|
660
|
-
|
679
|
+
// if a mustache is an eligible helper but not a definite
|
680
|
+
// helper, it is ambiguous, and will be resolved in a later
|
681
|
+
// pass or at runtime.
|
682
|
+
};
|
661
683
|
|
662
|
-
|
663
|
-
|
664
|
-
|
665
|
-
|
666
|
-
|
684
|
+
Handlebars.AST.PartialNode = function(partialName, context) {
|
685
|
+
this.type = "partial";
|
686
|
+
this.partialName = partialName;
|
687
|
+
this.context = context;
|
688
|
+
};
|
667
689
|
|
690
|
+
Handlebars.AST.BlockNode = function(mustache, program, inverse, close) {
|
668
691
|
var verifyMatch = function(open, close) {
|
669
692
|
if(open.original !== close.original) {
|
670
693
|
throw new Handlebars.Exception(open.original + " doesn't match " + close.original);
|
671
694
|
}
|
672
695
|
};
|
673
696
|
|
674
|
-
|
675
|
-
|
676
|
-
|
677
|
-
|
678
|
-
|
679
|
-
this.inverse = inverse;
|
697
|
+
verifyMatch(mustache.id, close);
|
698
|
+
this.type = "block";
|
699
|
+
this.mustache = mustache;
|
700
|
+
this.program = program;
|
701
|
+
this.inverse = inverse;
|
680
702
|
|
681
|
-
|
682
|
-
|
683
|
-
|
684
|
-
|
703
|
+
if (this.inverse && !this.program) {
|
704
|
+
this.isInverse = true;
|
705
|
+
}
|
706
|
+
};
|
685
707
|
|
686
|
-
|
687
|
-
|
688
|
-
|
689
|
-
|
708
|
+
Handlebars.AST.ContentNode = function(string) {
|
709
|
+
this.type = "content";
|
710
|
+
this.string = string;
|
711
|
+
};
|
690
712
|
|
691
|
-
|
692
|
-
|
693
|
-
|
694
|
-
|
713
|
+
Handlebars.AST.HashNode = function(pairs) {
|
714
|
+
this.type = "hash";
|
715
|
+
this.pairs = pairs;
|
716
|
+
};
|
695
717
|
|
696
|
-
|
697
|
-
|
698
|
-
|
718
|
+
Handlebars.AST.IdNode = function(parts) {
|
719
|
+
this.type = "ID";
|
720
|
+
this.original = parts.join(".");
|
699
721
|
|
700
|
-
|
722
|
+
var dig = [], depth = 0;
|
701
723
|
|
702
|
-
|
703
|
-
|
724
|
+
for(var i=0,l=parts.length; i<l; i++) {
|
725
|
+
var part = parts[i];
|
704
726
|
|
705
|
-
|
706
|
-
|
707
|
-
else
|
727
|
+
if (part === ".." || part === "." || part === "this") {
|
728
|
+
if (dig.length > 0) { throw new Handlebars.Exception("Invalid path: " + this.original); }
|
729
|
+
else if (part === "..") { depth++; }
|
730
|
+
else { this.isScoped = true; }
|
708
731
|
}
|
732
|
+
else { dig.push(part); }
|
733
|
+
}
|
709
734
|
|
710
|
-
|
711
|
-
|
712
|
-
|
713
|
-
|
714
|
-
// an ID is simple if it only has one part, and that part is not
|
715
|
-
// `..` or `this`.
|
716
|
-
this.isSimple = parts.length === 1 && !this.isScoped && depth === 0;
|
735
|
+
this.parts = dig;
|
736
|
+
this.string = dig.join('.');
|
737
|
+
this.depth = depth;
|
717
738
|
|
718
|
-
|
719
|
-
|
739
|
+
// an ID is simple if it only has one part, and that part is not
|
740
|
+
// `..` or `this`.
|
741
|
+
this.isSimple = parts.length === 1 && !this.isScoped && depth === 0;
|
720
742
|
|
721
|
-
|
722
|
-
|
723
|
-
this.name = name;
|
724
|
-
};
|
743
|
+
this.stringModeValue = this.string;
|
744
|
+
};
|
725
745
|
|
726
|
-
|
727
|
-
|
728
|
-
|
729
|
-
|
746
|
+
Handlebars.AST.PartialNameNode = function(name) {
|
747
|
+
this.type = "PARTIAL_NAME";
|
748
|
+
this.name = name;
|
749
|
+
};
|
730
750
|
|
731
|
-
|
732
|
-
|
733
|
-
|
734
|
-
|
735
|
-
};
|
751
|
+
Handlebars.AST.DataNode = function(id) {
|
752
|
+
this.type = "DATA";
|
753
|
+
this.id = id;
|
754
|
+
};
|
736
755
|
|
737
|
-
|
738
|
-
|
739
|
-
|
740
|
-
|
741
|
-
|
756
|
+
Handlebars.AST.StringNode = function(string) {
|
757
|
+
this.type = "STRING";
|
758
|
+
this.string = string;
|
759
|
+
this.stringModeValue = string;
|
760
|
+
};
|
742
761
|
|
743
|
-
|
744
|
-
|
745
|
-
|
746
|
-
|
747
|
-
|
762
|
+
Handlebars.AST.IntegerNode = function(integer) {
|
763
|
+
this.type = "INTEGER";
|
764
|
+
this.integer = integer;
|
765
|
+
this.stringModeValue = Number(integer);
|
766
|
+
};
|
748
767
|
|
749
|
-
|
750
|
-
|
751
|
-
|
752
|
-
|
768
|
+
Handlebars.AST.BooleanNode = function(bool) {
|
769
|
+
this.type = "BOOLEAN";
|
770
|
+
this.bool = bool;
|
771
|
+
this.stringModeValue = bool === "true";
|
772
|
+
};
|
753
773
|
|
754
|
-
|
774
|
+
Handlebars.AST.CommentNode = function(comment) {
|
775
|
+
this.type = "comment";
|
776
|
+
this.comment = comment;
|
777
|
+
};
|
778
|
+
;
|
755
779
|
// lib/handlebars/utils.js
|
756
780
|
|
757
781
|
var errorProps = ['description', 'fileName', 'lineNumber', 'message', 'name', 'number', 'stack'];
|
@@ -774,1135 +798,1294 @@ Handlebars.SafeString.prototype.toString = function() {
|
|
774
798
|
return this.string.toString();
|
775
799
|
};
|
776
800
|
|
777
|
-
|
778
|
-
|
779
|
-
|
780
|
-
|
781
|
-
|
782
|
-
|
783
|
-
|
784
|
-
|
785
|
-
};
|
801
|
+
var escape = {
|
802
|
+
"&": "&",
|
803
|
+
"<": "<",
|
804
|
+
">": ">",
|
805
|
+
'"': """,
|
806
|
+
"'": "'",
|
807
|
+
"`": "`"
|
808
|
+
};
|
786
809
|
|
787
|
-
|
788
|
-
|
810
|
+
var badChars = /[&<>"'`]/g;
|
811
|
+
var possible = /[&<>"'`]/;
|
789
812
|
|
790
|
-
|
791
|
-
|
792
|
-
|
813
|
+
var escapeChar = function(chr) {
|
814
|
+
return escape[chr] || "&";
|
815
|
+
};
|
793
816
|
|
794
|
-
|
795
|
-
|
796
|
-
|
797
|
-
|
798
|
-
|
799
|
-
|
800
|
-
|
801
|
-
|
817
|
+
Handlebars.Utils = {
|
818
|
+
escapeExpression: function(string) {
|
819
|
+
// don't escape SafeStrings, since they're already safe
|
820
|
+
if (string instanceof Handlebars.SafeString) {
|
821
|
+
return string.toString();
|
822
|
+
} else if (string == null || string === false) {
|
823
|
+
return "";
|
824
|
+
}
|
802
825
|
|
803
|
-
|
804
|
-
|
805
|
-
|
826
|
+
if(!possible.test(string)) { return string; }
|
827
|
+
return string.replace(badChars, escapeChar);
|
828
|
+
},
|
806
829
|
|
807
|
-
|
808
|
-
|
809
|
-
|
810
|
-
|
811
|
-
|
812
|
-
|
813
|
-
|
814
|
-
}
|
830
|
+
isEmpty: function(value) {
|
831
|
+
if (!value && value !== 0) {
|
832
|
+
return true;
|
833
|
+
} else if(toString.call(value) === "[object Array]" && value.length === 0) {
|
834
|
+
return true;
|
835
|
+
} else {
|
836
|
+
return false;
|
815
837
|
}
|
816
|
-
}
|
817
|
-
}
|
838
|
+
}
|
839
|
+
};
|
840
|
+
;
|
818
841
|
// lib/handlebars/compiler/compiler.js
|
819
842
|
|
820
843
|
/*jshint eqnull:true*/
|
821
|
-
Handlebars.Compiler = function() {};
|
822
|
-
Handlebars.JavaScriptCompiler = function() {};
|
844
|
+
var Compiler = Handlebars.Compiler = function() {};
|
845
|
+
var JavaScriptCompiler = Handlebars.JavaScriptCompiler = function() {};
|
823
846
|
|
824
|
-
|
825
|
-
|
826
|
-
|
827
|
-
|
828
|
-
// and then proceed as if the resulting value was provided to blockHelperMissing.
|
847
|
+
// the foundHelper register will disambiguate helper lookup from finding a
|
848
|
+
// function in a context. This is necessary for mustache compatibility, which
|
849
|
+
// requires that context functions in blocks are evaluated by blockHelperMissing,
|
850
|
+
// and then proceed as if the resulting value was provided to blockHelperMissing.
|
829
851
|
|
830
|
-
|
831
|
-
|
852
|
+
Compiler.prototype = {
|
853
|
+
compiler: Compiler,
|
832
854
|
|
833
|
-
|
834
|
-
|
855
|
+
disassemble: function() {
|
856
|
+
var opcodes = this.opcodes, opcode, out = [], params, param;
|
835
857
|
|
836
|
-
|
837
|
-
|
858
|
+
for (var i=0, l=opcodes.length; i<l; i++) {
|
859
|
+
opcode = opcodes[i];
|
838
860
|
|
839
|
-
|
840
|
-
|
841
|
-
|
842
|
-
|
843
|
-
|
844
|
-
|
845
|
-
|
846
|
-
|
847
|
-
}
|
848
|
-
params.push(param);
|
861
|
+
if (opcode.opcode === 'DECLARE') {
|
862
|
+
out.push("DECLARE " + opcode.name + "=" + opcode.value);
|
863
|
+
} else {
|
864
|
+
params = [];
|
865
|
+
for (var j=0; j<opcode.args.length; j++) {
|
866
|
+
param = opcode.args[j];
|
867
|
+
if (typeof param === "string") {
|
868
|
+
param = "\"" + param.replace("\n", "\\n") + "\"";
|
849
869
|
}
|
850
|
-
|
870
|
+
params.push(param);
|
851
871
|
}
|
872
|
+
out.push(opcode.opcode + " " + params.join(" "));
|
852
873
|
}
|
874
|
+
}
|
853
875
|
|
854
|
-
|
855
|
-
|
876
|
+
return out.join("\n");
|
877
|
+
},
|
878
|
+
equals: function(other) {
|
879
|
+
var len = this.opcodes.length;
|
880
|
+
if (other.opcodes.length !== len) {
|
881
|
+
return false;
|
882
|
+
}
|
856
883
|
|
857
|
-
|
858
|
-
|
859
|
-
|
860
|
-
|
861
|
-
|
862
|
-
|
863
|
-
|
864
|
-
|
865
|
-
|
866
|
-
this.options.knownHelpers = {
|
867
|
-
'helperMissing': true,
|
868
|
-
'blockHelperMissing': true,
|
869
|
-
'each': true,
|
870
|
-
'if': true,
|
871
|
-
'unless': true,
|
872
|
-
'with': true,
|
873
|
-
'log': true
|
874
|
-
};
|
875
|
-
if (knownHelpers) {
|
876
|
-
for (var name in knownHelpers) {
|
877
|
-
this.options.knownHelpers[name] = knownHelpers[name];
|
884
|
+
for (var i = 0; i < len; i++) {
|
885
|
+
var opcode = this.opcodes[i],
|
886
|
+
otherOpcode = other.opcodes[i];
|
887
|
+
if (opcode.opcode !== otherOpcode.opcode || opcode.args.length !== otherOpcode.args.length) {
|
888
|
+
return false;
|
889
|
+
}
|
890
|
+
for (var j = 0; j < opcode.args.length; j++) {
|
891
|
+
if (opcode.args[j] !== otherOpcode.args[j]) {
|
892
|
+
return false;
|
878
893
|
}
|
879
894
|
}
|
895
|
+
}
|
880
896
|
|
881
|
-
|
882
|
-
|
883
|
-
|
884
|
-
|
885
|
-
|
886
|
-
|
897
|
+
len = this.children.length;
|
898
|
+
if (other.children.length !== len) {
|
899
|
+
return false;
|
900
|
+
}
|
901
|
+
for (i = 0; i < len; i++) {
|
902
|
+
if (!this.children[i].equals(other.children[i])) {
|
903
|
+
return false;
|
904
|
+
}
|
905
|
+
}
|
887
906
|
|
888
|
-
|
889
|
-
|
890
|
-
this.opcodes = [];
|
907
|
+
return true;
|
908
|
+
},
|
891
909
|
|
892
|
-
|
893
|
-
|
894
|
-
|
910
|
+
guid: 0,
|
911
|
+
|
912
|
+
compile: function(program, options) {
|
913
|
+
this.children = [];
|
914
|
+
this.depths = {list: []};
|
915
|
+
this.options = options;
|
916
|
+
|
917
|
+
// These changes will propagate to the other compiler components
|
918
|
+
var knownHelpers = this.options.knownHelpers;
|
919
|
+
this.options.knownHelpers = {
|
920
|
+
'helperMissing': true,
|
921
|
+
'blockHelperMissing': true,
|
922
|
+
'each': true,
|
923
|
+
'if': true,
|
924
|
+
'unless': true,
|
925
|
+
'with': true,
|
926
|
+
'log': true
|
927
|
+
};
|
928
|
+
if (knownHelpers) {
|
929
|
+
for (var name in knownHelpers) {
|
930
|
+
this.options.knownHelpers[name] = knownHelpers[name];
|
895
931
|
}
|
896
|
-
|
932
|
+
}
|
897
933
|
|
898
|
-
|
899
|
-
|
900
|
-
});
|
934
|
+
return this.program(program);
|
935
|
+
},
|
901
936
|
|
902
|
-
|
903
|
-
|
937
|
+
accept: function(node) {
|
938
|
+
return this[node.type](node);
|
939
|
+
},
|
904
940
|
|
905
|
-
|
906
|
-
|
907
|
-
|
941
|
+
program: function(program) {
|
942
|
+
var statements = program.statements, statement;
|
943
|
+
this.opcodes = [];
|
908
944
|
|
909
|
-
|
945
|
+
for(var i=0, l=statements.length; i<l; i++) {
|
946
|
+
statement = statements[i];
|
947
|
+
this[statement.type](statement);
|
948
|
+
}
|
949
|
+
this.isSimple = l === 1;
|
910
950
|
|
911
|
-
|
951
|
+
this.depths.list = this.depths.list.sort(function(a, b) {
|
952
|
+
return a - b;
|
953
|
+
});
|
912
954
|
|
913
|
-
|
914
|
-
|
955
|
+
return this;
|
956
|
+
},
|
915
957
|
|
916
|
-
|
917
|
-
|
918
|
-
|
958
|
+
compileProgram: function(program) {
|
959
|
+
var result = new this.compiler().compile(program, this.options);
|
960
|
+
var guid = this.guid++, depth;
|
919
961
|
|
920
|
-
|
921
|
-
},
|
962
|
+
this.usePartial = this.usePartial || result.usePartial;
|
922
963
|
|
923
|
-
|
924
|
-
var mustache = block.mustache,
|
925
|
-
program = block.program,
|
926
|
-
inverse = block.inverse;
|
964
|
+
this.children[guid] = result;
|
927
965
|
|
928
|
-
|
929
|
-
|
930
|
-
}
|
966
|
+
for(var i=0, l=result.depths.list.length; i<l; i++) {
|
967
|
+
depth = result.depths.list[i];
|
931
968
|
|
932
|
-
if
|
933
|
-
|
934
|
-
|
969
|
+
if(depth < 2) { continue; }
|
970
|
+
else { this.addDepth(depth - 1); }
|
971
|
+
}
|
935
972
|
|
936
|
-
|
973
|
+
return guid;
|
974
|
+
},
|
937
975
|
|
938
|
-
|
939
|
-
|
940
|
-
|
941
|
-
|
976
|
+
block: function(block) {
|
977
|
+
var mustache = block.mustache,
|
978
|
+
program = block.program,
|
979
|
+
inverse = block.inverse;
|
942
980
|
|
943
|
-
|
944
|
-
|
945
|
-
|
946
|
-
this.opcode('pushProgram', inverse);
|
947
|
-
this.opcode('pushHash');
|
948
|
-
this.opcode('blockValue');
|
949
|
-
} else {
|
950
|
-
this.ambiguousMustache(mustache, program, inverse);
|
951
|
-
|
952
|
-
// now that the simple mustache is resolved, we need to
|
953
|
-
// evaluate it by executing `blockHelperMissing`
|
954
|
-
this.opcode('pushProgram', program);
|
955
|
-
this.opcode('pushProgram', inverse);
|
956
|
-
this.opcode('pushHash');
|
957
|
-
this.opcode('ambiguousBlockValue');
|
958
|
-
}
|
981
|
+
if (program) {
|
982
|
+
program = this.compileProgram(program);
|
983
|
+
}
|
959
984
|
|
960
|
-
|
961
|
-
|
985
|
+
if (inverse) {
|
986
|
+
inverse = this.compileProgram(inverse);
|
987
|
+
}
|
962
988
|
|
963
|
-
|
964
|
-
var pairs = hash.pairs, pair, val;
|
989
|
+
var type = this.classifyMustache(mustache);
|
965
990
|
|
966
|
-
|
991
|
+
if (type === "helper") {
|
992
|
+
this.helperMustache(mustache, program, inverse);
|
993
|
+
} else if (type === "simple") {
|
994
|
+
this.simpleMustache(mustache);
|
967
995
|
|
968
|
-
|
969
|
-
|
970
|
-
|
996
|
+
// now that the simple mustache is resolved, we need to
|
997
|
+
// evaluate it by executing `blockHelperMissing`
|
998
|
+
this.opcode('pushProgram', program);
|
999
|
+
this.opcode('pushProgram', inverse);
|
1000
|
+
this.opcode('emptyHash');
|
1001
|
+
this.opcode('blockValue');
|
1002
|
+
} else {
|
1003
|
+
this.ambiguousMustache(mustache, program, inverse);
|
971
1004
|
|
972
|
-
|
973
|
-
|
974
|
-
|
975
|
-
|
976
|
-
|
1005
|
+
// now that the simple mustache is resolved, we need to
|
1006
|
+
// evaluate it by executing `blockHelperMissing`
|
1007
|
+
this.opcode('pushProgram', program);
|
1008
|
+
this.opcode('pushProgram', inverse);
|
1009
|
+
this.opcode('emptyHash');
|
1010
|
+
this.opcode('ambiguousBlockValue');
|
1011
|
+
}
|
977
1012
|
|
978
|
-
|
979
|
-
|
980
|
-
},
|
1013
|
+
this.opcode('append');
|
1014
|
+
},
|
981
1015
|
|
982
|
-
|
983
|
-
|
984
|
-
this.usePartial = true;
|
1016
|
+
hash: function(hash) {
|
1017
|
+
var pairs = hash.pairs, pair, val;
|
985
1018
|
|
986
|
-
|
987
|
-
|
1019
|
+
this.opcode('pushHash');
|
1020
|
+
|
1021
|
+
for(var i=0, l=pairs.length; i<l; i++) {
|
1022
|
+
pair = pairs[i];
|
1023
|
+
val = pair[1];
|
1024
|
+
|
1025
|
+
if (this.options.stringParams) {
|
1026
|
+
this.opcode('pushStringParam', val.stringModeValue, val.type);
|
988
1027
|
} else {
|
989
|
-
this.
|
1028
|
+
this.accept(val);
|
990
1029
|
}
|
991
1030
|
|
992
|
-
this.opcode('
|
993
|
-
|
994
|
-
|
995
|
-
|
996
|
-
content: function(content) {
|
997
|
-
this.opcode('appendContent', content.string);
|
998
|
-
},
|
1031
|
+
this.opcode('assignToHash', pair[0]);
|
1032
|
+
}
|
1033
|
+
this.opcode('popHash');
|
1034
|
+
},
|
999
1035
|
|
1000
|
-
|
1001
|
-
|
1002
|
-
|
1036
|
+
partial: function(partial) {
|
1037
|
+
var partialName = partial.partialName;
|
1038
|
+
this.usePartial = true;
|
1003
1039
|
|
1004
|
-
|
1005
|
-
|
1006
|
-
|
1007
|
-
|
1008
|
-
|
1009
|
-
this.ambiguousMustache(mustache);
|
1010
|
-
}
|
1040
|
+
if(partial.context) {
|
1041
|
+
this.ID(partial.context);
|
1042
|
+
} else {
|
1043
|
+
this.opcode('push', 'depth0');
|
1044
|
+
}
|
1011
1045
|
|
1012
|
-
|
1013
|
-
|
1014
|
-
|
1015
|
-
this.opcode('append');
|
1016
|
-
}
|
1017
|
-
},
|
1046
|
+
this.opcode('invokePartial', partialName.name);
|
1047
|
+
this.opcode('append');
|
1048
|
+
},
|
1018
1049
|
|
1019
|
-
|
1020
|
-
|
1050
|
+
content: function(content) {
|
1051
|
+
this.opcode('appendContent', content.string);
|
1052
|
+
},
|
1021
1053
|
|
1022
|
-
|
1054
|
+
mustache: function(mustache) {
|
1055
|
+
var options = this.options;
|
1056
|
+
var type = this.classifyMustache(mustache);
|
1023
1057
|
|
1024
|
-
|
1025
|
-
this.
|
1058
|
+
if (type === "simple") {
|
1059
|
+
this.simpleMustache(mustache);
|
1060
|
+
} else if (type === "helper") {
|
1061
|
+
this.helperMustache(mustache);
|
1062
|
+
} else {
|
1063
|
+
this.ambiguousMustache(mustache);
|
1064
|
+
}
|
1026
1065
|
|
1027
|
-
|
1028
|
-
|
1066
|
+
if(mustache.escaped && !options.noEscape) {
|
1067
|
+
this.opcode('appendEscaped');
|
1068
|
+
} else {
|
1069
|
+
this.opcode('append');
|
1070
|
+
}
|
1071
|
+
},
|
1029
1072
|
|
1030
|
-
|
1031
|
-
|
1073
|
+
ambiguousMustache: function(mustache, program, inverse) {
|
1074
|
+
var id = mustache.id,
|
1075
|
+
name = id.parts[0],
|
1076
|
+
isBlock = program != null || inverse != null;
|
1032
1077
|
|
1033
|
-
|
1034
|
-
this.DATA(id);
|
1035
|
-
} else if (id.parts.length) {
|
1036
|
-
this.ID(id);
|
1037
|
-
} else {
|
1038
|
-
// Simplified ID for `this`
|
1039
|
-
this.addDepth(id.depth);
|
1040
|
-
this.opcode('getContext', id.depth);
|
1041
|
-
this.opcode('pushContext');
|
1042
|
-
}
|
1078
|
+
this.opcode('getContext', id.depth);
|
1043
1079
|
|
1044
|
-
|
1045
|
-
|
1080
|
+
this.opcode('pushProgram', program);
|
1081
|
+
this.opcode('pushProgram', inverse);
|
1046
1082
|
|
1047
|
-
|
1048
|
-
|
1049
|
-
name = mustache.id.parts[0];
|
1083
|
+
this.opcode('invokeAmbiguous', name, isBlock);
|
1084
|
+
},
|
1050
1085
|
|
1051
|
-
|
1052
|
-
|
1053
|
-
} else if (this.knownHelpersOnly) {
|
1054
|
-
throw new Error("You specified knownHelpersOnly, but used the unknown helper " + name);
|
1055
|
-
} else {
|
1056
|
-
this.opcode('invokeHelper', params.length, name);
|
1057
|
-
}
|
1058
|
-
},
|
1086
|
+
simpleMustache: function(mustache) {
|
1087
|
+
var id = mustache.id;
|
1059
1088
|
|
1060
|
-
|
1089
|
+
if (id.type === 'DATA') {
|
1090
|
+
this.DATA(id);
|
1091
|
+
} else if (id.parts.length) {
|
1092
|
+
this.ID(id);
|
1093
|
+
} else {
|
1094
|
+
// Simplified ID for `this`
|
1061
1095
|
this.addDepth(id.depth);
|
1062
1096
|
this.opcode('getContext', id.depth);
|
1097
|
+
this.opcode('pushContext');
|
1098
|
+
}
|
1063
1099
|
|
1064
|
-
|
1065
|
-
|
1066
|
-
this.opcode('pushContext');
|
1067
|
-
} else {
|
1068
|
-
this.opcode('lookupOnContext', id.parts[0]);
|
1069
|
-
}
|
1070
|
-
|
1071
|
-
for(var i=1, l=id.parts.length; i<l; i++) {
|
1072
|
-
this.opcode('lookup', id.parts[i]);
|
1073
|
-
}
|
1074
|
-
},
|
1100
|
+
this.opcode('resolvePossibleLambda');
|
1101
|
+
},
|
1075
1102
|
|
1076
|
-
|
1077
|
-
|
1078
|
-
|
1079
|
-
},
|
1103
|
+
helperMustache: function(mustache, program, inverse) {
|
1104
|
+
var params = this.setupFullMustacheParams(mustache, program, inverse),
|
1105
|
+
name = mustache.id.parts[0];
|
1080
1106
|
|
1081
|
-
|
1082
|
-
this.opcode('
|
1083
|
-
}
|
1107
|
+
if (this.options.knownHelpers[name]) {
|
1108
|
+
this.opcode('invokeKnownHelper', params.length, name);
|
1109
|
+
} else if (this.knownHelpersOnly) {
|
1110
|
+
throw new Error("You specified knownHelpersOnly, but used the unknown helper " + name);
|
1111
|
+
} else {
|
1112
|
+
this.opcode('invokeHelper', params.length, name);
|
1113
|
+
}
|
1114
|
+
},
|
1084
1115
|
|
1085
|
-
|
1086
|
-
|
1087
|
-
|
1116
|
+
ID: function(id) {
|
1117
|
+
this.addDepth(id.depth);
|
1118
|
+
this.opcode('getContext', id.depth);
|
1088
1119
|
|
1089
|
-
|
1090
|
-
|
1091
|
-
|
1120
|
+
var name = id.parts[0];
|
1121
|
+
if (!name) {
|
1122
|
+
this.opcode('pushContext');
|
1123
|
+
} else {
|
1124
|
+
this.opcode('lookupOnContext', id.parts[0]);
|
1125
|
+
}
|
1092
1126
|
|
1093
|
-
|
1127
|
+
for(var i=1, l=id.parts.length; i<l; i++) {
|
1128
|
+
this.opcode('lookup', id.parts[i]);
|
1129
|
+
}
|
1130
|
+
},
|
1094
1131
|
|
1095
|
-
|
1096
|
-
|
1097
|
-
|
1098
|
-
|
1132
|
+
DATA: function(data) {
|
1133
|
+
this.options.data = true;
|
1134
|
+
this.opcode('lookupData', data.id);
|
1135
|
+
},
|
1099
1136
|
|
1100
|
-
|
1101
|
-
|
1102
|
-
|
1137
|
+
STRING: function(string) {
|
1138
|
+
this.opcode('pushString', string.string);
|
1139
|
+
},
|
1103
1140
|
|
1104
|
-
|
1105
|
-
|
1106
|
-
|
1141
|
+
INTEGER: function(integer) {
|
1142
|
+
this.opcode('pushLiteral', integer.integer);
|
1143
|
+
},
|
1107
1144
|
|
1108
|
-
|
1109
|
-
|
1110
|
-
|
1111
|
-
}
|
1112
|
-
},
|
1145
|
+
BOOLEAN: function(bool) {
|
1146
|
+
this.opcode('pushLiteral', bool.bool);
|
1147
|
+
},
|
1113
1148
|
|
1114
|
-
|
1115
|
-
var isHelper = mustache.isHelper;
|
1116
|
-
var isEligible = mustache.eligibleHelper;
|
1117
|
-
var options = this.options;
|
1149
|
+
comment: function() {},
|
1118
1150
|
|
1119
|
-
|
1120
|
-
|
1121
|
-
|
1151
|
+
// HELPERS
|
1152
|
+
opcode: function(name) {
|
1153
|
+
this.opcodes.push({ opcode: name, args: [].slice.call(arguments, 1) });
|
1154
|
+
},
|
1122
1155
|
|
1123
|
-
|
1124
|
-
|
1125
|
-
|
1126
|
-
isEligible = false;
|
1127
|
-
}
|
1128
|
-
}
|
1156
|
+
declare: function(name, value) {
|
1157
|
+
this.opcodes.push({ opcode: 'DECLARE', name: name, value: value });
|
1158
|
+
},
|
1129
1159
|
|
1130
|
-
|
1131
|
-
|
1132
|
-
|
1133
|
-
},
|
1160
|
+
addDepth: function(depth) {
|
1161
|
+
if(isNaN(depth)) { throw new Error("EWOT"); }
|
1162
|
+
if(depth === 0) { return; }
|
1134
1163
|
|
1135
|
-
|
1136
|
-
|
1164
|
+
if(!this.depths[depth]) {
|
1165
|
+
this.depths[depth] = true;
|
1166
|
+
this.depths.list.push(depth);
|
1167
|
+
}
|
1168
|
+
},
|
1137
1169
|
|
1138
|
-
|
1139
|
-
|
1170
|
+
classifyMustache: function(mustache) {
|
1171
|
+
var isHelper = mustache.isHelper;
|
1172
|
+
var isEligible = mustache.eligibleHelper;
|
1173
|
+
var options = this.options;
|
1140
1174
|
|
1141
|
-
|
1142
|
-
|
1143
|
-
|
1144
|
-
}
|
1175
|
+
// if ambiguous, we can possibly resolve the ambiguity now
|
1176
|
+
if (isEligible && !isHelper) {
|
1177
|
+
var name = mustache.id.parts[0];
|
1145
1178
|
|
1146
|
-
|
1147
|
-
|
1148
|
-
|
1149
|
-
|
1150
|
-
}
|
1179
|
+
if (options.knownHelpers[name]) {
|
1180
|
+
isHelper = true;
|
1181
|
+
} else if (options.knownHelpersOnly) {
|
1182
|
+
isEligible = false;
|
1151
1183
|
}
|
1152
|
-
}
|
1153
|
-
|
1154
|
-
setupMustacheParams: function(mustache) {
|
1155
|
-
var params = mustache.params;
|
1156
|
-
this.pushParams(params);
|
1184
|
+
}
|
1157
1185
|
|
1158
|
-
|
1159
|
-
|
1160
|
-
|
1161
|
-
|
1162
|
-
}
|
1186
|
+
if (isHelper) { return "helper"; }
|
1187
|
+
else if (isEligible) { return "ambiguous"; }
|
1188
|
+
else { return "simple"; }
|
1189
|
+
},
|
1163
1190
|
|
1164
|
-
|
1165
|
-
|
1191
|
+
pushParams: function(params) {
|
1192
|
+
var i = params.length, param;
|
1166
1193
|
|
1167
|
-
|
1168
|
-
|
1169
|
-
var params = mustache.params;
|
1170
|
-
this.pushParams(params);
|
1194
|
+
while(i--) {
|
1195
|
+
param = params[i];
|
1171
1196
|
|
1172
|
-
this.
|
1173
|
-
|
1197
|
+
if(this.options.stringParams) {
|
1198
|
+
if(param.depth) {
|
1199
|
+
this.addDepth(param.depth);
|
1200
|
+
}
|
1174
1201
|
|
1175
|
-
|
1176
|
-
this.
|
1202
|
+
this.opcode('getContext', param.depth || 0);
|
1203
|
+
this.opcode('pushStringParam', param.stringModeValue, param.type);
|
1177
1204
|
} else {
|
1178
|
-
this.
|
1205
|
+
this[param.type](param);
|
1179
1206
|
}
|
1207
|
+
}
|
1208
|
+
},
|
1209
|
+
|
1210
|
+
setupMustacheParams: function(mustache) {
|
1211
|
+
var params = mustache.params;
|
1212
|
+
this.pushParams(params);
|
1180
1213
|
|
1181
|
-
|
1214
|
+
if(mustache.hash) {
|
1215
|
+
this.hash(mustache.hash);
|
1216
|
+
} else {
|
1217
|
+
this.opcode('emptyHash');
|
1182
1218
|
}
|
1183
|
-
};
|
1184
1219
|
|
1185
|
-
|
1186
|
-
|
1187
|
-
};
|
1220
|
+
return params;
|
1221
|
+
},
|
1188
1222
|
|
1189
|
-
|
1190
|
-
|
1191
|
-
|
1192
|
-
|
1193
|
-
if (/^[0-9]+$/.test(name)) {
|
1194
|
-
return parent + "[" + name + "]";
|
1195
|
-
} else if (JavaScriptCompiler.isValidJavaScriptVariableName(name)) {
|
1196
|
-
return parent + "." + name;
|
1197
|
-
}
|
1198
|
-
else {
|
1199
|
-
return parent + "['" + name + "']";
|
1200
|
-
}
|
1201
|
-
},
|
1223
|
+
// this will replace setupMustacheParams when we're done
|
1224
|
+
setupFullMustacheParams: function(mustache, program, inverse) {
|
1225
|
+
var params = mustache.params;
|
1226
|
+
this.pushParams(params);
|
1202
1227
|
|
1203
|
-
|
1204
|
-
|
1205
|
-
return "return " + string + ";";
|
1206
|
-
} else {
|
1207
|
-
return "buffer += " + string + ";";
|
1208
|
-
}
|
1209
|
-
},
|
1228
|
+
this.opcode('pushProgram', program);
|
1229
|
+
this.opcode('pushProgram', inverse);
|
1210
1230
|
|
1211
|
-
|
1212
|
-
|
1213
|
-
}
|
1231
|
+
if(mustache.hash) {
|
1232
|
+
this.hash(mustache.hash);
|
1233
|
+
} else {
|
1234
|
+
this.opcode('emptyHash');
|
1235
|
+
}
|
1214
1236
|
|
1215
|
-
|
1216
|
-
|
1237
|
+
return params;
|
1238
|
+
}
|
1239
|
+
};
|
1217
1240
|
|
1218
|
-
|
1219
|
-
|
1220
|
-
|
1241
|
+
var Literal = function(value) {
|
1242
|
+
this.value = value;
|
1243
|
+
};
|
1221
1244
|
|
1222
|
-
|
1245
|
+
JavaScriptCompiler.prototype = {
|
1246
|
+
// PUBLIC API: You can override these methods in a subclass to provide
|
1247
|
+
// alternative compiled forms for name lookup and buffering semantics
|
1248
|
+
nameLookup: function(parent, name /* , type*/) {
|
1249
|
+
if (/^[0-9]+$/.test(name)) {
|
1250
|
+
return parent + "[" + name + "]";
|
1251
|
+
} else if (JavaScriptCompiler.isValidJavaScriptVariableName(name)) {
|
1252
|
+
return parent + "." + name;
|
1253
|
+
}
|
1254
|
+
else {
|
1255
|
+
return parent + "['" + name + "']";
|
1256
|
+
}
|
1257
|
+
},
|
1223
1258
|
|
1224
|
-
|
1225
|
-
|
1226
|
-
|
1227
|
-
|
1228
|
-
|
1259
|
+
appendToBuffer: function(string) {
|
1260
|
+
if (this.environment.isSimple) {
|
1261
|
+
return "return " + string + ";";
|
1262
|
+
} else {
|
1263
|
+
return {
|
1264
|
+
appendToBuffer: true,
|
1265
|
+
content: string,
|
1266
|
+
toString: function() { return "buffer += " + string + ";"; }
|
1229
1267
|
};
|
1268
|
+
}
|
1269
|
+
},
|
1230
1270
|
|
1231
|
-
|
1271
|
+
initializeBuffer: function() {
|
1272
|
+
return this.quotedString("");
|
1273
|
+
},
|
1232
1274
|
|
1233
|
-
|
1234
|
-
|
1235
|
-
this.registers = { list: [] };
|
1236
|
-
this.compileStack = [];
|
1275
|
+
namespace: "Handlebars",
|
1276
|
+
// END PUBLIC API
|
1237
1277
|
|
1238
|
-
|
1278
|
+
compile: function(environment, options, context, asObject) {
|
1279
|
+
this.environment = environment;
|
1280
|
+
this.options = options || {};
|
1239
1281
|
|
1240
|
-
|
1282
|
+
Handlebars.log(Handlebars.logger.DEBUG, this.environment.disassemble() + "\n\n");
|
1241
1283
|
|
1242
|
-
|
1284
|
+
this.name = this.environment.name;
|
1285
|
+
this.isChild = !!context;
|
1286
|
+
this.context = context || {
|
1287
|
+
programs: [],
|
1288
|
+
environments: [],
|
1289
|
+
aliases: { }
|
1290
|
+
};
|
1243
1291
|
|
1244
|
-
|
1245
|
-
opcode = opcodes[this.i];
|
1292
|
+
this.preamble();
|
1246
1293
|
|
1247
|
-
|
1248
|
-
|
1249
|
-
|
1250
|
-
|
1251
|
-
|
1252
|
-
}
|
1294
|
+
this.stackSlot = 0;
|
1295
|
+
this.stackVars = [];
|
1296
|
+
this.registers = { list: [] };
|
1297
|
+
this.compileStack = [];
|
1298
|
+
this.inlineStack = [];
|
1253
1299
|
|
1254
|
-
|
1255
|
-
},
|
1300
|
+
this.compileChildren(environment, options);
|
1256
1301
|
|
1257
|
-
|
1258
|
-
var opcodes = this.environment.opcodes, opcode = opcodes[this.i + 1];
|
1259
|
-
return opcodes[this.i + 1];
|
1260
|
-
},
|
1302
|
+
var opcodes = environment.opcodes, opcode;
|
1261
1303
|
|
1262
|
-
|
1263
|
-
this.i = this.i + 1;
|
1264
|
-
},
|
1304
|
+
this.i = 0;
|
1265
1305
|
|
1266
|
-
|
1267
|
-
|
1306
|
+
for(l=opcodes.length; this.i<l; this.i++) {
|
1307
|
+
opcode = opcodes[this.i];
|
1268
1308
|
|
1269
|
-
if
|
1270
|
-
|
1271
|
-
var copies = "helpers = helpers || " + namespace + ".helpers;";
|
1272
|
-
if (this.environment.usePartial) { copies = copies + " partials = partials || " + namespace + ".partials;"; }
|
1273
|
-
if (this.options.data) { copies = copies + " data = data || {};"; }
|
1274
|
-
out.push(copies);
|
1309
|
+
if(opcode.opcode === 'DECLARE') {
|
1310
|
+
this[opcode.name] = opcode.value;
|
1275
1311
|
} else {
|
1276
|
-
|
1312
|
+
this[opcode.opcode].apply(this, opcode.args);
|
1277
1313
|
}
|
1314
|
+
}
|
1278
1315
|
|
1279
|
-
|
1280
|
-
|
1281
|
-
} else {
|
1282
|
-
out.push("");
|
1283
|
-
}
|
1316
|
+
return this.createFunctionContext(asObject);
|
1317
|
+
},
|
1284
1318
|
|
1285
|
-
|
1286
|
-
|
1287
|
-
|
1288
|
-
|
1289
|
-
},
|
1319
|
+
nextOpcode: function() {
|
1320
|
+
var opcodes = this.environment.opcodes;
|
1321
|
+
return opcodes[this.i + 1];
|
1322
|
+
},
|
1290
1323
|
|
1291
|
-
|
1292
|
-
|
1324
|
+
eat: function() {
|
1325
|
+
this.i = this.i + 1;
|
1326
|
+
},
|
1293
1327
|
|
1294
|
-
|
1295
|
-
|
1296
|
-
}
|
1328
|
+
preamble: function() {
|
1329
|
+
var out = [];
|
1297
1330
|
|
1298
|
-
|
1299
|
-
|
1300
|
-
|
1301
|
-
|
1302
|
-
|
1303
|
-
|
1304
|
-
|
1331
|
+
if (!this.isChild) {
|
1332
|
+
var namespace = this.namespace;
|
1333
|
+
var copies = "helpers = helpers || " + namespace + ".helpers;";
|
1334
|
+
if (this.environment.usePartial) { copies = copies + " partials = partials || " + namespace + ".partials;"; }
|
1335
|
+
if (this.options.data) { copies = copies + " data = data || {};"; }
|
1336
|
+
out.push(copies);
|
1337
|
+
} else {
|
1338
|
+
out.push('');
|
1339
|
+
}
|
1305
1340
|
|
1306
|
-
|
1307
|
-
|
1308
|
-
|
1341
|
+
if (!this.environment.isSimple) {
|
1342
|
+
out.push(", buffer = " + this.initializeBuffer());
|
1343
|
+
} else {
|
1344
|
+
out.push("");
|
1345
|
+
}
|
1309
1346
|
|
1310
|
-
|
1311
|
-
|
1312
|
-
|
1313
|
-
|
1347
|
+
// track the last context pushed into place to allow skipping the
|
1348
|
+
// getContext opcode when it would be a noop
|
1349
|
+
this.lastContext = 0;
|
1350
|
+
this.source = out;
|
1351
|
+
},
|
1314
1352
|
|
1315
|
-
|
1316
|
-
|
1317
|
-
}
|
1353
|
+
createFunctionContext: function(asObject) {
|
1354
|
+
var locals = this.stackVars.concat(this.registers.list);
|
1318
1355
|
|
1319
|
-
|
1356
|
+
if(locals.length > 0) {
|
1357
|
+
this.source[1] = this.source[1] + ", " + locals.join(", ");
|
1358
|
+
}
|
1320
1359
|
|
1321
|
-
|
1322
|
-
|
1360
|
+
// Generate minimizer alias mappings
|
1361
|
+
if (!this.isChild) {
|
1362
|
+
for (var alias in this.context.aliases) {
|
1363
|
+
this.source[1] = this.source[1] + ', ' + alias + '=' + this.context.aliases[alias];
|
1323
1364
|
}
|
1365
|
+
}
|
1366
|
+
|
1367
|
+
if (this.source[1]) {
|
1368
|
+
this.source[1] = "var " + this.source[1].substring(2) + ";";
|
1369
|
+
}
|
1370
|
+
|
1371
|
+
// Merge children
|
1372
|
+
if (!this.isChild) {
|
1373
|
+
this.source[1] += '\n' + this.context.programs.join('\n') + '\n';
|
1374
|
+
}
|
1375
|
+
|
1376
|
+
if (!this.environment.isSimple) {
|
1377
|
+
this.source.push("return buffer;");
|
1378
|
+
}
|
1324
1379
|
|
1325
|
-
|
1326
|
-
params.push(this.source.join("\n "));
|
1380
|
+
var params = this.isChild ? ["depth0", "data"] : ["Handlebars", "depth0", "helpers", "partials", "data"];
|
1327
1381
|
|
1328
|
-
|
1382
|
+
for(var i=0, l=this.environment.depths.list.length; i<l; i++) {
|
1383
|
+
params.push("depth" + this.environment.depths.list[i]);
|
1384
|
+
}
|
1385
|
+
|
1386
|
+
// Perform a second pass over the output to merge content when possible
|
1387
|
+
var source = this.mergeSource();
|
1388
|
+
|
1389
|
+
if (!this.isChild) {
|
1390
|
+
var revision = Handlebars.COMPILER_REVISION,
|
1391
|
+
versions = Handlebars.REVISION_CHANGES[revision];
|
1392
|
+
source = "this.compilerInfo = ["+revision+",'"+versions+"'];\n"+source;
|
1393
|
+
}
|
1394
|
+
|
1395
|
+
if (asObject) {
|
1396
|
+
params.push(source);
|
1397
|
+
|
1398
|
+
return Function.apply(this, params);
|
1399
|
+
} else {
|
1400
|
+
var functionSource = 'function ' + (this.name || '') + '(' + params.join(',') + ') {\n ' + source + '}';
|
1401
|
+
Handlebars.log(Handlebars.logger.DEBUG, functionSource + "\n\n");
|
1402
|
+
return functionSource;
|
1403
|
+
}
|
1404
|
+
},
|
1405
|
+
mergeSource: function() {
|
1406
|
+
// WARN: We are not handling the case where buffer is still populated as the source should
|
1407
|
+
// not have buffer append operations as their final action.
|
1408
|
+
var source = '',
|
1409
|
+
buffer;
|
1410
|
+
for (var i = 0, len = this.source.length; i < len; i++) {
|
1411
|
+
var line = this.source[i];
|
1412
|
+
if (line.appendToBuffer) {
|
1413
|
+
if (buffer) {
|
1414
|
+
buffer = buffer + '\n + ' + line.content;
|
1415
|
+
} else {
|
1416
|
+
buffer = line.content;
|
1417
|
+
}
|
1329
1418
|
} else {
|
1330
|
-
|
1331
|
-
|
1332
|
-
|
1419
|
+
if (buffer) {
|
1420
|
+
source += 'buffer += ' + buffer + ';\n ';
|
1421
|
+
buffer = undefined;
|
1422
|
+
}
|
1423
|
+
source += line + '\n ';
|
1333
1424
|
}
|
1334
|
-
}
|
1425
|
+
}
|
1426
|
+
return source;
|
1427
|
+
},
|
1335
1428
|
|
1336
|
-
|
1337
|
-
|
1338
|
-
|
1339
|
-
|
1340
|
-
|
1341
|
-
|
1342
|
-
|
1343
|
-
|
1344
|
-
|
1345
|
-
|
1346
|
-
|
1347
|
-
|
1348
|
-
|
1349
|
-
|
1350
|
-
|
1351
|
-
|
1352
|
-
|
1353
|
-
|
1354
|
-
|
1355
|
-
|
1429
|
+
// [blockValue]
|
1430
|
+
//
|
1431
|
+
// On stack, before: hash, inverse, program, value
|
1432
|
+
// On stack, after: return value of blockHelperMissing
|
1433
|
+
//
|
1434
|
+
// The purpose of this opcode is to take a block of the form
|
1435
|
+
// `{{#foo}}...{{/foo}}`, resolve the value of `foo`, and
|
1436
|
+
// replace it on the stack with the result of properly
|
1437
|
+
// invoking blockHelperMissing.
|
1438
|
+
blockValue: function() {
|
1439
|
+
this.context.aliases.blockHelperMissing = 'helpers.blockHelperMissing';
|
1440
|
+
|
1441
|
+
var params = ["depth0"];
|
1442
|
+
this.setupParams(0, params);
|
1443
|
+
|
1444
|
+
this.replaceStack(function(current) {
|
1445
|
+
params.splice(1, 0, current);
|
1446
|
+
return "blockHelperMissing.call(" + params.join(", ") + ")";
|
1447
|
+
});
|
1448
|
+
},
|
1356
1449
|
|
1357
|
-
|
1358
|
-
|
1359
|
-
|
1360
|
-
|
1361
|
-
|
1362
|
-
|
1363
|
-
|
1364
|
-
|
1450
|
+
// [ambiguousBlockValue]
|
1451
|
+
//
|
1452
|
+
// On stack, before: hash, inverse, program, value
|
1453
|
+
// Compiler value, before: lastHelper=value of last found helper, if any
|
1454
|
+
// On stack, after, if no lastHelper: same as [blockValue]
|
1455
|
+
// On stack, after, if lastHelper: value
|
1456
|
+
ambiguousBlockValue: function() {
|
1457
|
+
this.context.aliases.blockHelperMissing = 'helpers.blockHelperMissing';
|
1365
1458
|
|
1366
|
-
|
1367
|
-
|
1459
|
+
var params = ["depth0"];
|
1460
|
+
this.setupParams(0, params);
|
1368
1461
|
|
1369
|
-
|
1370
|
-
|
1462
|
+
var current = this.topStack();
|
1463
|
+
params.splice(1, 0, current);
|
1371
1464
|
|
1372
|
-
|
1373
|
-
|
1465
|
+
// Use the options value generated from the invocation
|
1466
|
+
params[params.length-1] = 'options';
|
1374
1467
|
|
1375
|
-
|
1376
|
-
|
1377
|
-
// On stack, before: ...
|
1378
|
-
// On stack, after: ...
|
1379
|
-
//
|
1380
|
-
// Appends the string value of `content` to the current buffer
|
1381
|
-
appendContent: function(content) {
|
1382
|
-
this.source.push(this.appendToBuffer(this.quotedString(content)));
|
1383
|
-
},
|
1468
|
+
this.source.push("if (!" + this.lastHelper + ") { " + current + " = blockHelperMissing.call(" + params.join(", ") + "); }");
|
1469
|
+
},
|
1384
1470
|
|
1385
|
-
|
1386
|
-
|
1387
|
-
|
1388
|
-
|
1389
|
-
|
1390
|
-
|
1391
|
-
|
1392
|
-
|
1393
|
-
|
1394
|
-
append: function() {
|
1395
|
-
var local = this.popStack();
|
1396
|
-
this.source.push("if(" + local + " || " + local + " === 0) { " + this.appendToBuffer(local) + " }");
|
1397
|
-
if (this.environment.isSimple) {
|
1398
|
-
this.source.push("else { " + this.appendToBuffer("''") + " }");
|
1399
|
-
}
|
1400
|
-
},
|
1471
|
+
// [appendContent]
|
1472
|
+
//
|
1473
|
+
// On stack, before: ...
|
1474
|
+
// On stack, after: ...
|
1475
|
+
//
|
1476
|
+
// Appends the string value of `content` to the current buffer
|
1477
|
+
appendContent: function(content) {
|
1478
|
+
this.source.push(this.appendToBuffer(this.quotedString(content)));
|
1479
|
+
},
|
1401
1480
|
|
1402
|
-
|
1403
|
-
|
1404
|
-
|
1405
|
-
|
1406
|
-
|
1407
|
-
|
1408
|
-
|
1409
|
-
|
1410
|
-
|
1411
|
-
|
1412
|
-
|
1413
|
-
|
1414
|
-
|
1415
|
-
|
1481
|
+
// [append]
|
1482
|
+
//
|
1483
|
+
// On stack, before: value, ...
|
1484
|
+
// On stack, after: ...
|
1485
|
+
//
|
1486
|
+
// Coerces `value` to a String and appends it to the current buffer.
|
1487
|
+
//
|
1488
|
+
// If `value` is truthy, or 0, it is coerced into a string and appended
|
1489
|
+
// Otherwise, the empty string is appended
|
1490
|
+
append: function() {
|
1491
|
+
// Force anything that is inlined onto the stack so we don't have duplication
|
1492
|
+
// when we examine local
|
1493
|
+
this.flushInline();
|
1494
|
+
var local = this.popStack();
|
1495
|
+
this.source.push("if(" + local + " || " + local + " === 0) { " + this.appendToBuffer(local) + " }");
|
1496
|
+
if (this.environment.isSimple) {
|
1497
|
+
this.source.push("else { " + this.appendToBuffer("''") + " }");
|
1498
|
+
}
|
1499
|
+
},
|
1416
1500
|
|
1417
|
-
|
1418
|
-
|
1501
|
+
// [appendEscaped]
|
1502
|
+
//
|
1503
|
+
// On stack, before: value, ...
|
1504
|
+
// On stack, after: ...
|
1505
|
+
//
|
1506
|
+
// Escape `value` and append it to the buffer
|
1507
|
+
appendEscaped: function() {
|
1508
|
+
this.context.aliases.escapeExpression = 'this.escapeExpression';
|
1419
1509
|
|
1420
|
-
|
1421
|
-
|
1422
|
-
// On stack, before: ...
|
1423
|
-
// On stack, after: ...
|
1424
|
-
// Compiler value, after: lastContext=depth
|
1425
|
-
//
|
1426
|
-
// Set the value of the `lastContext` compiler value to the depth
|
1427
|
-
getContext: function(depth) {
|
1428
|
-
if(this.lastContext !== depth) {
|
1429
|
-
this.lastContext = depth;
|
1430
|
-
}
|
1431
|
-
},
|
1510
|
+
this.source.push(this.appendToBuffer("escapeExpression(" + this.popStack() + ")"));
|
1511
|
+
},
|
1432
1512
|
|
1433
|
-
|
1434
|
-
|
1435
|
-
|
1436
|
-
|
1437
|
-
|
1438
|
-
|
1439
|
-
|
1440
|
-
|
1441
|
-
|
1442
|
-
|
1513
|
+
// [getContext]
|
1514
|
+
//
|
1515
|
+
// On stack, before: ...
|
1516
|
+
// On stack, after: ...
|
1517
|
+
// Compiler value, after: lastContext=depth
|
1518
|
+
//
|
1519
|
+
// Set the value of the `lastContext` compiler value to the depth
|
1520
|
+
getContext: function(depth) {
|
1521
|
+
if(this.lastContext !== depth) {
|
1522
|
+
this.lastContext = depth;
|
1523
|
+
}
|
1524
|
+
},
|
1443
1525
|
|
1444
|
-
|
1445
|
-
|
1446
|
-
|
1447
|
-
|
1448
|
-
|
1449
|
-
|
1450
|
-
|
1451
|
-
|
1452
|
-
|
1526
|
+
// [lookupOnContext]
|
1527
|
+
//
|
1528
|
+
// On stack, before: ...
|
1529
|
+
// On stack, after: currentContext[name], ...
|
1530
|
+
//
|
1531
|
+
// Looks up the value of `name` on the current context and pushes
|
1532
|
+
// it onto the stack.
|
1533
|
+
lookupOnContext: function(name) {
|
1534
|
+
this.push(this.nameLookup('depth' + this.lastContext, name, 'context'));
|
1535
|
+
},
|
1453
1536
|
|
1454
|
-
|
1455
|
-
|
1456
|
-
|
1457
|
-
|
1458
|
-
|
1459
|
-
|
1460
|
-
|
1461
|
-
|
1462
|
-
|
1463
|
-
|
1464
|
-
this.replaceStack(function(current) {
|
1465
|
-
return "typeof " + current + " === functionType ? " + current + ".apply(depth0) : " + current;
|
1466
|
-
});
|
1467
|
-
},
|
1537
|
+
// [pushContext]
|
1538
|
+
//
|
1539
|
+
// On stack, before: ...
|
1540
|
+
// On stack, after: currentContext, ...
|
1541
|
+
//
|
1542
|
+
// Pushes the value of the current context onto the stack.
|
1543
|
+
pushContext: function() {
|
1544
|
+
this.pushStackLiteral('depth' + this.lastContext);
|
1545
|
+
},
|
1468
1546
|
|
1469
|
-
|
1470
|
-
|
1471
|
-
|
1472
|
-
|
1473
|
-
|
1474
|
-
|
1475
|
-
|
1476
|
-
|
1477
|
-
|
1478
|
-
|
1479
|
-
|
1480
|
-
|
1547
|
+
// [resolvePossibleLambda]
|
1548
|
+
//
|
1549
|
+
// On stack, before: value, ...
|
1550
|
+
// On stack, after: resolved value, ...
|
1551
|
+
//
|
1552
|
+
// If the `value` is a lambda, replace it on the stack by
|
1553
|
+
// the return value of the lambda
|
1554
|
+
resolvePossibleLambda: function() {
|
1555
|
+
this.context.aliases.functionType = '"function"';
|
1556
|
+
|
1557
|
+
this.replaceStack(function(current) {
|
1558
|
+
return "typeof " + current + " === functionType ? " + current + ".apply(depth0) : " + current;
|
1559
|
+
});
|
1560
|
+
},
|
1481
1561
|
|
1482
|
-
|
1483
|
-
|
1484
|
-
|
1485
|
-
|
1486
|
-
|
1487
|
-
|
1488
|
-
|
1489
|
-
|
1490
|
-
|
1562
|
+
// [lookup]
|
1563
|
+
//
|
1564
|
+
// On stack, before: value, ...
|
1565
|
+
// On stack, after: value[name], ...
|
1566
|
+
//
|
1567
|
+
// Replace the value on the stack with the result of looking
|
1568
|
+
// up `name` on `value`
|
1569
|
+
lookup: function(name) {
|
1570
|
+
this.replaceStack(function(current) {
|
1571
|
+
return current + " == null || " + current + " === false ? " + current + " : " + this.nameLookup(current, name, 'context');
|
1572
|
+
});
|
1573
|
+
},
|
1491
1574
|
|
1492
|
-
|
1493
|
-
|
1494
|
-
|
1495
|
-
|
1496
|
-
|
1497
|
-
|
1498
|
-
|
1499
|
-
|
1500
|
-
|
1501
|
-
this.pushStackLiteral('depth' + this.lastContext);
|
1502
|
-
|
1503
|
-
this.pushString(type);
|
1504
|
-
|
1505
|
-
if (typeof string === 'string') {
|
1506
|
-
this.pushString(string);
|
1507
|
-
} else {
|
1508
|
-
this.pushStackLiteral(string);
|
1509
|
-
}
|
1510
|
-
},
|
1575
|
+
// [lookupData]
|
1576
|
+
//
|
1577
|
+
// On stack, before: ...
|
1578
|
+
// On stack, after: data[id], ...
|
1579
|
+
//
|
1580
|
+
// Push the result of looking up `id` on the current data
|
1581
|
+
lookupData: function(id) {
|
1582
|
+
this.push(this.nameLookup('data', id, 'data'));
|
1583
|
+
},
|
1511
1584
|
|
1512
|
-
|
1513
|
-
|
1585
|
+
// [pushStringParam]
|
1586
|
+
//
|
1587
|
+
// On stack, before: ...
|
1588
|
+
// On stack, after: string, currentContext, ...
|
1589
|
+
//
|
1590
|
+
// This opcode is designed for use in string mode, which
|
1591
|
+
// provides the string value of a parameter along with its
|
1592
|
+
// depth rather than resolving it immediately.
|
1593
|
+
pushStringParam: function(string, type) {
|
1594
|
+
this.pushStackLiteral('depth' + this.lastContext);
|
1595
|
+
|
1596
|
+
this.pushString(type);
|
1597
|
+
|
1598
|
+
if (typeof string === 'string') {
|
1599
|
+
this.pushString(string);
|
1600
|
+
} else {
|
1601
|
+
this.pushStackLiteral(string);
|
1602
|
+
}
|
1603
|
+
},
|
1514
1604
|
|
1515
|
-
|
1516
|
-
|
1517
|
-
}
|
1518
|
-
},
|
1605
|
+
emptyHash: function() {
|
1606
|
+
this.pushStackLiteral('{}');
|
1519
1607
|
|
1520
|
-
|
1521
|
-
|
1522
|
-
|
1523
|
-
|
1524
|
-
|
1525
|
-
|
1526
|
-
|
1527
|
-
|
1528
|
-
|
1608
|
+
if (this.options.stringParams) {
|
1609
|
+
this.register('hashTypes', '{}');
|
1610
|
+
}
|
1611
|
+
},
|
1612
|
+
pushHash: function() {
|
1613
|
+
this.hash = {values: [], types: []};
|
1614
|
+
},
|
1615
|
+
popHash: function() {
|
1616
|
+
var hash = this.hash;
|
1617
|
+
this.hash = undefined;
|
1529
1618
|
|
1530
|
-
|
1531
|
-
|
1532
|
-
|
1533
|
-
|
1534
|
-
|
1535
|
-
// Push an expression onto the stack
|
1536
|
-
push: function(expr) {
|
1537
|
-
this.pushStack(expr);
|
1538
|
-
},
|
1619
|
+
if (this.options.stringParams) {
|
1620
|
+
this.register('hashTypes', '{' + hash.types.join(',') + '}');
|
1621
|
+
}
|
1622
|
+
this.push('{\n ' + hash.values.join(',\n ') + '\n }');
|
1623
|
+
},
|
1539
1624
|
|
1540
|
-
|
1541
|
-
|
1542
|
-
|
1543
|
-
|
1544
|
-
|
1545
|
-
|
1546
|
-
|
1547
|
-
|
1548
|
-
|
1549
|
-
this.pushStackLiteral(value);
|
1550
|
-
},
|
1625
|
+
// [pushString]
|
1626
|
+
//
|
1627
|
+
// On stack, before: ...
|
1628
|
+
// On stack, after: quotedString(string), ...
|
1629
|
+
//
|
1630
|
+
// Push a quoted version of `string` onto the stack
|
1631
|
+
pushString: function(string) {
|
1632
|
+
this.pushStackLiteral(this.quotedString(string));
|
1633
|
+
},
|
1551
1634
|
|
1552
|
-
|
1553
|
-
|
1554
|
-
|
1555
|
-
|
1556
|
-
|
1557
|
-
|
1558
|
-
|
1559
|
-
|
1560
|
-
|
1561
|
-
|
1562
|
-
this.pushStackLiteral(this.programExpression(guid));
|
1563
|
-
} else {
|
1564
|
-
this.pushStackLiteral(null);
|
1565
|
-
}
|
1566
|
-
},
|
1635
|
+
// [push]
|
1636
|
+
//
|
1637
|
+
// On stack, before: ...
|
1638
|
+
// On stack, after: expr, ...
|
1639
|
+
//
|
1640
|
+
// Push an expression onto the stack
|
1641
|
+
push: function(expr) {
|
1642
|
+
this.inlineStack.push(expr);
|
1643
|
+
return expr;
|
1644
|
+
},
|
1567
1645
|
|
1568
|
-
|
1569
|
-
|
1570
|
-
|
1571
|
-
|
1572
|
-
|
1573
|
-
|
1574
|
-
|
1575
|
-
|
1576
|
-
|
1577
|
-
|
1578
|
-
|
1579
|
-
|
1580
|
-
var helper = this.lastHelper = this.setupHelper(paramSize, name);
|
1581
|
-
this.register('foundHelper', helper.name);
|
1582
|
-
|
1583
|
-
this.pushStack("foundHelper ? foundHelper.call(" +
|
1584
|
-
helper.callParams + ") " + ": helperMissing.call(" +
|
1585
|
-
helper.helperMissingParams + ")");
|
1586
|
-
},
|
1646
|
+
// [pushLiteral]
|
1647
|
+
//
|
1648
|
+
// On stack, before: ...
|
1649
|
+
// On stack, after: value, ...
|
1650
|
+
//
|
1651
|
+
// Pushes a value onto the stack. This operation prevents
|
1652
|
+
// the compiler from creating a temporary variable to hold
|
1653
|
+
// it.
|
1654
|
+
pushLiteral: function(value) {
|
1655
|
+
this.pushStackLiteral(value);
|
1656
|
+
},
|
1587
1657
|
|
1588
|
-
|
1589
|
-
|
1590
|
-
|
1591
|
-
|
1592
|
-
|
1593
|
-
|
1594
|
-
|
1595
|
-
|
1596
|
-
|
1597
|
-
|
1598
|
-
|
1658
|
+
// [pushProgram]
|
1659
|
+
//
|
1660
|
+
// On stack, before: ...
|
1661
|
+
// On stack, after: program(guid), ...
|
1662
|
+
//
|
1663
|
+
// Push a program expression onto the stack. This takes
|
1664
|
+
// a compile-time guid and converts it into a runtime-accessible
|
1665
|
+
// expression.
|
1666
|
+
pushProgram: function(guid) {
|
1667
|
+
if (guid != null) {
|
1668
|
+
this.pushStackLiteral(this.programExpression(guid));
|
1669
|
+
} else {
|
1670
|
+
this.pushStackLiteral(null);
|
1671
|
+
}
|
1672
|
+
},
|
1599
1673
|
|
1600
|
-
|
1601
|
-
|
1602
|
-
|
1603
|
-
|
1604
|
-
|
1605
|
-
|
1606
|
-
|
1607
|
-
|
1608
|
-
|
1609
|
-
|
1610
|
-
|
1611
|
-
|
1612
|
-
|
1613
|
-
|
1614
|
-
|
1615
|
-
|
1616
|
-
|
1617
|
-
|
1618
|
-
|
1619
|
-
|
1620
|
-
|
1621
|
-
var nonHelper = this.nameLookup('depth' + this.lastContext, name, 'context');
|
1622
|
-
var nextStack = this.nextStack();
|
1623
|
-
|
1624
|
-
this.source.push('if (foundHelper) { ' + nextStack + ' = foundHelper.call(' + helper.callParams + '); }');
|
1625
|
-
this.source.push('else { ' + nextStack + ' = ' + nonHelper + '; ' + nextStack + ' = typeof ' + nextStack + ' === functionType ? ' + nextStack + '.apply(depth0) : ' + nextStack + '; }');
|
1626
|
-
},
|
1674
|
+
// [invokeHelper]
|
1675
|
+
//
|
1676
|
+
// On stack, before: hash, inverse, program, params..., ...
|
1677
|
+
// On stack, after: result of helper invocation
|
1678
|
+
//
|
1679
|
+
// Pops off the helper's parameters, invokes the helper,
|
1680
|
+
// and pushes the helper's return value onto the stack.
|
1681
|
+
//
|
1682
|
+
// If the helper is not found, `helperMissing` is called.
|
1683
|
+
invokeHelper: function(paramSize, name) {
|
1684
|
+
this.context.aliases.helperMissing = 'helpers.helperMissing';
|
1685
|
+
|
1686
|
+
var helper = this.lastHelper = this.setupHelper(paramSize, name, true);
|
1687
|
+
|
1688
|
+
this.push(helper.name);
|
1689
|
+
this.replaceStack(function(name) {
|
1690
|
+
return name + ' ? ' + name + '.call(' +
|
1691
|
+
helper.callParams + ") " + ": helperMissing.call(" +
|
1692
|
+
helper.helperMissingParams + ")";
|
1693
|
+
});
|
1694
|
+
},
|
1627
1695
|
|
1628
|
-
|
1629
|
-
|
1630
|
-
|
1631
|
-
|
1632
|
-
|
1633
|
-
|
1634
|
-
|
1635
|
-
|
1636
|
-
|
1637
|
-
|
1638
|
-
|
1639
|
-
params.push("data");
|
1640
|
-
}
|
1696
|
+
// [invokeKnownHelper]
|
1697
|
+
//
|
1698
|
+
// On stack, before: hash, inverse, program, params..., ...
|
1699
|
+
// On stack, after: result of helper invocation
|
1700
|
+
//
|
1701
|
+
// This operation is used when the helper is known to exist,
|
1702
|
+
// so a `helperMissing` fallback is not required.
|
1703
|
+
invokeKnownHelper: function(paramSize, name) {
|
1704
|
+
var helper = this.setupHelper(paramSize, name);
|
1705
|
+
this.push(helper.name + ".call(" + helper.callParams + ")");
|
1706
|
+
},
|
1641
1707
|
|
1642
|
-
|
1643
|
-
|
1644
|
-
|
1708
|
+
// [invokeAmbiguous]
|
1709
|
+
//
|
1710
|
+
// On stack, before: hash, inverse, program, params..., ...
|
1711
|
+
// On stack, after: result of disambiguation
|
1712
|
+
//
|
1713
|
+
// This operation is used when an expression like `{{foo}}`
|
1714
|
+
// is provided, but we don't know at compile-time whether it
|
1715
|
+
// is a helper or a path.
|
1716
|
+
//
|
1717
|
+
// This operation emits more code than the other options,
|
1718
|
+
// and can be avoided by passing the `knownHelpers` and
|
1719
|
+
// `knownHelpersOnly` flags at compile-time.
|
1720
|
+
invokeAmbiguous: function(name, helperCall) {
|
1721
|
+
this.context.aliases.functionType = '"function"';
|
1722
|
+
|
1723
|
+
this.pushStackLiteral('{}'); // Hash value
|
1724
|
+
var helper = this.setupHelper(0, name, helperCall);
|
1725
|
+
|
1726
|
+
var helperName = this.lastHelper = this.nameLookup('helpers', name, 'helper');
|
1727
|
+
|
1728
|
+
var nonHelper = this.nameLookup('depth' + this.lastContext, name, 'context');
|
1729
|
+
var nextStack = this.nextStack();
|
1730
|
+
|
1731
|
+
this.source.push('if (' + nextStack + ' = ' + helperName + ') { ' + nextStack + ' = ' + nextStack + '.call(' + helper.callParams + '); }');
|
1732
|
+
this.source.push('else { ' + nextStack + ' = ' + nonHelper + '; ' + nextStack + ' = typeof ' + nextStack + ' === functionType ? ' + nextStack + '.apply(depth0) : ' + nextStack + '; }');
|
1733
|
+
},
|
1645
1734
|
|
1646
|
-
|
1647
|
-
|
1648
|
-
|
1649
|
-
|
1650
|
-
|
1651
|
-
|
1652
|
-
|
1653
|
-
|
1654
|
-
|
1735
|
+
// [invokePartial]
|
1736
|
+
//
|
1737
|
+
// On stack, before: context, ...
|
1738
|
+
// On stack after: result of partial invocation
|
1739
|
+
//
|
1740
|
+
// This operation pops off a context, invokes a partial with that context,
|
1741
|
+
// and pushes the result of the invocation back.
|
1742
|
+
invokePartial: function(name) {
|
1743
|
+
var params = [this.nameLookup('partials', name, 'partial'), "'" + name + "'", this.popStack(), "helpers", "partials"];
|
1744
|
+
|
1745
|
+
if (this.options.data) {
|
1746
|
+
params.push("data");
|
1747
|
+
}
|
1655
1748
|
|
1656
|
-
|
1657
|
-
|
1658
|
-
|
1659
|
-
|
1660
|
-
|
1749
|
+
this.context.aliases.self = "this";
|
1750
|
+
this.push("self.invokePartial(" + params.join(", ") + ")");
|
1751
|
+
},
|
1752
|
+
|
1753
|
+
// [assignToHash]
|
1754
|
+
//
|
1755
|
+
// On stack, before: value, hash, ...
|
1756
|
+
// On stack, after: hash, ...
|
1757
|
+
//
|
1758
|
+
// Pops a value and hash off the stack, assigns `hash[key] = value`
|
1759
|
+
// and pushes the hash back onto the stack.
|
1760
|
+
assignToHash: function(key) {
|
1761
|
+
var value = this.popStack(),
|
1762
|
+
type;
|
1763
|
+
|
1764
|
+
if (this.options.stringParams) {
|
1765
|
+
type = this.popStack();
|
1766
|
+
this.popStack();
|
1767
|
+
}
|
1661
1768
|
|
1662
|
-
|
1769
|
+
var hash = this.hash;
|
1770
|
+
if (type) {
|
1771
|
+
hash.types.push("'" + key + "': " + type);
|
1772
|
+
}
|
1773
|
+
hash.values.push("'" + key + "': (" + value + ")");
|
1774
|
+
},
|
1663
1775
|
|
1664
|
-
|
1665
|
-
},
|
1776
|
+
// HELPERS
|
1666
1777
|
|
1667
|
-
|
1778
|
+
compiler: JavaScriptCompiler,
|
1668
1779
|
|
1669
|
-
|
1780
|
+
compileChildren: function(environment, options) {
|
1781
|
+
var children = environment.children, child, compiler;
|
1670
1782
|
|
1671
|
-
|
1672
|
-
|
1783
|
+
for(var i=0, l=children.length; i<l; i++) {
|
1784
|
+
child = children[i];
|
1785
|
+
compiler = new this.compiler();
|
1673
1786
|
|
1674
|
-
|
1675
|
-
child = children[i];
|
1676
|
-
compiler = new this.compiler();
|
1787
|
+
var index = this.matchExistingProgram(child);
|
1677
1788
|
|
1789
|
+
if (index == null) {
|
1678
1790
|
this.context.programs.push(''); // Placeholder to prevent name conflicts for nested children
|
1679
|
-
|
1791
|
+
index = this.context.programs.length;
|
1680
1792
|
child.index = index;
|
1681
1793
|
child.name = 'program' + index;
|
1682
1794
|
this.context.programs[index] = compiler.compile(child, options, this.context);
|
1795
|
+
this.context.environments[index] = child;
|
1796
|
+
} else {
|
1797
|
+
child.index = index;
|
1798
|
+
child.name = 'program' + index;
|
1683
1799
|
}
|
1684
|
-
}
|
1800
|
+
}
|
1801
|
+
},
|
1802
|
+
matchExistingProgram: function(child) {
|
1803
|
+
for (var i = 0, len = this.context.environments.length; i < len; i++) {
|
1804
|
+
var environment = this.context.environments[i];
|
1805
|
+
if (environment && environment.equals(child)) {
|
1806
|
+
return i;
|
1807
|
+
}
|
1808
|
+
}
|
1809
|
+
},
|
1685
1810
|
|
1686
|
-
|
1687
|
-
|
1811
|
+
programExpression: function(guid) {
|
1812
|
+
this.context.aliases.self = "this";
|
1688
1813
|
|
1689
|
-
|
1690
|
-
|
1691
|
-
|
1814
|
+
if(guid == null) {
|
1815
|
+
return "self.noop";
|
1816
|
+
}
|
1692
1817
|
|
1693
|
-
|
1694
|
-
|
1818
|
+
var child = this.environment.children[guid],
|
1819
|
+
depths = child.depths.list, depth;
|
1695
1820
|
|
1696
|
-
|
1821
|
+
var programParams = [child.index, child.name, "data"];
|
1697
1822
|
|
1698
|
-
|
1699
|
-
|
1823
|
+
for(var i=0, l = depths.length; i<l; i++) {
|
1824
|
+
depth = depths[i];
|
1700
1825
|
|
1701
|
-
|
1702
|
-
|
1703
|
-
|
1826
|
+
if(depth === 1) { programParams.push("depth0"); }
|
1827
|
+
else { programParams.push("depth" + (depth - 1)); }
|
1828
|
+
}
|
1704
1829
|
|
1705
|
-
|
1706
|
-
|
1707
|
-
|
1708
|
-
|
1709
|
-
|
1710
|
-
|
1711
|
-
|
1830
|
+
if(depths.length === 0) {
|
1831
|
+
return "self.program(" + programParams.join(", ") + ")";
|
1832
|
+
} else {
|
1833
|
+
programParams.shift();
|
1834
|
+
return "self.programWithDepth(" + programParams.join(", ") + ")";
|
1835
|
+
}
|
1836
|
+
},
|
1712
1837
|
|
1713
|
-
|
1714
|
-
|
1715
|
-
|
1716
|
-
|
1838
|
+
register: function(name, val) {
|
1839
|
+
this.useRegister(name);
|
1840
|
+
this.source.push(name + " = " + val + ";");
|
1841
|
+
},
|
1717
1842
|
|
1718
|
-
|
1719
|
-
|
1720
|
-
|
1721
|
-
|
1722
|
-
|
1723
|
-
|
1843
|
+
useRegister: function(name) {
|
1844
|
+
if(!this.registers[name]) {
|
1845
|
+
this.registers[name] = true;
|
1846
|
+
this.registers.list.push(name);
|
1847
|
+
}
|
1848
|
+
},
|
1724
1849
|
|
1725
|
-
|
1726
|
-
|
1727
|
-
|
1728
|
-
|
1850
|
+
pushStackLiteral: function(item) {
|
1851
|
+
return this.push(new Literal(item));
|
1852
|
+
},
|
1853
|
+
|
1854
|
+
pushStack: function(item) {
|
1855
|
+
this.flushInline();
|
1729
1856
|
|
1730
|
-
|
1731
|
-
|
1857
|
+
var stack = this.incrStack();
|
1858
|
+
if (item) {
|
1732
1859
|
this.source.push(stack + " = " + item + ";");
|
1733
|
-
|
1734
|
-
|
1735
|
-
|
1860
|
+
}
|
1861
|
+
this.compileStack.push(stack);
|
1862
|
+
return stack;
|
1863
|
+
},
|
1864
|
+
|
1865
|
+
replaceStack: function(callback) {
|
1866
|
+
var prefix = '',
|
1867
|
+
inline = this.isInline(),
|
1868
|
+
stack;
|
1736
1869
|
|
1737
|
-
|
1738
|
-
|
1739
|
-
|
1870
|
+
// If we are currently inline then we want to merge the inline statement into the
|
1871
|
+
// replacement statement via ','
|
1872
|
+
if (inline) {
|
1873
|
+
var top = this.popStack(true);
|
1740
1874
|
|
1875
|
+
if (top instanceof Literal) {
|
1876
|
+
// Literals do not need to be inlined
|
1877
|
+
stack = top.value;
|
1878
|
+
} else {
|
1879
|
+
// Get or create the current stack name for use by the inline
|
1880
|
+
var name = this.stackSlot ? this.topStackName() : this.incrStack();
|
1881
|
+
|
1882
|
+
prefix = '(' + this.push(name) + ' = ' + top + '),';
|
1883
|
+
stack = this.topStack();
|
1884
|
+
}
|
1885
|
+
} else {
|
1886
|
+
stack = this.topStack();
|
1887
|
+
}
|
1888
|
+
|
1889
|
+
var item = callback.call(this, stack);
|
1890
|
+
|
1891
|
+
if (inline) {
|
1892
|
+
if (this.inlineStack.length || this.compileStack.length) {
|
1893
|
+
this.popStack();
|
1894
|
+
}
|
1895
|
+
this.push('(' + prefix + item + ')');
|
1896
|
+
} else {
|
1741
1897
|
// Prevent modification of the context depth variable. Through replaceStack
|
1742
|
-
if (
|
1898
|
+
if (!/^stack/.test(stack)) {
|
1743
1899
|
stack = this.nextStack();
|
1744
1900
|
}
|
1745
1901
|
|
1746
|
-
this.source.push(stack + " = " + item + ";");
|
1747
|
-
|
1748
|
-
|
1902
|
+
this.source.push(stack + " = (" + prefix + item + ");");
|
1903
|
+
}
|
1904
|
+
return stack;
|
1905
|
+
},
|
1749
1906
|
|
1750
|
-
|
1751
|
-
|
1752
|
-
|
1753
|
-
return name;
|
1754
|
-
},
|
1907
|
+
nextStack: function() {
|
1908
|
+
return this.pushStack();
|
1909
|
+
},
|
1755
1910
|
|
1756
|
-
|
1757
|
-
|
1758
|
-
|
1759
|
-
|
1760
|
-
|
1911
|
+
incrStack: function() {
|
1912
|
+
this.stackSlot++;
|
1913
|
+
if(this.stackSlot > this.stackVars.length) { this.stackVars.push("stack" + this.stackSlot); }
|
1914
|
+
return this.topStackName();
|
1915
|
+
},
|
1916
|
+
topStackName: function() {
|
1917
|
+
return "stack" + this.stackSlot;
|
1918
|
+
},
|
1919
|
+
flushInline: function() {
|
1920
|
+
var inlineStack = this.inlineStack;
|
1921
|
+
if (inlineStack.length) {
|
1922
|
+
this.inlineStack = [];
|
1923
|
+
for (var i = 0, len = inlineStack.length; i < len; i++) {
|
1924
|
+
var entry = inlineStack[i];
|
1925
|
+
if (entry instanceof Literal) {
|
1926
|
+
this.compileStack.push(entry);
|
1927
|
+
} else {
|
1928
|
+
this.pushStack(entry);
|
1929
|
+
}
|
1930
|
+
}
|
1931
|
+
}
|
1932
|
+
},
|
1933
|
+
isInline: function() {
|
1934
|
+
return this.inlineStack.length;
|
1935
|
+
},
|
1761
1936
|
|
1762
|
-
|
1763
|
-
|
1937
|
+
popStack: function(wrapped) {
|
1938
|
+
var inline = this.isInline(),
|
1939
|
+
item = (inline ? this.inlineStack : this.compileStack).pop();
|
1764
1940
|
|
1765
|
-
|
1766
|
-
|
1767
|
-
|
1941
|
+
if (!wrapped && (item instanceof Literal)) {
|
1942
|
+
return item.value;
|
1943
|
+
} else {
|
1944
|
+
if (!inline) {
|
1768
1945
|
this.stackSlot--;
|
1769
|
-
return item;
|
1770
1946
|
}
|
1771
|
-
|
1947
|
+
return item;
|
1948
|
+
}
|
1949
|
+
},
|
1772
1950
|
|
1773
|
-
|
1774
|
-
|
1951
|
+
topStack: function(wrapped) {
|
1952
|
+
var stack = (this.isInline() ? this.inlineStack : this.compileStack),
|
1953
|
+
item = stack[stack.length - 1];
|
1775
1954
|
|
1776
|
-
|
1777
|
-
|
1778
|
-
|
1779
|
-
|
1780
|
-
|
1781
|
-
|
1955
|
+
if (!wrapped && (item instanceof Literal)) {
|
1956
|
+
return item.value;
|
1957
|
+
} else {
|
1958
|
+
return item;
|
1959
|
+
}
|
1960
|
+
},
|
1782
1961
|
|
1783
|
-
|
1784
|
-
|
1785
|
-
|
1786
|
-
|
1787
|
-
|
1788
|
-
|
1789
|
-
|
1962
|
+
quotedString: function(str) {
|
1963
|
+
return '"' + str
|
1964
|
+
.replace(/\\/g, '\\\\')
|
1965
|
+
.replace(/"/g, '\\"')
|
1966
|
+
.replace(/\n/g, '\\n')
|
1967
|
+
.replace(/\r/g, '\\r') + '"';
|
1968
|
+
},
|
1790
1969
|
|
1791
|
-
|
1792
|
-
|
1793
|
-
|
1794
|
-
|
1970
|
+
setupHelper: function(paramSize, name, missingParams) {
|
1971
|
+
var params = [];
|
1972
|
+
this.setupParams(paramSize, params, missingParams);
|
1973
|
+
var foundHelper = this.nameLookup('helpers', name, 'helper');
|
1795
1974
|
|
1796
|
-
|
1797
|
-
|
1798
|
-
|
1799
|
-
|
1800
|
-
|
1801
|
-
|
1802
|
-
|
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
|
+
};
|
1981
|
+
},
|
1803
1982
|
|
1804
|
-
|
1805
|
-
|
1806
|
-
|
1807
|
-
|
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;
|
1808
1987
|
|
1809
|
-
|
1988
|
+
options.push("hash:" + this.popStack());
|
1810
1989
|
|
1811
|
-
|
1812
|
-
|
1990
|
+
inverse = this.popStack();
|
1991
|
+
program = this.popStack();
|
1813
1992
|
|
1814
|
-
|
1815
|
-
|
1816
|
-
|
1817
|
-
|
1818
|
-
|
1819
|
-
|
1820
|
-
|
1821
|
-
|
1822
|
-
if (!inverse) {
|
1823
|
-
this.context.aliases.self = "this";
|
1824
|
-
inverse = "self.noop";
|
1825
|
-
}
|
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
|
+
}
|
1826
2000
|
|
1827
|
-
|
1828
|
-
|
2001
|
+
if (!inverse) {
|
2002
|
+
this.context.aliases.self = "this";
|
2003
|
+
inverse = "self.noop";
|
1829
2004
|
}
|
1830
2005
|
|
1831
|
-
|
1832
|
-
|
1833
|
-
|
2006
|
+
options.push("inverse:" + inverse);
|
2007
|
+
options.push("fn:" + program);
|
2008
|
+
}
|
1834
2009
|
|
1835
|
-
|
1836
|
-
|
1837
|
-
|
1838
|
-
}
|
1839
|
-
}
|
2010
|
+
for(var i=0; i<paramSize; i++) {
|
2011
|
+
param = this.popStack();
|
2012
|
+
params.push(param);
|
1840
2013
|
|
1841
|
-
if
|
1842
|
-
|
1843
|
-
|
1844
|
-
options.push("hashTypes:hashTypes");
|
2014
|
+
if(this.options.stringParams) {
|
2015
|
+
types.push(this.popStack());
|
2016
|
+
contexts.push(this.popStack());
|
1845
2017
|
}
|
2018
|
+
}
|
1846
2019
|
|
1847
|
-
|
1848
|
-
|
1849
|
-
|
2020
|
+
if (this.options.stringParams) {
|
2021
|
+
options.push("contexts:[" + contexts.join(",") + "]");
|
2022
|
+
options.push("types:[" + types.join(",") + "]");
|
2023
|
+
options.push("hashTypes:hashTypes");
|
2024
|
+
}
|
1850
2025
|
|
1851
|
-
|
1852
|
-
|
2026
|
+
if(this.options.data) {
|
2027
|
+
options.push("data:data");
|
1853
2028
|
}
|
1854
|
-
};
|
1855
2029
|
|
1856
|
-
|
1857
|
-
|
1858
|
-
|
1859
|
-
|
1860
|
-
|
1861
|
-
|
1862
|
-
|
1863
|
-
"
|
1864
|
-
" abstract enum int short" +
|
1865
|
-
" boolean export interface static" +
|
1866
|
-
" byte extends long super" +
|
1867
|
-
" char final native synchronized" +
|
1868
|
-
" class float package throws" +
|
1869
|
-
" const goto private transient" +
|
1870
|
-
" debugger implements protected volatile" +
|
1871
|
-
" double import public let yield"
|
1872
|
-
).split(" ");
|
1873
|
-
|
1874
|
-
var compilerWords = JavaScriptCompiler.RESERVED_WORDS = {};
|
1875
|
-
|
1876
|
-
for(var i=0, l=reservedWords.length; i<l; i++) {
|
1877
|
-
compilerWords[reservedWords[i]] = true;
|
2030
|
+
options = "{" + options.join(",") + "}";
|
2031
|
+
if (useRegister) {
|
2032
|
+
this.register('options', options);
|
2033
|
+
params.push('options');
|
2034
|
+
} else {
|
2035
|
+
params.push(options);
|
2036
|
+
}
|
2037
|
+
return params.join(", ");
|
1878
2038
|
}
|
2039
|
+
};
|
1879
2040
|
|
1880
|
-
|
1881
|
-
|
1882
|
-
|
1883
|
-
|
1884
|
-
|
1885
|
-
|
2041
|
+
var reservedWords = (
|
2042
|
+
"break else new var" +
|
2043
|
+
" case finally return void" +
|
2044
|
+
" catch for switch while" +
|
2045
|
+
" continue function this with" +
|
2046
|
+
" default if throw" +
|
2047
|
+
" delete in try" +
|
2048
|
+
" do instanceof typeof" +
|
2049
|
+
" abstract enum int short" +
|
2050
|
+
" boolean export interface static" +
|
2051
|
+
" byte extends long super" +
|
2052
|
+
" char final native synchronized" +
|
2053
|
+
" class float package throws" +
|
2054
|
+
" const goto private transient" +
|
2055
|
+
" debugger implements protected volatile" +
|
2056
|
+
" double import public let yield"
|
2057
|
+
).split(" ");
|
2058
|
+
|
2059
|
+
var compilerWords = JavaScriptCompiler.RESERVED_WORDS = {};
|
2060
|
+
|
2061
|
+
for(var i=0, l=reservedWords.length; i<l; i++) {
|
2062
|
+
compilerWords[reservedWords[i]] = true;
|
2063
|
+
}
|
1886
2064
|
|
1887
|
-
|
2065
|
+
JavaScriptCompiler.isValidJavaScriptVariableName = function(name) {
|
2066
|
+
if(!JavaScriptCompiler.RESERVED_WORDS[name] && /^[a-zA-Z_$][0-9a-zA-Z_$]+$/.test(name)) {
|
2067
|
+
return true;
|
2068
|
+
}
|
2069
|
+
return false;
|
2070
|
+
};
|
1888
2071
|
|
1889
|
-
Handlebars.precompile = function(
|
1890
|
-
if (typeof
|
1891
|
-
throw new Handlebars.Exception("You must pass a string to Handlebars.
|
2072
|
+
Handlebars.precompile = function(input, options) {
|
2073
|
+
if (!input || (typeof input !== 'string' && input.constructor !== Handlebars.AST.ProgramNode)) {
|
2074
|
+
throw new Handlebars.Exception("You must pass a string or Handlebars AST to Handlebars.precompile. You passed " + input);
|
1892
2075
|
}
|
1893
2076
|
|
1894
2077
|
options = options || {};
|
1895
2078
|
if (!('data' in options)) {
|
1896
2079
|
options.data = true;
|
1897
2080
|
}
|
1898
|
-
var ast = Handlebars.parse(
|
1899
|
-
var environment = new
|
1900
|
-
return new
|
2081
|
+
var ast = Handlebars.parse(input);
|
2082
|
+
var environment = new Compiler().compile(ast, options);
|
2083
|
+
return new JavaScriptCompiler().compile(environment, options);
|
1901
2084
|
};
|
1902
2085
|
|
1903
|
-
Handlebars.compile = function(
|
1904
|
-
if (typeof
|
1905
|
-
throw new Handlebars.Exception("You must pass a string to Handlebars.compile. You passed " +
|
2086
|
+
Handlebars.compile = function(input, options) {
|
2087
|
+
if (!input || (typeof input !== 'string' && input.constructor !== Handlebars.AST.ProgramNode)) {
|
2088
|
+
throw new Handlebars.Exception("You must pass a string or Handlebars AST to Handlebars.compile. You passed " + input);
|
1906
2089
|
}
|
1907
2090
|
|
1908
2091
|
options = options || {};
|
@@ -1911,9 +2094,9 @@ Handlebars.compile = function(string, options) {
|
|
1911
2094
|
}
|
1912
2095
|
var compiled;
|
1913
2096
|
function compile() {
|
1914
|
-
var ast = Handlebars.parse(
|
1915
|
-
var environment = new
|
1916
|
-
var templateSpec = new
|
2097
|
+
var ast = Handlebars.parse(input);
|
2098
|
+
var environment = new Compiler().compile(ast, options);
|
2099
|
+
var templateSpec = new JavaScriptCompiler().compile(environment, options, undefined, true);
|
1917
2100
|
return Handlebars.template(templateSpec);
|
1918
2101
|
}
|
1919
2102
|
|
@@ -1925,8 +2108,10 @@ Handlebars.compile = function(string, options) {
|
|
1925
2108
|
return compiled.call(this, context, options);
|
1926
2109
|
};
|
1927
2110
|
};
|
2111
|
+
|
1928
2112
|
;
|
1929
2113
|
// lib/handlebars/runtime.js
|
2114
|
+
|
1930
2115
|
Handlebars.VM = {
|
1931
2116
|
template: function(templateSpec) {
|
1932
2117
|
// Just add water
|
@@ -1946,12 +2131,32 @@ Handlebars.VM = {
|
|
1946
2131
|
}
|
1947
2132
|
},
|
1948
2133
|
programWithDepth: Handlebars.VM.programWithDepth,
|
1949
|
-
noop: Handlebars.VM.noop
|
2134
|
+
noop: Handlebars.VM.noop,
|
2135
|
+
compilerInfo: null
|
1950
2136
|
};
|
1951
2137
|
|
1952
2138
|
return function(context, options) {
|
1953
2139
|
options = options || {};
|
1954
|
-
|
2140
|
+
var result = templateSpec.call(container, Handlebars, context, options.helpers, options.partials, options.data);
|
2141
|
+
|
2142
|
+
var compilerInfo = container.compilerInfo || [],
|
2143
|
+
compilerRevision = compilerInfo[0] || 1,
|
2144
|
+
currentRevision = Handlebars.COMPILER_REVISION;
|
2145
|
+
|
2146
|
+
if (compilerRevision !== currentRevision) {
|
2147
|
+
if (compilerRevision < currentRevision) {
|
2148
|
+
var runtimeVersions = Handlebars.REVISION_CHANGES[currentRevision],
|
2149
|
+
compilerVersions = Handlebars.REVISION_CHANGES[compilerRevision];
|
2150
|
+
throw "Template was precompiled with an older version of Handlebars than the current runtime. "+
|
2151
|
+
"Please update your precompiler to a newer version ("+runtimeVersions+") or downgrade your runtime to an older version ("+compilerVersions+").";
|
2152
|
+
} else {
|
2153
|
+
// Use the embedded version info since the runtime doesn't know about this revision yet
|
2154
|
+
throw "Template was precompiled with a newer version of Handlebars than the current runtime. "+
|
2155
|
+
"Please update your runtime to a newer version ("+compilerInfo[1]+").";
|
2156
|
+
}
|
2157
|
+
}
|
2158
|
+
|
2159
|
+
return result;
|
1955
2160
|
};
|
1956
2161
|
},
|
1957
2162
|
|
@@ -1990,3 +2195,6 @@ Handlebars.VM = {
|
|
1990
2195
|
|
1991
2196
|
Handlebars.template = Handlebars.VM.template;
|
1992
2197
|
;
|
2198
|
+
// lib/handlebars/browser-suffix.js
|
2199
|
+
})(Handlebars);
|
2200
|
+
;
|