summaryrefslogtreecommitdiffstats
path: root/lib/handlebars
diff options
context:
space:
mode:
Diffstat (limited to 'lib/handlebars')
-rw-r--r--lib/handlebars/ast.js13
-rw-r--r--lib/handlebars/base.js26
-rw-r--r--lib/handlebars/compiler.js (renamed from lib/handlebars/vm.js)127
-rw-r--r--lib/handlebars/printer.js8
-rw-r--r--lib/handlebars/runtime.js267
-rw-r--r--lib/handlebars/utils.js11
6 files changed, 126 insertions, 326 deletions
diff --git a/lib/handlebars/ast.js b/lib/handlebars/ast.js
index a266fe7..d7cc150 100644
--- a/lib/handlebars/ast.js
+++ b/lib/handlebars/ast.js
@@ -60,7 +60,7 @@ var Handlebars = require("handlebars");
Handlebars.AST.IdNode = function(parts) {
this.type = "ID";
- this.original = parts.join("/");
+ this.original = parts.join(".");
var dig = [], depth = 0;
@@ -73,6 +73,7 @@ var Handlebars = require("handlebars");
}
this.parts = dig;
+ this.string = dig.join('.');
this.depth = depth;
this.isSimple = (dig.length === 1) && (depth === 0);
};
@@ -82,6 +83,16 @@ var Handlebars = require("handlebars");
this.string = string;
};
+ Handlebars.AST.IntegerNode = function(integer) {
+ this.type = "INTEGER";
+ this.integer = integer;
+ };
+
+ Handlebars.AST.BooleanNode = function(bool) {
+ this.type = "BOOLEAN";
+ this.bool = bool;
+ };
+
Handlebars.AST.CommentNode = function(comment) {
this.type = "comment";
this.comment = comment;
diff --git a/lib/handlebars/base.js b/lib/handlebars/base.js
index 387ff76..b1cc4ba 100644
--- a/lib/handlebars/base.js
+++ b/lib/handlebars/base.js
@@ -3,6 +3,8 @@ var handlebars = require("handlebars/parser").parser;
// BEGIN(BROWSER)
var Handlebars = {};
+Handlebars.VERSION = "1.0.beta.2";
+
Handlebars.Parser = handlebars;
Handlebars.parse = function(string) {
@@ -14,22 +16,6 @@ Handlebars.print = function(ast) {
return new Handlebars.PrintVisitor().accept(ast);
};
-Handlebars.Runtime = {};
-
-Handlebars.Runtime.compile = function(string) {
- var ast = Handlebars.parse(string);
-
- return function(context, helpers, partials) {
- helpers = helpers || Handlebars.helpers;
- partials = partials || Handlebars.partials;
-
- var internalContext = new Handlebars.Context(context, helpers, partials);
- var runtime = new Handlebars.Runtime(internalContext);
- runtime.accept(ast);
- return runtime.buffer;
- };
-};
-
Handlebars.helpers = {};
Handlebars.partials = {};
@@ -42,6 +28,14 @@ Handlebars.registerPartial = function(name, str) {
this.partials[name] = str;
};
+Handlebars.registerHelper('helperMissing', function(arg) {
+ if(arguments.length === 2) {
+ return undefined;
+ } else {
+ throw new Error("Could not find property '" + arg + "'");
+ }
+});
+
Handlebars.registerHelper('blockHelperMissing', function(context, fn, inverse) {
inverse = inverse || function() {};
diff --git a/lib/handlebars/vm.js b/lib/handlebars/compiler.js
index 3dbd8f4..6f52482 100644
--- a/lib/handlebars/vm.js
+++ b/lib/handlebars/compiler.js
@@ -20,7 +20,8 @@ Handlebars.JavaScriptCompiler = function() {};
invokePartial: 12,
push: 13,
invokeInverse: 14,
- assignToHash: 15
+ assignToHash: 15,
+ pushStringParam: 16
};
Compiler.MULTI_PARAM_OPCODES = {
@@ -36,7 +37,8 @@ Handlebars.JavaScriptCompiler = function() {};
invokePartial: 1,
push: 1,
invokeInverse: 1,
- assignToHash: 1
+ assignToHash: 1,
+ pushStringParam: 1
};
Compiler.DISASSEMBLE_MAP = {};
@@ -51,6 +53,8 @@ Handlebars.JavaScriptCompiler = function() {};
};
Compiler.prototype = {
+ compiler: Compiler,
+
disassemble: function() {
var opcodes = this.opcodes, opcode, nextCode;
var out = [], str, name, value;
@@ -89,9 +93,10 @@ Handlebars.JavaScriptCompiler = function() {};
guid: 0,
- compile: function(program) {
+ compile: function(program, options) {
this.children = [];
this.depths = {list: []};
+ this.options = options || {};
return this.program(program);
},
@@ -116,7 +121,7 @@ Handlebars.JavaScriptCompiler = function() {};
},
compileProgram: function(program) {
- var result = new Compiler().compile(program);
+ var result = new this.compiler().compile(program, this.options);
var guid = this.guid++;
this.usePartial = this.usePartial || result.usePartial;
@@ -219,6 +224,14 @@ Handlebars.JavaScriptCompiler = function() {};
this.opcode('pushString', string.string);
},
+ INTEGER: function(integer) {
+ this.opcode('push', integer.integer);
+ },
+
+ BOOLEAN: function(bool) {
+ this.opcode('push', bool.bool);
+ },
+
comment: function() {},
// HELPERS
@@ -227,7 +240,17 @@ Handlebars.JavaScriptCompiler = function() {};
while(i--) {
param = params[i];
- this[param.type](param);
+
+ if(this.options.stringParams) {
+ if(param.depth) {
+ this.addDepth(param.depth);
+ }
+
+ this.opcode('getContext', param.depth || 0);
+ this.opcode('pushStringParam', param.string);
+ } else {
+ this[param.type](param);
+ }
}
},
@@ -273,8 +296,10 @@ Handlebars.JavaScriptCompiler = function() {};
// 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]) {
+ if(JavaScriptCompiler.RESERVED_WORDS[name] || name.indexOf('-') !== -1 || !isNaN(name)) {
return parent + "['" + name + "']";
+ } else if (/^[0-9]+$/.test(name)) {
+ return parent + "[" + name + "]";
} else {
return parent + "." + name;
}
@@ -289,9 +314,9 @@ Handlebars.JavaScriptCompiler = function() {};
},
// END PUBLIC API
- compile: function(environment, data) {
+ compile: function(environment, options) {
this.environment = environment;
- this.data = data;
+ this.options = options || {};
this.preamble();
@@ -299,7 +324,7 @@ Handlebars.JavaScriptCompiler = function() {};
this.stackVars = [];
this.registers = {list: []};
- this.compileChildren(environment, data);
+ this.compileChildren(environment, options);
Handlebars.log(Handlebars.logger.DEBUG, environment.disassemble() + "\n\n");
@@ -393,13 +418,12 @@ Handlebars.JavaScriptCompiler = function() {};
var params = ["Handlebars", "context", "helpers", "partials"];
- if(this.data) { params.push("data"); }
+ if(this.options.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 === 4 && !this.environment.usePartial) { params.pop(); }
params.push(this.source.join("\n"));
@@ -413,10 +437,12 @@ Handlebars.JavaScriptCompiler = function() {};
container.children = this.environment.children;
- return function(context, helpers, partials, data, $depth) {
+ return function(context, options, $depth) {
try {
- var args = Array.prototype.slice.call(arguments);
- args.unshift(Handlebars);
+ options = options || {};
+ var args = [Handlebars, context, options.helpers, options.partials, options.data];
+ var depth = Array.prototype.slice.call(arguments, 2);
+ args = args.concat(depth);
return container.render.apply(container, args);
} catch(e) {
throw e;
@@ -477,6 +503,11 @@ Handlebars.JavaScriptCompiler = function() {};
this.source.push(topStack + " = " + this.nameLookup(topStack, name, 'context') + ";");
},
+ pushStringParam: function(string) {
+ this.pushStack("currentContext");
+ this.pushString(string);
+ },
+
pushString: function(string) {
this.pushStack(this.quotedString(string));
},
@@ -503,24 +534,32 @@ Handlebars.JavaScriptCompiler = function() {};
populateParams: function(paramSize, helperId, program, inverse, fn) {
var id = this.popStack(), nextStack;
- var params = [];
+ var params = [], param, stringParam;
var hash = this.popStack();
+ this.register('tmp1', program);
+ this.source.push('tmp1.hash = ' + hash + ';');
+
+ if(this.options.stringParams) {
+ this.source.push('tmp1.contexts = [];');
+ }
+
for(var i=0; i<paramSize; i++) {
- var param = this.popStack();
+ param = this.popStack();
params.push(param);
- }
- this.register('tmp1', program);
- this.source.push('tmp1.hash = ' + hash + ';');
+ if(this.options.stringParams) {
+ this.source.push('tmp1.contexts.push(' + this.popStack() + ');');
+ }
+ }
if(inverse) {
this.source.push('tmp1.fn = tmp1;');
this.source.push('tmp1.inverse = ' + inverse + ';');
}
- if(this.data) {
+ if(this.options.data) {
this.source.push('tmp1.data = data;');
}
@@ -566,7 +605,7 @@ Handlebars.JavaScriptCompiler = function() {};
compiler: JavaScriptCompiler,
- compileChildren: function(environment, data) {
+ compileChildren: function(environment, options) {
var children = environment.children, child, compiler;
var compiled = [];
@@ -574,7 +613,7 @@ Handlebars.JavaScriptCompiler = function() {};
child = children[i];
compiler = new this.compiler();
- compiled[i] = compiler.compile(child, data);
+ compiled[i] = compiler.compile(child, options);
}
environment.rawChildren = children;
@@ -588,7 +627,7 @@ Handlebars.JavaScriptCompiler = function() {};
var depths = this.environment.rawChildren[guid].depths.list;
- if(this.data) { programParams.push("data"); }
+ if(this.options.data) { programParams.push("data"); }
for(var i=0, l = depths.length; i<l; i++) {
depth = depths[i];
@@ -646,7 +685,7 @@ Handlebars.JavaScriptCompiler = function() {};
quotedString: function(str) {
return '"' + str
- .replace(/\\/, '\\\\')
+ .replace(/\\/g, '\\\\')
.replace(/"/g, '\\"')
.replace(/\n/g, '\\n')
.replace(/\r/g, '\\r') + '"';
@@ -666,34 +705,46 @@ Handlebars.JavaScriptCompiler = function() {};
})(Handlebars.Compiler, Handlebars.JavaScriptCompiler);
Handlebars.VM = {
- programWithDepth: function(fn) {
- var args = Array.prototype.slice.call(arguments, 1);
- return function(context, helpers, partials, data) {
- args[0] = helpers || args[0];
- args[1] = partials || args[1];
- args[2] = data || args[2];
- return fn.apply(this, [context].concat(args));
+ programWithDepth: function(fn, helpers, partials, data, $depth) {
+ var args = Array.prototype.slice.call(arguments, 4);
+
+ return function(context, options) {
+ options = options || {};
+
+ options = {
+ helpers: options.helpers || helpers,
+ partials: options.partials || partials,
+ data: options.data || data
+ };
+
+ return fn.apply(this, [context, options].concat(args));
};
},
program: function(fn, helpers, partials, data) {
- return function(context, h2, p2, d2) {
- return fn(context, h2 || helpers, p2 || partials, d2 || data);
+ return function(context, options) {
+ options = options || {};
+
+ return fn(context, {
+ helpers: options.helpers || helpers,
+ partials: options.partials || partials,
+ data: options.data || data
+ });
};
},
noop: function() { return ""; },
- compile: function(string, data) {
+ compile: function(string, options) {
var ast = Handlebars.parse(string);
- var environment = new Handlebars.Compiler().compile(ast);
- return new Handlebars.JavaScriptCompiler().compile(environment, data);
+ var environment = new Handlebars.Compiler().compile(ast, options);
+ return new Handlebars.JavaScriptCompiler().compile(environment, options);
},
invokePartial: function(partial, name, context, helpers, partials) {
if(partial === undefined) {
throw new Handlebars.Exception("The partial " + name + " could not be found");
} else if(partial instanceof Function) {
- return partial(context, helpers, partials);
+ return partial(context, {helpers: helpers, partials: partials});
} else {
partials[name] = Handlebars.VM.compile(partial);
- return partials[name](context, helpers, partials);
+ return partials[name](context, {helpers: helpers, partials: partials});
}
}
};
diff --git a/lib/handlebars/printer.js b/lib/handlebars/printer.js
index 2da7bcc..7be3f98 100644
--- a/lib/handlebars/printer.js
+++ b/lib/handlebars/printer.js
@@ -109,6 +109,14 @@ Handlebars.PrintVisitor.prototype.STRING = function(string) {
return '"' + string.string + '"';
};
+Handlebars.PrintVisitor.prototype.INTEGER = function(integer) {
+ return "INTEGER{" + integer.integer + "}";
+};
+
+Handlebars.PrintVisitor.prototype.BOOLEAN = function(bool) {
+ return "BOOLEAN{" + bool.bool + "}";
+};
+
Handlebars.PrintVisitor.prototype.ID = function(id) {
var path = id.parts.join("/");
if(id.parts.length > 1) {
diff --git a/lib/handlebars/runtime.js b/lib/handlebars/runtime.js
deleted file mode 100644
index 29ace86..0000000
--- a/lib/handlebars/runtime.js
+++ /dev/null
@@ -1,267 +0,0 @@
-var inspect = function(obj) {
- require("sys").print(require("sys").inspect(obj) + "\n");
-};
-
-var Handlebars = require("handlebars");
-
-// BEGIN(BROWSER)
-// A Context wraps data, and makes it possible to extract a
-// new Context given a path. For instance, if the data
-// is { person: { name: "Alan" } }, a Context wrapping
-// "Alan" can be extracted by searching for "person/name"
-Handlebars.Context = function(data, helpers, partials) {
- this.data = data;
- this.helpers = helpers || {};
- this.partials = partials || {};
-};
-
-Handlebars.Context.prototype = {
- isContext: true,
-
- // Make a shallow copy of the Context
- clone: function() {
- return new Handlebars.Context(this.data, this.helpers, this.partials);
- },
-
- // Search for an object inside the Context's data. The
- // path parameter is an object with parts
- // ("person/name" represented as ["person", "name"]),
- // and depth (the amount of levels to go up the stack,
- // originally represented as ..). The stack parameter
- // is the objects already searched from the root of
- // the original Context in order to get to this point.
- //
- // Return a new Context wrapping the data found in
- // the search.
- evaluate: function(path, stack) {
- var context = this.clone();
- var depth = path.depth, parts = path.parts;
-
- if(depth > stack.length) { context.data = null; }
- else if(depth > 0) { context = stack[stack.length - depth].clone(); }
-
- for(var i=0,l=parts.length; i<l && context.data != null; i++) {
- context.data = context.data[parts[i]];
- }
-
- if(context.data !== undefined) { return context; }
-
- if(parts.length === 1 && context.data === undefined) {
- context.data = context.helpers[parts[0]];
- }
-
- return context;
- }
-};
-
-Handlebars.K = function() { return this; };
-
-Handlebars.proxy = function(obj) {
- var Proxy = this.K;
- Proxy.prototype = obj;
- return new Proxy();
-};
-
-Handlebars.Runtime = function(context, stack) {
- this.stack = stack || [];
- this.buffer = "";
-
- this.context = context;
-};
-
-Handlebars.Runtime.prototype = {
- accept: Handlebars.Visitor.prototype.accept,
-
- ID: function(path) {
- return this.context.evaluate(path, this.stack);
- },
-
- STRING: function(string) {
- return { data: string.string };
- },
-
- program: function(program) {
- var statements = program.statements;
-
- for(var i=0, l=statements.length; i<l; i++) {
- var statement = statements[i];
- this[statement.type](statement);
- }
-
- return this.buffer;
- },
-
- mustache: function(mustache) {
- var idObj = this.ID(mustache.id);
- var params = mustache.params.slice(0);
- var buf;
-
- for(var i=0, l=params.length; i<l; i++) {
- var param = params[i];
- params[i] = this[param.type](param).data;
- }
-
- var data = idObj.data;
-
- var type = Object.prototype.toString.call(data);
- var functionType = (type === "[object Function]");
-
- if(!functionType && params.length) {
- params = params.slice(0);
- params.unshift(data || mustache.id.original);
- data = this.context.helpers.helperMissing;
- functionType = true;
- }
-
- if(functionType) {
- buf = data.apply(this.wrapContext(), params);
- } else {
- buf = data;
- }
-
- if(buf && mustache.escaped) { buf = Handlebars.Utils.escapeExpression(buf); }
-
- this.buffer = this.buffer + ((!buf && buf !== 0) ? '' : buf);
- },
-
- block: function(block) {
- var mustache = block.mustache, data;
-
- var id = mustache.id,
- idObj = this.ID(mustache.id),
- data = idObj.data;
-
- var result;
-
- if(typeof data === "function") {
- params = this.evaluateParams(mustache.params);
- } else {
- params = [data];
- data = this.context.helpers.blockHelperMissing;
- }
-
- params.push(this.wrapProgram(block.program));
- result = data.apply(this.wrapContext(), params);
- this.buffer = this.buffer + ((result === undefined) ? "" : result);
-
- if(block.program.inverse) {
- params.pop();
- params.push(this.wrapProgram(block.program.inverse));
- result = data.not ? data.not.apply(this.wrapContext(), params) : "";
- this.buffer = this.buffer + result;
- }
- },
-
- partial: function(partial) {
- var partials = this.context.partials || {};
- var id = partial.id.original;
-
- var partialBody = partials[partial.id.original];
- var program, context;
-
- if(!partialBody) {
- throw new Handlebars.Exception("The partial " + partial.id.original + " does not exist");
- }
-
- if(typeof partialBody === "string") {
- program = Handlebars.parse(partialBody);
- partials[id] = program;
- } else {
- program = partialBody;
- }
-
- if(partial.context) {
- context = this.ID(partial.context);
- } else {
- context = this.context;
- }
- var runtime = new Handlebars.Runtime(context, this.stack);
- this.buffer = this.buffer + runtime.program(program);
- },
-
- not: function(context, fn) {
- return fn(context);
- },
-
- // TODO: Write down the actual spec for inverse sections...
- inverse: function(block) {
- var mustache = block.mustache,
- id = mustache.id,
- not;
-
- var idObj = this.ID(id),
- data = idObj.data,
- isInverse = Handlebars.Utils.isEmpty(data);
-
-
- var context = this.wrapContext();
-
- if(Object.prototype.toString.call(data) === "[object Function]") {
- params = this.evaluateParams(mustache.params);
- id = id.parts.join("/");
-
- data = data.apply(context, params);
- if(Handlebars.Utils.isEmpty(data)) { isInverse = true; }
- if(data.not) { not = data.not; } else { not = this.not; }
- } else {
- not = this.not;
- }
-
- var result = not(context, this.wrapProgram(block.program));
- if(result != null) { this.buffer = this.buffer + result; }
- return;
- },
-
- content: function(content) {
- this.buffer += content.string;
- },
-
- comment: function() {},
-
- evaluateParams: function(params) {
- var ret = [];
-
- for(var i=0, l=params.length; i<l; i++) {
- var param = params[i];
- ret[i] = this[param.type](param).data;
- }
-
- if(ret.length === 0) { ret = [this.wrapContext()]; }
- return ret;
- },
-
- wrapContext: function() {
- var data = this.context.data;
- var proxy = Handlebars.proxy(data);
- var context = proxy.__context__ = this.context;
- var stack = proxy.__stack__ = this.stack.slice(0);
-
- proxy.__get__ = function(path) {
- path = new Handlebars.AST.IdNode(path.split("/"));
- return context.evaluate(path, stack).data;
- };
-
- proxy.isWrappedContext = true;
- proxy.__data__ = data;
-
- return proxy;
- },
-
- wrapProgram: function(program) {
- var currentContext = this.context;
- var stack = this.stack.slice(0);
-
- return function(context) {
- if(context && context.isWrappedContext) { context = context.__data__; }
-
- stack.push(currentContext);
- var newContext = new Handlebars.Context(context, currentContext.helpers, currentContext.partials);
- var runtime = new Handlebars.Runtime(newContext, stack);
- runtime.program(program);
- return runtime.buffer;
- };
- }
-
-};
-// END(BROWSER)
-
diff --git a/lib/handlebars/utils.js b/lib/handlebars/utils.js
index 981bb1f..4202c77 100644
--- a/lib/handlebars/utils.js
+++ b/lib/handlebars/utils.js
@@ -16,14 +16,17 @@ Handlebars.SafeString.prototype.toString = function() {
(function() {
var escape = {
"<": "&lt;",
- ">": "&gt;"
+ ">": "&gt;",
+ '"': "&quot;",
+ "'": "&#x27;",
+ "`": "&#x60;"
};
- var badChars = /&(?!\w+;)|[<>]/g;
- var possible = /[&<>]/
+ var badChars = /&(?!\w+;)|[<>"'`]/g;
+ var possible = /[&<>"'`]/;
var escapeChar = function(chr) {
- return escape[chr] || "&amp;"
+ return escape[chr] || "&amp;";
};
Handlebars.Utils = {