diff --git a/mustache.js b/mustache.js index 7ea9845..c3ff790 100644 --- a/mustache.js +++ b/mustache.js @@ -130,7 +130,7 @@ function stripSpace () { if (hasTag && !nonSpace) { while (spaces.length) - delete tokens[spaces.pop()]; + delete tokens[spaces.pop()]; } else { spaces = []; } @@ -168,7 +168,7 @@ chr = value.charAt(i); if (isWhitespace(chr)) { - spaces.push(tokens.length); + spaces.push(tokens.length); } else { nonSpace = true; } @@ -185,11 +185,16 @@ // Match the opening tag. if (!scanner.scan(openingTagRe)) break; - + hasTag = true; // Get the tag type. type = scanner.scan(tagRe) || 'name'; + + if (type == '>') { + spaces = []; + } + scanner.scan(whiteRe); // Get the tag value. @@ -238,7 +243,7 @@ if (openSection) throw new Error('Unclosed section "' + openSection[1] + '" at ' + scanner.pos); - return nestTokens(squashTokens(tokens)); + return nestTokens(squashTokens(tokens)); } /** @@ -528,7 +533,7 @@ */ Writer.prototype.renderTokens = function renderTokens (tokens, context, partials, originalTemplate, tags) { var buffer = ''; - var indentationContext = {spacer: ''}; + var indentationContext = {spacer: '', active: true}; var token, symbol, value; for (var i = 0, numTokens = tokens.length; i < numTokens; ++i) { @@ -554,11 +559,15 @@ Writer.prototype.updateIndentationContext = function updateIndentationContext (indentationContext, value) { for (var j = 0; j < value.length; j++) { if (value[j] == '\n') { + indentationContext.active = true; indentationContext.spacer = ''; } else if (isWhitespace(value[j])) { - indentationContext.spacer += value[j]; + if (indentationContext.active) { + indentationContext.spacer += value[j]; + } } else { - indentationContext.spacer += ' '; + indentationContext.active = false; + indentationContext.spacer = ''; } } }; @@ -611,22 +620,25 @@ var value = isFunction(partials) ? partials(token[1]) : partials[token[1]]; if (value != null) { - var renderResult = this.renderTokens(this.parse(value, tags), context, partials, value); - return this.indent(renderResult, indentationContext); + var indentedValue = this.indent(value, indentationContext); + return this.renderTokens(this.parse(indentedValue, tags), context, partials, value); } }; Writer.prototype.indent = function indent (value, indentationContext) { var indentedValue = ''; - var lines = value.split('\n'); + var val = value + '$'; + var lines = val.split('\n'); for (var i=0; inode}}{{/nodes}}>'}; + var expected = 'X>'; + var renderResult = Mustache.render(template, data, partials); + assert.equal(renderResult, expected); + }); + it('The greater-than operator should not alter surrounding whitespace.', function () { + var template = '| {{>partial}} |'; + var data = {}; + var partials = {'partial':'\t|\t'}; + var expected = '| \t|\t |'; + var renderResult = Mustache.render(template, data, partials); + assert.equal(renderResult, expected); + }); + it('"\r\n" should be considered a newline for standalone tags.', function () { + var template = '|\r\n{{>partial}}\r\n|'; + var data = {}; + var partials = {'partial':'>'}; + var expected = '|\r\n>|'; + var renderResult = Mustache.render(template, data, partials); + assert.equal(renderResult, expected); + }); + it('Standalone tags should not require a newline to precede them.', function () { + var template = ' {{>partial}}\n>'; + var data = {}; + var partials = {'partial':'>\n>'}; + var expected = ' >\n >>'; + var renderResult = Mustache.render(template, data, partials); + assert.equal(renderResult, expected); + }); + it('Superfluous in-tag whitespace should be ignored.', function () { + var template = '|{{> partial }}|'; + var data = {'boolean':true}; + var partials = {'partial':'[]'}; + var expected = '|[]|'; + var renderResult = Mustache.render(template, data, partials); + assert.equal(renderResult, expected); + }); + it('Each line of the partial should be indented before rendering.', function () { + var template = '\\\n {{>partial}}\n/\n'; + var data = { + 'content': '<\n->' + }; + var partials = { + 'partial': '|\n{{{content}}}\n|\n' + }; + var expected = '\\\n |\n <\n->\n |\n/\n'; + var renderResult = Mustache.render(template, data, partials); + assert.equal(renderResult, expected); + }); + + it('Standalone tags should not require a newline to follow them.', function () { + var template = '>\n {{>partial}}'; + var data = { + + }; + var partials = { + 'partial': '>\n>' + }; + var expected = '>\n >\n >'; + var renderResult = Mustache.render(template, data, partials); + assert.equal(renderResult, expected); + }); + + it('Whitespace should be left untouched.', function () { + var template = ' {{data}} {{> partial}}\n'; + var data = { + 'data': '|' + }; + var partials = { + 'partial': '>\n>' + }; + var expected = ' | >\n>\n'; + var renderResult = Mustache.render(template, data, partials); + assert.equal(renderResult, expected); + }); +}); \ No newline at end of file diff --git a/test/render-test.js b/test/render-test.js index 4922e7a..4f1c063 100644 --- a/test/render-test.js +++ b/test/render-test.js @@ -18,11 +18,13 @@ describe('Mustache.render', function () { }); describe('preserve indentation when using partials', function() { - it.only ('should preserve indentation', function() { - var template = 'line1\n bla la \t\r\f foo line2{{>p1}}'; + + it ('should preserve indentation with whitespaces', function() { + var template = 'a\n {{>p1}}'; var renderResult = Mustache.render(template, {}, {p1: 'l1\nl2'}); - assert.equal(renderResult, 'line1\n bla la \t\r\f foo line2l1\n \t\r\f l2'); + assert.equal(renderResult, 'a\n l1\n l2'); }); + }); describe('custom tags', function () {