summaryrefslogtreecommitdiffstats
path: root/lib/handlebars/compiler
diff options
context:
space:
mode:
Diffstat (limited to 'lib/handlebars/compiler')
-rw-r--r--lib/handlebars/compiler/ast.js35
-rw-r--r--lib/handlebars/compiler/compiler.js23
-rw-r--r--lib/handlebars/compiler/javascript-compiler.js71
3 files changed, 95 insertions, 34 deletions
diff --git a/lib/handlebars/compiler/ast.js b/lib/handlebars/compiler/ast.js
index 336492d..f6229e2 100644
--- a/lib/handlebars/compiler/ast.js
+++ b/lib/handlebars/compiler/ast.js
@@ -1,15 +1,25 @@
import Exception from "../exception";
-export function ProgramNode(statements, inverse) {
+export function ProgramNode(statements, inverseStrip, inverse) {
this.type = "program";
this.statements = statements;
- if(inverse) { this.inverse = new ProgramNode(inverse); }
+ this.strip = {};
+
+ if(inverse) {
+ this.inverse = new ProgramNode(inverse, inverseStrip);
+ this.strip.right = inverseStrip.left;
+ } else if (inverseStrip) {
+ this.strip.left = inverseStrip.right;
+ }
}
-export function MustacheNode(rawParams, hash, unescaped) {
+export function MustacheNode(rawParams, hash, open, strip) {
this.type = "mustache";
- this.escaped = !unescaped;
this.hash = hash;
+ this.strip = strip;
+
+ var escapeFlag = open[3] || open[2];
+ this.escaped = escapeFlag !== '{' && escapeFlag !== '&';
var id = this.id = rawParams[0];
var params = this.params = rawParams.slice(1);
@@ -28,15 +38,16 @@ export function MustacheNode(rawParams, hash, unescaped) {
// pass or at runtime.
}
-export function PartialNode(partialName, context) {
+export function PartialNode(partialName, context, strip) {
this.type = "partial";
this.partialName = partialName;
this.context = context;
+ this.strip = strip;
}
export function BlockNode(mustache, program, inverse, close) {
- if(mustache.id.original !== close.original) {
- throw new Exception(mustache.id.original + " doesn't match " + close.original);
+ if(mustache.id.original !== close.path.original) {
+ throw new Exception(mustache.id.original + " doesn't match " + close.path.original);
}
this.type = "block";
@@ -44,7 +55,15 @@ export function BlockNode(mustache, program, inverse, close) {
this.program = program;
this.inverse = inverse;
- if (this.inverse && !this.program) {
+ this.strip = {
+ left: mustache.strip.left,
+ right: close.strip.right
+ };
+
+ (program || inverse).strip.left = mustache.strip.right;
+ (inverse || program).strip.right = close.strip.left;
+
+ if (inverse && !program) {
this.isInverse = true;
}
}
diff --git a/lib/handlebars/compiler/compiler.js b/lib/handlebars/compiler/compiler.js
index 50195e3..4f232eb 100644
--- a/lib/handlebars/compiler/compiler.js
+++ b/lib/handlebars/compiler/compiler.js
@@ -72,6 +72,7 @@ Compiler.prototype = {
guid: 0,
compile: function(program, options) {
+ this.opcodes = [];
this.children = [];
this.depths = {list: []};
this.options = options;
@@ -93,20 +94,30 @@ Compiler.prototype = {
}
}
- return this.program(program);
+ return this.accept(program);
},
accept: function(node) {
- return this[node.type](node);
+ var strip = node.strip || {},
+ ret;
+ if (strip.left) {
+ this.opcode('strip');
+ }
+
+ ret = this[node.type](node);
+
+ if (strip.right) {
+ this.opcode('strip');
+ }
+
+ return ret;
},
program: function(program) {
- var statements = program.statements, statement;
- this.opcodes = [];
+ var statements = program.statements;
for(var i=0, l=statements.length; i<l; i++) {
- statement = statements[i];
- this[statement.type](statement);
+ this.accept(statements[i]);
}
this.isSimple = l === 1;
diff --git a/lib/handlebars/compiler/javascript-compiler.js b/lib/handlebars/compiler/javascript-compiler.js
index 283c20c..b04ef1a 100644
--- a/lib/handlebars/compiler/javascript-compiler.js
+++ b/lib/handlebars/compiler/javascript-compiler.js
@@ -75,18 +75,17 @@ JavaScriptCompiler.prototype = {
} else {
this[opcode.opcode].apply(this, opcode.args);
}
- }
- return this.createFunctionContext(asObject);
- },
+ // Reset the stripNext flag if it was not set by this operation.
+ if (opcode.opcode !== this.stripNext) {
+ this.stripNext = false;
+ }
+ }
- nextOpcode: function() {
- var opcodes = this.environment.opcodes;
- return opcodes[this.i + 1];
- },
+ // Flush any trailing content that might be pending.
+ this.pushSource('');
- eat: function() {
- this.i = this.i + 1;
+ return this.createFunctionContext(asObject);
},
preamble: function() {
@@ -141,7 +140,7 @@ JavaScriptCompiler.prototype = {
}
if (!this.environment.isSimple) {
- this.source.push("return buffer;");
+ this.pushSource("return buffer;");
}
var params = this.isChild ? ["depth0", "data"] : ["Handlebars", "depth0", "helpers", "partials", "data"];
@@ -232,7 +231,7 @@ JavaScriptCompiler.prototype = {
// Use the options value generated from the invocation
params[params.length-1] = 'options';
- this.source.push("if (!" + this.lastHelper + ") { " + current + " = blockHelperMissing.call(" + params.join(", ") + "); }");
+ this.pushSource("if (!" + this.lastHelper + ") { " + current + " = blockHelperMissing.call(" + params.join(", ") + "); }");
},
// [appendContent]
@@ -242,7 +241,28 @@ JavaScriptCompiler.prototype = {
//
// Appends the string value of `content` to the current buffer
appendContent: function(content) {
- this.source.push(this.appendToBuffer(this.quotedString(content)));
+ if (this.pendingContent) {
+ content = this.pendingContent + content;
+ }
+ if (this.stripNext) {
+ content = content.replace(/^\s+/, '');
+ }
+
+ this.pendingContent = content;
+ },
+
+ // [strip]
+ //
+ // On stack, before: ...
+ // On stack, after: ...
+ //
+ // Removes any trailing whitespace from the prior content node and flags
+ // the next operation for stripping if it is a content node.
+ strip: function() {
+ if (this.pendingContent) {
+ this.pendingContent = this.pendingContent.replace(/\s+$/, '');
+ }
+ this.stripNext = 'strip';
},
// [append]
@@ -259,9 +279,9 @@ JavaScriptCompiler.prototype = {
// when we examine local
this.flushInline();
var local = this.popStack();
- this.source.push("if(" + local + " || " + local + " === 0) { " + this.appendToBuffer(local) + " }");
+ this.pushSource("if(" + local + " || " + local + " === 0) { " + this.appendToBuffer(local) + " }");
if (this.environment.isSimple) {
- this.source.push("else { " + this.appendToBuffer("''") + " }");
+ this.pushSource("else { " + this.appendToBuffer("''") + " }");
}
},
@@ -274,7 +294,7 @@ JavaScriptCompiler.prototype = {
appendEscaped: function() {
this.context.aliases.escapeExpression = 'this.escapeExpression';
- this.source.push(this.appendToBuffer("escapeExpression(" + this.popStack() + ")"));
+ this.pushSource(this.appendToBuffer("escapeExpression(" + this.popStack() + ")"));
},
// [getContext]
@@ -498,8 +518,8 @@ JavaScriptCompiler.prototype = {
var nonHelper = this.nameLookup('depth' + this.lastContext, name, 'context');
var nextStack = this.nextStack();
- this.source.push('if (' + nextStack + ' = ' + helperName + ') { ' + nextStack + ' = ' + nextStack + '.call(' + helper.callParams + '); }');
- this.source.push('else { ' + nextStack + ' = ' + nonHelper + '; ' + nextStack + ' = typeof ' + nextStack + ' === functionType ? ' + nextStack + '.call(' + helper.callParams + ') : ' + nextStack + '; }');
+ this.pushSource('if (' + nextStack + ' = ' + helperName + ') { ' + nextStack + ' = ' + nextStack + '.call(' + helper.callParams + '); }');
+ this.pushSource('else { ' + nextStack + ' = ' + nonHelper + '; ' + nextStack + ' = typeof ' + nextStack + ' === functionType ? ' + nextStack + '.call(' + helper.callParams + ') : ' + nextStack + '; }');
},
// [invokePartial]
@@ -606,7 +626,7 @@ JavaScriptCompiler.prototype = {
register: function(name, val) {
this.useRegister(name);
- this.source.push(name + " = " + val + ";");
+ this.pushSource(name + " = " + val + ";");
},
useRegister: function(name) {
@@ -620,12 +640,23 @@ JavaScriptCompiler.prototype = {
return this.push(new Literal(item));
},
+ pushSource: function(source) {
+ if (this.pendingContent) {
+ this.source.push(this.appendToBuffer(this.quotedString(this.pendingContent)));
+ this.pendingContent = undefined;
+ }
+
+ if (source) {
+ this.source.push(source);
+ }
+ },
+
pushStack: function(item) {
this.flushInline();
var stack = this.incrStack();
if (item) {
- this.source.push(stack + " = " + item + ";");
+ this.pushSource(stack + " = " + item + ";");
}
this.compileStack.push(stack);
return stack;
@@ -668,7 +699,7 @@ JavaScriptCompiler.prototype = {
stack = this.nextStack();
}
- this.source.push(stack + " = (" + prefix + item + ");");
+ this.pushSource(stack + " = (" + prefix + item + ");");
}
return stack;
},