213 lines
5.7 KiB
JavaScript
213 lines
5.7 KiB
JavaScript
import Exception from "../exception";
|
|
|
|
export function stripFlags(open, close) {
|
|
return {
|
|
left: open.charAt(2) === '~',
|
|
right: close.charAt(close.length-3) === '~'
|
|
};
|
|
}
|
|
|
|
|
|
export function prepareBlock(mustache, program, inverseAndProgram, close, inverted, locInfo) {
|
|
/*jshint -W040 */
|
|
if (mustache.sexpr.id.original !== close.path.original) {
|
|
throw new Exception(mustache.sexpr.id.original + ' doesn\'t match ' + close.path.original, mustache);
|
|
}
|
|
|
|
var inverse = inverseAndProgram && inverseAndProgram.program;
|
|
|
|
var strip = {
|
|
left: mustache.strip.left,
|
|
right: close.strip.right,
|
|
|
|
// Determine the standalone candiacy. Basically flag our content as being possibly standalone
|
|
// so our parent can determine if we actually are standalone
|
|
openStandalone: isNextWhitespace(program.statements),
|
|
closeStandalone: isPrevWhitespace((inverse || program).statements)
|
|
};
|
|
|
|
if (mustache.strip.right) {
|
|
omitRight(program.statements, null, true);
|
|
}
|
|
|
|
if (inverse) {
|
|
var inverseStrip = inverseAndProgram.strip;
|
|
|
|
if (inverseStrip.left) {
|
|
omitLeft(program.statements, null, true);
|
|
}
|
|
if (inverseStrip.right) {
|
|
omitRight(inverse.statements, null, true);
|
|
}
|
|
if (close.strip.left) {
|
|
omitLeft(inverse.statements, null, true);
|
|
}
|
|
|
|
// Find standalone else statments
|
|
if (isPrevWhitespace(program.statements)
|
|
&& isNextWhitespace(inverse.statements)) {
|
|
|
|
omitLeft(program.statements);
|
|
omitRight(inverse.statements);
|
|
}
|
|
} else {
|
|
if (close.strip.left) {
|
|
omitLeft(program.statements, null, true);
|
|
}
|
|
}
|
|
|
|
if (inverted) {
|
|
return new this.BlockNode(mustache, inverse, program, strip, locInfo);
|
|
} else {
|
|
return new this.BlockNode(mustache, program, inverse, strip, locInfo);
|
|
}
|
|
}
|
|
|
|
|
|
export function prepareProgram(statements, isRoot) {
|
|
for (var i = 0, l = statements.length; i < l; i++) {
|
|
var current = statements[i],
|
|
strip = current.strip;
|
|
|
|
if (!strip) {
|
|
continue;
|
|
}
|
|
|
|
var _isPrevWhitespace = isPrevWhitespace(statements, i, isRoot, current.type === 'partial'),
|
|
_isNextWhitespace = isNextWhitespace(statements, i, isRoot),
|
|
|
|
openStandalone = strip.openStandalone && _isPrevWhitespace,
|
|
closeStandalone = strip.closeStandalone && _isNextWhitespace,
|
|
inlineStandalone = strip.inlineStandalone && _isPrevWhitespace && _isNextWhitespace;
|
|
|
|
if (strip.right) {
|
|
omitRight(statements, i, true);
|
|
}
|
|
if (strip.left) {
|
|
omitLeft(statements, i, true);
|
|
}
|
|
|
|
if (inlineStandalone) {
|
|
omitRight(statements, i);
|
|
|
|
if (omitLeft(statements, i)) {
|
|
// If we are on a standalone node, save the indent info for partials
|
|
if (current.type === 'partial') {
|
|
current.indent = statements[i-1].original;
|
|
}
|
|
}
|
|
}
|
|
if (openStandalone) {
|
|
omitRight((current.program || current.inverse).statements);
|
|
|
|
// Strip out the previous content node if it's whitespace only
|
|
omitLeft(statements, i);
|
|
}
|
|
if (closeStandalone) {
|
|
// Always strip the next node
|
|
omitRight(statements, i);
|
|
|
|
omitLeft((current.inverse || current.program).statements);
|
|
}
|
|
}
|
|
|
|
return statements;
|
|
}
|
|
|
|
function isPrevWhitespace(statements, i, isRoot, disallowIndent) {
|
|
if (i === undefined) {
|
|
i = statements.length;
|
|
}
|
|
|
|
// Nodes that end with newlines are considered whitespace (but are special
|
|
// cased for strip operations)
|
|
var prev = statements[i-1];
|
|
if (prev && /\n$/.test(prev.string)) {
|
|
return true;
|
|
}
|
|
|
|
return checkWhitespace(isRoot, prev, statements[i-2]);
|
|
}
|
|
function isNextWhitespace(statements, i, isRoot) {
|
|
if (i === undefined) {
|
|
i = -1;
|
|
}
|
|
|
|
return checkWhitespace(isRoot, statements[i+1], statements[i+2]);
|
|
}
|
|
function checkWhitespace(isRoot, next1, next2) {
|
|
if (!next1) {
|
|
return isRoot;
|
|
} else if (next1.type === 'content') {
|
|
// Check if the previous node is empty or whitespace only
|
|
if (/^[\s]*$/.test(next1.string)) {
|
|
if (next2) {
|
|
return next2.type === 'content' || /\n$/.test(next1.string);
|
|
} else {
|
|
return isRoot || (next1.string.indexOf('\n') >= 0);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// Marks the node to the right of the position as omitted.
|
|
// I.e. {{foo}}' ' will mark the ' ' node as omitted.
|
|
//
|
|
// If i is undefined, then the first child will be marked as such.
|
|
//
|
|
// If mulitple is truthy then all whitespace will be stripped out until non-whitespace
|
|
// content is met.
|
|
function omitRight(statements, i, multiple) {
|
|
i = i == null ? 0 : i + 1;
|
|
|
|
var current = statements[i];
|
|
while (current) {
|
|
if (current.type !== 'content') {
|
|
return;
|
|
}
|
|
|
|
current.string = current.string.replace(/^[\s]+/, '');
|
|
|
|
if (multiple && !current.string) {
|
|
current = statements[++i];
|
|
} else {
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
|
|
// Marks the node to the left of the position as omitted.
|
|
// I.e. ' '{{foo}} will mark the ' ' node as omitted.
|
|
//
|
|
// If i is undefined then the last child will be marked as such.
|
|
//
|
|
// If mulitple is truthy then all whitespace will be stripped out until non-whitespace
|
|
// content is met.
|
|
function omitLeft(statements, i, multiple) {
|
|
if (i == null) {
|
|
i = statements.length;
|
|
}
|
|
|
|
var current = statements[--i],
|
|
prev = statements[i-1];
|
|
|
|
while (current) {
|
|
if (current.type !== 'content') {
|
|
return;
|
|
}
|
|
|
|
// We omit the last node if it's whitespace only and not preceeded by a non-content node.
|
|
if (multiple || (/^[\s]*$/.test(current.string) && (!prev || prev.type === 'content'))) {
|
|
current.string = current.string.replace(/[\s]+$/, '');
|
|
|
|
if (multiple && !current.string) {
|
|
current = statements[--i];
|
|
} else {
|
|
return true;
|
|
}
|
|
} else {
|
|
return;
|
|
}
|
|
}
|
|
}
|