From a15ae62460437bd1447bc8b5f2867becb52cecd0 Mon Sep 17 00:00:00 2001 From: wycats Date: Wed, 17 Nov 2010 13:32:04 -0800 Subject: [PATCH 1/9] Whitespace --- lib/handlebars.js | 40 ++++++++++++++++++++-------------------- 1 file changed, 20 insertions(+), 20 deletions(-) diff --git a/lib/handlebars.js b/lib/handlebars.js index f6a929f3..2c58e1ac 100644 --- a/lib/handlebars.js +++ b/lib/handlebars.js @@ -5,7 +5,7 @@ var Handlebars = { if (Handlebars.compilerCache[string] == null) { var fnBody = Handlebars.compileFunctionBody(string); var fn = new Function("context", "fallback", "stack", "Handlebars", fnBody); - Handlebars.compilerCache[string] = + Handlebars.compilerCache[string] = function(context, fallback, stack) { return fn(context, fallback, stack, Handlebars); }; } @@ -16,7 +16,7 @@ var Handlebars = { var fnBody = Handlebars.compileFunctionBody(string); return "function(context, fallback, stack) { " + fnBody + "}"; }, - + compileFunctionBody: function(string) { var compiler = new Handlebars.Compiler(string); compiler.compile(); @@ -29,7 +29,7 @@ var Handlebars = { }, trim: function(str) { - return str.replace(/^\s+|\s+$/g, ''); + return str.replace(/^\s+|\s+$/g, ''); }, escapeText: function(string) { @@ -75,13 +75,13 @@ var Handlebars = { } else { compiled = Handlebars.compile(partial); } - + return compiled; }, evalExpression: function(path, context, stack, fallback) { fallback = fallback || {}; - + var parsedPath = Handlebars.parsePath(path); var depth = parsedPath[0]; var parts = parsedPath[1]; @@ -94,16 +94,16 @@ var Handlebars = { for (var i = 0; i < parts.length && typeof context !== "undefined" && context != null ; i++) { context = context[parts[i]]; } - + if (parts.length == 1 && typeof context === "undefined") { return fallback[parts[0]]; } - + return context; }, buildContext: function(context, stack) { - var ContextWrapper = function(stack) { + var ContextWrapper = function(stack) { this.__stack__ = stack.slice(0); this.__get__ = function(path) { return Handlebars.evalExpression(path, this, this.__stack__); @@ -136,7 +136,7 @@ var Handlebars = { switch(parts[i]) { case "..": if (readDepth) { - throw new Handlebars.Exception("Cannot jump out of context after moving into a context."); + throw new Handlebars.Exception("Cannot jump out of context after moving into a context."); } else { depth += 1; } @@ -151,7 +151,7 @@ var Handlebars = { dig.push(parts[i]); } } - + var ret = [depth, dig]; Handlebars.pathPatterns["hbs" + path] = ret; return ret; @@ -173,11 +173,11 @@ var Handlebars = { // Escapes output and converts empty values to empty strings filterOutput: function(value, escape) { - + if (Handlebars.isEmpty(value)) { return ""; } else if (escape) { - return Handlebars.escapeExpression(value); + return Handlebars.escapeExpression(value); } else { return value; } @@ -188,19 +188,19 @@ var Handlebars = { if (Handlebars.isFunction(lookup)) { out = out + lookup.call(context, arg, fn); if (notFn != null && Handlebars.isFunction(lookup.not)) { - out = out + lookup.not.call(context, arg, notFn); - } + out = out + lookup.not.call(context, arg, notFn); + } } else { if (!Handlebars.isEmpty(lookup)) { out = out + Handlebars.helperMissing.call(arg, lookup, fn); } - + if (notFn != null) { out = out + Handlebars.helperMissing.not.call(arg, lookup, notFn); } } - + return out; }, @@ -334,7 +334,7 @@ Handlebars.Compiler.prototype = { }, addExpression: function(mustache, param) { - param = param || null; + param = param || null; var expr = this.lookupFor(mustache); this.fn += "var proxy = Handlebars.buildContext(context, stack);" this.fn += "out = out + Handlebars.handleExpression(" + expr + ", proxy, " + param + ", " + this.escaped + ");"; @@ -343,7 +343,7 @@ Handlebars.Compiler.prototype = { addInvertedSection: function(mustache) { var compiler = this.compileToEndOfBlock(mustache); var result = compiler.fn; - + // 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(); @@ -413,7 +413,7 @@ Handlebars.Compiler.prototype = { addPartial: function(mustache, param) { // either used a cached copy of the partial or compile a new one - this.fn += "if (typeof fallback['partials'] === 'undefined' || typeof fallback['partials']['" + mustache + "'] === 'undefined') throw new Handlebars.Exception('Attempted to render undefined partial: " + mustache + "');"; + this.fn += "if (typeof fallback['partials'] === 'undefined' || typeof fallback['partials']['" + mustache + "'] === 'undefined') throw new Handlebars.Exception('Attempted to render undefined partial: " + mustache + "');"; this.fn += "out = out + Handlebars.compilePartial(fallback['partials']['" + mustache + "'])(" + param + ", fallback, stack);"; }, @@ -453,7 +453,7 @@ Handlebars.Compiler.prototype = { // finish reading off the close of the handlebars this.getChar(); - // {{{expression}} is techically valid, but if we started with {{{ we'll try to read + // {{{expression}} is techically valid, but if we started with {{{ we'll try to read // }}} off of the close of the handlebars if (!this.escaped && this.peek() === "}") { this.getChar(); From 75f2c8e3ce791465a4afd06c715a4cb1ec131f48 Mon Sep 17 00:00:00 2001 From: wycats Date: Wed, 17 Nov 2010 13:35:26 -0800 Subject: [PATCH 2/9] Fix indentation issue --- lib/handlebars.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/lib/handlebars.js b/lib/handlebars.js index 2c58e1ac..3138ab90 100644 --- a/lib/handlebars.js +++ b/lib/handlebars.js @@ -95,9 +95,9 @@ var Handlebars = { context = context[parts[i]]; } - if (parts.length == 1 && typeof context === "undefined") { - return fallback[parts[0]]; - } + if (parts.length == 1 && typeof context === "undefined") { + return fallback[parts[0]]; + } return context; }, From 766497bb553a926c046c5e5b50ee35529d168ba5 Mon Sep 17 00:00:00 2001 From: wycats Date: Wed, 17 Nov 2010 16:12:24 -0800 Subject: [PATCH 3/9] Add support for multiple params and String literals --- lib/handlebars.js | 78 +++++++++++++++++++++++++++++++++------------- test/handlebars.js | 48 ++++++++++++++++++++++++++++ 2 files changed, 105 insertions(+), 21 deletions(-) diff --git a/lib/handlebars.js b/lib/handlebars.js index 3138ab90..d85336d9 100644 --- a/lib/handlebars.js +++ b/lib/handlebars.js @@ -184,31 +184,37 @@ var Handlebars = { }, handleBlock: function(lookup, context, arg, fn, notFn) { - var out = ""; + var out = "", args; + originalArgs = arg.length ? arg : [null] + if (Handlebars.isFunction(lookup)) { - out = out + lookup.call(context, arg, fn); + args = originalArgs.concat(fn); + out = out + lookup.apply(context, args); + if (notFn != null && Handlebars.isFunction(lookup.not)) { - out = out + lookup.not.call(context, arg, notFn); + args = originalArgs.concat(notFn); + out = out + lookup.not.apply(context, args); } } else { if (!Handlebars.isEmpty(lookup)) { - out = out + Handlebars.helperMissing.call(arg, lookup, fn); + // TODO: which case is this, and what does it mean for multiple args + out = out + Handlebars.helperMissing.call(arg[0], lookup, fn); } if (notFn != null) { - out = out + Handlebars.helperMissing.not.call(arg, lookup, notFn); + out = out + Handlebars.helperMissing.not.call(arg[0], lookup, notFn); } } return out; }, - handleExpression: function(lookup, context, arg, isEscaped) { + handleExpression: function(lookup, context, args, isEscaped) { var out = ""; if (Handlebars.isFunction(lookup)) { - out = out + Handlebars.filterOutput(lookup.call(context, arg), isEscaped); + out = out + Handlebars.filterOutput(lookup.apply(context, args), isEscaped); } else if(!Handlebars.isEmpty(lookup)) { out = out + Handlebars.filterOutput(lookup, isEscaped); } @@ -333,11 +339,13 @@ Handlebars.Compiler.prototype = { } }, - addExpression: function(mustache, param) { - param = param || null; + addExpression: function(mustache, params) { + if(!params[0]) params = ["null"] + params = params.join(", ") + var expr = this.lookupFor(mustache); this.fn += "var proxy = Handlebars.buildContext(context, stack);" - this.fn += "out = out + Handlebars.handleExpression(" + expr + ", proxy, " + param + ", " + this.escaped + ");"; + this.fn += "out = out + Handlebars.handleExpression(" + expr + ", proxy, [" + params + "], " + this.escaped + ");"; }, addInvertedSection: function(mustache) { @@ -385,7 +393,7 @@ Handlebars.Compiler.prototype = { return compiler; }, - addBlock: function(mustache, param, parts) { + addBlock: function(mustache, params) { var compiler = this.compileToEndOfBlock(mustache); var result = compiler.fn; @@ -395,7 +403,7 @@ Handlebars.Compiler.prototype = { this.fn += "var wrappedContext = Handlebars.buildContext(context, stack);"; this.fn += "var " + fnId + " = function(context) {" + result + "}; "; this.fn += "lookup = " + this.lookupFor(mustache) + "; "; - this.fn += "arg = " + param + ";"; + this.fn += "arg = [" + params.join(", ") + "] ;"; this.fn += "stack.push(context);"; if (compiler.continueInverted) { @@ -441,13 +449,41 @@ Handlebars.Compiler.prototype = { } this.addText(); - this.mustache = " "; + var params = [""], currentParam = 0, literals = []; while(chr = this.getChar()) { - if(this.mustache && chr === "}" && this.peek() === "}") { - var parts = Handlebars.trim(this.mustache).split(/\s+/); - mustache = parts[0]; - param = this.lookupFor(parts[1]); + if(this.stringLiteral) { + params[currentParam] += chr; + + if(chr === "\\" && this.peek() === '"') { + params[currentParam] += '"'; + this.getChar(); + } else if(chr === '"') { + params[++currentParam] = "" + this.stringLiteral = false; + } + } else if(chr === '"') { + if(params[currentParam] !== "") { + throw new Handlebars.Exception("You are already in the middle of" + + "the " + params[currentParam] + " param. " + + "You cannot start a String param") + } + + this.stringLiteral = true; + params[currentParam] = chr; + literals[currentParam] = true; + } else if(chr === " ") { + if(params[currentParam] !== "") params[++currentParam] = "" + } else if(chr === "}" && this.peek() === "}") { + mustache = params[0]; + arguments = []; + + if(!params[1]) params[1] = undefined; + + for(var i=1,l=params.length; i Date: Wed, 17 Nov 2010 18:34:37 -0800 Subject: [PATCH 4/9] Make it possible for helpers to let downstream helpers know what the path to the current object is --- lib/handlebars.js | 23 +++++++++++++++-------- test/handlebars.js | 28 +++++++++++++++++++++++++++- 2 files changed, 42 insertions(+), 9 deletions(-) diff --git a/lib/handlebars.js b/lib/handlebars.js index d85336d9..32fac29b 100644 --- a/lib/handlebars.js +++ b/lib/handlebars.js @@ -183,7 +183,7 @@ var Handlebars = { } }, - handleBlock: function(lookup, context, arg, fn, notFn) { + handleBlock: function(lookup, context, arg, fn, notFn, name) { var out = "", args; originalArgs = arg.length ? arg : [null] @@ -199,11 +199,11 @@ var Handlebars = { else { if (!Handlebars.isEmpty(lookup)) { // TODO: which case is this, and what does it mean for multiple args - out = out + Handlebars.helperMissing.call(arg[0], lookup, fn); + out = out + Handlebars.helperMissing.call(arg[0], lookup, fn, name); } if (notFn != null) { - out = out + Handlebars.helperMissing.not.call(arg[0], lookup, notFn); + out = out + Handlebars.helperMissing.not.call(arg[0], lookup, notFn, name); } } @@ -260,7 +260,7 @@ Handlebars.SafeString.prototype.toString = function() { return this.string.toString(); } -Handlebars.helperMissing = function(object, fn) { +Handlebars.helperMissing = function(object, fn, name) { var ret = ""; if(object === true) { @@ -273,10 +273,17 @@ Handlebars.helperMissing = function(object, fn) { } return ret; } else { - return fn(object); + var ContextWrapper = function(parent) { + this.__path__ = parent.__path__ ? parent.__path__.concat(name) : [name]; + }; + + ContextWrapper.prototype = object; + var proxy = new ContextWrapper(this); + + return fn(proxy); } }; -Handlebars.helperMissing.not = function(context, fn) { +Handlebars.helperMissing.not = function(context, fn, name) { return fn(context); } @@ -404,7 +411,7 @@ Handlebars.Compiler.prototype = { this.fn += "var " + fnId + " = function(context) {" + result + "}; "; this.fn += "lookup = " + this.lookupFor(mustache) + "; "; this.fn += "arg = [" + params.join(", ") + "] ;"; - this.fn += "stack.push(context);"; + this.fn += "stack.push(context); "; if (compiler.continueInverted) { var invertedCompiler = this.compileToEndOfBlock(mustache); @@ -413,7 +420,7 @@ Handlebars.Compiler.prototype = { else { this.fn += " var " + fnId + "Not = null;"; } - this.fn += "out = out + Handlebars.handleBlock(lookup, wrappedContext, arg, " + fnId + ", " + fnId + "Not);" + this.fn += "out = out + Handlebars.handleBlock(lookup, wrappedContext, arg, " + fnId + ", " + fnId + "Not, '" + mustache + "');" this.fn += "stack.pop();"; this.openBlock = false; diff --git a/test/handlebars.js b/test/handlebars.js index c772f1e5..3bb3a711 100644 --- a/test/handlebars.js +++ b/test/handlebars.js @@ -89,6 +89,32 @@ test("functions with context argument", function() { "Frank", "functions are called with context arguments"); }); +test("functions receive the current path as part of the 'this'", function() { + template = "Hello {{#person}}{{show name}}{{/person}}" + object = { person: { name: "Alan" } }; + fallback = { + show: function(context) { + return context + " (from " + this.__path__.join("/") + ")"; + } + } + shouldCompileTo(template, [object, fallback], "Hello Alan (from person)") +}); + +test("block helpers don't mess with the path receive the current path as part of the 'this'", function() { + template = "Hello {{#block}}{{#person}}{{#info}}{{show name}}{{/info}}{{/person}}{{/block}}. {{show other/name}}" + object = { person: { info: { name: "Alan" } }, other: { name: "Yehuda" } }; + fallback = { + show: function(context) { + var ret = context; + if(this.__path__) ret += " (from " + this.__path__.join("/") + ")"; + return ret; + }, block: function(context, fn) { + return fn(this); + } + } + shouldCompileTo(template, [object, fallback], "Hello Alan (from person/info). Yehuda") +}); + test("nested paths", function() { shouldCompileTo("Goodbye {{alan/expression}} world!", {alan: {expression: "beautiful"}}, "Goodbye beautiful world!", "Nested paths access nested objects"); @@ -432,7 +458,7 @@ test("block multi-params work", function() { return fn({greeting: "Goodbye", adj: "cruel", noun: "world"}); }} shouldCompileTo(string, [hash, fallback], "Message: Goodbye cruel world", "block helpers with multiple params"); -}) +}); module("safestring"); From f1e6acf62837fe9b45bb15895a3f95085ef064c2 Mon Sep 17 00:00:00 2001 From: wycats Date: Fri, 19 Nov 2010 11:59:44 -0800 Subject: [PATCH 5/9] Revert "Make it possible for helpers to let downstream helpers know what the path to the current object is" This reverts commit 71dffd95c73909f04f139ecc3486165a2b246cd4. --- lib/handlebars.js | 23 ++++++++--------------- test/handlebars.js | 28 +--------------------------- 2 files changed, 9 insertions(+), 42 deletions(-) diff --git a/lib/handlebars.js b/lib/handlebars.js index 32fac29b..d85336d9 100644 --- a/lib/handlebars.js +++ b/lib/handlebars.js @@ -183,7 +183,7 @@ var Handlebars = { } }, - handleBlock: function(lookup, context, arg, fn, notFn, name) { + handleBlock: function(lookup, context, arg, fn, notFn) { var out = "", args; originalArgs = arg.length ? arg : [null] @@ -199,11 +199,11 @@ var Handlebars = { else { if (!Handlebars.isEmpty(lookup)) { // TODO: which case is this, and what does it mean for multiple args - out = out + Handlebars.helperMissing.call(arg[0], lookup, fn, name); + out = out + Handlebars.helperMissing.call(arg[0], lookup, fn); } if (notFn != null) { - out = out + Handlebars.helperMissing.not.call(arg[0], lookup, notFn, name); + out = out + Handlebars.helperMissing.not.call(arg[0], lookup, notFn); } } @@ -260,7 +260,7 @@ Handlebars.SafeString.prototype.toString = function() { return this.string.toString(); } -Handlebars.helperMissing = function(object, fn, name) { +Handlebars.helperMissing = function(object, fn) { var ret = ""; if(object === true) { @@ -273,17 +273,10 @@ Handlebars.helperMissing = function(object, fn, name) { } return ret; } else { - var ContextWrapper = function(parent) { - this.__path__ = parent.__path__ ? parent.__path__.concat(name) : [name]; - }; - - ContextWrapper.prototype = object; - var proxy = new ContextWrapper(this); - - return fn(proxy); + return fn(object); } }; -Handlebars.helperMissing.not = function(context, fn, name) { +Handlebars.helperMissing.not = function(context, fn) { return fn(context); } @@ -411,7 +404,7 @@ Handlebars.Compiler.prototype = { this.fn += "var " + fnId + " = function(context) {" + result + "}; "; this.fn += "lookup = " + this.lookupFor(mustache) + "; "; this.fn += "arg = [" + params.join(", ") + "] ;"; - this.fn += "stack.push(context); "; + this.fn += "stack.push(context);"; if (compiler.continueInverted) { var invertedCompiler = this.compileToEndOfBlock(mustache); @@ -420,7 +413,7 @@ Handlebars.Compiler.prototype = { else { this.fn += " var " + fnId + "Not = null;"; } - this.fn += "out = out + Handlebars.handleBlock(lookup, wrappedContext, arg, " + fnId + ", " + fnId + "Not, '" + mustache + "');" + this.fn += "out = out + Handlebars.handleBlock(lookup, wrappedContext, arg, " + fnId + ", " + fnId + "Not);" this.fn += "stack.pop();"; this.openBlock = false; diff --git a/test/handlebars.js b/test/handlebars.js index 3bb3a711..c772f1e5 100644 --- a/test/handlebars.js +++ b/test/handlebars.js @@ -89,32 +89,6 @@ test("functions with context argument", function() { "Frank", "functions are called with context arguments"); }); -test("functions receive the current path as part of the 'this'", function() { - template = "Hello {{#person}}{{show name}}{{/person}}" - object = { person: { name: "Alan" } }; - fallback = { - show: function(context) { - return context + " (from " + this.__path__.join("/") + ")"; - } - } - shouldCompileTo(template, [object, fallback], "Hello Alan (from person)") -}); - -test("block helpers don't mess with the path receive the current path as part of the 'this'", function() { - template = "Hello {{#block}}{{#person}}{{#info}}{{show name}}{{/info}}{{/person}}{{/block}}. {{show other/name}}" - object = { person: { info: { name: "Alan" } }, other: { name: "Yehuda" } }; - fallback = { - show: function(context) { - var ret = context; - if(this.__path__) ret += " (from " + this.__path__.join("/") + ")"; - return ret; - }, block: function(context, fn) { - return fn(this); - } - } - shouldCompileTo(template, [object, fallback], "Hello Alan (from person/info). Yehuda") -}); - test("nested paths", function() { shouldCompileTo("Goodbye {{alan/expression}} world!", {alan: {expression: "beautiful"}}, "Goodbye beautiful world!", "Nested paths access nested objects"); @@ -458,7 +432,7 @@ test("block multi-params work", function() { return fn({greeting: "Goodbye", adj: "cruel", noun: "world"}); }} shouldCompileTo(string, [hash, fallback], "Message: Goodbye cruel world", "block helpers with multiple params"); -}); +}) module("safestring"); From 38d2be4cc0d7ba800da53b7e3709dd0b1dc58d72 Mon Sep 17 00:00:00 2001 From: Alan Johnson Date: Sat, 20 Nov 2010 15:26:20 -0500 Subject: [PATCH 6/9] Working on getting context object working. --- lib/handlebars.js | 71 ++++++++++++++++++++++++++++++++++++++++------- 1 file changed, 61 insertions(+), 10 deletions(-) diff --git a/lib/handlebars.js b/lib/handlebars.js index d85336d9..34cae06a 100644 --- a/lib/handlebars.js +++ b/lib/handlebars.js @@ -4,6 +4,7 @@ var Handlebars = { compile: function(string) { if (Handlebars.compilerCache[string] == null) { var fnBody = Handlebars.compileFunctionBody(string); + console.log(fnBody); var fn = new Function("context", "fallback", "stack", "Handlebars", fnBody); Handlebars.compilerCache[string] = function(context, fallback, stack) { return fn(context, fallback, stack, Handlebars); }; @@ -21,7 +22,7 @@ var Handlebars = { var compiler = new Handlebars.Compiler(string); compiler.compile(); - return "fallback = fallback || {}; var stack = stack || [];" + compiler.fn; + return "context = new Handlebars.Context(context, fallback); var stack = stack || [];" + compiler.fn; }, isFunction: function(fn) { @@ -79,6 +80,7 @@ var Handlebars = { return compiled; }, + // TODO: Delete me, since I'm in Handlebars.Context now! evalExpression: function(path, context, stack, fallback) { fallback = fallback || {}; @@ -197,7 +199,7 @@ var Handlebars = { } } else { - if (!Handlebars.isEmpty(lookup)) { + if (!Handlebars.isEmpty(lookup.data)) { // TODO: which case is this, and what does it mean for multiple args out = out + Handlebars.helperMissing.call(arg[0], lookup, fn); } @@ -210,29 +212,78 @@ var Handlebars = { return out; }, + // lookup: The evaluated mustache, either function or value. A Handlebars.Context + // context: The wrapped context object + // args: The evaluated arguments to the mustache + // isEscaped: Is it escaped? handleExpression: function(lookup, context, args, isEscaped) { var out = ""; - if (Handlebars.isFunction(lookup)) { - out = out + Handlebars.filterOutput(lookup.apply(context, args), isEscaped); - } else if(!Handlebars.isEmpty(lookup)) { - out = out + Handlebars.filterOutput(lookup, isEscaped); + if (Handlebars.isFunction(lookup.data)) { + out = out + Handlebars.filterOutput(lookup.data.apply(context, args), isEscaped); + } else if(!Handlebars.isEmpty(lookup.data)) { + out = out + Handlebars.filterOutput(lookup.data, isEscaped); } return out; }, - + + // lookup: The evaluated mustache, either function or value. A Handlebars.Context + // context: The context where this item occurred + // fn: The compiled body of this inverted section handleInvertedSection: function(lookup, context, fn) { var out = ""; - if(Handlebars.isFunction(lookup) && Handlebars.isEmpty(lookup())) { + if(Handlebars.isFunction(lookup.data) && Handlebars.isEmpty(lookup.data())) { out = out + fn(context); - } else if (Handlebars.isEmpty(lookup)) { + } else if (Handlebars.isEmpty(lookup.data)) { out = out + fn(context); } return out; } } +Handlebars.Context = function(context, fallback, path) { + if (context instanceof Handlebars.Context) { + this.data = context.data; + this.fallback = context.fallback; + this.path = context.path; + } else { + this.data = Handlebars.isEmpty(context) ? {} : context; + this.fallback = fallback || {}; + this.path = path || ""; + } +}; +Handlebars.Context.prototype = { + // path: The path to evaluate + // stack: The stack to work with + evalExpression: function(path, stack) { + newContext = new Handlebars.Context(this); + console.log("--- New context:") + console.log(newContext); + + var parsedPath = Handlebars.parsePath(path); + var depth = parsedPath[0]; + var parts = parsedPath[1]; + if (depth > stack.length) { + newContext.data = null; + } else if (depth > 0) { + newContext.data = stack[stack.length - depth]; + } + + for (var i = 0; i < parts.length && typeof this.data !== "undefined" && this.data != null ; i++) { + newContext.data = newContext.data[parts[i]]; + } + + if (parts.length == 1 && typeof newContext.data === "undefined") { + newContext.data = newContext.fallback[parts[0]]; + } + + console.log("Evaluated expression '" + path + "' to:"); + console.log(newContext); + return newContext; + } +}; + Handlebars.Compiler = function(string) { this.string = string; this.pointer = -1; @@ -368,7 +419,7 @@ Handlebars.Compiler.prototype = { return "context"; } else { - return "(Handlebars.evalExpression('" + param + "', context, stack, fallback))"; + return "(context.evalExpression('" + param + "', stack))"; } }, From aa976283db244f3eb0761651db3c74c50a85f4dd Mon Sep 17 00:00:00 2001 From: Alan Johnson Date: Sat, 20 Nov 2010 17:08:06 -0500 Subject: [PATCH 7/9] Just two more failing tests on the new constext object rewrite. --- lib/handlebars.js | 40 +++++++++++++++++++++------------------- 1 file changed, 21 insertions(+), 19 deletions(-) diff --git a/lib/handlebars.js b/lib/handlebars.js index 34cae06a..d466d15a 100644 --- a/lib/handlebars.js +++ b/lib/handlebars.js @@ -22,7 +22,7 @@ var Handlebars = { var compiler = new Handlebars.Compiler(string); compiler.compile(); - return "context = new Handlebars.Context(context, fallback); var stack = stack || [];" + compiler.fn; + return "var stack = stack || [];" + compiler.fn; }, isFunction: function(fn) { @@ -106,13 +106,14 @@ var Handlebars = { buildContext: function(context, stack) { var ContextWrapper = function(stack) { + this.__context__ = context; this.__stack__ = stack.slice(0); this.__get__ = function(path) { - return Handlebars.evalExpression(path, this, this.__stack__); + return this.__context__.evalExpression(path, this.__stack__).data; }; }; - ContextWrapper.prototype = context; + ContextWrapper.prototype = context.data; return new ContextWrapper(stack); }, @@ -189,13 +190,13 @@ var Handlebars = { var out = "", args; originalArgs = arg.length ? arg : [null] - if (Handlebars.isFunction(lookup)) { + if (Handlebars.isFunction(lookup.data)) { args = originalArgs.concat(fn); - out = out + lookup.apply(context, args); + out = out + lookup.data.apply(context, args); - if (notFn != null && Handlebars.isFunction(lookup.not)) { + if (notFn != null && Handlebars.isFunction(lookup.data.not)) { args = originalArgs.concat(notFn); - out = out + lookup.not.apply(context, args); + out = out + lookup.data.not.apply(context, args); } } else { @@ -248,7 +249,7 @@ Handlebars.Context = function(context, fallback, path) { this.fallback = context.fallback; this.path = context.path; } else { - this.data = Handlebars.isEmpty(context) ? {} : context; + this.data = context; this.fallback = fallback || {}; this.path = path || ""; } @@ -257,7 +258,7 @@ Handlebars.Context.prototype = { // path: The path to evaluate // stack: The stack to work with evalExpression: function(path, stack) { - newContext = new Handlebars.Context(this); + var newContext = new Handlebars.Context(this); console.log("--- New context:") console.log(newContext); @@ -267,10 +268,10 @@ Handlebars.Context.prototype = { if (depth > stack.length) { newContext.data = null; } else if (depth > 0) { - newContext.data = stack[stack.length - depth]; + newContext = new Handlebars.Context(stack[stack.length - depth]); } - for (var i = 0; i < parts.length && typeof this.data !== "undefined" && this.data != null ; i++) { + for (var i = 0, j = parts.length; i < j && typeof this.data !== "undefined" && this.data !== null ; i++) { newContext.data = newContext.data[parts[i]]; } @@ -289,7 +290,7 @@ Handlebars.Compiler = function(string) { this.pointer = -1; this.mustache = false; this.text = ""; - this.fn = "var out = ''; var lookup; "; + this.fn = "context = new Handlebars.Context(context, fallback); var out = ''; var lookup; "; this.newlines = ""; this.comment = false; this.escaped = true; @@ -312,19 +313,20 @@ Handlebars.SafeString.prototype.toString = function() { } Handlebars.helperMissing = function(object, fn) { + console.log("HELPER MISSING"); var ret = ""; - if(object === true) { + if(object.data === true) { return fn(this); - } else if(object === false) { + } else if(object.data === false) { return ""; - } else if(Object.prototype.toString.call(object) === "[object Array]") { - for(var i=0, j=object.length; i Date: Sat, 20 Nov 2010 21:40:35 -0500 Subject: [PATCH 8/9] More work on context object. Just one more failing test. --- lib/handlebars.js | 32 ++++---------------------------- test/handlebars.js | 3 ++- 2 files changed, 6 insertions(+), 29 deletions(-) diff --git a/lib/handlebars.js b/lib/handlebars.js index d466d15a..9c5e7a95 100644 --- a/lib/handlebars.js +++ b/lib/handlebars.js @@ -80,30 +80,6 @@ var Handlebars = { return compiled; }, - // TODO: Delete me, since I'm in Handlebars.Context now! - evalExpression: function(path, context, stack, fallback) { - fallback = fallback || {}; - - var parsedPath = Handlebars.parsePath(path); - var depth = parsedPath[0]; - var parts = parsedPath[1]; - if (depth > stack.length) { - context = null; - } else if (depth > 0) { - context = stack[stack.length - depth]; - } - - for (var i = 0; i < parts.length && typeof context !== "undefined" && context != null ; i++) { - context = context[parts[i]]; - } - - if (parts.length == 1 && typeof context === "undefined") { - return fallback[parts[0]]; - } - - return context; - }, - buildContext: function(context, stack) { var ContextWrapper = function(stack) { this.__context__ = context; @@ -259,7 +235,7 @@ Handlebars.Context.prototype = { // stack: The stack to work with evalExpression: function(path, stack) { var newContext = new Handlebars.Context(this); - console.log("--- New context:") + console.log("--- New context:"); console.log(newContext); var parsedPath = Handlebars.parsePath(path); @@ -271,7 +247,7 @@ Handlebars.Context.prototype = { newContext = new Handlebars.Context(stack[stack.length - depth]); } - for (var i = 0, j = parts.length; i < j && typeof this.data !== "undefined" && this.data !== null ; i++) { + for (var i = 0, j = parts.length; i < j && typeof newContext.data !== "undefined" && newContext.data !== null ; i++) { newContext.data = newContext.data[parts[i]]; } @@ -474,8 +450,8 @@ Handlebars.Compiler.prototype = { addPartial: function(mustache, param) { // either used a cached copy of the partial or compile a new one - this.fn += "if (typeof fallback['partials'] === 'undefined' || typeof fallback['partials']['" + mustache + "'] === 'undefined') throw new Handlebars.Exception('Attempted to render undefined partial: " + mustache + "');"; - this.fn += "out = out + Handlebars.compilePartial(fallback['partials']['" + mustache + "'])(" + param + ", fallback, stack);"; + this.fn += "console.log(context); if (typeof context.fallback['partials'] === 'undefined' || typeof context.fallback['partials']['" + mustache + "'] === 'undefined') throw new Handlebars.Exception('Attempted to render undefined partial: " + mustache + "');"; + this.fn += "out = out + Handlebars.compilePartial(context.fallback['partials']['" + mustache + "'])(" + param + ", stack);"; }, parseMustache: function() { diff --git a/test/handlebars.js b/test/handlebars.js index c772f1e5..b7b14209 100644 --- a/test/handlebars.js +++ b/test/handlebars.js @@ -292,13 +292,14 @@ test("nested block helpers", function() { }); test("block inverted sections", function() { - shouldCompileTo("{{#people}}{{name}}{{^}}{{../none}}{{/people}}", {none: "No people"}, + shouldCompileTo("{{#people}}{{name}}{{^}}{{../none}}{{/people}}", {none: "No people"}, "No people"); }); test("block helper inverted sections", function() { var string = "{{#list people}}{{name}}{{^}}Nobody's here{{/list}}" var list = function(context, fn) { + console.log(context); if (context.length > 0) { var out = "
    "; for(var i = 0,j=context.length; i < j; i++) { From d2b8e0296dcd62cfb0d6f7c0f4afb5123cf77f93 Mon Sep 17 00:00:00 2001 From: Alan Johnson Date: Sat, 20 Nov 2010 22:03:00 -0500 Subject: [PATCH 9/9] Got rid of log messages for debugging. --- lib/handlebars.js | 12 +++--------- 1 file changed, 3 insertions(+), 9 deletions(-) diff --git a/lib/handlebars.js b/lib/handlebars.js index 9c5e7a95..c744046c 100644 --- a/lib/handlebars.js +++ b/lib/handlebars.js @@ -4,7 +4,6 @@ var Handlebars = { compile: function(string) { if (Handlebars.compilerCache[string] == null) { var fnBody = Handlebars.compileFunctionBody(string); - console.log(fnBody); var fn = new Function("context", "fallback", "stack", "Handlebars", fnBody); Handlebars.compilerCache[string] = function(context, fallback, stack) { return fn(context, fallback, stack, Handlebars); }; @@ -235,8 +234,6 @@ Handlebars.Context.prototype = { // stack: The stack to work with evalExpression: function(path, stack) { var newContext = new Handlebars.Context(this); - console.log("--- New context:"); - console.log(newContext); var parsedPath = Handlebars.parsePath(path); var depth = parsedPath[0]; @@ -255,8 +252,6 @@ Handlebars.Context.prototype = { newContext.data = newContext.fallback[parts[0]]; } - console.log("Evaluated expression '" + path + "' to:"); - console.log(newContext); return newContext; } }; @@ -289,7 +284,6 @@ Handlebars.SafeString.prototype.toString = function() { } Handlebars.helperMissing = function(object, fn) { - console.log("HELPER MISSING"); var ret = ""; if(object.data === true) { @@ -450,8 +444,8 @@ Handlebars.Compiler.prototype = { addPartial: function(mustache, param) { // either used a cached copy of the partial or compile a new one - this.fn += "console.log(context); if (typeof context.fallback['partials'] === 'undefined' || typeof context.fallback['partials']['" + mustache + "'] === 'undefined') throw new Handlebars.Exception('Attempted to render undefined partial: " + mustache + "');"; - this.fn += "out = out + Handlebars.compilePartial(context.fallback['partials']['" + mustache + "'])(" + param + ", stack);"; + this.fn += "if (typeof context.fallback['partials'] === 'undefined' || typeof context.fallback['partials']['" + mustache + "'] === 'undefined') throw new Handlebars.Exception('Attempted to render undefined partial: " + mustache + "');"; + this.fn += "out = out + Handlebars.compilePartial(context.fallback['partials']['" + mustache + "'])(" + param + ", null, stack);"; }, parseMustache: function() { @@ -528,7 +522,7 @@ Handlebars.Compiler.prototype = { this.comment = false; return; } else if (this.partial) { - this.addPartial(mustache, arguments[0]) + this.addPartial(mustache, "(" + this.lookupFor(argument) + ")"); this.partial = false; return; } else if (this.inverted) {