ember-source
Version:
A JavaScript framework for creating ambitious web applications
1,894 lines (1,856 loc) • 469 kB
JavaScript
const CURRIED_COMPONENT = 0;
const CURRIED_HELPER = 1;
const CURRIED_MODIFIER = 2;
// import Logger from './logger';
function assert$2(test, msg) {
}
/**
* This constant exists to make it easier to differentiate normal logs from
* errant console.logs. LOGGER can be used outside of LOCAL_TRACE_LOGGING checks,
* and is meant to be used in the rare situation where a console.* call is
* actually appropriate.
*/
const LOGGER$1 = console;
function assertNever$1(value, desc = 'unexpected unreachable branch') {
LOGGER$1.log('unreachable', value);
LOGGER$1.log(`${desc} :: ${JSON.stringify(value)} (${value})`);
throw new Error(`code reached unreachable`);
}
/**
* An object branded with a local debug type has special local trace logging
* behavior.
*
* If `LOCAL_DEBUG` is `false`, this function does nothing (and is removed
* by the minifier in builder).
*/
function setLocalDebugType(type, ...brand) {
}
function unwrap$1(val) {
return val;
}
function expect(val, message) {
return val;
}
function exhausted(value) {
}
function isPresentArray(list) {
return list ? list.length > 0 : false;
}
function asPresentArray(list, message = `unexpected empty list`) {
return list;
}
function getLast(list) {
return list.length === 0 ? undefined : list[list.length - 1];
}
function getFirst(list) {
return list.length === 0 ? undefined : list[0];
}
function mapPresentArray(list, mapper) {
if (list === null) {
return null;
}
let out = [];
for (let item of list) {
out.push(mapper(item));
}
return out;
}
const SIGN_BIT = -536870913;
function encodeNegative(num) {
return num & SIGN_BIT;
}
function decodeNegative(num) {
return num | ~SIGN_BIT;
}
function encodePositive(num) {
return ~num;
}
function decodePositive(num) {
return ~num;
}
function decodeHandle(num) {
return num;
}
function encodeImmediate(num) {
num |= 0;
return num < 0 ? encodeNegative(num) : encodePositive(num);
}
function decodeImmediate(num) {
num |= 0;
return num > SIGN_BIT ? decodePositive(num) : decodeNegative(num);
}
[1, -1].forEach(x => decodeImmediate(encodeImmediate(x)));
const VM_SYSCALL_SIZE = 113;
const VM_MACHINE_SIZE = 7;
/**
* This constant exists to make it easier to differentiate normal logs from
* errant console.logs. LOCAL_LOGGER should only be used inside a
* LOCAL_TRACE_LOGGING check.
*
* It does not alleviate the need to check LOCAL_TRACE_LOGGING, which is used
* for stripping.
*/
/**
* This constant exists to make it easier to differentiate normal logs from
* errant console.logs. LOGGER can be used outside of LOCAL_TRACE_LOGGING checks,
* and is meant to be used in the rare situation where a console.* call is
* actually appropriate.
*/
const LOGGER = console;
function dict() {
return Object.create(null);
}
const assign = Object.assign;
function assertNever(value, desc = 'unexpected unreachable branch') {
LOGGER.log('unreachable', value);
LOGGER.log(`${desc} :: ${JSON.stringify(value)} (${value})`);
throw new Error(`code reached unreachable`);
}
const opcodes = {
Append: 1,
TrustingAppend: 2,
Comment: 3,
Modifier: 4,
Block: 6,
Component: 8,
OpenElement: 10,
OpenElementWithSplat: 11,
FlushElement: 12,
CloseElement: 13,
StaticAttr: 14,
DynamicAttr: 15,
ComponentAttr: 16,
AttrSplat: 17,
Yield: 18,
TrustingDynamicAttr: 22,
TrustingComponentAttr: 23,
StaticComponentAttr: 24,
Debugger: 26,
Undefined: 27,
Call: 28,
Concat: 29,
GetSymbol: 30,
GetLexicalSymbol: 32,
GetStrictKeyword: 31,
GetFreeAsComponentOrHelperHead: 35,
GetFreeAsHelperHead: 37,
GetFreeAsModifierHead: 38,
GetFreeAsComponentHead: 39,
InElement: 40,
If: 41,
Each: 42,
Let: 44,
WithDynamicVars: 45,
InvokeComponent: 46,
HasBlock: 48,
HasBlockParams: 49,
Curry: 50,
Not: 51,
IfInline: 52,
GetDynamicVar: 53,
Log: 54
};
const WellKnownAttrNames = {
class: 0,
id: 1,
value: 2,
name: 3,
type: 4,
style: 5,
href: 6
};
const WellKnownTagNames = {
div: 0,
span: 1,
p: 2,
a: 3
};
const Char = {
NBSP: 0xa0,
QUOT: 0x22,
LT: 0x3c,
GT: 0x3e,
AMP: 0x26
};
// \x26 is ampersand, \xa0 is non-breaking space
const ATTR_VALUE_REGEX_TEST = /["\x26\xa0]/u;
const ATTR_VALUE_REGEX_REPLACE = new RegExp(ATTR_VALUE_REGEX_TEST.source, 'gu');
const TEXT_REGEX_TEST = /[&<>\xa0]/u;
const TEXT_REGEX_REPLACE = new RegExp(TEXT_REGEX_TEST.source, 'gu');
function attrValueReplacer(char) {
switch (char.charCodeAt(0)) {
case Char.NBSP:
return ' ';
case Char.QUOT:
return '"';
case Char.AMP:
return '&';
default:
return char;
}
}
function textReplacer(char) {
switch (char.charCodeAt(0)) {
case Char.NBSP:
return ' ';
case Char.AMP:
return '&';
case Char.LT:
return '<';
case Char.GT:
return '>';
default:
return char;
}
}
function escapeAttrValue(attrValue) {
if (ATTR_VALUE_REGEX_TEST.test(attrValue)) {
return attrValue.replace(ATTR_VALUE_REGEX_REPLACE, attrValueReplacer);
}
return attrValue;
}
function escapeText(text) {
if (TEXT_REGEX_TEST.test(text)) {
return text.replace(TEXT_REGEX_REPLACE, textReplacer);
}
return text;
}
function sortByLoc(a, b) {
// If either is invisible, don't try to order them
if (a.loc.isInvisible || b.loc.isInvisible) {
return 0;
}
if (a.loc.startPosition.line < b.loc.startPosition.line) {
return -1;
}
if (a.loc.startPosition.line === b.loc.startPosition.line && a.loc.startPosition.column < b.loc.startPosition.column) {
return -1;
}
if (a.loc.startPosition.line === b.loc.startPosition.line && a.loc.startPosition.column === b.loc.startPosition.column) {
return 0;
}
return 1;
}
const voidMap = new Set(['area', 'base', 'br', 'col', 'command', 'embed', 'hr', 'img', 'input', 'keygen', 'link', 'meta', 'param', 'source', 'track', 'wbr']);
const NON_WHITESPACE = /^\S/u;
/**
* Examples when true:
* - link
* - liNK
*
* Examples when false:
* - Link (component)
*/
function isVoidTag(tag) {
return voidMap.has(tag.toLowerCase()) && tag[0]?.toLowerCase() === tag[0];
}
class Printer {
buffer = '';
options;
constructor(options) {
this.options = options;
}
/*
This is used by _all_ methods on this Printer class that add to `this.buffer`,
it allows consumers of the printer to use alternate string representations for
a given node.
The primary use case for this are things like source -> source codemod utilities.
For example, ember-template-recast attempts to always preserve the original string
formatting in each AST node if no modifications are made to it.
*/
handledByOverride(node, ensureLeadingWhitespace = false) {
if (this.options.override !== undefined) {
let result = this.options.override(node, this.options);
if (typeof result === 'string') {
if (ensureLeadingWhitespace && NON_WHITESPACE.test(result)) {
result = ` ${result}`;
}
this.buffer += result;
return true;
}
}
return false;
}
Node(node) {
switch (node.type) {
case 'MustacheStatement':
case 'BlockStatement':
case 'MustacheCommentStatement':
case 'CommentStatement':
case 'TextNode':
case 'ElementNode':
case 'AttrNode':
case 'Block':
case 'Template':
return this.TopLevelStatement(node);
case 'StringLiteral':
case 'BooleanLiteral':
case 'NumberLiteral':
case 'UndefinedLiteral':
case 'NullLiteral':
case 'PathExpression':
case 'SubExpression':
return this.Expression(node);
case 'ConcatStatement':
// should have an AttrNode parent
return this.ConcatStatement(node);
case 'Hash':
return this.Hash(node);
case 'HashPair':
return this.HashPair(node);
case 'ElementModifierStatement':
return this.ElementModifierStatement(node);
}
}
Expression(expression) {
switch (expression.type) {
case 'StringLiteral':
case 'BooleanLiteral':
case 'NumberLiteral':
case 'UndefinedLiteral':
case 'NullLiteral':
return this.Literal(expression);
case 'PathExpression':
return this.PathExpression(expression);
case 'SubExpression':
return this.SubExpression(expression);
}
}
Literal(literal) {
switch (literal.type) {
case 'StringLiteral':
return this.StringLiteral(literal);
case 'BooleanLiteral':
return this.BooleanLiteral(literal);
case 'NumberLiteral':
return this.NumberLiteral(literal);
case 'UndefinedLiteral':
return this.UndefinedLiteral(literal);
case 'NullLiteral':
return this.NullLiteral(literal);
}
}
TopLevelStatement(statement) {
switch (statement.type) {
case 'MustacheStatement':
return this.MustacheStatement(statement);
case 'BlockStatement':
return this.BlockStatement(statement);
case 'MustacheCommentStatement':
return this.MustacheCommentStatement(statement);
case 'CommentStatement':
return this.CommentStatement(statement);
case 'TextNode':
return this.TextNode(statement);
case 'ElementNode':
return this.ElementNode(statement);
case 'Block':
return this.Block(statement);
case 'Template':
return this.Template(statement);
case 'AttrNode':
// should have element
return this.AttrNode(statement);
}
}
Template(template) {
this.TopLevelStatements(template.body);
}
Block(block) {
/*
When processing a template like:
```hbs
{{#if whatever}}
whatever
{{else if somethingElse}}
something else
{{else}}
fallback
{{/if}}
```
The AST still _effectively_ looks like:
```hbs
{{#if whatever}}
whatever
{{else}}{{#if somethingElse}}
something else
{{else}}
fallback
{{/if}}{{/if}}
```
The only way we can tell if that is the case is by checking for
`block.chained`, but unfortunately when the actual statements are
processed the `block.body[0]` node (which will always be a
`BlockStatement`) has no clue that its ancestor `Block` node was
chained.
This "forwards" the `chained` setting so that we can check
it later when processing the `BlockStatement`.
*/
if (block.chained) {
let firstChild = block.body[0];
firstChild.chained = true;
}
if (this.handledByOverride(block)) {
return;
}
this.TopLevelStatements(block.body);
}
TopLevelStatements(statements) {
statements.forEach(statement => this.TopLevelStatement(statement));
}
ElementNode(el) {
if (this.handledByOverride(el)) {
return;
}
this.OpenElementNode(el);
this.TopLevelStatements(el.children);
this.CloseElementNode(el);
}
OpenElementNode(el) {
this.buffer += `<${el.tag}`;
const parts = [...el.attributes, ...el.modifiers, ...el.comments].sort(sortByLoc);
for (const part of parts) {
this.buffer += ' ';
switch (part.type) {
case 'AttrNode':
this.AttrNode(part);
break;
case 'ElementModifierStatement':
this.ElementModifierStatement(part);
break;
case 'MustacheCommentStatement':
this.MustacheCommentStatement(part);
break;
}
}
if (el.blockParams.length) {
this.BlockParams(el.blockParams);
}
if (el.selfClosing) {
this.buffer += ' /';
}
this.buffer += '>';
}
CloseElementNode(el) {
if (el.selfClosing || isVoidTag(el.tag)) {
return;
}
this.buffer += `</${el.tag}>`;
}
AttrNode(attr) {
if (this.handledByOverride(attr)) {
return;
}
let {
name,
value
} = attr;
this.buffer += name;
const isAttribute = !name.startsWith('@');
const shouldElideValue = isAttribute && value.type == 'TextNode' && value.chars.length === 0;
if (!shouldElideValue) {
this.buffer += '=';
this.AttrNodeValue(value);
}
}
AttrNodeValue(value) {
if (value.type === 'TextNode') {
let quote = '"';
if (this.options.entityEncoding === 'raw') {
if (value.chars.includes('"') && !value.chars.includes("'")) {
quote = "'";
}
}
this.buffer += quote;
this.TextNode(value, quote);
this.buffer += quote;
} else {
this.Node(value);
}
}
TextNode(text, isInAttr) {
if (this.handledByOverride(text)) {
return;
}
if (this.options.entityEncoding === 'raw') {
if (isInAttr && text.chars.includes(isInAttr)) {
this.buffer += escapeAttrValue(text.chars);
} else {
this.buffer += text.chars;
}
} else if (isInAttr) {
this.buffer += escapeAttrValue(text.chars);
} else {
this.buffer += escapeText(text.chars);
}
}
MustacheStatement(mustache) {
if (this.handledByOverride(mustache)) {
return;
}
this.buffer += mustache.trusting ? '{{{' : '{{';
if (mustache.strip.open) {
this.buffer += '~';
}
this.Expression(mustache.path);
this.Params(mustache.params);
this.Hash(mustache.hash);
if (mustache.strip.close) {
this.buffer += '~';
}
this.buffer += mustache.trusting ? '}}}' : '}}';
}
BlockStatement(block) {
if (this.handledByOverride(block)) {
return;
}
if (block.chained) {
this.buffer += block.inverseStrip.open ? '{{~' : '{{';
this.buffer += 'else ';
} else {
this.buffer += block.openStrip.open ? '{{~#' : '{{#';
}
this.Expression(block.path);
this.Params(block.params);
this.Hash(block.hash);
if (block.program.blockParams.length) {
this.BlockParams(block.program.blockParams);
}
if (block.chained) {
this.buffer += block.inverseStrip.close ? '~}}' : '}}';
} else {
this.buffer += block.openStrip.close ? '~}}' : '}}';
}
this.Block(block.program);
if (block.inverse) {
if (!block.inverse.chained) {
this.buffer += block.inverseStrip.open ? '{{~' : '{{';
this.buffer += 'else';
this.buffer += block.inverseStrip.close ? '~}}' : '}}';
}
this.Block(block.inverse);
}
if (!block.chained) {
this.buffer += block.closeStrip.open ? '{{~/' : '{{/';
this.Expression(block.path);
this.buffer += block.closeStrip.close ? '~}}' : '}}';
}
}
BlockParams(blockParams) {
this.buffer += ` as |${blockParams.join(' ')}|`;
}
ConcatStatement(concat) {
if (this.handledByOverride(concat)) {
return;
}
this.buffer += '"';
concat.parts.forEach(part => {
if (part.type === 'TextNode') {
this.TextNode(part, '"');
} else {
this.Node(part);
}
});
this.buffer += '"';
}
MustacheCommentStatement(comment) {
if (this.handledByOverride(comment)) {
return;
}
this.buffer += `{{!--${comment.value}--}}`;
}
ElementModifierStatement(mod) {
if (this.handledByOverride(mod)) {
return;
}
this.buffer += '{{';
this.Expression(mod.path);
this.Params(mod.params);
this.Hash(mod.hash);
this.buffer += '}}';
}
CommentStatement(comment) {
if (this.handledByOverride(comment)) {
return;
}
this.buffer += `<!--${comment.value}-->`;
}
PathExpression(path) {
if (this.handledByOverride(path)) {
return;
}
this.buffer += path.original;
}
SubExpression(sexp) {
if (this.handledByOverride(sexp)) {
return;
}
this.buffer += '(';
this.Expression(sexp.path);
this.Params(sexp.params);
this.Hash(sexp.hash);
this.buffer += ')';
}
Params(params) {
// TODO: implement a top level Params AST node (just like the Hash object)
// so that this can also be overridden
if (params.length) {
params.forEach(param => {
this.buffer += ' ';
this.Expression(param);
});
}
}
Hash(hash) {
if (this.handledByOverride(hash, true)) {
return;
}
hash.pairs.forEach(pair => {
this.buffer += ' ';
this.HashPair(pair);
});
}
HashPair(pair) {
if (this.handledByOverride(pair)) {
return;
}
this.buffer += pair.key;
this.buffer += '=';
this.Node(pair.value);
}
StringLiteral(str) {
if (this.handledByOverride(str)) {
return;
}
this.buffer += JSON.stringify(str.value);
}
BooleanLiteral(bool) {
if (this.handledByOverride(bool)) {
return;
}
this.buffer += String(bool.value);
}
NumberLiteral(number) {
if (this.handledByOverride(number)) {
return;
}
this.buffer += String(number.value);
}
UndefinedLiteral(node) {
if (this.handledByOverride(node)) {
return;
}
this.buffer += 'undefined';
}
NullLiteral(node) {
if (this.handledByOverride(node)) {
return;
}
this.buffer += 'null';
}
print(node) {
let {
options
} = this;
if (options.override) {
let result = options.override(node, options);
if (result !== undefined) {
return result;
}
}
this.buffer = '';
this.Node(node);
return this.buffer;
}
}
function build(ast, options = {
entityEncoding: 'transformed'
}) {
// eslint-disable-next-line @typescript-eslint/no-unnecessary-condition -- JS users
if (!ast) {
return '';
}
let printer = new Printer(options);
return printer.print(ast);
}
function isKeyword(word, type) {
if (word in KEYWORDS_TYPES) {
{
return true;
}
} else {
return false;
}
}
/**
* This includes the full list of keywords currently in use in the template
* language, and where their valid usages are.
*/
const KEYWORDS_TYPES = {
action: ['Call', 'Modifier'],
component: ['Call', 'Append', 'Block'],
debugger: ['Append'],
'each-in': ['Block'],
each: ['Block'],
'has-block-params': ['Call', 'Append'],
'has-block': ['Call', 'Append'],
helper: ['Call', 'Append'],
if: ['Call', 'Append', 'Block'],
'in-element': ['Block'],
let: ['Block'],
log: ['Call', 'Append'],
modifier: ['Call', 'Modifier'],
mount: ['Append'],
mut: ['Call', 'Append'],
outlet: ['Append'],
readonly: ['Call', 'Append'],
unbound: ['Call', 'Append'],
unless: ['Call', 'Append', 'Block'],
yield: ['Append']
};
var errorProps = ['description', 'fileName', 'lineNumber', 'endLineNumber', 'message', 'name', 'number', 'stack'];
function Exception(message, node) {
var loc = node && node.loc,
line,
endLineNumber,
column,
endColumn;
if (loc) {
line = loc.start.line;
endLineNumber = loc.end.line;
column = loc.start.column;
endColumn = loc.end.column;
message += ' - ' + line + ':' + column;
}
var tmp = Error.prototype.constructor.call(this, message);
// Unfortunately errors are not enumerable in Chrome (at least), so `for prop in tmp` doesn't work.
for (var idx = 0; idx < errorProps.length; idx++) {
this[errorProps[idx]] = tmp[errorProps[idx]];
}
/* istanbul ignore else */
if (Error.captureStackTrace) {
Error.captureStackTrace(this, Exception);
}
try {
if (loc) {
this.lineNumber = line;
this.endLineNumber = endLineNumber;
// Work around issue under safari where we can't directly set the column value
/* istanbul ignore next */
if (Object.defineProperty) {
Object.defineProperty(this, 'column', {
value: column,
enumerable: true
});
Object.defineProperty(this, 'endColumn', {
value: endColumn,
enumerable: true
});
} else {
this.column = column;
this.endColumn = endColumn;
}
}
} catch (nop) {
/* Ignore if the browser is very particular */
}
}
Exception.prototype = new Error();
function Visitor() {
this.parents = [];
}
Visitor.prototype = {
constructor: Visitor,
mutating: false,
// Visits a given value. If mutating, will replace the value if necessary.
acceptKey: function (node, name) {
var value = this.accept(node[name]);
if (this.mutating) {
// Hacky sanity check: This may have a few false positives for type for the helper
// methods but will generally do the right thing without a lot of overhead.
if (value && !Visitor.prototype[value.type]) {
throw new Exception('Unexpected node type "' + value.type + '" found when accepting ' + name + ' on ' + node.type);
}
node[name] = value;
}
},
// Performs an accept operation with added sanity check to ensure
// required keys are not removed.
acceptRequired: function (node, name) {
this.acceptKey(node, name);
if (!node[name]) {
throw new Exception(node.type + ' requires ' + name);
}
},
// Traverses a given array. If mutating, empty responses will be removed
// for child elements.
acceptArray: function (array) {
for (var i = 0, l = array.length; i < l; i++) {
this.acceptKey(array, i);
if (!array[i]) {
array.splice(i, 1);
i--;
l--;
}
}
},
accept: function (object) {
if (!object) {
return;
}
/* istanbul ignore next: Sanity code */
if (!this[object.type]) {
throw new Exception('Unknown type: ' + object.type, object);
}
if (this.current) {
this.parents.unshift(this.current);
}
this.current = object;
var ret = this[object.type](object);
this.current = this.parents.shift();
if (!this.mutating || ret) {
return ret;
} else if (ret !== false) {
return object;
}
},
Program: function (program) {
this.acceptArray(program.body);
},
MustacheStatement: visitSubExpression,
Decorator: visitSubExpression,
BlockStatement: visitBlock,
DecoratorBlock: visitBlock,
PartialStatement: visitPartial,
PartialBlockStatement: function (partial) {
visitPartial.call(this, partial);
this.acceptKey(partial, 'program');
},
ContentStatement: function /* content */ () {},
CommentStatement: function /* comment */ () {},
SubExpression: visitSubExpression,
PathExpression: function /* path */ () {},
StringLiteral: function /* string */ () {},
NumberLiteral: function /* number */ () {},
BooleanLiteral: function /* bool */ () {},
UndefinedLiteral: function /* literal */ () {},
NullLiteral: function /* literal */ () {},
Hash: function (hash) {
this.acceptArray(hash.pairs);
},
HashPair: function (pair) {
this.acceptRequired(pair, 'value');
}
};
function visitSubExpression(mustache) {
this.acceptRequired(mustache, 'path');
this.acceptArray(mustache.params);
this.acceptKey(mustache, 'hash');
}
function visitBlock(block) {
visitSubExpression.call(this, block);
this.acceptKey(block, 'program');
this.acceptKey(block, 'inverse');
}
function visitPartial(partial) {
this.acceptRequired(partial, 'name');
this.acceptArray(partial.params);
this.acceptKey(partial, 'hash');
}
function WhitespaceControl(options) {
if (options === void 0) {
options = {};
}
this.options = options;
}
WhitespaceControl.prototype = new Visitor();
WhitespaceControl.prototype.Program = function (program) {
var doStandalone = !this.options.ignoreStandalone;
var isRoot = !this.isRootSeen;
this.isRootSeen = true;
var body = program.body;
for (var i = 0, l = body.length; i < l; i++) {
var current = body[i],
strip = this.accept(current);
if (!strip) {
continue;
}
var _isPrevWhitespace = isPrevWhitespace(body, i, isRoot),
_isNextWhitespace = isNextWhitespace(body, i, isRoot),
openStandalone = strip.openStandalone && _isPrevWhitespace,
closeStandalone = strip.closeStandalone && _isNextWhitespace,
inlineStandalone = strip.inlineStandalone && _isPrevWhitespace && _isNextWhitespace;
if (strip.close) {
omitRight(body, i, true);
}
if (strip.open) {
omitLeft(body, i, true);
}
if (doStandalone && inlineStandalone) {
omitRight(body, i);
if (omitLeft(body, i)) {
// If we are on a standalone node, save the indent info for partials
if (current.type === 'PartialStatement') {
// Pull out the whitespace from the final line
current.indent = /([ \t]+$)/.exec(body[i - 1].original)[1];
}
}
}
if (doStandalone && openStandalone) {
omitRight((current.program || current.inverse).body);
// Strip out the previous content node if it's whitespace only
omitLeft(body, i);
}
if (doStandalone && closeStandalone) {
// Always strip the next node
omitRight(body, i);
omitLeft((current.inverse || current.program).body);
}
}
return program;
};
WhitespaceControl.prototype.BlockStatement = WhitespaceControl.prototype.DecoratorBlock = WhitespaceControl.prototype.PartialBlockStatement = function (block) {
this.accept(block.program);
this.accept(block.inverse);
// Find the inverse program that is involved with whitespace stripping.
var program = block.program || block.inverse,
inverse = block.program && block.inverse,
firstInverse = inverse,
lastInverse = inverse;
if (inverse && inverse.chained) {
firstInverse = inverse.body[0].program;
// Walk the inverse chain to find the last inverse that is actually in the chain.
while (lastInverse.chained) {
lastInverse = lastInverse.body[lastInverse.body.length - 1].program;
}
}
var strip = {
open: block.openStrip.open,
close: block.closeStrip.close,
// Determine the standalone candidacy. Basically flag our content as being possibly standalone
// so our parent can determine if we actually are standalone
openStandalone: isNextWhitespace(program.body),
closeStandalone: isPrevWhitespace((firstInverse || program).body)
};
if (block.openStrip.close) {
omitRight(program.body, null, true);
}
if (inverse) {
var inverseStrip = block.inverseStrip;
if (inverseStrip.open) {
omitLeft(program.body, null, true);
}
if (inverseStrip.close) {
omitRight(firstInverse.body, null, true);
}
if (block.closeStrip.open) {
omitLeft(lastInverse.body, null, true);
}
// Find standalone else statements
if (!this.options.ignoreStandalone && isPrevWhitespace(program.body) && isNextWhitespace(firstInverse.body)) {
omitLeft(program.body);
omitRight(firstInverse.body);
}
} else if (block.closeStrip.open) {
omitLeft(program.body, null, true);
}
return strip;
};
WhitespaceControl.prototype.Decorator = WhitespaceControl.prototype.MustacheStatement = function (mustache) {
return mustache.strip;
};
WhitespaceControl.prototype.PartialStatement = WhitespaceControl.prototype.CommentStatement = function (node) {
/* istanbul ignore next */
var strip = node.strip || {};
return {
inlineStandalone: true,
open: strip.open,
close: strip.close
};
};
function isPrevWhitespace(body, i, isRoot) {
if (i === undefined) {
i = body.length;
}
// Nodes that end with newlines are considered whitespace (but are special
// cased for strip operations)
var prev = body[i - 1],
sibling = body[i - 2];
if (!prev) {
return isRoot;
}
if (prev.type === 'ContentStatement') {
return (sibling || !isRoot ? /\r?\n\s*?$/ : /(^|\r?\n)\s*?$/).test(prev.original);
}
}
function isNextWhitespace(body, i, isRoot) {
if (i === undefined) {
i = -1;
}
var next = body[i + 1],
sibling = body[i + 2];
if (!next) {
return isRoot;
}
if (next.type === 'ContentStatement') {
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 multiple is truthy then all whitespace will be stripped out until non-whitespace
// content is met.
function omitRight(body, i, multiple) {
var current = body[i == null ? 0 : i + 1];
if (!current || current.type !== 'ContentStatement' || !multiple && current.rightStripped) {
return;
}
var original = current.value;
current.value = current.value.replace(multiple ? /^\s+/ : /^[ \t]*\r?\n?/, '');
current.rightStripped = current.value !== 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 multiple is truthy then all whitespace will be stripped out until non-whitespace
// content is met.
function omitLeft(body, i, multiple) {
var current = body[i == null ? body.length - 1 : i - 1];
if (!current || current.type !== 'ContentStatement' || !multiple && current.leftStripped) {
return;
}
// We omit the last node if it's whitespace only and not preceded by a non-content node.
var original = current.value;
current.value = current.value.replace(multiple ? /\s+$/ : /[ \t]+$/, '');
current.leftStripped = current.value !== original;
return current.leftStripped;
}
/* parser generated by jison 0.4.18 */
/*
Returns a Parser object of the following structure:
Parser: {
yy: {}
}
Parser.prototype: {
yy: {},
trace: function(),
symbols_: {associative list: name ==> number},
terminals_: {associative list: number ==> name},
productions_: [...],
performAction: function anonymous(yytext, yyleng, yylineno, yy, yystate, $$, _$),
table: [...],
defaultActions: {...},
parseError: function(str, hash),
parse: function(input),
lexer: {
EOF: 1,
parseError: function(str, hash),
setInput: function(input),
input: function(),
unput: function(str),
more: function(),
less: function(n),
pastInput: function(),
upcomingInput: function(),
showPosition: function(),
test_match: function(regex_match_array, rule_index),
next: function(),
lex: function(),
begin: function(condition),
popState: function(),
_currentRules: function(),
topState: function(),
pushState: function(condition),
options: {
ranges: boolean (optional: true ==> token location info will include a .range[] member)
flex: boolean (optional: true ==> flex-like lexing behaviour where the rules are tested exhaustively to find the longest match)
backtrack_lexer: boolean (optional: true ==> lexer regexes are tested in order and for each matching regex the action code is invoked; the lexer terminates the scan when a token is returned by the action code)
},
performAction: function(yy, yy_, $avoiding_name_collisions, YY_START),
rules: [...],
conditions: {associative list: name ==> set},
}
}
token location info (@$, _$, etc.): {
first_line: n,
last_line: n,
first_column: n,
last_column: n,
range: [start_number, end_number] (where the numbers are indexes into the input string, regular zero-based)
}
the parseError function receives a 'hash' object with these members for lexer and parser errors: {
text: (matched text)
token: (the produced terminal token, if any)
line: (yylineno)
}
while parser (grammar) errors will also provide these members, i.e. parser errors deliver a superset of attributes: {
loc: (yylloc)
expected: (string describing the set of expected tokens)
recoverable: (boolean: TRUE when the parser has a error recovery rule available for this particular error)
}
*/
var parser = function () {
var o = function (k, v, o, l) {
for (o = o || {}, l = k.length; l--; o[k[l]] = v);
return o;
},
$V0 = [2, 52],
$V1 = [1, 20],
$V2 = [5, 14, 15, 19, 29, 34, 39, 44, 47, 48, 53, 57, 61],
$V3 = [1, 44],
$V4 = [1, 40],
$V5 = [1, 43],
$V6 = [1, 33],
$V7 = [1, 34],
$V8 = [1, 35],
$V9 = [1, 36],
$Va = [1, 37],
$Vb = [1, 42],
$Vc = [1, 46],
$Vd = [14, 15, 19, 29, 34, 39, 44, 47, 48, 53, 57, 61],
$Ve = [14, 15, 19, 29, 34, 44, 47, 48, 53, 57, 61],
$Vf = [15, 18],
$Vg = [14, 15, 19, 29, 34, 47, 48, 53, 57, 61],
$Vh = [33, 67, 73, 75, 84, 85, 86, 87, 88, 89],
$Vi = [23, 33, 56, 67, 68, 73, 75, 77, 79, 84, 85, 86, 87, 88, 89],
$Vj = [1, 62],
$Vk = [1, 63],
$Vl = [23, 33, 56, 68, 73, 79],
$Vm = [23, 33, 56, 67, 68, 73, 75, 77, 79, 84, 85, 86, 87, 88, 89, 92, 93],
$Vn = [2, 51],
$Vo = [1, 64],
$Vp = [67, 73, 75, 77, 84, 85, 86, 87, 88, 89],
$Vq = [56, 67, 73, 75, 84, 85, 86, 87, 88, 89],
$Vr = [1, 75],
$Vs = [1, 76],
$Vt = [1, 83],
$Vu = [33, 67, 73, 75, 79, 84, 85, 86, 87, 88, 89],
$Vv = [23, 67, 73, 75, 84, 85, 86, 87, 88, 89],
$Vw = [67, 68, 73, 75, 84, 85, 86, 87, 88, 89],
$Vx = [33, 79],
$Vy = [1, 134],
$Vz = [73, 81];
var parser = {
trace: function trace() {},
yy: {},
symbols_: {
"error": 2,
"root": 3,
"program": 4,
"EOF": 5,
"program_repetition0": 6,
"statement": 7,
"mustache": 8,
"block": 9,
"rawBlock": 10,
"partial": 11,
"partialBlock": 12,
"content": 13,
"COMMENT": 14,
"CONTENT": 15,
"openRawBlock": 16,
"rawBlock_repetition0": 17,
"END_RAW_BLOCK": 18,
"OPEN_RAW_BLOCK": 19,
"helperName": 20,
"openRawBlock_repetition0": 21,
"openRawBlock_option0": 22,
"CLOSE_RAW_BLOCK": 23,
"openBlock": 24,
"block_option0": 25,
"closeBlock": 26,
"openInverse": 27,
"block_option1": 28,
"OPEN_BLOCK": 29,
"openBlock_repetition0": 30,
"openBlock_option0": 31,
"openBlock_option1": 32,
"CLOSE": 33,
"OPEN_INVERSE": 34,
"openInverse_repetition0": 35,
"openInverse_option0": 36,
"openInverse_option1": 37,
"openInverseChain": 38,
"OPEN_INVERSE_CHAIN": 39,
"openInverseChain_repetition0": 40,
"openInverseChain_option0": 41,
"openInverseChain_option1": 42,
"inverseAndProgram": 43,
"INVERSE": 44,
"inverseChain": 45,
"inverseChain_option0": 46,
"OPEN_ENDBLOCK": 47,
"OPEN": 48,
"hash": 49,
"expr": 50,
"mustache_repetition0": 51,
"mustache_option0": 52,
"OPEN_UNESCAPED": 53,
"mustache_repetition1": 54,
"mustache_option1": 55,
"CLOSE_UNESCAPED": 56,
"OPEN_PARTIAL": 57,
"partial_repetition0": 58,
"partial_option0": 59,
"openPartialBlock": 60,
"OPEN_PARTIAL_BLOCK": 61,
"openPartialBlock_repetition0": 62,
"openPartialBlock_option0": 63,
"exprHead": 64,
"arrayLiteral": 65,
"sexpr": 66,
"OPEN_SEXPR": 67,
"CLOSE_SEXPR": 68,
"sexpr_repetition0": 69,
"sexpr_option0": 70,
"hash_repetition_plus0": 71,
"hashSegment": 72,
"ID": 73,
"EQUALS": 74,
"OPEN_ARRAY": 75,
"arrayLiteral_repetition0": 76,
"CLOSE_ARRAY": 77,
"blockParams": 78,
"OPEN_BLOCK_PARAMS": 79,
"blockParams_repetition_plus0": 80,
"CLOSE_BLOCK_PARAMS": 81,
"path": 82,
"dataName": 83,
"STRING": 84,
"NUMBER": 85,
"BOOLEAN": 86,
"UNDEFINED": 87,
"NULL": 88,
"DATA": 89,
"pathSegments": 90,
"sep": 91,
"SEP": 92,
"PRIVATE_SEP": 93,
"$accept": 0,
"$end": 1
},
terminals_: {
2: "error",
5: "EOF",
14: "COMMENT",
15: "CONTENT",
18: "END_RAW_BLOCK",
19: "OPEN_RAW_BLOCK",
23: "CLOSE_RAW_BLOCK",
29: "OPEN_BLOCK",
33: "CLOSE",
34: "OPEN_INVERSE",
39: "OPEN_INVERSE_CHAIN",
44: "INVERSE",
47: "OPEN_ENDBLOCK",
48: "OPEN",
53: "OPEN_UNESCAPED",
56: "CLOSE_UNESCAPED",
57: "OPEN_PARTIAL",
61: "OPEN_PARTIAL_BLOCK",
67: "OPEN_SEXPR",
68: "CLOSE_SEXPR",
73: "ID",
74: "EQUALS",
75: "OPEN_ARRAY",
77: "CLOSE_ARRAY",
79: "OPEN_BLOCK_PARAMS",
81: "CLOSE_BLOCK_PARAMS",
84: "STRING",
85: "NUMBER",
86: "BOOLEAN",
87: "UNDEFINED",
88: "NULL",
89: "DATA",
92: "SEP",
93: "PRIVATE_SEP"
},
productions_: [0, [3, 2], [4, 1], [7, 1], [7, 1], [7, 1], [7, 1], [7, 1], [7, 1], [7, 1], [13, 1], [10, 3], [16, 5], [9, 4], [9, 4], [24, 6], [27, 6], [38, 6], [43, 2], [45, 3], [45, 1], [26, 3], [8, 3], [8, 5], [8, 5], [11, 5], [12, 3], [60, 5], [50, 1], [50, 1], [64, 1], [64, 1], [66, 3], [66, 5], [49, 1], [72, 3], [65, 3], [78, 3], [20, 1], [20, 1], [20, 1], [20, 1], [20, 1], [20, 1], [20, 1], [83, 2], [91, 1], [91, 1], [82, 3], [82, 1], [90, 3], [90, 1], [6, 0], [6, 2], [17, 0], [17, 2], [21, 0], [21, 2], [22, 0], [22, 1], [25, 0], [25, 1], [28, 0], [28, 1], [30, 0], [30, 2], [31, 0], [31, 1], [32, 0], [32, 1], [35, 0], [35, 2], [36, 0], [36, 1], [37, 0], [37, 1], [40, 0], [40, 2], [41, 0], [41, 1], [42, 0], [42, 1], [46, 0], [46, 1], [51, 0], [51, 2], [52, 0], [52, 1], [54, 0], [54, 2], [55, 0], [55, 1], [58, 0], [58, 2], [59, 0], [59, 1], [62, 0], [62, 2], [63, 0], [63, 1], [69, 0], [69, 2], [70, 0], [70, 1], [71, 1], [71, 2], [76, 0], [76, 2], [80, 1], [80, 2]],
performAction: function anonymous(yytext, yyleng, yylineno, yy, yystate /* action[1] */, $$ /* vstack */, _$ /* lstack */) {
/* this == yyval */
var $0 = $$.length - 1;
switch (yystate) {
case 1:
return $$[$0 - 1];
case 2:
this.$ = yy.prepareProgram($$[$0]);
break;
case 3:
case 4:
case 5:
case 6:
case 7:
case 8:
case 20:
case 28:
case 29:
case 30:
case 31:
case 38:
case 39:
case 46:
case 47:
this.$ = $$[$0];
break;
case 9:
this.$ = {
type: 'CommentStatement',
value: yy.stripComment($$[$0]),
strip: yy.stripFlags($$[$0], $$[$0]),
loc: yy.locInfo(this._$)
};
break;
case 10:
this.$ = {
type: 'ContentStatement',
original: $$[$0],
value: $$[$0],
loc: yy.locInfo(this._$)
};
break;
case 11:
this.$ = yy.prepareRawBlock($$[$0 - 2], $$[$0 - 1], $$[$0], this._$);
break;
case 12:
this.$ = {
path: $$[$0 - 3],
params: $$[$0 - 2],
hash: $$[$0 - 1]
};
break;
case 13:
this.$ = yy.prepareBlock($$[$0 - 3], $$[$0 - 2], $$[$0 - 1], $$[$0], false, this._$);
break;
case 14:
this.$ = yy.prepareBlock($$[$0 - 3], $$[$0 - 2], $$[$0 - 1], $$[$0], true, this._$);
break;
case 15:
this.$ = {
open: $$[$0 - 5],
path: $$[$0 - 4],
params: $$[$0 - 3],
hash: $$[$0 - 2],
blockParams: $$[$0 - 1],
strip: yy.stripFlags($$[$0 - 5], $$[$0])
};
break;
case 16:
case 17:
this.$ = {
path: $$[$0 - 4],
params: $$[$0 - 3],
hash: $$[$0 - 2],
blockParams: $$[$0 - 1],
strip: yy.stripFlags($$[$0 - 5], $$[$0])
};
break;
case 18:
this.$ = {
strip: yy.stripFlags($$[$0 - 1], $$[$0 - 1]),
program: $$[$0]
};
break;
case 19:
var inverse = yy.prepareBlock($$[$0 - 2], $$[$0 - 1], $$[$0], $$[$0], false, this._$),
program = yy.prepareProgram([inverse], $$[$0 - 1].loc);
program.chained = true;
this.$ = {
strip: $$[$0 - 2].strip,
program: program,
chain: true
};
break;
case 21:
this.$ = {
path: $$[$0 - 1],
strip: yy.stripFlags($$[$0 - 2], $$[$0])
};
break;
case 22:
this.$ = yy.prepareMustache(yy.syntax.hash($$[$0 - 1], yy.locInfo(this._$), {
yy: yy,
syntax: 'expr'
}), [], undefined, $$[$0 - 2], yy.stripFlags($$[$0 - 2], $$[$0]), this._$);
break;
case 23:
case 24:
this.$ = yy.prepareMustache($$[$0 - 3], $$[$0 - 2], $$[$0 - 1], $$[$0 - 4], yy.stripFlags($$[$0 - 4], $$[$0]), this._$);
break;
case 25:
this.$ = {
type: 'PartialStatement',
name: $$[$0 - 3],
params: $$[$0 - 2],
hash: $$[$0 - 1],
indent: '',
strip: yy.stripFlags($$[$0 - 4], $$[$0]),
loc: yy.locInfo(this._$)
};
break;
case 26:
this.$ = yy.preparePartialBlock($$[$0 - 2], $$[$0 - 1], $$[$0], this._$);
break;
case 27:
this.$ = {
path: $$[$0 - 3],
params: $$[$0 - 2],
hash: $$[$0 - 1],
strip: yy.stripFlags($$[$0 - 4], $$[$0])
};
break;
case 32:
this.$ = yy.syntax.hash($$[$0 - 1], yy.locInfo(this._$), {
yy: yy,
syntax: 'expr'
});
break;
case 33:
this.$ = {
type: 'SubExpression',
path: $$[$0 - 3],
params: $$[$0 - 2],
hash: $$[$0 - 1],
loc: yy.locInfo(this._$)
};
break;
case 34:
this.$ = {
type: 'Hash',
pairs: $$[$0],
loc: yy.locInfo(this._$)
};
break;
case 35:
this.$ = {
type: 'HashPair',
key: yy.id($$[$0 - 2]),
value: $$[$0],
loc: yy.locInfo(this._$)
};
break;
case 36:
this.$ = yy.syntax.square($$[$0 - 1], yy.locInfo(this._$), {
yy: yy,
syntax: 'expr'
});
break;
case 37:
this.$ = yy.id($$[$0 - 1]);
break;
case 40:
this.$ = {
type: 'StringLiteral',
value: $$[$0],
original: $$[$0],
loc: yy.locInfo(this._$)
};
break;
case 41:
this.$ = {
type: 'NumberLiteral',
value: Number($$[$0]),
original: Number($$[$0]),
loc: yy.locInfo(this._$)
};
break;
case 42:
this.$ = {
type: 'BooleanLiteral',
value: $$[$0] === 'true',
original: $$[$0] === 'true',
loc: yy.locInfo(this._$)
};
break;
case 43:
this.$ = {
type: 'UndefinedLiteral',
original: undefined,
value: undefined,
loc: yy.locInfo(this._$)
};
break;
case 44:
this.$ = {
type: 'NullLiteral',
original: null,
value: null,
loc: yy.locInfo(this._$)
};
break;
case 45:
this.$ = yy.preparePath(true, false, $$[$0], this._$);
break;
case 48:
this.$ = yy.preparePath(false, $$[$0 - 2], $$[$0], this._$);
break;
case 49:
this.$ = yy.preparePath(false, false, $$[$0], this._$);
break;
case 50:
$$[$0 - 2].push({
part: yy.id($$[$0]),
original: $$[$0],
separator: $$[$0 - 1]
});
this.$ = $$[$0 - 2];
break;
case 51:
this.$ = [{
part: yy.id($$[$0]),
original: $$[$0]
}];
break;
case 52:
case 54:
case 56:
case 64:
case 70:
case 76:
case 84:
case 88:
case 92:
case 96:
case 100:
case 106:
this.$ = [];
break;
case 53:
case 55:
case 57:
case 65:
case 71:
case 77:
case 85:
case 89:
case 93:
case 97:
case 101:
case 105:
case 107:
case 109:
$$[$0 - 1].push($$[$0]);
break;
case 104:
case 108:
this.$ = [$$[$0]];
break;
}
},
table: [o([5, 14, 15, 19, 29, 34, 48, 53, 57, 61], $V0, {
3: 1,
4: 2,
6: 3
}), {
1: [3]
}, {
5: [1, 4]
}, o([5, 39, 44, 47], [2, 2], {
7: 5,
8: 6,
9: 7,
10: 8,
11: 9,
12: 10,
13: 11,
24: 15,
27: 16,
16: 17,
60: 19,
14: [1, 12],
15: $V1,
19: [1, 23],
29: [1, 21],
34: [1, 22],
48: [1, 13],
53: [1, 14],
57: [1, 18],
61: [1, 24]
}), {
1: [2, 1]
}, o($V2, [2, 53]), o($V2, [2, 3]), o($V2, [2, 4]), o($V2, [2, 5]), o($V2, [2, 6]), o($V2, [2, 7]), o($V2, [2, 8]), o($V2, [2, 9]), {
20: 28,
49: 25,
50: 26,
64: 29,
65: 38,
66: 39,
67: $V3,
71: 27,
72: 30,
73: $V4,
75: $V5,
82: 31,
83: 32,
84: $V6,
85: $V7,
86: $V8,
87: $V9,
88: $Va,
89: $Vb,
90: 41
}, {
20: 28,
50: 45,
64: 29,
65: 38,
66: 39,
67: $V3,
73: $Vc,
75: $V5,
82: 31,
83: 32,
84: $V6,
85: $V7,
86: $V8,
87: $V9,
88: $Va,
89: $Vb,
90: 41
}, o($Vd, $V0, {
6: 3,
4: 47
}), o($Ve, $V0, {
6: 3,
4: 48
}), o($Vf, [2, 54], {
17: 49
}), {
20: 28,
50: 50,
64: 29,
65: 38,
66: 39,
67: $V3,
73: $Vc,
75: $V5,
82: 31,
83: 32,
84: $V6,
85: $V7,
86: $V8,
87: $V9,
88: $Va,
89: $Vb,
90: 41
}, o($Vg, $V0, {
6: 3,
4: 51
}), o([5, 14, 15, 18, 19, 29, 34, 39, 44, 47, 48, 53, 57, 61], [2, 10]), {
20: 52,
64: 53,
65: 38,
66: 39,
67: $V3,
73: $Vc,
75: $V5,
82: 31,
83: 32,
84: $V6,
85: $V7,
86: $V8,
87: $V9,
88: $Va,
89: $Vb,
90: 41
}, {
20: 54,
64: 53,
65: 38,
66: 39,
67: $V3,
73: $Vc,
75: $V5,
82: 31,
83: 32,
84: $V6,
85: $V7,
86: $V8,
87: $V9,
88: $Va,
89: $Vb,
90: 41
}, {
20: 55,
64: 53,
65: 38,
66: 39,
67: $V3,
73: $Vc,
75: $V5,
82: 31,
83: 32,
84: $V6,
85: $V7,
86: $V8,
87: $V9,
88: $Va,
89: $Vb,
90: 41
}, {
20: 28,
50: 56,
64: 29,
65: 38,
66: 39,
67: $V3,
73: $Vc,
75: $V5,
82: 31,
83: 32,
84: $V6,
85: $V7,
86: $V8,
87: $V9,
88: $Va,
89: $Vb,
90: 41
}, {
33: [1, 57]
}, o($Vh, [2, 84], {
51: 58
}), o([23, 33, 56, 68, 79], [2, 34], {
72: 59,
73: [1, 60]
}), o($Vi, [2, 28]), o($Vi, [2, 29], {
91: 61,
92: $Vj,
93: $Vk
}), o($Vl, [2, 104]), o($Vi, [2, 38]), o($Vi, [2, 39]), o($Vi, [2, 40]), o($Vi, [2, 41]), o($Vi, [2, 42]), o($Vi, [2, 43]), o($Vi, [2, 44]), o($Vm, [2, 30]), o($Vm, [2, 31]), o([23, 33, 56, 67, 68, 73, 75, 79, 84, 85, 86, 87, 88, 89, 92, 93], $Vn, {
74: $Vo
}), o($Vi, [2, 49], {
91: 65,
92: $Vj,
93: $Vk
}), {
73: $Vc,
90: 66
}, o($Vp, [2, 106], {
76: 67
}), {
20: 28,
49: 68,
50: 69,
64: 29,
65: 38,
66: 39,
67: $V3,
71: 27,
72: 30,
73: $V4,
75: $V5,
82: 31,
83: 32,
84: $V6,
85: $V7,
86: $V8,
87: $V9,
88: $Va,
89: $Vb,
90: 41
}, o($Vq, [2, 88], {
54: 70
}), o($Vm, $Vn), {
25: 71,
38: 73,
39: $Vr,
43: 74,
44: $Vs,
45: 72,
47: [2, 60]
}, {
28: 77,
43: 78,
44: $Vs,
47: [2, 62]
}, {
13: 80,
15: $V1,
18: [1, 79]
}, o($Vh, [2, 92], {
58: 81
}), {
26: 82,
47: $Vt
}, o($Vu, [2, 64], {
30: 84
}), {
91: 61,
92: $Vj,
93: $Vk
}, o($Vu, [2, 70], {
35: 85
}), o($Vv, [2, 56], {
21: 86
}), o($Vh, [2, 96], {
62: 87
}), o($V2, [2, 22]), {
20: 28,
33: [2, 86],
49: 90,
50: 89,
52: 88,
64: 29,
65: 38,
66: 39,
67: $V3,
71: 27,
72: 30,
73: $V4,
75: $V5,
82: 31,
83: 32,
84: $V6,
85: $V7,
86: $V8,
87: $V9,
88: $Va,
89: $Vb,
90: 41
}, o($Vl, [2, 105]), {
74: $Vo
}, {
73: $Vc,
90: 91
}, {
73: [2, 46]
}, {
73: [2, 47]
}, {
20: 28,
50: 92,
64: 29,
65: 38,
66: 39,
67: $V3,
73: $Vc,
75: $V5,
82: 31,
83: 32,
84: $V6,
85: $V7,
86: $V8,
87: $V9,
88: $Va,
89: $Vb,
90: 41
}, {
73: [1, 93]
}, o($Vi, [2, 45], {
91: 65,
92: $Vj,
93: $Vk
}), {
20: 28,
50: 95,
64: 29,
65: 38,
66: 39,
67: $V3,
73: $Vc,
75: $V5,
77: [1, 94],