diff --git a/lib/handlebars.js b/lib/handlebars.js index 69717044..8bbde587 100644 --- a/lib/handlebars.js +++ b/lib/handlebars.js @@ -2,7 +2,6 @@ Handlebars = { compile: function(string) { var compiler = new Handlebars.Compiler(string); var result = compiler.compile(); - console.log(result); return new Function("context", "fallback", "fallback = fallback || {}; var stack = [];" + result); }, @@ -19,11 +18,34 @@ Handlebars.Compiler = function(string) { this.fn = "var out = ''; var lookup; "; this.newlines = ""; this.comment = false; -} +}; Handlebars.ParseError = function(message) { this.message = message; -} +}; + +Handlebars.Context = function(stack) { + this.__stack__ = stack; + this.__get__ = function(path) { + var context = this; + var parsedPath = Handlebars.parsePath(path); + var depth = parsedPath[0]; + var parts = parsedPath[1]; + if (depth > 0) { + context = this.__stack__[stack.length - depth]; + } + + for (var i = 0; i < parts.length; i++) { + context = context[parts[i]]; + } + + return context; + }; +}; +Handlebars.buildContext = function(context, stack) { + Handlebars.Context.prototype = context; + return new Handlebars.Context(stack); +}; // Returns a two element array containing the numbers of contexts to back up the stack and // the properties to dig into on the current context @@ -57,7 +79,7 @@ Handlebars.parsePath = function(path) { } return [depth, dig]; -} +}; Handlebars.helperMissing = function(object, fn) { var ret = ""; @@ -120,7 +142,8 @@ Handlebars.Compiler.prototype = { addExpression: function(mustache) { var expr = this.lookupFor(mustache); - this.fn += "out = out + (Handlebars.isFunction(" + expr + ") ? " + expr + "() : " + expr + "); "; + this.fn += "if (Handlebars.isFunction(" + expr + ")) out = out + " + expr + ".call(Handlebars.buildContext(context, stack)); "; + this.fn += "else if(typeof " + expr + "!== 'undefined') out = out + " + expr + "; "; }, lookupFor: function(param) { @@ -186,11 +209,11 @@ Handlebars.Compiler.prototype = { // each function made internally needs a unique IDs. These are locals, so they // don't need to be globally unique, just per compiler var fnId = "fn" + this.pointer.toString(); - + this.fn += "var proxy = Handlebars.buildContext(" + param + ", stack);"; this.fn += "var " + fnId + " = function(context) {" + result + "}; "; this.fn += "lookup = " + this.lookupFor(mustache) + "; "; - this.fn += "if(Handlebars.isFunction(lookup)) out = out + lookup.call(" + param + ", " + fnId + "); "; - this.fn += "else if(typeof lookup !== 'undefined') out = out + Handlebars.helperMissing.call(" + param + ", lookup, " + fnId + "); "; + this.fn += "if(Handlebars.isFunction(lookup)) out = out + lookup.call(proxy, " + fnId + "); "; + this.fn += "else if(typeof lookup !== 'undefined') out = out + Handlebars.helperMissing.call(proxy, lookup, " + fnId + "); "; this.fn += "stack.pop();"; this.openBlock = false; @@ -207,4 +230,3 @@ Handlebars.Compiler.prototype = { } } } - diff --git a/test/handlebars.js b/test/handlebars.js index 9fcc0d6b..199bbf68 100644 --- a/test/handlebars.js +++ b/test/handlebars.js @@ -88,6 +88,15 @@ test("block with complex lookup", function() { "Templates can access variables in contexts up the stack with relative path syntax"); }); +test("helper with complex lookup", function() { + var string = "{{#goodbyes}}{{link}}{{/goodbyes}}" + var hash = {prefix: "/root", goodbyes: [{text: "Goodbye", url: "goodbye"}]}; + var fallback = {link: function() { + return "" + this.text + "" + }}; + shouldCompileTo(string, [hash, fallback], "Goodbye") +}); + test("block with deep nested complex lookup", function() { var string = "{{#outer}}Goodbye {{#inner}}cruel {{../../omg}}{{/inner}}{{/outer}}"; var hash = {omg: "OMG!", outer: [{ inner: [{ text: "goodbye" }] }] }; @@ -107,7 +116,7 @@ test("block helper staying in the same context", function() { var string = "{{#form}}
{{name}}
{{/form}}" var template = Handlebars.compile(string); - result = template({form: function(fn) { return "" }, name: "Yehuda"}); + result = template({form: function(fn) { console.log(fn); return "" }, name: "Yehuda"}); equal(result, ""); }); @@ -119,6 +128,14 @@ test("block helper passing a new context", function() { equal(result, ""); }); +test("block helper passing a complex path context", function() { + var string = "{{#form yehuda/cat}}{{name}}
{{/form}}" + var template = Handlebars.compile(string); + + result = template({form: function(fn) { return "" }, yehuda: {name: "Yehuda", cat: {name: "Harold"}}}); + equal(result, ""); +}); + test("nested block helpers", function() { var string = "{{#form yehuda}}{{name}}
{{#link}}Hello{{/link}}{{/form}}" var template = Handlebars.compile(string);