From 7966ed587f326640fb95003b42e7bc4de02126e5 Mon Sep 17 00:00:00 2001 From: Daniel Fagerstrom Date: Sat, 16 Mar 2013 14:17:36 +0100 Subject: [PATCH 1/3] Fix 'Standalone Without Newline' Mustache specification failures. --- mustache.js | 4 ++++ test/mustache-spec-test.js | 12 ------------ 2 files changed, 4 insertions(+), 12 deletions(-) diff --git a/mustache.js b/mustache.js index af48fd1c5..f6f67f136 100644 --- a/mustache.js +++ b/mustache.js @@ -467,6 +467,10 @@ tagRes = escapeTags(tags); } } + // Standalone tags should not require a newline to follow them + // therefore we strip spaces from the last line if it is a standalone tag, + // even if it might not be followed by a newline + stripSpace(); // Make sure there are no open sections when we're done. var openSection = sections.pop(); diff --git a/test/mustache-spec-test.js b/test/mustache-spec-test.js index 1050ea43d..775a5dd87 100644 --- a/test/mustache-spec-test.js +++ b/test/mustache-spec-test.js @@ -5,23 +5,11 @@ var path = require('path'); var specsDir = path.join(__dirname, 'spec/specs'); var skipTests = { - comments: [ - 'Standalone Without Newline' - ], - delimiters: [ - 'Standalone Without Newline' - ], - inverted: [ - 'Standalone Without Newline' - ], partials: [ 'Standalone Without Previous Line', 'Standalone Without Newline', 'Standalone Indentation' ], - sections: [ - 'Standalone Without Newline' - ], '~lambdas': [ 'Interpolation', 'Interpolation - Expansion', From 31ca052d07ff8d18c7947231934c48c332d0f4b9 Mon Sep 17 00:00:00 2001 From: Daniel Fagerstrom Date: Sat, 16 Mar 2013 16:09:17 +0100 Subject: [PATCH 2/3] Fix indentation before standalone partials. --- mustache.js | 18 +++++++++++++++++- test/mustache-spec-test.js | 2 -- 2 files changed, 17 insertions(+), 3 deletions(-) diff --git a/mustache.js b/mustache.js index f6f67f136..dbf6621b2 100644 --- a/mustache.js +++ b/mustache.js @@ -278,7 +278,12 @@ break; case '>': value = writer.getPartial(tokenValue); - if (typeof value === 'function') buffer += value(context); + if (typeof value === 'function') { + value = value(context); + if (token[4]) + value = value.replace(/^(?=.)/gm, token[4]); + buffer += value; + } break; case '&': value = context.lookup(tokenValue); @@ -381,6 +386,7 @@ var sections = []; // Stack to hold section tokens var tokens = []; // Buffer to hold the tokens var spaces = []; // Indices of whitespace tokens on the current line + var whitespace = ''; // Whitespace characters at the same line before a tag var hasTag = false; // Is there a {{tag}} on the current line? var nonSpace = false; // Is there a non-space char on the current line? @@ -397,6 +403,7 @@ hasTag = false; nonSpace = false; + whitespace = ''; } var start, type, value, chr, token; @@ -411,6 +418,7 @@ if (isWhitespace(chr)) { spaces.push(tokens.length); + whitespace += chr; } else { nonSpace = true; } @@ -423,6 +431,9 @@ } } + // The following tag is not standalone + if (whitespace && nonSpace) whitespace = ''; + // Match the opening tag. if (!scanner.scan(tagRes[0])) break; hasTag = true; @@ -449,6 +460,10 @@ if (!scanner.scan(tagRes[1])) throw new Error('Unclosed tag at ' + scanner.pos); token = [type, value, start, scanner.pos]; + + // Store whitespaces before standalone partials + if (type === '>' && whitespace) token[4] = whitespace; + tokens.push(token); if (type === '#' || type === '^') { @@ -466,6 +481,7 @@ if (tags.length !== 2) throw new Error('Invalid tags at ' + start + ': ' + tags.join(', ')); tagRes = escapeTags(tags); } + whitespace = ''; } // Standalone tags should not require a newline to follow them // therefore we strip spaces from the last line if it is a standalone tag, diff --git a/test/mustache-spec-test.js b/test/mustache-spec-test.js index 775a5dd87..3c2f69f1b 100644 --- a/test/mustache-spec-test.js +++ b/test/mustache-spec-test.js @@ -6,8 +6,6 @@ var specsDir = path.join(__dirname, 'spec/specs'); var skipTests = { partials: [ - 'Standalone Without Previous Line', - 'Standalone Without Newline', 'Standalone Indentation' ], '~lambdas': [ From 18c8aadc460f8bacc4bc558f2bba0e0b14870ca0 Mon Sep 17 00:00:00 2001 From: Daniel Fagerstrom Date: Sun, 17 Mar 2013 16:57:15 +0100 Subject: [PATCH 3/3] Spec compliance - standalone indentation of partials. The template of a standalone template should be indented before it is applied on view data rather than after as in the previous commit. To accomplish this I added a cache for keeping the partial templates, not just the complied result and added an 'indentation' argument to getPartial and compilePartial. --- mustache.js | 33 ++++++++++++++++++++------------- test/mustache-spec-test.js | 3 --- 2 files changed, 20 insertions(+), 16 deletions(-) diff --git a/mustache.js b/mustache.js index dbf6621b2..c8989a65e 100644 --- a/mustache.js +++ b/mustache.js @@ -181,6 +181,7 @@ Writer.prototype.clearCache = function () { this._cache = {}; this._partialCache = {}; + this._partials = {}; }; Writer.prototype.compile = function (template, tags) { @@ -194,18 +195,27 @@ return fn; }; - Writer.prototype.compilePartial = function (name, template, tags) { + Writer.prototype.compilePartial = function (name, template, tags, indentation) { + // A standalone partial should be indented with the whitespace before the + // partial on the same row. We keep track on instances of the same partial + // with different indentations by prefixing its name with the indentation in + // the partial cache. + var cacheName = (indentation || '') + name; + if (indentation) + template = template.replace(/^(?=.)/gm, indentation); var fn = this.compile(template, tags); - this._partialCache[name] = fn; + this._partialCache[cacheName] = fn; return fn; }; - Writer.prototype.getPartial = function (name) { - if (!(name in this._partialCache) && this._loadPartial) { - this.compilePartial(name, this._loadPartial(name)); + Writer.prototype.getPartial = function (name, indentation) { + var cacheName = (indentation || '') + name; + if (!(cacheName in this._partialCache)) { + var partial = this._loadPartial ? this._loadPartial(name) : this._partials[name]; + if (partial) this.compilePartial(name, partial, null, indentation); } - return this._partialCache[name]; + return this._partialCache[cacheName]; }; Writer.prototype.compileTokens = function (tokens, template) { @@ -217,6 +227,7 @@ } else { for (var name in partials) { self.compilePartial(name, partials[name]); + self._partials[name] = partials[name]; } } } @@ -277,13 +288,9 @@ break; case '>': - value = writer.getPartial(tokenValue); - if (typeof value === 'function') { - value = value(context); - if (token[4]) - value = value.replace(/^(?=.)/gm, token[4]); - buffer += value; - } + // indentation for standalone partial in token[4] + value = writer.getPartial(tokenValue, token[4]); + if (typeof value === 'function') buffer += value(context); break; case '&': value = context.lookup(tokenValue); diff --git a/test/mustache-spec-test.js b/test/mustache-spec-test.js index 3c2f69f1b..efb82c217 100644 --- a/test/mustache-spec-test.js +++ b/test/mustache-spec-test.js @@ -5,9 +5,6 @@ var path = require('path'); var specsDir = path.join(__dirname, 'spec/specs'); var skipTests = { - partials: [ - 'Standalone Indentation' - ], '~lambdas': [ 'Interpolation', 'Interpolation - Expansion',