* Added a few public API methods to JavaScriptCompiler.prototype, so it can be subclassed.

* made it possible to define an alternate name lookup scheme (so that {{foo}} does not have to be
    context.foo, but can instead be something like context.get('foo'))
  * made it possible to substitute an alternate buffer instead of the default empty String and override how
    the compiled template appends to the buffer
* Added the concept of template-local data. In order to enable template-local data, pass true
  as the second parameter to the template compiler. Then, pass in the data as the fourth
  parameter (context, helpers, partials, data). These signatures may change before the 1.0 release.
This commit is contained in:
tomhuda
2011-02-11 17:02:26 -08:00
parent 2eb1b30870
commit 88e7003331
4 changed files with 125 additions and 38 deletions
+55 -34
View File
@@ -246,8 +246,28 @@ Handlebars.JavaScriptCompiler = function() {};
};
JavaScriptCompiler.prototype = {
compile: function(environment) {
// PUBLIC API: You can override these methods in a subclass to provide
// alternative compiled forms for name lookup and buffering semantics
nameLookup: function(parent, name, type) {
if(JavaScriptCompiler.RESERVED_WORDS[name]) {
return parent + "['" + name + "']";
} else {
return parent + "." + name;
}
},
appendToBuffer: function(string) {
return "buffer = buffer + " + string + ";";
},
initializeBuffer: function() {
return this.quotedString("");
},
// END PUBLIC API
compile: function(environment, data) {
this.environment = environment;
this.data = data;
this.preamble();
@@ -255,7 +275,7 @@ Handlebars.JavaScriptCompiler = function() {};
this.stackVars = [];
this.registers = {list: []};
this.compileChildren(environment);
this.compileChildren(environment, data);
Handlebars.log(Handlebars.logger.DEBUG, environment.disassemble() + "\n\n");
@@ -306,7 +326,7 @@ Handlebars.JavaScriptCompiler = function() {};
preamble: function() {
var out = [];
out.push("var buffer = '', currentContext = context");
out.push("var buffer = " + this.initializeBuffer() + ", currentContext = context");
var copies = "helpers = helpers || Handlebars.helpers;";
if(this.environment.usePartial) { copies = copies + " partials = partials || Handlebars.partials;"; }
@@ -323,9 +343,11 @@ Handlebars.JavaScriptCompiler = function() {};
escapeExpression: Handlebars.Utils.escapeExpression,
invokePartial: Handlebars.VM.invokePartial,
programs: [],
program: function(i, helpers, partials) {
program: function(i, helpers, partials, data) {
var programWrapper = this.programs[i];
if(programWrapper) {
if(data) {
return Handlebars.VM.program(this.children[i], helpers, partials, data);
} else if(programWrapper) {
return programWrapper;
} else {
programWrapper = this.programs[i] = Handlebars.VM.program(this.children[i], helpers, partials);
@@ -347,11 +369,14 @@ Handlebars.JavaScriptCompiler = function() {};
var params = ["Handlebars", "context", "helpers", "partials"];
if(this.data) { params.push("data"); }
for(var i=0, l=this.environment.depths.list.length; i<l; i++) {
params.push("depth" + this.environment.depths.list[i]);
}
if(params.length === 3 && !this.environment.usePartial) { params.pop(); }
if(params.length === 4 && !this.environment.usePartial) { params.pop(); }
params.push(this.source.join("\n"));
@@ -364,9 +389,11 @@ Handlebars.JavaScriptCompiler = function() {};
container.children = this.environment.children;
return function(context, helpers, partials, depth) {
return function(context, helpers, partials, data, $depth) {
try {
return container.render.call(container, Handlebars, context, helpers, partials, depth);
var args = Array.prototype.slice.call(arguments);
args.unshift(Handlebars);
return container.render.apply(container, args);
} catch(e) {
throw e;
}
@@ -374,12 +401,12 @@ Handlebars.JavaScriptCompiler = function() {};
},
appendContent: function(content) {
this.source.push("buffer = buffer + " + this.quotedString(content) + ";");
this.source.push(this.appendToBuffer(this.quotedString(content)));
},
append: function() {
var local = this.popStack();
this.source.push("if(" + local + " || " + local + " === 0) { buffer = buffer + " + local + "; }");
this.source.push("if(" + local + " || " + local + " === 0) { " + this.appendToBuffer(local) + " }");
},
appendEscaped: function() {
@@ -390,7 +417,7 @@ Handlebars.JavaScriptCompiler = function() {};
this.eat(opcode);
}
this.source.push("buffer = buffer + this.escapeExpression(" + this.popStack() + ")" + extra + ";");
this.source.push(this.appendToBuffer("this.escapeExpression(" + this.popStack() + ")" + extra));
},
getContext: function(depth) {
@@ -405,19 +432,11 @@ Handlebars.JavaScriptCompiler = function() {};
}
},
nameLookup: function(parent, name) {
if(JavaScriptCompiler.RESERVED_WORDS[name]) {
return parent + "['" + name + "']";
} else {
return parent + "." + name;
}
},
lookupWithFallback: function(name) {
if(name) {
this.pushStack(this.nameLookup('currentContext', name));
this.pushStack(this.nameLookup('currentContext', name, 'context'));
var topStack = this.topStack();
this.source.push("if(" + topStack + " === undefined) { " + topStack + " = " + this.nameLookup('helpers', name) + "; }");
this.source.push("if(" + topStack + " === undefined) { " + topStack + " = " + this.nameLookup('helpers', name, 'helper') + "; }");
} else {
this.pushStack("currentContext");
}
@@ -425,7 +444,7 @@ Handlebars.JavaScriptCompiler = function() {};
lookup: function(name) {
var topStack = this.topStack();
this.source.push(topStack + " = " + this.nameLookup(topStack, name) + ";");
this.source.push(topStack + " = " + this.nameLookup(topStack, name, 'context') + ";");
},
pushString: function(string) {
@@ -445,6 +464,8 @@ Handlebars.JavaScriptCompiler = function() {};
params.push(this.popStack());
}
if(this.data) { params.push("data"); }
var paramString = params.join(", ");
var helperMissing = ["context"].concat(this.quotedString(original)).concat(params.slice(1));
@@ -492,12 +513,12 @@ Handlebars.JavaScriptCompiler = function() {};
},
invokePartial: function(context) {
this.pushStack("this.invokePartial(" + this.nameLookup('partials', context) + ", '" + context + "', " + this.popStack() + ", helpers, partials);");
this.pushStack("this.invokePartial(" + this.nameLookup('partials', context, 'partial') + ", '" + context + "', " + this.popStack() + ", helpers, partials);");
},
// HELPERS
compileChildren: function(environment) {
compileChildren: function(environment, data) {
var children = environment.children, child, compiler;
var compiled = [];
@@ -505,7 +526,7 @@ Handlebars.JavaScriptCompiler = function() {};
child = children[i];
compiler = new JavaScriptCompiler();
compiled[i] = compiler.compile(child);
compiled[i] = compiler.compile(child, data);
}
environment.rawChildren = children;
@@ -519,6 +540,8 @@ Handlebars.JavaScriptCompiler = function() {};
var depths = this.environment.rawChildren[guid].depths.list;
if(this.data) { programParams.push("data"); }
for(var i=0, l = depths.length; i<l; i++) {
depth = depths[i];
@@ -526,7 +549,7 @@ Handlebars.JavaScriptCompiler = function() {};
else { programParams.push("depth" + (depth - 1)); }
}
if(!this.environment.usePartial) {
if(!this.environment.usePartial) {
if(programParams[3]) {
programParams[2] = "null";
} else {
@@ -540,8 +563,6 @@ Handlebars.JavaScriptCompiler = function() {};
programParams[0] = "this.children[" + guid + "]";
return "this.programWithDepth(" + programParams.join(", ") + ")";
}
return "this.program(" + programParams.join(", ") + ")";
},
register: function(name, val) {
@@ -597,22 +618,22 @@ Handlebars.JavaScriptCompiler = function() {};
})(Handlebars.Compiler, Handlebars.JavaScriptCompiler);
Handlebars.VM = {
programWithDepth: function(fn, helpers, partials, depth) {
var args = [].slice.call(arguments, 1);
programWithDepth: function(fn) {
var args = Array.prototype.slice.call(arguments, 1);
return function(context) {
return fn.apply(this, [context].concat(args));
};
},
program: function(fn, helpers, partials) {
program: function(fn, helpers, partials, data) {
return function(context) {
return fn(context, helpers, partials);
return fn(context, helpers, partials, data);
};
},
noop: function() { return ""; },
compile: function(string) {
compile: function(string, data) {
var ast = Handlebars.parse(string);
var environment = new Handlebars.Compiler().compile(ast);
return new Handlebars.JavaScriptCompiler().compile(environment);
return new Handlebars.JavaScriptCompiler().compile(environment, data);
},
invokePartial: function(partial, name, context, helpers, partials) {
if(partial === undefined) {