As referenced in this issue: https://github.com/janl/mustache.js/issues/562 Partials aren't indented as per the spec: https://github.com/mustache/spec/blob/master/specs/partials.yml#L13-L15 This adds indentation tracking for partials and applies them to the first instance of a partial on a line (to not indent inline partials which would violate a different part of the spec).tags/v3.0.2
| @@ -49,7 +49,7 @@ | |||||
| * Safe way of detecting whether or not the given thing is a primitive and | * Safe way of detecting whether or not the given thing is a primitive and | ||||
| * whether it has the given property | * whether it has the given property | ||||
| */ | */ | ||||
| function primitiveHasOwnProperty (primitive, propName) { | |||||
| function primitiveHasOwnProperty (primitive, propName) { | |||||
| return ( | return ( | ||||
| primitive != null | primitive != null | ||||
| && typeof primitive !== 'object' | && typeof primitive !== 'object' | ||||
| @@ -114,6 +114,10 @@ | |||||
| * Tokens that are the root node of a subtree contain two more elements: 1) an | * Tokens that are the root node of a subtree contain two more elements: 1) an | ||||
| * array of tokens in the subtree and 2) the index in the original template at | * array of tokens in the subtree and 2) the index in the original template at | ||||
| * which the closing tag for that section begins. | * which the closing tag for that section begins. | ||||
| * | |||||
| * Tokens for partials also contain two more elements: 1) a string value of | |||||
| * indendation prior to that tag and 2) the index of that tag on that line - | |||||
| * eg a value of 2 indicates the partial is the third tag on this line. | |||||
| */ | */ | ||||
| function parseTemplate (template, tags) { | function parseTemplate (template, tags) { | ||||
| if (!template) | if (!template) | ||||
| @@ -124,6 +128,8 @@ | |||||
| var spaces = []; // Indices of whitespace tokens on the current line | var spaces = []; // Indices of whitespace tokens on the current line | ||||
| var hasTag = false; // Is there a {{tag}} on the current line? | var hasTag = false; // Is there a {{tag}} on the current line? | ||||
| var nonSpace = false; // Is there a non-space char on the current line? | var nonSpace = false; // Is there a non-space char on the current line? | ||||
| var indentation = ''; // Tracks indentation for tags that use it | |||||
| var tagIndex = 0; // Stores a count of number of tags encountered on a line | |||||
| // Strips all whitespace tokens array for the current line | // Strips all whitespace tokens array for the current line | ||||
| // if there was a {{#tag}} on it and otherwise only space. | // if there was a {{#tag}} on it and otherwise only space. | ||||
| @@ -169,6 +175,8 @@ | |||||
| if (isWhitespace(chr)) { | if (isWhitespace(chr)) { | ||||
| spaces.push(tokens.length); | spaces.push(tokens.length); | ||||
| if (!nonSpace) | |||||
| indentation += chr; | |||||
| } else { | } else { | ||||
| nonSpace = true; | nonSpace = true; | ||||
| } | } | ||||
| @@ -177,8 +185,11 @@ | |||||
| start += 1; | start += 1; | ||||
| // Check for whitespace on the current line. | // Check for whitespace on the current line. | ||||
| if (chr === '\n') | |||||
| if (chr === '\n') { | |||||
| stripSpace(); | stripSpace(); | ||||
| indentation = ''; | |||||
| tagIndex = 0; | |||||
| } | |||||
| } | } | ||||
| } | } | ||||
| @@ -210,7 +221,12 @@ | |||||
| if (!scanner.scan(closingTagRe)) | if (!scanner.scan(closingTagRe)) | ||||
| throw new Error('Unclosed tag at ' + scanner.pos); | throw new Error('Unclosed tag at ' + scanner.pos); | ||||
| token = [ type, value, start, scanner.pos ]; | |||||
| if (type == '>') { | |||||
| token = [ type, value, start, scanner.pos, indentation, tagIndex ]; | |||||
| } else { | |||||
| token = [ type, value, start, scanner.pos ]; | |||||
| } | |||||
| tagIndex++; | |||||
| tokens.push(token); | tokens.push(token); | ||||
| if (type === '#' || type === '^') { | if (type === '#' || type === '^') { | ||||
| @@ -418,7 +434,7 @@ | |||||
| while (intermediateValue != null && index < names.length) { | while (intermediateValue != null && index < names.length) { | ||||
| if (index === names.length - 1) | if (index === names.length - 1) | ||||
| lookupHit = ( | lookupHit = ( | ||||
| hasProperty(intermediateValue, names[index]) | |||||
| hasProperty(intermediateValue, names[index]) | |||||
| || primitiveHasOwnProperty(intermediateValue, names[index]) | || primitiveHasOwnProperty(intermediateValue, names[index]) | ||||
| ); | ); | ||||
| @@ -592,12 +608,30 @@ | |||||
| return this.renderTokens(token[4], context, partials, originalTemplate); | return this.renderTokens(token[4], context, partials, originalTemplate); | ||||
| }; | }; | ||||
| Writer.prototype.indentPartial = function indentPartial (partial, indentation) { | |||||
| var filteredIndentation = indentation.replace(/[^ \t]/g, ''); | |||||
| var partialByNl = partial.split('\n'); | |||||
| for (var i = 0; i < partialByNl.length; i++) { | |||||
| if (partialByNl[i].length) { | |||||
| partialByNl[i] = filteredIndentation + partialByNl[i]; | |||||
| } | |||||
| } | |||||
| return partialByNl.join('\n'); | |||||
| }; | |||||
| Writer.prototype.renderPartial = function renderPartial (token, context, partials, tags) { | Writer.prototype.renderPartial = function renderPartial (token, context, partials, tags) { | ||||
| if (!partials) return; | if (!partials) return; | ||||
| var value = isFunction(partials) ? partials(token[1]) : partials[token[1]]; | var value = isFunction(partials) ? partials(token[1]) : partials[token[1]]; | ||||
| if (value != null) | |||||
| return this.renderTokens(this.parse(value, tags), context, partials, value); | |||||
| if (value != null) { | |||||
| var tagIndex = token[5]; | |||||
| var indentation = token[4]; | |||||
| var indentedValue = value; | |||||
| if (tagIndex == 0 && indentation) { | |||||
| indentedValue = this.indentPartial(value, indentation); | |||||
| } | |||||
| return this.renderTokens(this.parse(indentedValue, tags), context, partials, value); | |||||
| } | |||||
| }; | }; | ||||
| Writer.prototype.unescapedValue = function unescapedValue (token, context) { | Writer.prototype.unescapedValue = function unescapedValue (token, context) { | ||||
| @@ -16,8 +16,7 @@ var skipTests = { | |||||
| ], | ], | ||||
| partials: [ | partials: [ | ||||
| 'Standalone Without Previous Line', | 'Standalone Without Previous Line', | ||||
| 'Standalone Without Newline', | |||||
| 'Standalone Indentation' | |||||
| 'Standalone Without Newline' | |||||
| ], | ], | ||||
| sections: [ | sections: [ | ||||
| 'Standalone Without Newline' | 'Standalone Without Newline' | ||||
| @@ -40,9 +40,12 @@ var expectations = { | |||||
| 'a\n{{#a}}\n{{#b}}\n{{/b}}\n{{/a}}\nb' : [ [ 'text', 'a\n', 0, 2 ], [ '#', 'a', 2, 8, [ [ '#', 'b', 9, 15, [], 16 ] ], 23 ], [ 'text', 'b', 30, 31 ] ], | 'a\n{{#a}}\n{{#b}}\n{{/b}}\n{{/a}}\nb' : [ [ 'text', 'a\n', 0, 2 ], [ '#', 'a', 2, 8, [ [ '#', 'b', 9, 15, [], 16 ] ], 23 ], [ 'text', 'b', 30, 31 ] ], | ||||
| 'a\n {{#a}}\n{{#b}}\n{{/b}}\n{{/a}}\nb' : [ [ 'text', 'a\n', 0, 2 ], [ '#', 'a', 3, 9, [ [ '#', 'b', 10, 16, [], 17 ] ], 24 ], [ 'text', 'b', 31, 32 ] ], | 'a\n {{#a}}\n{{#b}}\n{{/b}}\n{{/a}}\nb' : [ [ 'text', 'a\n', 0, 2 ], [ '#', 'a', 3, 9, [ [ '#', 'b', 10, 16, [], 17 ] ], 24 ], [ 'text', 'b', 31, 32 ] ], | ||||
| 'a\n {{#a}}\n{{#b}}\n{{/b}}\n{{/a}} \nb' : [ [ 'text', 'a\n', 0, 2 ], [ '#', 'a', 3, 9, [ [ '#', 'b', 10, 16, [], 17 ] ], 24 ], [ 'text', 'b', 32, 33 ] ], | 'a\n {{#a}}\n{{#b}}\n{{/b}}\n{{/a}} \nb' : [ [ 'text', 'a\n', 0, 2 ], [ '#', 'a', 3, 9, [ [ '#', 'b', 10, 16, [], 17 ] ], 24 ], [ 'text', 'b', 32, 33 ] ], | ||||
| '{{>abc}}' : [ [ '>', 'abc', 0, 8 ] ], | |||||
| '{{> abc }}' : [ [ '>', 'abc', 0, 10 ] ], | |||||
| '{{ > abc }}' : [ [ '>', 'abc', 0, 11 ] ], | |||||
| '{{>abc}}' : [ [ '>', 'abc', 0, 8, '', 0 ] ], | |||||
| '{{> abc }}' : [ [ '>', 'abc', 0, 10, '', 0 ] ], | |||||
| '{{ > abc }}' : [ [ '>', 'abc', 0, 11, '', 0 ] ], | |||||
| ' {{> abc }}\n' : [ [ '>', 'abc', 2, 12, ' ', 0 ] ], | |||||
| ' {{> abc }} {{> abc }}\n' : [ [ '>', 'abc', 2, 12, ' ', 0 ], [ '>', 'abc', 13, 23, ' ', 1 ] ], | |||||
| '{{ > abc }}' : [ [ '>', 'abc', 0, 11, '', 0 ] ], | |||||
| '{{=<% %>=}}' : [ [ '=', '<% %>', 0, 11 ] ], | '{{=<% %>=}}' : [ [ '=', '<% %>', 0, 11 ] ], | ||||
| '{{= <% %> =}}' : [ [ '=', '<% %>', 0, 13 ] ], | '{{= <% %> =}}' : [ [ '=', '<% %>', 0, 13 ] ], | ||||
| '{{=<% %>=}}<%={{ }}=%>' : [ [ '=', '<% %>', 0, 11 ], [ '=', '{{ }}', 11, 22 ] ], | '{{=<% %>=}}<%={{ }}=%>' : [ [ '=', '<% %>', 0, 11 ], [ '=', '{{ }}', 11, 22 ] ], | ||||