summaryrefslogtreecommitdiffstats
path: root/lib/handlebars/compiler/helpers.js
diff options
context:
space:
mode:
Diffstat (limited to 'lib/handlebars/compiler/helpers.js')
-rw-r--r--lib/handlebars/compiler/helpers.js271
1 files changed, 76 insertions, 195 deletions
diff --git a/lib/handlebars/compiler/helpers.js b/lib/handlebars/compiler/helpers.js
index 50a3c53..1daddf6 100644
--- a/lib/handlebars/compiler/helpers.js
+++ b/lib/handlebars/compiler/helpers.js
@@ -1,9 +1,21 @@
import Exception from "../exception";
+export function SourceLocation(source, locInfo) {
+ this.source = source;
+ this.start = {
+ line: locInfo.first_line,
+ column: locInfo.first_column
+ };
+ this.end = {
+ line: locInfo.last_line,
+ column: locInfo.last_column
+ };
+}
+
export function stripFlags(open, close) {
return {
- left: open.charAt(2) === '~',
- right: close.charAt(close.length-3) === '~'
+ open: open.charAt(2) === '~',
+ close: close.charAt(close.length-3) === '~'
};
}
@@ -12,222 +24,91 @@ export function stripComment(comment) {
.replace(/-?-?~?\}\}$/, '');
}
-export function prepareRawBlock(openRawBlock, content, close, locInfo) {
+export function preparePath(data, parts, locInfo) {
/*jshint -W040 */
- if (openRawBlock.sexpr.id.original !== close) {
- var errorNode = {
- firstLine: openRawBlock.sexpr.firstLine,
- firstColumn: openRawBlock.sexpr.firstColumn
- };
-
- throw new Exception(openRawBlock.sexpr.id.original + " doesn't match " + close, errorNode);
+ locInfo = this.locInfo(locInfo);
+
+ var original = data ? '@' : '',
+ dig = [],
+ depth = 0,
+ depthString = '';
+
+ for(var i=0,l=parts.length; i<l; i++) {
+ var part = parts[i].part;
+ original += (parts[i].separator || '') + part;
+
+ if (part === '..' || part === '.' || part === 'this') {
+ if (dig.length > 0) {
+ throw new Exception('Invalid path: ' + original, {loc: locInfo});
+ } else if (part === '..') {
+ depth++;
+ depthString += '../';
+ }
+ } else {
+ dig.push(part);
+ }
}
- var program = new this.ProgramNode([content], null, {}, locInfo);
-
- return new this.BlockNode(openRawBlock.sexpr, program, undefined, undefined, locInfo);
+ return new this.PathExpression(data, depth, dig, original, locInfo);
}
-export function prepareBlock(openBlock, program, inverseAndProgram, close, inverted, locInfo) {
+export function prepareMustache(sexpr, open, strip, locInfo) {
/*jshint -W040 */
- // When we are chaining inverse calls, we will not have a close path
- if (close && close.path && openBlock.sexpr.id.original !== close.path.original) {
- var errorNode = {
- firstLine: openBlock.sexpr.firstLine,
- firstColumn: openBlock.sexpr.firstColumn
- };
+ // Must use charAt to support IE pre-10
+ var escapeFlag = open.charAt(3) || open.charAt(2),
+ escaped = escapeFlag !== '{' && escapeFlag !== '&';
- throw new Exception(openBlock.sexpr.id.original + ' doesn\'t match ' + close.path.original, errorNode);
- }
+ return new this.MustacheStatement(sexpr, escaped, strip, this.locInfo(locInfo));
+}
- program.blockParams = openBlock.blockParams;
+export function prepareRawBlock(openRawBlock, content, close, locInfo) {
+ /*jshint -W040 */
+ if (openRawBlock.sexpr.path.original !== close) {
+ var errorNode = {loc: openRawBlock.sexpr.loc};
- // Safely handle a chained inverse that does not have a non-conditional inverse
- // (i.e. both inverseAndProgram AND close are undefined)
- if (!close) {
- close = {strip: {}};
+ throw new Exception(openRawBlock.sexpr.path.original + " doesn't match " + close, errorNode);
}
- // Find the inverse program that is involed with whitespace stripping.
- var inverse = inverseAndProgram && inverseAndProgram.program,
- firstInverse = inverse,
- lastInverse = inverse;
- if (inverse && inverse.inverse) {
- firstInverse = inverse.statements[0].program;
+ locInfo = this.locInfo(locInfo);
+ var program = new this.Program([content], null, {}, locInfo);
- // Walk the inverse chain to find the last inverse that is actually in the chain.
- while (lastInverse.inverse) {
- lastInverse = lastInverse.statements[lastInverse.statements.length-1].program;
- }
- }
-
- var strip = {
- left: openBlock.strip.left,
- right: close.strip.right,
+ return new this.BlockStatement(
+ openRawBlock.sexpr, program, undefined,
+ {}, {}, {},
+ locInfo);
+}
- // 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((firstInverse || program).statements)
- };
+export function prepareBlock(openBlock, program, inverseAndProgram, close, inverted, locInfo) {
+ /*jshint -W040 */
+ // When we are chaining inverse calls, we will not have a close path
+ if (close && close.path && openBlock.sexpr.path.original !== close.path.original) {
+ var errorNode = {loc: openBlock.sexpr.loc};
- if (openBlock.strip.right) {
- omitRight(program.statements, null, true);
+ throw new Exception(openBlock.sexpr.path.original + ' doesn\'t match ' + close.path.original, errorNode);
}
- if (inverse) {
- var inverseStrip = inverseAndProgram.strip;
+ program.blockParams = openBlock.blockParams;
- if (inverseStrip.left) {
- omitLeft(program.statements, null, true);
- }
+ var inverse,
+ inverseStrip;
- if (inverseStrip.right) {
- omitRight(firstInverse.statements, null, true);
- }
- if (close.strip.left) {
- omitLeft(lastInverse.statements, null, true);
+ if (inverseAndProgram) {
+ if (inverseAndProgram.chain) {
+ inverseAndProgram.program.body[0].closeStrip = close.strip || close.openStrip;
}
- // Find standalone else statments
- if (isPrevWhitespace(program.statements)
- && isNextWhitespace(firstInverse.statements)) {
-
- omitLeft(program.statements);
- omitRight(firstInverse.statements);
- }
- } else {
- if (close.strip.left) {
- omitLeft(program.statements, null, true);
- }
+ inverseStrip = inverseAndProgram.strip;
+ inverse = inverseAndProgram.program;
}
if (inverted) {
- return new this.BlockNode(openBlock.sexpr, inverse, program, strip, locInfo);
- } else {
- return new this.BlockNode(openBlock.sexpr, 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') {
- // Pull out the whitespace from the final line
- current.indent = (/([ \t]+$)/).exec(statements[i-1].original)[1];
- }
- }
- }
- 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) {
- 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],
- sibling = statements[i-2];
- if (!prev) {
- return isRoot;
- }
-
- if (prev.type === 'content') {
- return (sibling || !isRoot ? (/\r?\n\s*?$/) : (/(^|\r?\n)\s*?$/)).test(prev.original);
- }
-}
-function isNextWhitespace(statements, i, isRoot) {
- if (i === undefined) {
- i = -1;
- }
-
- var next = statements[i+1],
- sibling = statements[i+2];
- if (!next) {
- return isRoot;
- }
-
- if (next.type === 'content') {
- return (sibling || !isRoot ? (/^\s*?\r?\n/) : (/^\s*?(\r?\n|$)/)).test(next.original);
- }
-}
-
-// 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) {
- var current = statements[i == null ? 0 : i + 1];
- if (!current || current.type !== 'content' || (!multiple && current.rightStripped)) {
- return;
- }
-
- var original = current.string;
- current.string = current.string.replace(multiple ? (/^\s+/) : (/^[ \t]*\r?\n?/), '');
- current.rightStripped = current.string !== original;
-}
-
-// 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) {
- var current = statements[i == null ? statements.length - 1 : i - 1];
- if (!current || current.type !== 'content' || (!multiple && current.leftStripped)) {
- return;
+ inverted = inverse;
+ inverse = program;
+ program = inverted;
}
- // We omit the last node if it's whitespace only and not preceeded by a non-content node.
- var original = current.string;
- current.string = current.string.replace(multiple ? (/\s+$/) : (/[ \t]+$/), '');
- current.leftStripped = current.string !== original;
- return current.leftStripped;
+ return new this.BlockStatement(
+ openBlock.sexpr, program, inverse,
+ openBlock.strip, inverseStrip, close && (close.strip || close.openStrip),
+ this.locInfo(locInfo));
}