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