* 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
* 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) {
if (!template)
return [];
-
+ var lineHasNonSpace = false;
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 hasTag = false; // Is there a {{tag}} 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
// if there was a {{#tag}} on it and otherwise only space.
if (isWhitespace(chr)) {
spaces.push(tokens.length);
+ indentation += chr;
} else {
nonSpace = true;
+ lineHasNonSpace = true;
+ indentation += ' ';
}
tokens.push([ 'text', chr, start, start + 1 ]);
start += 1;
// Check for whitespace on the current line.
- if (chr === '\n')
+ if (chr === '\n') {
stripSpace();
+ indentation = '';
+ tagIndex = 0;
+ lineHasNonSpace = false;
+ }
}
}
if (!scanner.scan(closingTagRe))
throw new Error('Unclosed tag at ' + scanner.pos);
- token = [ type, value, start, scanner.pos ];
+ if (type == '>') {
+ token = [ type, value, start, scanner.pos, indentation, tagIndex, lineHasNonSpace ];
+ } else {
+ token = [ type, value, start, scanner.pos ];
+ }
+ tagIndex++;
tokens.push(token);
if (type === '#' || type === '^' || type === '$' || type === '<') {
}
}
+ stripSpace();
+
// Make sure there are no open sections when we're done.
openSection = sections.pop();
return this.renderTokens(token[4], context, partials, originalTemplate);
};
+ Writer.prototype.indentPartial = function indentPartial (partial, indentation, lineHasNonSpace) {
+ var filteredIndentation = indentation.replace(/[^ \t]/g, '');
+ var partialByNl = partial.split('\n');
+ for (var i = 0; i < partialByNl.length; i++) {
+ if (partialByNl[i].length && (i > 0 || !lineHasNonSpace)) {
+ partialByNl[i] = filteredIndentation + partialByNl[i];
+ }
+ }
+ return partialByNl.join('\n');
+ };
+
Writer.prototype.renderPartial = function renderPartial (token, context, partials, tags) {
if (!partials) return;
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 lineHasNonSpace = token[6];
+ var tagIndex = token[5];
+ var indentation = token[4];
+ var indentedValue = value;
+ if (tagIndex == 0 && indentation) {
+ indentedValue = this.indentPartial(value, indentation, lineHasNonSpace);
+ }
+ return this.renderTokens(this.parse(indentedValue, tags), context, partials, indentedValue);
+ }
};
Writer.prototype.renderBlock = function renderBlock (token, context, partials, originalTemplate) {
var value = isFunction(partials) ? partials(token[1]) : partials[token[1]];
if (value != null)
- // Ignore any wrongly set block vars before we started.
- context.clearBlockVars();
- // We are only rendering to record the default block variables.
- this.renderTokens(token[4], context, partials, originalTemplate);
- // Now we render and return the result.
- var result = this.renderTokens(this.parse(value), context, partials, value);
- // Don't leak the block variables outside this include.
+ // Ignore any wrongly set block vars before we started.
context.clearBlockVars();
- return result;
+ // We are only rendering to record the default block variables.
+ this.renderTokens(token[4], context, partials, originalTemplate);
+ // Now we render and return the result.
+ var result = this.renderTokens(this.parse(value), context, partials, value);
+ // Don't leak the block variables outside this include.
+ context.clearBlockVars();
+ return result;
};
Writer.prototype.renderBlockVariable = function renderBlockVariable (token, context, partials, originalTemplate) {
};
mustache.name = 'mustache.js';
- mustache.version = '3.0.1';
+ mustache.version = '3.1.0';
mustache.tags = [ '{{', '}}' ];
// All high-level mustache.* functions use this writer.