| @@ -130,7 +130,7 @@ | |||||
| function stripSpace () { | function stripSpace () { | ||||
| if (hasTag && !nonSpace) { | if (hasTag && !nonSpace) { | ||||
| while (spaces.length) | while (spaces.length) | ||||
| delete tokens[spaces.pop()]; | |||||
| delete tokens[spaces.pop()]; | |||||
| } else { | } else { | ||||
| spaces = []; | spaces = []; | ||||
| } | } | ||||
| @@ -168,7 +168,7 @@ | |||||
| chr = value.charAt(i); | chr = value.charAt(i); | ||||
| if (isWhitespace(chr)) { | if (isWhitespace(chr)) { | ||||
| spaces.push(tokens.length); | |||||
| spaces.push(tokens.length); | |||||
| } else { | } else { | ||||
| nonSpace = true; | nonSpace = true; | ||||
| } | } | ||||
| @@ -185,11 +185,16 @@ | |||||
| // Match the opening tag. | // Match the opening tag. | ||||
| if (!scanner.scan(openingTagRe)) | if (!scanner.scan(openingTagRe)) | ||||
| break; | break; | ||||
| hasTag = true; | hasTag = true; | ||||
| // Get the tag type. | // Get the tag type. | ||||
| type = scanner.scan(tagRe) || 'name'; | type = scanner.scan(tagRe) || 'name'; | ||||
| if (type == '>') { | |||||
| spaces = []; | |||||
| } | |||||
| scanner.scan(whiteRe); | scanner.scan(whiteRe); | ||||
| // Get the tag value. | // Get the tag value. | ||||
| @@ -238,7 +243,7 @@ | |||||
| if (openSection) | if (openSection) | ||||
| throw new Error('Unclosed section "' + openSection[1] + '" at ' + scanner.pos); | 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) { | Writer.prototype.renderTokens = function renderTokens (tokens, context, partials, originalTemplate, tags) { | ||||
| var buffer = ''; | var buffer = ''; | ||||
| var indentationContext = {spacer: ''}; | |||||
| var indentationContext = {spacer: '', active: true}; | |||||
| var token, symbol, value; | var token, symbol, value; | ||||
| for (var i = 0, numTokens = tokens.length; i < numTokens; ++i) { | for (var i = 0, numTokens = tokens.length; i < numTokens; ++i) { | ||||
| @@ -554,11 +559,15 @@ | |||||
| Writer.prototype.updateIndentationContext = function updateIndentationContext (indentationContext, value) { | Writer.prototype.updateIndentationContext = function updateIndentationContext (indentationContext, value) { | ||||
| for (var j = 0; j < value.length; j++) { | for (var j = 0; j < value.length; j++) { | ||||
| if (value[j] == '\n') { | if (value[j] == '\n') { | ||||
| indentationContext.active = true; | |||||
| indentationContext.spacer = ''; | indentationContext.spacer = ''; | ||||
| } else if (isWhitespace(value[j])) { | } else if (isWhitespace(value[j])) { | ||||
| indentationContext.spacer += value[j]; | |||||
| if (indentationContext.active) { | |||||
| indentationContext.spacer += value[j]; | |||||
| } | |||||
| } else { | } else { | ||||
| indentationContext.spacer += ' '; | |||||
| indentationContext.active = false; | |||||
| indentationContext.spacer = ''; | |||||
| } | } | ||||
| } | } | ||||
| }; | }; | ||||
| @@ -611,22 +620,25 @@ | |||||
| var value = isFunction(partials) ? partials(token[1]) : partials[token[1]]; | var value = isFunction(partials) ? partials(token[1]) : partials[token[1]]; | ||||
| if (value != null) { | 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) { | Writer.prototype.indent = function indent (value, indentationContext) { | ||||
| var indentedValue = ''; | var indentedValue = ''; | ||||
| var lines = value.split('\n'); | |||||
| var val = value + '$'; | |||||
| var lines = val.split('\n'); | |||||
| for (var i=0; i<lines.length; i++) { | for (var i=0; i<lines.length; i++) { | ||||
| if (i == 0) { | if (i == 0) { | ||||
| indentedValue += lines[i]; | indentedValue += lines[i]; | ||||
| } else if (lines[i] == '$') { | |||||
| indentedValue += '\n$'; | |||||
| } else { | } else { | ||||
| indentedValue += ('\n' + indentationContext.spacer + lines[i]); | indentedValue += ('\n' + indentationContext.spacer + lines[i]); | ||||
| } | } | ||||
| } | } | ||||
| return indentedValue; | |||||
| return indentedValue.substring(0, indentedValue.length-1); // remove the $ | |||||
| }; | }; | ||||
| Writer.prototype.unescapedValue = function unescapedValue (token, context) { | Writer.prototype.unescapedValue = function unescapedValue (token, context) { | ||||
| @@ -0,0 +1,112 @@ | |||||
| /* eslint-disable func-names */ | |||||
| require('./helper'); | |||||
| describe('Partials spec', function () { | |||||
| beforeEach(function () { | |||||
| Mustache.clearCache(); | |||||
| }); | |||||
| it('The greater-than operator should expand to the named partial.', function () { | |||||
| var template = '"{{>text}}"'; | |||||
| var data = {}; | |||||
| var partials = {'text':'from partial'}; | |||||
| var expected = '"from partial"'; | |||||
| var renderResult = Mustache.render(template, data, partials); | |||||
| assert.equal(renderResult, expected); | |||||
| }); | |||||
| it('The empty string should be used when the named partial is not found.', function () { | |||||
| var template = '"{{>text}}"'; | |||||
| var data = {}; | |||||
| var partials = {}; | |||||
| var expected = '""'; | |||||
| var renderResult = Mustache.render(template, data, partials); | |||||
| assert.equal(renderResult, expected); | |||||
| }); | |||||
| it('The greater-than operator should operate within the current context.', function () { | |||||
| var template = '"{{>partial}}"'; | |||||
| var data = {'text':'content'}; | |||||
| var partials = {'partial':'*{{text}}*'}; | |||||
| var expected = '"*content*"'; | |||||
| var renderResult = Mustache.render(template, data, partials); | |||||
| assert.equal(renderResult, expected); | |||||
| }); | |||||
| it('The greater-than operator should properly recurse.', function () { | |||||
| var template = '{{>node}}'; | |||||
| var data = {'content':'X','nodes':[{'content':'Y','nodes':[]}]}; | |||||
| var partials = {'node':'{{content}}<{{#nodes}}{{>node}}{{/nodes}}>'}; | |||||
| var expected = 'X<Y<>>'; | |||||
| 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); | |||||
| }); | |||||
| }); | |||||
| @@ -18,11 +18,13 @@ describe('Mustache.render', function () { | |||||
| }); | }); | ||||
| describe('preserve indentation when using partials', 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'}); | 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 () { | describe('custom tags', function () { | ||||