Implement parser for else chaining of helpers

Allows users to chain multiple helpers together using their inverse callbacks. I.e.

```
{{#if foo}}
{{else if bar}}
{{else}}
{{/if}}
```

The control flow here effectively causes the helpers to be nested. The above is actually syntactic sugar for this:

```
{{#if foo}}
{{else}}
  {{#if bar}}
  {{else}}
  {{/if}}
{{/if}}
```

Any helper may be used in this manner, the only requirement is they support normal calls and inverse calls.

Introduces a breaking change in that `{{else foo}}` may no longer be used as a root level operator. Instead `{{^foo}}` must be used.

Fixes #72.
This commit is contained in:
kpdecker
2014-10-27 00:23:32 -05:00
parent 8aa2a34a8d
commit 4282668d47
5 changed files with 72 additions and 11 deletions
+26 -7
View File
@@ -10,11 +10,29 @@ export function stripFlags(open, close) {
export function prepareBlock(mustache, program, inverseAndProgram, close, inverted, locInfo) {
/*jshint -W040 */
if (mustache.sexpr.id.original !== close.path.original) {
// When we are chaining inverse calls, we will not have a close path
if (close && close.path && (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;
// 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: {}};
}
// 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;
// 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: mustache.strip.left,
@@ -23,7 +41,7 @@ export function prepareBlock(mustache, program, inverseAndProgram, close, invert
// 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)
closeStandalone: isPrevWhitespace((firstInverse || program).statements)
};
if (mustache.strip.right) {
@@ -36,19 +54,20 @@ export function prepareBlock(mustache, program, inverseAndProgram, close, invert
if (inverseStrip.left) {
omitLeft(program.statements, null, true);
}
if (inverseStrip.right) {
omitRight(inverse.statements, null, true);
omitRight(firstInverse.statements, null, true);
}
if (close.strip.left) {
omitLeft(inverse.statements, null, true);
omitLeft(lastInverse.statements, null, true);
}
// Find standalone else statments
if (isPrevWhitespace(program.statements)
&& isNextWhitespace(inverse.statements)) {
&& isNextWhitespace(firstInverse.statements)) {
omitLeft(program.statements);
omitRight(inverse.statements);
omitRight(firstInverse.statements);
}
} else {
if (close.strip.left) {