Socket
Socket
Sign inDemoInstall

context-parser-handlebars

Package Overview
Dependencies
Maintainers
1
Versions
11
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

context-parser-handlebars - npm Package Compare versions

Comparing version 1.0.1 to 1.0.2

bin/handlebars

2

package.json
{
"name": "context-parser-handlebars",
"version": "1.0.1",
"version": "1.0.2",
"licenses": [

@@ -5,0 +5,0 @@ {

@@ -50,9 +50,5 @@ /*

/* The flag is used to print out the char to console */
/* the flag is used to print out the char to console */
this._printChar = typeof printChar !== undefined? printChar : true;
/* The flag is used to enable the output filter format as Handlebars 2.0.0 subexpression or not. */
this._enableSubexpression = true;
this._handlebarsVersion = require('handlebars').VERSION;
/* save the line number being processed */

@@ -63,4 +59,2 @@ this._lineNo = 1;

debug("_printChar:"+this._printChar);
debug("_enableSubexpression:"+this._enableSubexpression);
debug("_handlebarsVersion:"+this._handlebarsVersion);
}

@@ -133,6 +127,4 @@

/* '{' 'space'* 'non-space'+ 'space'* 'non-{'* '}' */
ContextParserHandlebars.expressionRegExp = /^{\s*(\S+)\s*([^\}]*)?}/;
/* '{' 'non-{,non-}'+ '}' and not follow by '}' */
ContextParserHandlebars.escapeExpressionRegExp = /^\{[^\}\{]+?\}(?!})/;
/* '{{' '~'? 'space'* ('not space{}~'+) 'space'* ('not {}~'*) '~'? greedy '}}' and not follow by '}' */
ContextParserHandlebars.expressionRegExp = /^\{\{~?\s*([^\s\}\{~]+)\s*([^\}\{~]*)~??\}\}(?!})/;

@@ -155,4 +147,3 @@ /**

var firststr = "",
obj = {
var obj = {
filter: '',

@@ -163,20 +154,8 @@ isPrefixWithKnownFilter: false,

/*
* Substring the input string and match it
* Note: the expected format is "{.*}".
* it is isValidExpression and !isHandlebarsReservedExpression.
*/
var str = input.slice(i);
var j = str.indexOf('}');
str = str.slice(0, j+1);
var m = ContextParserHandlebars.expressionRegExp.exec(str);
if (m !== null) {
var isReservedChar;
if (m[1] !== undefined) {
firststr = m[1];
isReservedChar = handlebarsUtil.isReservedChar(m[1][0]);
/* special handling for {else} */
if (firststr === 'else') {
if (m[1] !== undefined && m[2] !== undefined) {
if (m[1] === 'else') {
obj.isSingleIdentifier = false;

@@ -186,15 +165,21 @@ obj.isPrefixWithKnownFilter = true;

}
if (isReservedChar) {
if (m[1] === '^' && m[2] === '') {
obj.isSingleIdentifier = false;
obj.isPrefixWithKnownFilter = true;
return obj;
}
}
if (m[2] === undefined) {
obj.isSingleIdentifier = true;
} else {
obj.filter = firststr;
var k = this._knownFilters.indexOf(obj.filter);
if (k !== -1) {
obj.isPrefixWithKnownFilter = true;
if (handlebarsUtil.isReservedChar(m[1], 0)) {
obj.isSingleIdentifier = false;
obj.isPrefixWithKnownFilter = false;
return obj;
}
if (m[2] === '') {
obj.isSingleIdentifier = true;
} else {
obj.filter = m[1];
var k = this._knownFilters.indexOf(obj.filter);
if (k !== -1) {
obj.isPrefixWithKnownFilter = true;
}
}
}

@@ -211,4 +196,3 @@ }

* @param {string} input - The input string of HTML5 web page.
* @param {integer} ptr - The index of the current character in the input string, it is pointing to the last brace of the open brace of the expression.
* @param {string} extraInfo - The extra information for filters judgement.
* @param {string} expressionInfo - The extra information for filters judgement.
* @returns {Array} The Array of the customized filters.

@@ -220,8 +204,6 @@ *

*/
ContextParserHandlebars.prototype._addFilters = function(state, input, ptr, extraInfo) {
ContextParserHandlebars.prototype._addFilters = function(state, input, expressionInfo) {
/* transitent var */
var e,
f,
msg;
var e, f, msg;

@@ -231,4 +213,4 @@ /* return filters */

var attributeName = extraInfo.attributeName,
attributeValue = extraInfo.attributeValue;
var attributeName = expressionInfo.attributeName,
attributeValue = expressionInfo.attributeValue;

@@ -417,10 +399,5 @@ debug("_addFilters:state:"+state+",attrname:"+attributeName+",attrvalue:"+attributeValue);

// validate the Handlebars template before analysis.
// validate the Handlebars template before/after analysis.
ContextParserHandlebars.prototype._validateTemplate = function(template) {
var msg;
if (!Handlebars.VERSION.match(/^2\./)) {
msg = "[ERROR] ContextParserHandlebars: We support Handlebars 2.0 ONLY!";
handlebarsUtil.handleError(msg, true);
}
try {

@@ -434,10 +411,159 @@ Handlebars.parse(template);

// TODO: the current assumption is partial keeps in/out state the same (DATA state)
ContextParserHandlebars.prototype._handlePartialTemplate = function() {
// consume the raw expression.
ContextParserHandlebars.prototype._handleRawExpression = function(input, i, len, state) {
var msg;
for(var j=i;j<len;++j) {
if (input[j] === '}' && j+2<len && input[j+1] === '}' && input[j+2] === '}') {
this.printChar('}}}');
/* advance the index pointer j to the char after the last brace of expression. */
j=j+3;
/* update the Context Parser's state if it is raw expression */
this.state = state;
/* for printCharWithState */
this.bytes[j] = input[j-1];
this.symbols[j] = this.lookupChar(input[j-1]);
this.states[j] = state;
return j;
}
this.printChar(input[j]);
}
msg = "[ERROR] ContextParserHandlebars: Parsing error! Cannot encounter '}}}' close brace of raw expression. ["+this._lineNo+":"+this._charNo+"]";
handlebarsUtil.handleError(msg, true);
};
// TODO: refactor the LOGIC #A into this function
ContextParserHandlebars.prototype._handleBranchTemplate = function() {
// consume the escape expression.
ContextParserHandlebars.prototype._handleEscapeExpression = function(input, i, len, state) {
var msg,
str = '{{';
/* parse expression */
var extraExpressionInfo = this._parseExpression(input, i),
isPrefixWithKnownFilter = extraExpressionInfo.isPrefixWithKnownFilter,
filters = [];
/* add filters if it is not prefix with known filter */
if (!isPrefixWithKnownFilter) {
/* we suppress the escapeExpression of handlebars by changing the {{expression}} into {{{expression}}} */
str += '{';
/* get the customized filter based on the current HTML5 state before the Handlebars template expression. */
var expressionInfo = {
'attributeName': this.getAttributeName(),
'attributeValue': this.getAttributeValue(),
};
filters = this._addFilters(state, input, expressionInfo);
for(var k=filters.length-1;k>=0;--k) {
if (extraExpressionInfo.isSingleIdentifier && k === 0) {
str += filters[k] + " ";
} else {
str += filters[k] + " (";
}
}
}
this.printChar(str);
for(var j=i+2;j<len;++j) {
if (input[j] === '}' && j+1 < len && input[j+1] === '}') {
for(var l=filters.length-1;l>=0;--l) {
if (extraExpressionInfo.isSingleIdentifier && l === 0) {
} else {
this.printChar(')');
}
}
/* advance the index pointer j to the char after the last brace of expression. */
this.printChar('}}');
j=j+2;
/* we suppress the escapeExpression of handlebars by changing the {{expression}} into {{{expression}}} */
if (!isPrefixWithKnownFilter) {
this.printChar('}');
}
/* update the Context Parser's state if it is not reserved tag */
this.state = state;
/* for printCharWithState */
this.bytes[j] = input[j-1];
this.symbols[j] = this.lookupChar(input[j-1]);
this.states[j] = state;
return j;
} else {
this.printChar(input[j]);
}
}
msg = "[ERROR] ContextParserHandlebars: Parsing error! Cannot encounter '}}' close brace of escape expression. ["+this._lineNo+":"+this._charNo+"]";
handlebarsUtil.handleError(msg, true);
};
// consume the comment expression.
ContextParserHandlebars.prototype._handleCommentExpression = function(input, i, len, type) {
var msg;
for(var j=i;j<len;++j) {
if (type === handlebarsUtil.COMMENT_EXPRESSION_LONG_FORM) {
if (input[j] === '-' && j+3<len && input[j+1] === '-' && input[j+2] === '}' && input[j+3] === '}') {
this.printChar('--}}');
/* advance the index pointer j to the char after the last brace of expression. */
j=j+4;
return j;
}
} else if (type === handlebarsUtil.COMMENT_EXPRESSION_SHORT_FORM) {
if (input[j] === '}' && j+1<len && input[j+1] === '}') {
this.printChar('}}');
/* advance the index pointer j to the char after the last brace of expression. */
j=j+2;
return j;
}
}
this.printChar(input[j]);
}
msg = "[ERROR] ContextParserHandlebars: Parsing error! Cannot encounter '}}' or '--}}' close brace of comment expression. ["+this._lineNo+":"+this._charNo+"]";
handlebarsUtil.handleError(msg, true);
};
// consume the expression with }} at the end
ContextParserHandlebars.prototype._handleExpression = function(input, i, len) {
var msg;
for(var j=i;j<len;++j) {
if (input[j] === '}' && j+1<len && input[j+1] === '}') {
this.printChar('}}');
/* advance the index pointer j to the char after the last brace of expression. */
j=j+2;
return j;
}
this.printChar(input[j]);
}
msg = "[ERROR] ContextParserHandlebars: Parsing error! Cannot encounter '}}' close brace of partial expression. ["+this._lineNo+":"+this._charNo+"]";
handlebarsUtil.handleError(msg, true);
};
// consume the branching expression.
ContextParserHandlebars.prototype._handleBranchExpression = function(input, i, state) {
var msg;
try {
var ast = handlebarsUtil.buildBranchAst(input, i);
var result = handlebarsUtil.analyseBranchAst(ast, state);
/* print the output */
this.printChar(result.output);
/* advance the index pointer i to the char after the last brace of branching expression. */
i = i+ast.index+1;
this.state = result.lastStates[0];
debug("_handleBranchTemplate: state:"+this.state+",i:"+i);
return i;
} catch (err) {
msg = err + " ["+this._lineNo+":"+this._charNo+"]";
handlebarsUtil.handleError(msg, true);
}
};
/**

@@ -467,252 +593,114 @@ * @function module:ContextParserHandlebars._handleTemplate

var index = i;
/* the length of the input */
var len = input.length;
/* regular expression validation result */
var re;
/* error msg */
var msg;
/* the length of the input */
var len = input.length;
/* Handlebars expression type */
var handlebarsExpressionType = handlebarsUtil.NOT_EXPRESSION;
/* Extra information of expression */
var extraExpressionInfo;
/* Handlebars reserved expression */
var isHandlebarsReservedExpression = false;
/* Handlebars template context */
var isHandlebarsContext = false;
/* Handlebars {{#if}} {{else}} {{#with}} {{#each}} {{#unless}} expression */
var isBranchExpressions = false;
/* regular expression validation result */
var re;
/* context filters */
var filters = [];
var noOfFilter = 0;
/* Encounter a known filter, we will not add any customized filters if it is known filter */
var isPrefixWithKnownFilter = false;
debug("_handleTemplate:len:"+len+",i:"+i);
/*
* ---- LOGIC #1 - is handlebars template? ----
* Determine the type of Handlebars expression
* Note: character comparison is the faster as compared with any type of string operation.
*/
if (ch === '{' && i+2 < len && input[i+1] === '{' && input[i+2] === '{') {
isHandlebarsContext = true;
/* handling different type of expression */
if (ch === '{' && i+3 < len && input[i+1] === '{' && input[i+2] === '{' && input[i+3] === '{') {
msg = "[ERROR] ContextParserHandlebars: Not yet support RAW BLOCK! ["+this._lineNo+":"+this._charNo+"]";
handlebarsUtil.handleError(msg, true);
} else if (ch === '{' && i+2 < len && input[i+1] === '{' && input[i+2] === '{') {
handlebarsExpressionType = handlebarsUtil.RAW_EXPRESSION;
re = handlebarsUtil.isValidExpression(input, i, handlebarsUtil.RAW_EXPRESSION);
re = handlebarsUtil.isValidExpression(input, i, handlebarsExpressionType);
if (re.result === false) {
msg = "[ERROR] ContextParserHandlebars: Parsing error! Invalid expression. ["+this._lineNo+":"+this._charNo+"]";
msg = "[ERROR] ContextParserHandlebars: Parsing error! Invalid raw expression. ["+this._lineNo+":"+this._charNo+"]";
handlebarsUtil.handleError(msg, true);
}
} else if (ch === '{' && i+1 < len && input[i+1] === '{') {
isHandlebarsContext = true;
handlebarsExpressionType = handlebarsUtil.ESCAPE_EXPRESSION;
re = handlebarsUtil.isValidExpression(input, i, handlebarsUtil.ESCAPE_EXPRESSION);
if (re.result === false) {
msg = "[ERROR] ContextParserHandlebars: Parsing error! Invalid expression. ["+this._lineNo+":"+this._charNo+"]";
handlebarsUtil.handleError(msg, true);
}
} else {
isHandlebarsContext = false;
handlebarsExpressionType = handlebarsUtil.NOT_EXPRESSION;
/* return immediately for non template start char '{' */
return index;
}
debug("_handleTemplate:LOGIC#1:handlebarsExpressionType:"+handlebarsExpressionType,",i:"+i);
/*
* ---- LOGIC #2 - OPEN BRACE ----
* Determine the type of Handlebars escape expression
*/
if (handlebarsExpressionType === handlebarsUtil.ESCAPE_EXPRESSION) {
isBranchExpressions = handlebarsUtil.isBranchExpressions(input, i);
if (i+2<len) {
isHandlebarsReservedExpression = handlebarsUtil.isReservedChar(input[i+2]);
}
if (!isBranchExpressions) {
/* Consume 2 '{' chars */
this.printChar(input[i]);
i=i+1; /** Point to next '{' */
this.printChar(input[i]);
}
} else if (handlebarsExpressionType === handlebarsUtil.RAW_EXPRESSION) {
/* consume 3 '{' chars */
this.printChar(input[i]);
i=i+1; /* point to next '{' */
this.printChar(input[i]);
i=i+1; /* point to last '{' */
this.printChar(input[i]);
}
debug("_handleTemplate:LOGIC#2:isBranchExpressions:"+isBranchExpressions+",isHandlebarsReservedExpression:"+isHandlebarsReservedExpression+",i:"+i);
/* for printCharWithState */
this.bytes[index+1] = ch;
this.symbols[index+1] = this.lookupChar(ch);
this.states[index+1] = state;
/*
* ---- LOGIC #3 - ADD FILTERS ----
*/
if (handlebarsExpressionType === handlebarsUtil.ESCAPE_EXPRESSION && !isHandlebarsReservedExpression) {
/*
* Check whether there is a known filter being added,
* if yes, then we will not add any customized filters.
*/
extraExpressionInfo = this._parseExpression(input, i);
isPrefixWithKnownFilter = extraExpressionInfo.isPrefixWithKnownFilter;
if (!isPrefixWithKnownFilter) {
/* We suppress the escapeExpression of handlebars by changing the {{expression}} into {{{expression}}} */
this.printChar('{');
/* Get the customized filter based on the current HTML5 state before the Handlebars template expression. */
var extraInfo = {
'attributeName': this.getAttributeName(),
'attributeValue': this.getAttributeValue(),
};
filters = this._addFilters(state, input, i, extraInfo);
for(noOfFilter=filters.length-1;noOfFilter>=0;--noOfFilter) {
if (this._enableSubexpression) {
if (extraExpressionInfo.isSingleIdentifier && noOfFilter === 0) {
this.printChar(filters[noOfFilter] + " ");
} else {
this.printChar(filters[noOfFilter] + " (");
}
} else {
this.printChar(filters[noOfFilter] + " ");
/* _handleRawExpression */
debug("_handleTemplate:LOGIC#1:handlebarsExpressionType:"+handlebarsExpressionType,",i:"+i);
return this._handleRawExpression(input, i, len, state);
} else if (ch === '{' && i+1 < len && input[i+1] === '{') {
// this type may not be 100% correct (the case is BRANCH_EXPRESSION), so it need the isValidExpression call below.
handlebarsExpressionType = handlebarsUtil.getExpressionType(input, i, len);
switch (handlebarsExpressionType) {
case handlebarsUtil.ESCAPE_EXPRESSION:
re = handlebarsUtil.isValidExpression(input, i, handlebarsExpressionType);
if (re.result === false) {
msg = "[ERROR] ContextParserHandlebars: Parsing error! Invalid escape expression. ["+this._lineNo+":"+this._charNo+"]";
handlebarsUtil.handleError(msg, true);
}
}
}
}
debug("_handleTemplate:LOGIC#3:extraExpressionInfo:"+extraExpressionInfo+",filters:"+filters);
/*
* ---- LOGIC #A - BRANCHING STATEMENT HANDLING ----
*/
if (handlebarsExpressionType === handlebarsUtil.ESCAPE_EXPRESSION && isBranchExpressions && isHandlebarsReservedExpression) {
try {
/* Extract the branching statement, and subpress non-branching expression. */
var objMaskedStmt = handlebarsUtil.extractBranchStmt(input, i, true);
/* for printCharWithState */
this.bytes[index+1] = ch;
this.symbols[index+1] = this.lookupChar(ch);
this.states[index+1] = state;
/* Parse the branching statement. */
var ast = handlebarsUtil.parseBranchStmt(objMaskedStmt.stmt);
/* _handleEscapeExpression */
debug("_handleTemplate:LOGIC#1:handlebarsExpressionType:"+handlebarsExpressionType,",i:"+i);
return this._handleEscapeExpression(input, i, len, state);
/* Restore the open/close_brace_nonce with {} for analysis */
objMaskedStmt.stmt = objMaskedStmt.stmt.replace(new RegExp(objMaskedStmt.openBracePlaceHolder, 'g'), '{');
objMaskedStmt.stmt = objMaskedStmt.stmt.replace(new RegExp(objMaskedStmt.closeBracePlaceHolder, 'g'), '}');
var result = handlebarsUtil.parseAstTreeState(ast, state, objMaskedStmt);
/* echo to output */
this.printChar(result.stmt);
/* Advance the index pointer i to the char after the last brace of branching expression. */
var objUnmaskedStmt = handlebarsUtil.extractBranchStmt(input, i, false);
i=i+objUnmaskedStmt.stmt.length;
this.state = result.lastStates[0];
debug("_handleTemplate:LOGIC#A:state:"+this.state+",i:"+i);
return i;
} catch (err) {
msg = err + " ["+this._lineNo+":"+this._charNo+"]";
handlebarsUtil.handleError(msg, true);
}
}
/*
* ---- LOGIC #4 - CLOSE BRACE ----
* After the customized filter is added, we simply consume the character till we meet the close braces of Handlebars expression
*/
for(var j=i+1;j<len;++j) {
i=j;
if (handlebarsExpressionType === handlebarsUtil.ESCAPE_EXPRESSION) {
/* Encounter the end of Handlebars expression close brace */
if (input[j] === '}' && j+1 < len && input[j+1] === '}') {
/* close the filters subexpression */
if (this._enableSubexpression) {
for(noOfFilter=filters.length-1;noOfFilter>=0;--noOfFilter) {
if (extraExpressionInfo.isSingleIdentifier && noOfFilter === 0) {
} else {
this.printChar(")");
}
}
case handlebarsUtil.PARTIAL_EXPRESSION:
re = handlebarsUtil.isValidExpression(input, i, handlebarsExpressionType);
if (re.result === false) {
msg = "[ERROR] ContextParserHandlebars: Parsing error! Invalid partial expression. ["+this._lineNo+":"+this._charNo+"]";
handlebarsUtil.handleError(msg, true);
}
/* _handleExpression */
debug("_handleTemplate:LOGIC#1:handlebarsExpressionType:"+handlebarsExpressionType,",i:"+i);
return this._handleExpression(input, i, len);
/* Print the first '}' */
this.printChar(input[j]);
j++;
i=j;
/* Print the second '}' */
this.printChar(input[j]);
/* we suppress the escapeExpression of handlebars by changing the {{expression}} into {{{expression}}} */
if (!isHandlebarsReservedExpression && !isPrefixWithKnownFilter) {
this.printChar('}');
case handlebarsUtil.DATA_VAR_EXPRESSION:
re = handlebarsUtil.isValidExpression(input, i, handlebarsExpressionType);
if (re.result === false) {
msg = "[ERROR] ContextParserHandlebars: Parsing error! Invalid data var expression. ["+this._lineNo+":"+this._charNo+"]";
handlebarsUtil.handleError(msg, true);
}
/* _handleExpression */
debug("_handleTemplate:LOGIC#1:handlebarsExpressionType:"+handlebarsExpressionType,",i:"+i);
return this._handleExpression(input, i, len);
isHandlebarsContext = false;
handlebarsExpressionType = handlebarsUtil.NOT_EXPRESSION;
i=i+1; /* Point to the char right after the last '}' */
/* update the Context Parser's state if it is not reserved tag */
if (!isHandlebarsReservedExpression) {
this.state = state;
/* just for debugging */
this.bytes[index+1] = ch;
this.symbols[index+1] = this.lookupChar(ch);
this.states[index+1] = state;
this.bytes[i] = input[i-1];
this.symbols[i] = this.lookupChar(input[i-1]);
this.states[i] = state;
case handlebarsUtil.BRANCH_EXPRESSION:
re = handlebarsUtil.isValidExpression(input, i, handlebarsExpressionType);
if (re.result === false) {
msg = "[ERROR] ContextParserHandlebars: Parsing error! Invalid branch expression. ["+this._lineNo+":"+this._charNo+"]";
handlebarsUtil.handleError(msg, true);
}
/* _handleBranchExpression */
debug("_handleTemplate:LOGIC#1:handlebarsExpressionType:"+handlebarsExpressionType,",i:"+i);
return this._handleBranchExpression(input, i, state);
case handlebarsUtil.BRANCH_END_EXPRESSION:
msg = "[ERROR] ContextParserHandlebars: Parsing error! Unexpected {{/.*}} expression. ["+this._lineNo+":"+this._charNo+"]";
handlebarsUtil.handleError(msg, true);
break;
} else {
this.printChar(input[j]);
}
} else if (handlebarsExpressionType === handlebarsUtil.RAW_EXPRESSION) {
/* Encounter the end of Handlebars expression close brace */
if (input[j] === '}' && j+2 < len && input[j+1] === '}' && input[j+2] === '}') {
/* Print the first '}' */
this.printChar(input[j]);
j++;
i=j;
/* Print the second '}' */
this.printChar(input[j]);
j++;
i=j;
/* Print the third '}' */
this.printChar(input[j]);
isHandlebarsContext = false;
handlebarsExpressionType = handlebarsUtil.NOT_EXPRESSION;
i=i+1; /* Point to the char right after the last '}' */
/* update the Context Parser's state if it is not reserved tag */
this.state = state;
case handlebarsUtil.COMMENT_EXPRESSION_LONG_FORM:
// no need to validate the comment expression as the content inside are skipped.
/* _handleCommentExpression */
debug("_handleTemplate:LOGIC#1:handlebarsExpressionType:"+handlebarsExpressionType,",i:"+i);
return this._handleCommentExpression(input, i, len, handlebarsExpressionType);
/* just for debugging */
this.bytes[index+1] = ch;
this.symbols[index+1] = this.lookupChar(ch);
this.states[index+1] = state;
this.bytes[i] = input[i-1];
this.symbols[i] = this.lookupChar(input[i-1]);
this.states[i] = state;
case handlebarsUtil.COMMENT_EXPRESSION_SHORT_FORM:
// no need to validate the comment expression as the content inside are skipped.
/* _handleCommentExpression */
debug("_handleTemplate:LOGIC#1:handlebarsExpressionType:"+handlebarsExpressionType,",i:"+i);
return this._handleCommentExpression(input, i, len, handlebarsExpressionType);
case handlebarsUtil.ELSE_EXPRESSION:
msg = "[ERROR] ContextParserHandlebars: Parsing error! Unexpected {{else}} or {{^}} expression. ["+this._lineNo+":"+this._charNo+"]";
handlebarsUtil.handleError(msg, true);
break;
} else {
this.printChar(input[j]);
}
default:
msg = "[ERROR] ContextParserHandlebars: Parsing error! Unknown expression. ["+this._lineNo+":"+this._charNo+"]";
handlebarsUtil.handleError(msg, true);
break;
}
} else {
/* return immediately for non template start char '{' */
return index;
}
debug("_handleTemplate:LOGIC#4:i:"+i);
/*
* ---- LOGIC #5 - BROKEN TEMPLATE ----
* If we meet the EOF of the input string, while the Context Parser is still in the Handlebars context,
* it indicates that the input Handlebars template file is an incomplete file.
*/
if (isHandlebarsContext) {
msg = "[ERROR] ContextParserHandlebars: Parsing error! Cannot encounter '}}' or '}}}' close brace. ["+this._lineNo+":"+this._charNo+"]";
handlebarsUtil.handleError(msg, true);
}
return i;
};

@@ -719,0 +707,0 @@

@@ -25,42 +25,89 @@ /*

/* type of expression */
HandlebarsUtils.NOT_EXPRESSION = 0;
HandlebarsUtils.RAW_EXPRESSION = 1; // {{{expression}}}
HandlebarsUtils.ESCAPE_EXPRESSION = 2; // {{expression}}
HandlebarsUtils.PARTIAL_EXPRESSION = 3; // {{>.*}}
HandlebarsUtils.DATA_VAR_EXPRESSION = 4; // {{@.*}}
HandlebarsUtils.BRANCH_EXPRESSION = 5; // {{#.*}}, {{^.*}}
HandlebarsUtils.BRANCH_END_EXPRESSION = 6; // {{/.*}}
HandlebarsUtils.ELSE_EXPRESSION = 7; // {{else}}, {{^}}
HandlebarsUtils.COMMENT_EXPRESSION_LONG_FORM = 8; // {{!--.*--}}
HandlebarsUtils.COMMENT_EXPRESSION_SHORT_FORM = 9; // {{!.*}}
HandlebarsUtils.RAW_BLOCK = 10; // {{{{block}}}}
/* reference: http://handlebarsjs.com/expressions.html */
/* '{{{{' '~'? 'not {}~'+ '~'? non-greedy '}}}}' and not follow by '}' */
HandlebarsUtils.rawBlockRegExp = /^\{\{\{\{~?([^\}\{~]+)~??\}\}\}\}(?!})/;
/* '{{{' '~'? 'not {}~'+ '~'? non-greedy '}}}' and not follow by '}' */
HandlebarsUtils.rawExpressionRegExp = /^\{\{\{~?([^\}\{~]+)~??\}\}\}(?!})/;
/* '{{' '~'? 'space'* ('not {}~'+) '~'? non-greedy '}}' and not follow by '}' */
HandlebarsUtils.escapeExpressionRegExp = /^\{\{~?\s*([^\}\{~]+)~??\}\}(?!})/;
/* '{{' '~'? '>' 'space'* ('not {}~'+) 'space'* '~'? non-greedy '}}' and not follow by '}' */
HandlebarsUtils.partialExpressionRegExp = /^\{\{~?>\s*([^\}\{~]+)\s*~??\}\}(?!})/;
/* '{{' '~'? '@' 'space'* ('not {}~'+) 'space'* '~'? non-greedy '}}' and not follow by '}' */
HandlebarsUtils.dataVarExpressionRegExp = /^\{\{~?@\s*([^\}\{~]+)\s*~??\}\}(?!})/;
// need to capture the first non-whitespace string and capture the rest
/* '{{' '~'? '# or ^' 'space'* ('not \s{}~'+) 'space'* ('not {}~')* '~'? non-greedy '}}' and not follow by '}' */
HandlebarsUtils.branchExpressionRegExp = /^\{\{~?[#|\^]\s*([^\s\}\{~]+)\s*([^\}\{~]*)~??\}\}(?!})/;
/* '{{' '~'? '/' 'space'* ('not \s{}~'+) 'space'* ('not {}~')* '~'? non-greedy '}}' and not follow by '}' */
HandlebarsUtils.branchEndExpressionRegExp = /^\{\{~?\/\s*([^\s\}\{~]+)\s*([^\}\{~]*)~??\}\}(?!})/;
/* '{{' '~'? 'space'* 'else' 'space'* '~'? non-greedy '}}' and not follow by '}' */
HandlebarsUtils.elseExpressionRegExp = /^\{\{~?\s*else\s*~??\}\}(?!})/;
/* '{{' '~'? 'space'* '^'{1} 'space'* '~'? non-greedy '}}' and not follow by '}' */
HandlebarsUtils.elseShortFormExpressionRegExp = /^\{\{~?\s*\^{1}\s*~??\}\}(?!})/;
/**
* @function HandlebarsUtils.generateNonce
* @function HandlebarsUtils.getExpressionType
*
* @static
*
* @returns {string} A random string in the length of 5 (10000 combinations).
*
* @param {string} input - The input string of the HTML5 web page.
* @param {integer} i - The current index of the input string.
* @param {integer} len - The max len of the input.
* @returns {integer} The expression type.
* *
* @description
* <p>This function generates a random string for masking double open/close brace to simplify AST construction.</p>
* <p>this method is to judge the type of expression</p>
*
*/
HandlebarsUtils.generateNonce = function() {
var nonce = "";
/* this char set must be non-template reserved char set */
var str = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";
var l = str.length;
for(var i=0;i<5;i++) {
nonce += str.charAt(Math.floor(Math.random()*l));
HandlebarsUtils.getExpressionType = function(input, i, len) {
// TODO: can optimize
if ((input[i] === '{' && i+2<len && input[i+1] === '{' && input[i+2] === '>') ||
(input[i] === '{' && i+3<len && input[i+1] === '{' && input[i+2] === '~' && input[i+3] === '>')
) {
return HandlebarsUtils.PARTIAL_EXPRESSION;
} else if ((input[i] === '{' && i+2<len && input[i+1] === '{' && input[i+2] === '@') ||
(input[i] === '{' && i+3<len && input[i+1] === '{' && input[i+2] === '~' && input[i+3] === '@')
) {
return HandlebarsUtils.DATA_VAR_EXPRESSION;
} else if ((input[i] === '{' && i+2<len && input[i+1] === '{' && input[i+2] === '#') ||
(input[i] === '{' && i+3<len && input[i+1] === '{' && input[i+2] === '~' && input[i+3] === '#')
) {
return HandlebarsUtils.BRANCH_EXPRESSION;
} else if ((input[i] === '{' && i+2<len && input[i+1] === '{' && input[i+2] === '^') ||
(input[i] === '{' && i+3<len && input[i+1] === '{' && input[i+2] === '~' && input[i+3] === '^')
) {
// this one is not exact, {{~?^}} will pass!
return HandlebarsUtils.BRANCH_EXPRESSION;
} else if ((input[i] === '{' && i+2<len && input[i+1] === '{' && input[i+2] === '/') ||
(input[i] === '{' && i+3<len && input[i+1] === '{' && input[i+2] === '~' && input[i+3] === '/')
) {
return HandlebarsUtils.BRANCH_END_EXPRESSION;
} else if ((input[i] === '{' && i+4<len && input[i+1] === '{' && input[i+2] === '!' && input[i+3] === '-' && input[i+4] === '-') ||
(input[i] === '{' && i+4<len && input[i+1] === '{' && input[i+2] === '~' && input[i+3] === '!' && input[i+4] === '-' && input[i+5] === '-')
) {
return HandlebarsUtils.COMMENT_EXPRESSION_LONG_FORM;
} else if ((input[i] === '{' && i+2<len && input[i+1] === '{' && input[i+2] === '!') ||
(input[i] === '{' && i+3<len && input[i+1] === '{' && input[i+2] === '~' && input[i+3] === '!')
) {
return HandlebarsUtils.COMMENT_EXPRESSION_SHORT_FORM;
}
return nonce;
return HandlebarsUtils.ESCAPE_EXPRESSION;
};
/* type of expression */
HandlebarsUtils.NOT_EXPRESSION = 0;
HandlebarsUtils.ESCAPE_EXPRESSION = 1;
HandlebarsUtils.RAW_EXPRESSION = 2;
/* RegExp to match expression */
/* '{{' 'non-{,non-}'+ '}}' and not follow by '}' */
HandlebarsUtils.escapeExpressionRegExp = /^\{\{[^\}\{]+?\}\}(?!})/;
/* '{{{' 'non-{,non-}'+ '}}}' and not follow by '}' */
HandlebarsUtils.rawExpressionRegExp = /^\{\{\{[^\}\{]+?\}\}\}(?!})/;
/* '{{' '# or ^' 'space'* 'non-space,non-},non-{'+ first-'space or }' */
HandlebarsUtils.branchExpressionRegExp = /^\{\{[#|\\^]\s*([^\s\}\{]+)?[\s\}]/;
/* '{{' '/' 'space'* 'non-space,non-},non-{'+ first-'space or }' */
HandlebarsUtils.branchEndExpressionRegExp = /^\{\{\/\s*([^\s\}\{]+)?[\s\}]/;
/* '{{' 'space'* 'else' 'space'* '}}' */
HandlebarsUtils.elseExpressionRegExp = /^\{\{\s*else\s*?\}\}/;
/**

@@ -78,9 +125,37 @@ * @function HandlebarsUtils.isValidExpression

re.result = false;
var s = input.slice(i);
switch(type) {
case HandlebarsUtils.ESCAPE_EXPRESSION:
re = HandlebarsUtils.escapeExpressionRegExp.exec(input.slice(i));
case HandlebarsUtils.RAW_BLOCK:
re = HandlebarsUtils.rawBlock.exec(s);
break;
case HandlebarsUtils.RAW_EXPRESSION:
re = HandlebarsUtils.rawExpressionRegExp.exec(input.slice(i));
re = HandlebarsUtils.rawExpressionRegExp.exec(s);
break;
case HandlebarsUtils.ESCAPE_EXPRESSION:
re = HandlebarsUtils.escapeExpressionRegExp.exec(s);
if (re !== null && re[1] !== undefined) {
if (HandlebarsUtils.isReservedChar(re[1], 0)) {
re.result = false;
return re;
}
}
break;
case HandlebarsUtils.PARTIAL_EXPRESSION:
re = HandlebarsUtils.partialExpressionRegExp.exec(s);
break;
case HandlebarsUtils.DATA_VAR_EXPRESSION:
re = HandlebarsUtils.dataVarExpressionRegExp.exec(s);
break;
case HandlebarsUtils.BRANCH_EXPRESSION:
re = HandlebarsUtils.branchExpressionRegExp.exec(s);
break;
case HandlebarsUtils.BRANCH_END_EXPRESSION:
re = HandlebarsUtils.branchEndExpressionRegExp.exec(s);
break;
case HandlebarsUtils.ELSE_EXPRESSION:
re = HandlebarsUtils.elseExpressionRegExp.exec(s);
if (re === null) {
re = HandlebarsUtils.elseShortFormExpressionRegExp.exec(s);
}
break;
default:

@@ -114,3 +189,8 @@ return re;

*/
HandlebarsUtils.isReservedChar = function(ch) {
HandlebarsUtils.isReservedChar = function(input, i) {
var ch = input[i];
if (ch === '~' && input.length > i+1) {
ch = input[i+1];
}
if (ch === '#' || ch === '/' || ch === '>' || ch === '@' || ch === '^' || ch === '!') {

@@ -177,8 +257,12 @@ return true;

HandlebarsUtils.isElseExpression = function(input, i) {
var re = HandlebarsUtils.elseExpressionRegExp.exec(input.slice(i));
if (re === null) {
return false;
} else {
var s = input.slice(i);
var re = HandlebarsUtils.elseExpressionRegExp.exec(s);
if (re !== null) {
return true;
}
re = HandlebarsUtils.elseShortFormExpressionRegExp.exec(s);
if (re !== null) {
return true;
}
return false;
};

@@ -230,192 +314,124 @@

/**
* @function HandlebarsUtils.parseBranchStmt
*
* @static
/*
* @function HandlebarsUtils._analyzeContext
*/
HandlebarsUtils._analyzeContext = function(state, str) {
var r = {
lastState: '',
output: ''
};
// TODO: refactor
/* factory class */
var parser,
ContextParserHandlebars = require('./context-parser-handlebars');
/* parse the string */
debugBranch("_analyzeContext:"+str);
parser = new ContextParserHandlebars(false);
parser.setInitState(state);
parser.contextualize(str);
r.lastState = parser.getLastState();
r.output = parser.getBuffer().join('');
debugBranch("_analyzeContext:"+r.output);
return r;
};
/*
* @function HandlebarsUtils.blacklistProtocol
*
* @param {string} s - The string in the format of balanced branching statement like {{#if}}, {{#each}}, {{#list}}, {{#unless}}, {{#with}}, {{#anything}} and {{^anything}}.
* @returns {array} An array of statement in the AST tree.
*
* @description
* <p>This function uses the native Handlebars parser to parse branching statement to generate a AST.</p>
*
* Reference:
* https://github.com/yahoo/xss-filters/blob/master/src/private-xss-filters.js#L266
*/
HandlebarsUtils.parseBranchStmt = function(s) {
if (!Handlebars.VERSION.match(/^2\./)) {
var msg = "[ERROR] ContextParserHandlebars: We support Handlebars 2.0 ONLY!";
HandlebarsUtils.handleError(msg, true);
}
// TODO: we should build our own data structure instead of using Handlebars directly.
var ast = Handlebars.parse(s);
if (ast.statements !== undefined) {
return ast.statements;
HandlebarsUtils._URI_BLACKLIST = null;
HandlebarsUtils._URI_BLACKLIST_REGEXPSTR = "^(?:&#[xX]0*(?:1?[1-9a-fA-F]|10|20);?|&#0*(?:[1-9]|[1-2][0-9]|30|31|32);?|&Tab;|&NewLine;)*(?:(?:j|J|&#[xX]0*(?:6|4)[aA];?|&#0*(?:106|74);?)(?:&#[xX]0*[9aAdD];?|&#0*(?:9|10|13);?|&Tab;|&NewLine;)*(?:a|A|&#[xX]0*(?:6|4)1;?|&#0*(?:97|65);?)(?:&#[xX]0*[9aAdD];?|&#0*(?:9|10|13);?|&Tab;|&NewLine;)*(?:v|V|&#[xX]0*(?:7|5)6;?|&#0*(?:118|86);?)(?:&#[xX]0*[9aAdD];?|&#0*(?:9|10|13);?|&Tab;|&NewLine;)*(?:a|A|&#[xX]0*(?:6|4)1;?|&#0*(?:97|65);?)|(?:v|V|&#[xX]0*(?:7|5)6;?|&#0*(?:118|86);?)(?:&#[xX]0*[9aAdD];?|&#0*(?:9|10|13);?|&Tab;|&NewLine;)*(?:b|B|&#[xX]0*(?:6|4)2;?|&#0*(?:98|66);?))(?:&#[xX]0*[9aAdD];?|&#0*(?:9|10|13);?|&Tab;|&NewLine;)*(?:s|S|&#[xX]0*(?:7|5)3;?|&#0*(?:115|83);?)(?:&#[xX]0*[9aAdD];?|&#0*(?:9|10|13);?|&Tab;|&NewLine;)*(?:c|C|&#[xX]0*(?:6|4)3;?|&#0*(?:99|67);?)(?:&#[xX]0*[9aAdD];?|&#0*(?:9|10|13);?|&Tab;|&NewLine;)*(?:r|R|&#[xX]0*(?:7|5)2;?|&#0*(?:114|82);?)(?:&#[xX]0*[9aAdD];?|&#0*(?:9|10|13);?|&Tab;|&NewLine;)*(?:i|I|&#[xX]0*(?:6|4)9;?|&#0*(?:105|73);?)(?:&#[xX]0*[9aAdD];?|&#0*(?:9|10|13);?|&Tab;|&NewLine;)*(?:p|P|&#[xX]0*(?:7|5)0;?|&#0*(?:112|80);?)(?:&#[xX]0*[9aAdD];?|&#0*(?:9|10|13);?|&Tab;|&NewLine;)*(?:t|T|&#[xX]0*(?:7|5)4;?|&#0*(?:116|84);?)(?:&#[xX]0*[9aAdD];?|&#0*(?:9|10|13);?|&Tab;|&NewLine;)*(?::|&#[xX]0*3[aA];?|&#0*58;?)";
HandlebarsUtils.blacklistProtocol = function(s) {
var URI_FASTLANE = ['&', 'j', 'J', 'v', 'V'];
if (URI_FASTLANE.indexOf(s[0]) === -1) {
return false;
} else {
return [];
if (HandlebarsUtils._URI_BLACKLIST === null) {
HandlebarsUtils._URI_BLACKLIST = new RegExp(HandlebarsUtils._URI_BLACKLIST_REGEXPSTR);
}
if (HandlebarsUtils._URI_BLACKLIST.test(s)) {
return true;
} else {
return false;
}
}
return true;
};
/**
* @function HandlebarsUtils.extractBranchStmt
*
* @static
*
* @param {string} input - The template input string.
* @param {int} k - The pointer to the first char of the first brace expression of Handlebars template.
* @param {boolean} masked - The flag to mask the non branching statement expression.
* @returns {Object} The object with branching statement and place holder.
*
* @description
* <p>This function extracts a branching statement with balanced expression.</p>
*
* @function HandlebarsUtils.analyseBranchAst
*/
HandlebarsUtils.extractBranchStmt = function(input, k, masked) {
HandlebarsUtils.analyseBranchAst = function(ast, state) {
var obj = {},
len = ast.program.length;
var stmt = '',
msg = '',
sp = [],
r = {},
j = 0;
var r = {},
s = [],
t, msg,
newLastState;
r.lastStates = [];
r.lastStates[0] = state;
r.lastStates[1] = state;
r.output = '';
/* init */
var str = input.slice(k);
var l = str.length;
/*
* the reason for extracting the branching statement is to
* be used for building the AST for finding all the combination of strings,
* however the non-branching expression will make the AST more
* complicated, so this function masks out all open/close brace
* with a random nonce.
*/
r.filterPlaceHolder = [];
r.openBracePlaceHolder = HandlebarsUtils.generateNonce();
r.closeBracePlaceHolder = HandlebarsUtils.generateNonce();
for(var i=0;i<l;++i) {
var tag = HandlebarsUtils.isBranchExpression(str, i),
endExpression = HandlebarsUtils.isBranchEndExpression(str, i);
/* push the branching tokens */
if (tag !== false) {
sp.push(tag);
/* do nothing for 'else' token */
} else if (HandlebarsUtils.isElseExpression(str, i)) {
/* pop the branching tokens (TODO: not fast enough) */
} else if (endExpression !== false) {
if (sp.length > 0) {
var lastExpression = sp[sp.length-1];
/* check for balanced branching statement */
if (lastExpression === endExpression) {
sp.pop();
/* consume till the end of '}}' */
for(j=i;j<l;++j) {
if (str[j] === '}' && j+1<l && str[j+1] === '}') {
stmt += str[j];
i=j+1;
break;
} else {
stmt += str[j];
}
}
} else {
/* broken template as the end expression does not match, throw exception before function returns */
msg = "[ERROR] ContextParserHandlebars: Template expression mismatch (startExpression:"+lastExpression+"/endExpression:"+endExpression+")";
HandlebarsUtils.handleError(msg, true);
}
} else {
/* broken template, throw exception before function returns */
msg = "[ERROR] ContextParserHandlebars: Cannot find the corresponding start expression (tag:"+endExpression+")";
HandlebarsUtils.handleError(msg, true);
}
/* non-branching expression */
} else {
/* {{{expression}}} case */
if (str[i] === '{' && i+2<l && str[i+1] === '{' && str[i+2] === '{' && masked) {
/* masked the '{{{' */
stmt += r.openBracePlaceHolder;
stmt += r.openBracePlaceHolder;
stmt += r.openBracePlaceHolder;
/* loop till the end of '}}}' */
for(j=i+3;j<l;++j) {
if (str[j] === '}' && i+3<l && str[j+1] === '}' && str[j+2] === '}') {
/* append 3 chars, }}} */
stmt += r.closeBracePlaceHolder;
stmt += r.closeBracePlaceHolder;
stmt += r.closeBracePlaceHolder;
/* advance the pointer by 2, the for loop will increase by one more for next char */
i=j+2;
break;
}
stmt += str[j];
}
continue;
/* {{[!@/>]expression}} */
} else if (str[i] === '{' && i+2<l && str[i+1] === '{' && masked && HandlebarsUtils.isReservedChar(str[i+2])) {
/* masked the '{{' */
stmt += r.openBracePlaceHolder;
stmt += r.openBracePlaceHolder;
/* loop till the end of '}}' */
for(j=i+2;j<l;++j) {
if (str[j] === '}' && i+2<l && str[j+1] === '}') {
/* append 2 chars, }} */
stmt += r.closeBracePlaceHolder;
stmt += r.closeBracePlaceHolder;
/* advance the pointer by 1, the for loop will increase by one more for next char */
i=j+1;
break;
}
stmt += str[j];
}
continue;
/* {{expression}} */
} else if (str[i] === '{' && i+2<l && str[i+1] === '{' && masked && !HandlebarsUtils.isReservedChar(str[i+2])) {
/* masked the '{{' */
stmt += r.openBracePlaceHolder;
stmt += r.openBracePlaceHolder;
/* add the filter place holder */
var filterPlaceHolder = HandlebarsUtils.generateNonce();
stmt += filterPlaceHolder;
r.filterPlaceHolder.push(filterPlaceHolder);
/* loop till the end of '}}' */
for(j=i+2;j<l;++j) {
if (str[j] === '}' && i+2<l && str[j+1] === '}') {
/* append 2 chars, }} */
stmt += r.closeBracePlaceHolder;
stmt += r.closeBracePlaceHolder;
/* advance the pointer by 1, the for loop will increase by one more for next char */
i=j+1;
break;
}
stmt += str[j];
}
continue;
}
for(var i=0;i<len;++i) {
obj = ast.program[i];
if (obj.type === 'content') {
t = HandlebarsUtils._analyzeContext(r.lastStates[0], obj.content);
newLastState = t.lastState;
r.output += t.output;
debugBranch("analyseBranchAst:program:content,["+r.lastStates[0]+"/"+newLastState+"],["+obj.content+"],["+r.output+"]");
r.lastStates[0] = newLastState;
} else if (obj.type === 'node') {
t = HandlebarsUtils.analyseBranchAst(obj.content, r.lastStates[0]);
newLastState = t.lastStates[0]; // index 0 and 1 MUST be equal
debugBranch("analyseBranchAst:program:node,["+r.lastStates[0]+"/"+newLastState+"]");
r.lastStates[0] = newLastState;
r.output += t.output;
} else if (obj.type === 'branch' ||
obj.type === 'branchelse' ||
obj.type === 'branchend') {
r.output += obj.content;
}
stmt += str[i];
/* The stack is empty, we can return */
if (sp.length === 0) {
break;
}
len = ast.inverse.length;
for(i=0;i<len;++i) {
obj = ast.inverse[i];
if (obj.type === 'content') {
t = HandlebarsUtils._analyzeContext(r.lastStates[1], obj.content);
newLastState = t.lastState;
r.output += t.output;
debugBranch("analyseBranchAst:inverse:content,["+r.lastStates[1]+"/"+newLastState+"],["+obj.content+"],["+r.output+"]");
r.lastStates[1] = newLastState;
} else if (obj.type === 'node') {
t = HandlebarsUtils.analyseBranchAst(obj.content, r.lastStates[1]);
newLastState = t.lastStates[1]; // index 0 and 1 MUST be equal
debugBranch("analyseBranchAst:inverse:node,["+r.lastStates[1]+"/"+newLastState+"]");
r.lastStates[1] = newLastState;
r.output += t.output;
} else if (obj.type === 'branch' ||
obj.type === 'branchelse' ||
obj.type === 'branchend') {
r.output += obj.content;
}
}
/* if all chars are consumed while the sp is not empty, the template is broken */
if (sp.length > 0) {
/* throw error on the template */
msg = "[ERROR] ContextParserHandlebars: Template does not have balanced branching expression.";
if (ast.program.length > 0 && ast.inverse.length === 0) {
debugBranch("panalyseBranchAst:["+r.lastStates[0]+"/"+r.lastStates[0]+"]");
r.lastStates[1] = r.lastStates[0];
} else if (ast.program.length === 0 && ast.inverse.length > 0) {
debugBranch("analyseBranchAst:["+r.lastStates[1]+"/"+r.lastStates[1]+"]");
r.lastStates[0] = r.lastStates[1];
}
if (r.lastStates[0] !== r.lastStates[1]) {
msg = "[ERROR] ContextParserHandlebars: Parsing error! Inconsitent HTML5 state after conditional branches. Please fix your template!";
HandlebarsUtils.handleError(msg, true);
}
r.stmt = stmt;
debugBranch("extractBranchStmt:"+stmt);
return r;

@@ -425,290 +441,195 @@ };

/**
* @function HandlebarsUtils.parseAstTreeState
*
* @static
*
* @description
* <p>This function transverses the AST tree of the brnaching statement of Handlebars,
* and parse and relace filter in the string on the fly.</p>
*
* @param {array} o - The AST of the branching statement.
* @param {int} state - The init state of the string.
* @param {object} obj - The object contains the open/close brace place holder, filter place holder and branching statement.
* @returns {array} The last states of the 1st and 2nd branches.
*
* @function HandlebarsUtils.buildBranchAst
*/
HandlebarsUtils.parseAstTreeState = function(o, state, obj) {
HandlebarsUtils.buildBranchAst = function(input, i) {
/*
* the expected data structure of branching statement from the AST tree
*
* ________________ branching _____________
* | |
* 1st branch (if etc.) 2nd branch (else)
* ----------------------------------- ----------------------------------
* | context | sub-branch | context | | context | sub-branch | context |
* 1st node 2nd node 3rd node 1st node 2nd node 3rd node
*
*/
/* init the data structure */
var ast = {};
ast.program = [];
ast.inverse = [];
/* being used for which node is being handled next */
var nodeFlag = [];
/* true for first node, false for third node (1st branch) */
nodeFlag[0] = true;
/* true for first node, false for third node (2nd branch) */
nodeFlag[1] = true;
var str = input.slice(i),
len = str.length;
/* indicating which branches have been handled before */
var branchesFlag = [];
/* true for 1st branch */
branchesFlag[0] = false;
/* true for 2nd branch */
branchesFlag[1] = false;
var sp = [],
msg,
content = '',
inverse = false,
obj = {},
k,j,r = 0;
/* last state */
var newLastState;
for(j=0;j<len;++j) {
var exp = HandlebarsUtils.isBranchExpression(str, j),
endExpression = HandlebarsUtils.isBranchEndExpression(str, j);
if (exp !== false) {
/* encounter the first branch expression */
if (sp.length === 0) {
/* save the branch expression name */
sp.push(exp);
/* return object */
var r = {};
r.lastStates = [];
r.lastStates[0] = -1;
r.lastStates[1] = -1;
r.stmt = obj.stmt;
content = '';
inverse = false;
/* transitent variables */
var t,
j = 0,
s = [],
str = '',
msg = '',
output = '';
/* consume till the end of expression */
r = HandlebarsUtils._consumeTillCloseBrace(str, j, len);
j = r.index;
obj = HandlebarsUtils._saveAstObject('branch', r.str);
if (!inverse) {
ast.program.push(obj);
} else if (inverse) {
ast.inverse.push(obj);
}
for(var i=0;i<o.length;++i) {
} else {
/* encounter another branch expression, save the previous string */
obj = HandlebarsUtils._saveAstObject('content', content);
if (!inverse) {
ast.program.push(obj);
} else if (inverse) {
ast.inverse.push(obj);
}
content = '';
/* if/with/each/list/tag/unless token */
if (typeof o[i].program !== 'undefined') {
branchesFlag[0] = true;
for(j=0;j<o[i].program.statements.length;++j) {
/* 1st node */
if (o[i].program.statements[j].type === 'content' && nodeFlag[0]) {
nodeFlag[0] = false;
str = o[i].program.statements[j].string;
/* restore the open/close brace place holder */
str = str.replace(new RegExp(obj.openBracePlaceHolder, 'g'), '{');
str = str.replace(new RegExp(obj.closeBracePlaceHolder, 'g'), '}');
/* parse the string */
t = HandlebarsUtils._analyzeContext(state, str);
newLastState = t.lastState;
output = t.output;
debugBranch("parseAstTreeState:if:1,["+state+"/"+newLastState+"],["+str+"],["+output+"]");
r.lastStates[0] = newLastState;
/* replace the filter place holder */
obj = HandlebarsUtils._replaceFilterPlaceHolder(obj, output);
/* 2nd node */
} else if (o[i].program.statements[j].type === 'block') {
s = [];
s[0] = o[i].program.statements[j];
t = HandlebarsUtils.parseAstTreeState(s, r.lastStates[0], obj);
newLastState = t.lastStates[0]; // index 0 and 1 MUST be equal
obj.stmt = t.stmt;
debugBranch("parseAstTreeState:if:2,["+r.lastStates[0]+"/"+newLastState+"]");
r.lastStates[0] = newLastState;
/* 3rd node */
} else if (o[i].program.statements[j].type === 'content' && !nodeFlag[0]) {
str = o[i].program.statements[j].string;
/* restore the open/close brace place holder */
str = str.replace(new RegExp(obj.openBracePlaceHolder, 'g'), '{');
str = str.replace(new RegExp(obj.closeBracePlaceHolder, 'g'), '}');
/* parse the string */
t = HandlebarsUtils._analyzeContext(r.lastStates[0], str);
newLastState = t.lastState;
output = t.output;
debugBranch("parseAstTreeState:if:3,["+r.lastStates[0]+"/"+newLastState+"],["+str+"],["+output+"]");
r.lastStates[0] = newLastState;
/* replace the filter place holder */
obj = HandlebarsUtils._replaceFilterPlaceHolder(obj, output);
r = HandlebarsUtils.buildBranchAst(str, j);
obj = HandlebarsUtils._saveAstObject('node', r);
j = j + r.index;
if (!inverse) {
ast.program.push(obj);
} else if (inverse) {
ast.inverse.push(obj);
}
}
}
} else if (HandlebarsUtils.isElseExpression(str, j)) {
obj = HandlebarsUtils._saveAstObject('content', content);
if (!inverse) {
ast.program.push(obj);
} else if (inverse) {
ast.inverse.push(obj);
}
/* else token */
if (typeof o[i].inverse !== 'undefined') {
branchesFlag[1] = true;
inverse = true;
content = '';
for(j=0;j<o[i].inverse.statements.length;++j) {
/* consume till the end of expression */
r = HandlebarsUtils._consumeTillCloseBrace(str, j, len);
j = r.index;
obj = HandlebarsUtils._saveAstObject('branchelse', r.str);
if (!inverse) {
ast.program.push(obj);
} else if (inverse) {
ast.inverse.push(obj);
}
/* 1st node */
if (o[i].inverse.statements[j].type === 'content' && nodeFlag[1]) {
nodeFlag[1] = false;
str = o[i].inverse.statements[j].string;
} else if (endExpression !== false) {
var t = sp.pop();
if (t === endExpression) {
obj = HandlebarsUtils._saveAstObject('content', content);
if (!inverse) {
ast.program.push(obj);
} else if (inverse) {
ast.inverse.push(obj);
}
/* restore the open/close brace place holder */
str = str.replace(new RegExp(obj.openBracePlaceHolder, 'g'), '{');
str = str.replace(new RegExp(obj.closeBracePlaceHolder, 'g'), '}');
/* consume till the end of expression */
r = HandlebarsUtils._consumeTillCloseBrace(str, j, len);
j = r.index;
obj = HandlebarsUtils._saveAstObject('branchend', r.str);
if (!inverse) {
ast.program.push(obj);
} else if (inverse) {
ast.inverse.push(obj);
}
/* parse the string */
t = HandlebarsUtils._analyzeContext(state, str);
newLastState = t.lastState;
output = t.output;
debugBranch("parseAstTreeState:else:1,["+state+"/"+newLastState+"],["+str+"],["+output+"]");
r.lastStates[1] = newLastState;
/* replace the filter place holder */
obj = HandlebarsUtils._replaceFilterPlaceHolder(obj, output);
/* 2nd node */
} else if (o[i].inverse.statements[j].type === 'block') {
s = [];
s[0] = o[i].inverse.statements[j];
t = HandlebarsUtils.parseAstTreeState(s, r.lastStates[1], obj);
newLastState = t.lastStates[0]; // index 0 and 1 MUST be equal
obj.stmt = t.stmt;
debugBranch("parseAstTreeState:else:2,["+r.lastStates[1]+"/"+newLastState+"]");
r.lastStates[1] = newLastState;
/* 3rd node */
} else if (o[i].inverse.statements[j].type === 'content' && !nodeFlag[1]) {
str = o[i].inverse.statements[j].string;
/* restore the open/close brace place holder */
str = str.replace(new RegExp(obj.openBracePlaceHolder, 'g'), '{');
str = str.replace(new RegExp(obj.closeBracePlaceHolder, 'g'), '}');
/* parse the string */
t = HandlebarsUtils._analyzeContext(r.lastStates[1], str);
newLastState = t.lastState;
output = t.output;
debugBranch("parseAstTreeState:else:3,["+r.lastStates[1]+"/"+newLastState+"],["+str+"],["+output+"]");
r.lastStates[1] = newLastState;
/* replace the filter place holder */
obj = HandlebarsUtils._replaceFilterPlaceHolder(obj, output);
}
break;
} else {
/* broken template as the end expression does not match, throw exception before function returns */
msg = "[ERROR] ContextParserHandlebars: Template expression mismatch (startExpression:"+t+"/endExpression:"+endExpression+")";
HandlebarsUtils.handleError(msg, true);
}
} else {
var expressionType = HandlebarsUtils.getExpressionType(str, j, len);
if (expressionType === HandlebarsUtils.COMMENT_EXPRESSION_LONG_FORM ||
expressionType === HandlebarsUtils.COMMENT_EXPRESSION_SHORT_FORM) {
/* capturing the string till the end of comment */
r = HandlebarsUtils._consumeTillCommentCloseBrace(str, j, len, expressionType);
j = r.index;
content += r.str;
} else {
/* capturing the string */
content += str[j];
}
}
}
if (branchesFlag[0] && !branchesFlag[1]) {
debugBranch("parseAstTreeState:else:0,["+state+"/"+state+"]");
r.lastStates[1] = state;
} else if (!branchesFlag[0] && branchesFlag[1]) {
debugBranch("parseAstTreeState:if:0,["+state+"/"+state+"]");
r.lastStates[0] = state;
}
if (r.lastStates[0] !== r.lastStates[1]) {
msg = "[ERROR] ContextParserHandlebars: Parsing error! Inconsitent HTML5 state after conditional branches. Please fix your template!";
if (sp.length > 0) {
/* throw error on the template */
msg = "[ERROR] ContextParserHandlebars: Template does not have balanced branching expression.";
HandlebarsUtils.handleError(msg, true);
}
r.stmt = obj.stmt;
return r;
ast.index = j;
return ast;
};
/*
* @function HandlebarsUtils._analyzeContext
*
* @static
* @private
*
/**
* @function HandlebarsUtils._saveAstObject
*/
HandlebarsUtils._analyzeContext = function(state, str) {
var r = {
lastState: '',
output: ''
};
// TODO: refactor
/* factory class */
var parser,
ContextParserHandlebars = require('./context-parser-handlebars');
/* parse the string */
debugBranch("_analyzeContext:"+str);
parser = new ContextParserHandlebars(false);
parser.setInitState(state);
parser.contextualize(str);
r.lastState = parser.getLastState();
r.output = parser.getBuffer().join('');
debugBranch("_analyzeContext:"+r.output);
return r;
HandlebarsUtils._saveAstObject = function(type, content) {
var obj = {};
obj.type = type;
obj.content = content;
return obj;
};
/*
* @function HandlebarsUtils._replaceFilterPlaceHolder
*
* @static
* @private
*
/**
* @function HandlebarsUtils._consumeTillCloseBrace
*/
HandlebarsUtils._replaceFilterPlaceHolder = function(obj, str) {
var filterPlaceHolders = obj.filterPlaceHolder.slice(0);
filterPlaceHolders.forEach(function(filterPlaceHolder) {
/* get the output expression from masked stmt */
var outputMarkup = new RegExp('\{\{' + filterPlaceHolder + '.*?\}\}', 'g');
var m1 = outputMarkup.exec(obj.stmt);
/* get the output filter expression from processed stmt */
var i = str.indexOf(filterPlaceHolder);
if (i !== -1 && m1 !== null && m1[0]) {
var s = str.substr(0,i).lastIndexOf('{');
var e = str.substr(i).indexOf('}') + i + 1;
var m2 = str.substring(s-2, e+2);
debugBranch("replaceFilterPlaceHolder:i:"+i+",s:"+s+",e:"+e+",m1:"+m1+",m2"+m2);
if (m1 !== null && m1[0] &&
m2 !== null && m2) {
/* Replace the output expression with filter expression */
obj.stmt = obj.stmt.replace(m1[0], m2);
/* Replace the filterPlaceHolder with empty string */
obj.stmt = obj.stmt.replace(filterPlaceHolder, "");
/* Remove element from array */
var j = obj.filterPlaceHolder.indexOf(filterPlaceHolder);
obj.filterPlaceHolder.splice(j, 1);
}
HandlebarsUtils._consumeTillCloseBrace = function(input, i, len) {
var msg,
str = '',
obj = {};
for(var j=i;j<len;++j) {
if (input[j] === '}' && j+1 < len && input[j+1] === '}') {
str += '}}';
j++;
obj.index = j;
obj.str = str;
return obj;
}
});
return obj;
str += input[j];
}
msg = "[ERROR] ContextParserHandlebars: Parsing error! Cannot encounter '}}' close brace of expression.";
HandlebarsUtils.handleError(msg, true);
};
/*
* @function HandlebarsUtils.blacklistProtocol
*
* Reference:
* https://github.com/yahoo/xss-filters/blob/master/src/private-xss-filters.js#L266
/**
* @function HandlebarsUtils._consumeTillCommentCloseBrace
*/
HandlebarsUtils._URI_BLACKLIST = null;
HandlebarsUtils._URI_BLACKLIST_REGEXPSTR = "^(?:&#[xX]0*(?:1?[1-9a-fA-F]|10|20);?|&#0*(?:[1-9]|[1-2][0-9]|30|31|32);?|&Tab;|&NewLine;)*(?:(?:j|J|&#[xX]0*(?:6|4)[aA];?|&#0*(?:106|74);?)(?:&#[xX]0*[9aAdD];?|&#0*(?:9|10|13);?|&Tab;|&NewLine;)*(?:a|A|&#[xX]0*(?:6|4)1;?|&#0*(?:97|65);?)(?:&#[xX]0*[9aAdD];?|&#0*(?:9|10|13);?|&Tab;|&NewLine;)*(?:v|V|&#[xX]0*(?:7|5)6;?|&#0*(?:118|86);?)(?:&#[xX]0*[9aAdD];?|&#0*(?:9|10|13);?|&Tab;|&NewLine;)*(?:a|A|&#[xX]0*(?:6|4)1;?|&#0*(?:97|65);?)|(?:v|V|&#[xX]0*(?:7|5)6;?|&#0*(?:118|86);?)(?:&#[xX]0*[9aAdD];?|&#0*(?:9|10|13);?|&Tab;|&NewLine;)*(?:b|B|&#[xX]0*(?:6|4)2;?|&#0*(?:98|66);?))(?:&#[xX]0*[9aAdD];?|&#0*(?:9|10|13);?|&Tab;|&NewLine;)*(?:s|S|&#[xX]0*(?:7|5)3;?|&#0*(?:115|83);?)(?:&#[xX]0*[9aAdD];?|&#0*(?:9|10|13);?|&Tab;|&NewLine;)*(?:c|C|&#[xX]0*(?:6|4)3;?|&#0*(?:99|67);?)(?:&#[xX]0*[9aAdD];?|&#0*(?:9|10|13);?|&Tab;|&NewLine;)*(?:r|R|&#[xX]0*(?:7|5)2;?|&#0*(?:114|82);?)(?:&#[xX]0*[9aAdD];?|&#0*(?:9|10|13);?|&Tab;|&NewLine;)*(?:i|I|&#[xX]0*(?:6|4)9;?|&#0*(?:105|73);?)(?:&#[xX]0*[9aAdD];?|&#0*(?:9|10|13);?|&Tab;|&NewLine;)*(?:p|P|&#[xX]0*(?:7|5)0;?|&#0*(?:112|80);?)(?:&#[xX]0*[9aAdD];?|&#0*(?:9|10|13);?|&Tab;|&NewLine;)*(?:t|T|&#[xX]0*(?:7|5)4;?|&#0*(?:116|84);?)(?:&#[xX]0*[9aAdD];?|&#0*(?:9|10|13);?|&Tab;|&NewLine;)*(?::|&#[xX]0*3[aA];?|&#0*58;?)";
HandlebarsUtils.blacklistProtocol = function(s) {
var URI_FASTLANE = ['&', 'j', 'J', 'v', 'V'];
if (URI_FASTLANE.indexOf(s[0]) === -1) {
return false;
} else {
if (HandlebarsUtils._URI_BLACKLIST === null) {
HandlebarsUtils._URI_BLACKLIST = new RegExp(HandlebarsUtils._URI_BLACKLIST_REGEXPSTR);
HandlebarsUtils._consumeTillCommentCloseBrace = function(input, i, len, type) {
var msg,
str = '',
obj = {};
for(var j=i;j<len;++j) {
if (type === HandlebarsUtils.COMMENT_EXPRESSION_LONG_FORM) {
if (input[j] === '-' && j+3<len && input[j+1] === '-' && input[j+2] === '}' && input[j+3] === '}') {
str += '--}}';
j=j+3;
obj.index = j;
obj.str = str;
return obj;
}
} else if (type === HandlebarsUtils.COMMENT_EXPRESSION_SHORT_FORM) {
if (input[j] === '}' && j+1<len && input[j+1] === '}') {
str += '}}';
j++;
obj.index = j;
obj.str = str;
return obj;
}
}
if (HandlebarsUtils._URI_BLACKLIST.test(s)) {
return true;
} else {
return false;
}
str += input[j];
}
return true;
msg = "[ERROR] ContextParserHandlebars: Parsing error! Cannot encounter '}}' or '--}}' close brace of comment expression.";
HandlebarsUtils.handleError(msg, true);
};

@@ -715,0 +636,0 @@

@@ -105,3 +105,3 @@ /*

/* this test will not throw exception, but the vanilla handlebars will complain, no need to handle */
/* this test will throw exception, and the vanilla handlebars will complain */
it("./bin/handlebarspc broken conditional {{/if}} template test", function(done) {

@@ -111,3 +111,3 @@ var exec = promise.promisify(require("child_process").exec);

.timeout(300)
.then(function(e){
.catch(function(e){
done();

@@ -114,0 +114,0 @@ });

@@ -34,3 +34,3 @@ /*

it("handlebars {{expression}} subexpression with \r\n test", function() {
it("handlebars {{expression}} subexpression with \\r\\n test", function() {
[

@@ -96,2 +96,35 @@ '{{expression\rion}}',

it("handlebars white space control test", function() {
[
'{{#each nav ~}}\
xxx\
{{~/each}}',
'{{#each nav ~}}\
xxx\
{{~/ each}}'
].forEach(function(t) {
var ast = handlebars.parse(t);
expect(ast.statements[0].program.statements[0].string).to.equal('xxx');
});
[
// cannot have space between ~ and }}
'{{#each nav ~ }}\
xxx\
{{~/each}}',
// cannot have space between ~ and /
'{{#each nav ~}}\
xxx\
{{~ / each}}'
].forEach(function(t) {
try {
var ast = handlebars.parse(e);
expect(false).to.equal(true);
} catch (err) {
expect(true).to.equal(true);
}
});
});
/* we are not using handlebars to build the AST, so the following test can be skipped
*
it("handlebars {{#if}} {{!-- comment --}} {{/if}} parsing test", function() {

@@ -184,2 +217,28 @@ var t = '{{#if}}xxx{{!-- comment --}}yyy{{/if}}';

expect(ast.statements[0].inverse.statements[0].string).to.equal('yyy');
t = '{{~# if}}xxx{{~ else ~}}yyy{{/if}}';
ast = handlebars.parse(t);
expect(ast.statements[0].mustache.id.string).to.equal('if');
expect(ast.statements[0].program.statements[0].string).to.equal('xxx');
expect(ast.statements[0].inverse.statements[0].string).to.equal('yyy');
t = '{{#if}}xxx{{^}}yyy{{/if}}';
ast = handlebars.parse(t);
expect(ast.statements[0].mustache.id.string).to.equal('if');
expect(ast.statements[0].program.statements[0].string).to.equal('xxx');
expect(ast.statements[0].inverse.statements[0].string).to.equal('yyy');
t = '{{#if}}xxx{{^ }}yyy{{/if}}';
ast = handlebars.parse(t);
expect(ast.statements[0].mustache.id.string).to.equal('if');
expect(ast.statements[0].program.statements[0].string).to.equal('xxx');
expect(ast.statements[0].inverse.statements[0].string).to.equal('yyy');
t = '{{#if}}xxx{{ ^ }}yyy{{/if}}';
try {
ast = handlebars.parse(t);
expect(false).to.equal(true);
} catch (err) {
expect(true).to.equal(true);
}
});

@@ -245,3 +304,3 @@

it("handlebars nested {{#if}} / {{#with}} / {{else}} parsing test", function() {
it("handlebars nested {{#if}} / {{#with}} / {{else}} parsing test", function() {
var t = '{{#if}}111{{#with}}222{{else}}333{{/with}}444{{else}}555{{#if}}666{{else}}777{{/if}}888{{/if}}';

@@ -282,3 +341,3 @@ var ast = handlebars.parse(t);

it("handlebars nested {{#if}} / {{#each}} / {{else}} parsing test", function() {
it("handlebars nested {{#if}} / {{#each}} / {{else}} parsing test", function() {
var t = '{{#if}}111{{#each}}222{{else}}333{{/each}}444{{else}}555{{#if}}666{{else}}777{{/if}}888{{/if}}';

@@ -319,3 +378,3 @@ var ast = handlebars.parse(t);

it("handlebars nested {{#if}} / {{#list}} / {{else}} parsing test", function() {
it("handlebars nested {{#if}} / {{#list}} / {{else}} parsing test", function() {
var t = '{{#if}}111{{#list people}}people{{else}}no people{{/list}}444{{else}}555{{#if}}666{{else}}777{{/if}}888{{/if}}';

@@ -356,3 +415,3 @@ var ast = handlebars.parse(t);

it("handlebars nested {{#if}} / {{#tag}} / {{else}} parsing test", function() {
it("handlebars nested {{#if}} / {{#tag}} / {{else}} parsing test", function() {
var t = '{{#if}}111{{#tag people}}people{{else}}no people{{/tag}}444{{else}}555{{#if}}666{{else}}777{{/if}}888{{/if}}';

@@ -393,3 +452,3 @@ var ast = handlebars.parse(t);

it("handlebars nested {{#if}} / {{^msg}} / {{else}} parsing test", function() {
it("handlebars nested {{#if}} / {{^msg}} / {{else}} parsing test", function() {
var t = '{{#if}}111{{^msg}}222{{else}}333{{/msg}}444{{else}}555{{#if}}666{{else}}777{{/if}}888{{/if}}';

@@ -518,3 +577,20 @@ var ast = handlebars.parse(t);

it("handlebars parallel {{#if}} parsing test", function() {
var t = '{{#if}} a {{#if}} b {{else}} c {{/if}} d {{#if}} e {{else}} f {{/if}} g {{#if}} h {{else}} i {{/if}} j {{/if}}';
var ast = handlebars.parse(t);
expect(ast.statements[0].mustache.id.string).to.equal('if');
expect(ast.statements[0].program.statements[0].string).to.equal(' a ');
expect(ast.statements[0].program.statements[1].mustache.id.string).to.equal('if');
expect(ast.statements[0].program.statements[1].program.statements[0].string).to.equal(' b ');
expect(ast.statements[0].program.statements[1].inverse.statements[0].string).to.equal(' c ');
expect(ast.statements[0].program.statements[2].string).to.equal(' d ');
expect(ast.statements[0].program.statements[3].program.statements[0].string).to.equal(' e ');
expect(ast.statements[0].program.statements[3].inverse.statements[0].string).to.equal(' f ');
expect(ast.statements[0].program.statements[4].string).to.equal(' g ');
expect(ast.statements[0].program.statements[5].program.statements[0].string).to.equal(' h ');
expect(ast.statements[0].program.statements[5].inverse.statements[0].string).to.equal(' i ');
expect(ast.statements[0].program.statements[6].string).to.equal(' j ');
});
*/
});
}());

@@ -93,3 +93,3 @@ /*

/{{!-- comment1 --}}/,
/{{!-- comment2 }}/,
/{{!-- comment2 }} --}}/,
/{{! comment3 }}/

@@ -96,0 +96,0 @@ ];

@@ -18,215 +18,6 @@ /*

describe("handlebars-handlebarsUtilss test suite", function() {
describe("handlebars-utils test suite", function() {
it("handlebars-handlebarsUtilss#generateNonce test", function() {
var n1 = handlebarsUtils.generateNonce();
var n2 = handlebarsUtils.generateNonce();
expect(n1).not.to.equal(n2);
});
it("handlebars-handlebarsUtilss#_parseExpression invalid format test", function() {
});
it("handlebars-handlebarsUtilss#_parseExpression {{else}} test", function() {
var parser = new ContextParserHandlebars();
var arr = [
// test for {{else}}
{str:'{else}}', isPrefixWithKnownFilter:true, filter:'', isSingleIdentifier:false},
// test for {{else}} with space after else
{str:'{else }}', isPrefixWithKnownFilter:true, filter:'', isSingleIdentifier:false},
// test for {{else}} with space before/after else
{str:'{ else }}', isPrefixWithKnownFilter:true, filter:'', isSingleIdentifier:false},
// test for {{else}} with space before/after else
// {str:'{{else}}', isPrefixWithKnownFilter:false, filter:'', isSingleIdentifier:false}
];
arr.forEach(function(obj) {
var r = parser._parseExpression(obj.str, 0);
utils.testExpression(r, obj);
});
});
it("handlebars-handlebarsUtilss#_parseExpression basic test", function() {
var parser = new ContextParserHandlebars();
var arr = [
// test for single identifier with the same name as known filter {y}}
{str:'{y}}', isPrefixWithKnownFilter:false, filter:'', isSingleIdentifier:true},
{str:'{y }}', isPrefixWithKnownFilter:false, filter:'', isSingleIdentifier:true},
{str:'{ y }}', isPrefixWithKnownFilter:false, filter:'', isSingleIdentifier:true},
// test for single identifier with the same name as known default filter {h}}
{str:'{h}}', isPrefixWithKnownFilter:false, filter:'', isSingleIdentifier:true},
// test for single identifier with dot notation
{str:'{people.name}}', isPrefixWithKnownFilter:false, filter:'', isSingleIdentifier:true},
// test for single identifier with ../
{str:'{../name}}', isPrefixWithKnownFilter:false, filter:'', isSingleIdentifier:true},
// test for single identifier with /
{str:'{article/name}}', isPrefixWithKnownFilter:false, filter:'', isSingleIdentifier:true},
// test for single identifier with []
{str:'{article[0]}}', isPrefixWithKnownFilter:false, filter:'', isSingleIdentifier:true},
// test for single identifier with []
{str:'{article.[0].[#comments]}}', isPrefixWithKnownFilter:false, filter:'', isSingleIdentifier:true},
// test for expression with \r and \n as separator
{str:'{y\rparam}}', isPrefixWithKnownFilter:true, filter:'y', isSingleIdentifier:false},
{str:'{y\nparam}}', isPrefixWithKnownFilter:true, filter:'y', isSingleIdentifier:false},
{str:'{y\r\nparam}}', isPrefixWithKnownFilter:true, filter:'y', isSingleIdentifier:false},
];
arr.forEach(function(obj) {
var r = parser._parseExpression(obj.str, 0);
utils.testExpression(r, obj);
});
});
it("handlebars-handlebarsUtilss#_parseExpression test", function() {
var parser = new ContextParserHandlebars();
var arr = [
// test for expression with the same name as known filter {y}}
{str:'{y output}}', isPrefixWithKnownFilter:true, filter:'y', isSingleIdentifier:false},
{str:'{y output}}', isPrefixWithKnownFilter:true, filter:'y', isSingleIdentifier:false},
{str:'{ y output}}', isPrefixWithKnownFilter:true, filter:'y', isSingleIdentifier:false},
// test for expression with the same name as default filter {h}}
{str:'{h output}}', isPrefixWithKnownFilter:false, filter:'h', isSingleIdentifier:false},
{str:'{h output}}', isPrefixWithKnownFilter:false, filter:'h', isSingleIdentifier:false},
{str:'{ h output}}', isPrefixWithKnownFilter:false, filter:'h', isSingleIdentifier:false},
// test for expression with dot notation filter
{str:'{people.name output}}', isPrefixWithKnownFilter:false, filter:'people.name', isSingleIdentifier:false},
// test for expression with ../ filter
{str:'{../name output}}', isPrefixWithKnownFilter:false, filter:'../name', isSingleIdentifier:false},
// test for expression with the same name as known filter {y}} and parameter in dot notation
{str:'{y people.name}}', isPrefixWithKnownFilter:true, filter:'y', isSingleIdentifier:false},
{str:'{y people.name}}', isPrefixWithKnownFilter:true, filter:'y', isSingleIdentifier:false},
{str:'{ y people.name}}', isPrefixWithKnownFilter:true, filter:'y', isSingleIdentifier:false},
// test for expression with the same name as known filter {y}} and parameter with ../
{str:'{y ../output}}', isPrefixWithKnownFilter:true, filter:'y', isSingleIdentifier:false},
{str:'{y ../output}}', isPrefixWithKnownFilter:true, filter:'y', isSingleIdentifier:false},
{str:'{ y ../output}}', isPrefixWithKnownFilter:true, filter:'y', isSingleIdentifier:false},
];
arr.forEach(function(obj) {
var r = parser._parseExpression(obj.str, 0);
utils.testExpression(r, obj);
});
});
it("handlebars-handlebarsUtilss#_parseExpression 2 arguments test", function() {
var parser = new ContextParserHandlebars();
var arr = [
// test for expression with the same name as known filter {y}}
{str:'{y xxx zzz}}', isPrefixWithKnownFilter:true, filter:'y', isSingleIdentifier:false},
{str:'{y xxx zzz}}', isPrefixWithKnownFilter:true, filter:'y', isSingleIdentifier:false},
{str:'{ y xxx zzz}}', isPrefixWithKnownFilter:true, filter:'y', isSingleIdentifier:false},
// test for expression with the same name as unknown filter
{str:'{unknown xxx zzz}}', isPrefixWithKnownFilter:false, filter:'unknown', isSingleIdentifier:false},
{str:'{unknown xxx zzz}}', isPrefixWithKnownFilter:false, filter:'unknown', isSingleIdentifier:false},
{str:'{ unknown xxx zzz}}', isPrefixWithKnownFilter:false, filter:'unknown', isSingleIdentifier:false}
];
arr.forEach(function(obj) {
var r = parser._parseExpression(obj.str, 0);
utils.testExpression(r, obj);
});
});
it("handlebars-handlebarsUtilss#_parseExpression 2 arguments (reference format) test", function() {
var parser = new ContextParserHandlebars();
var arr = [
// test for expression with the same name as known filter {y}} with different parameter format
{str:'{y people.name ../name}}', isPrefixWithKnownFilter:true, filter:'y', isSingleIdentifier:false},
{str:'{y article[0] article/name}}', isPrefixWithKnownFilter:true, filter:'y', isSingleIdentifier:false},
{str:'{y article.[0].[#comments] article/name}}', isPrefixWithKnownFilter:true, filter:'y', isSingleIdentifier:false},
// test for expression with the same name as known filter {unknown}} with different parameter format
{str:'{unknown people.name ../name}}', isPrefixWithKnownFilter:false, filter:'unknown', isSingleIdentifier:false},
{str:'{unknown article[0] article/name}}', isPrefixWithKnownFilter:false, filter:'unknown', isSingleIdentifier:false},
{str:'{unknown article.[0].[#comments] article/name}}', isPrefixWithKnownFilter:false, filter:'unknown', isSingleIdentifier:false}
];
arr.forEach(function(obj) {
var r = parser._parseExpression(obj.str, 0);
utils.testExpression(r, obj);
});
});
it("handlebars-handlebarsUtilss#_parseExpression reserved tag test", function() {
var parser = new ContextParserHandlebars();
var arr = [
// test for reserved expression {{#.*}}
{str:'{#y}}', isPrefixWithKnownFilter:false, filter:'', isSingleIdentifier:false},
{str:'{# y xxx}}', isPrefixWithKnownFilter:false, filter:'', isSingleIdentifier:false},
// test for reserved expression {{/.*}}
{str:'{/y}}', isPrefixWithKnownFilter:false, filter:'', isSingleIdentifier:false},
{str:'{/ y }}', isPrefixWithKnownFilter:false, filter:'', isSingleIdentifier:false},
// test for reserved expression {{>.*}}
{str:'{>partial}}', isPrefixWithKnownFilter:false, filter:'', isSingleIdentifier:false},
{str:'{> partial }}', isPrefixWithKnownFilter:false, filter:'', isSingleIdentifier:false},
// test for reserved expression {{^.*}}
{str:'{^negation}}', isPrefixWithKnownFilter:false, filter:'', isSingleIdentifier:false},
{str:'{^ negation }}', isPrefixWithKnownFilter:false, filter:'', isSingleIdentifier:false},
// test for reserved expression {{!.*}}
{str:'{!comment}}', isPrefixWithKnownFilter:false, filter:'', isSingleIdentifier:false},
{str:'{! comment }}', isPrefixWithKnownFilter:false, filter:'', isSingleIdentifier:false},
// test for reserved expression {{@.*}}
{str:'{@var}}', isPrefixWithKnownFilter:false, filter:'', isSingleIdentifier:false},
{str:'{@ var }}', isPrefixWithKnownFilter:false, filter:'', isSingleIdentifier:false}
];
arr.forEach(function(obj) {
var r = parser._parseExpression(obj.str, 0);
utils.testExpression(r, obj);
});
});
it("handlebars-handlebarsUtilss#_parseExpression subexpression test", function() {
var parser = new ContextParserHandlebars();
var arr = [
// not a valid handlebars syntax, no need to test
// {str:'{y(output)}}', isPrefixWithKnownFilter:true, filter:'y', isSingleIdentifier:false},
// subexpression with one chain
{str:'{y (output)}}', isPrefixWithKnownFilter:true, filter:'y', isSingleIdentifier:false},
{str:'{y ( output ) }}', isPrefixWithKnownFilter:true, filter:'y', isSingleIdentifier:false},
// subexpression with two chain
{str:'{y (helper xxx)}}', isPrefixWithKnownFilter:true, filter:'y', isSingleIdentifier:false},
{str:'{y ( helper xxx ) }}', isPrefixWithKnownFilter:true, filter:'y', isSingleIdentifier:false},
{str:'{y (helper "xxx")}}', isPrefixWithKnownFilter:true, filter:'y', isSingleIdentifier:false},
{str:'{y ( helper "xxx" ) }}', isPrefixWithKnownFilter:true, filter:'y', isSingleIdentifier:false},
// subexpression with three chain
{str:'{y helper2 (helper1 xxx)}}', isPrefixWithKnownFilter:true, filter:'y', isSingleIdentifier:false},
{str:'{y helper2 ( helper1 xxx )}}', isPrefixWithKnownFilter:true, filter:'y', isSingleIdentifier:false},
{str:'{y helper2 ( helper1 "xxx" )}}', isPrefixWithKnownFilter:true, filter:'y', isSingleIdentifier:false},
//
{str:'{y ( outer-helper (inner-helper "abc") "def")}}', isPrefixWithKnownFilter:true, filter:'y', isSingleIdentifier:false}
];
arr.forEach(function(obj) {
var r = parser._parseExpression(obj.str, 0);
utils.testExpression(r, obj);
});
});
it("handlebars-handlebarsUtilss#_parseExpression greedy match test", function() {
var parser = new ContextParserHandlebars();
var arr = [
// immediate after an expression
{str:'{else}}{{h zzz }}', isPrefixWithKnownFilter:true, filter:'', isSingleIdentifier:false},
{str:'{y}}{{h zzz }}', isPrefixWithKnownFilter:false, filter:'', isSingleIdentifier:true},
{str:'{y param}}{{h zzz }}', isPrefixWithKnownFilter:true, filter:'y', isSingleIdentifier:false},
// not immediate after an expression
{str:'{else}}xxxx{{h zzz }}', isPrefixWithKnownFilter:true, filter:'', isSingleIdentifier:false},
{str:'{y}}xxxx{{h zzz }}', isPrefixWithKnownFilter:false, filter:'', isSingleIdentifier:true},
{str:'{y param}}xxxx{{h zzz }}', isPrefixWithKnownFilter:true, filter:'y', isSingleIdentifier:false}
];
arr.forEach(function(obj) {
var r = parser._parseExpression(obj.str, 0);
utils.testExpression(r, obj);
});
});
it("handlebars-handlebarsUtilss#isValidExpression escapeExpressionRegExp test", function() {
var arr = [
it("handlebars-utils#isValidExpression escapeExpressionRegExp test", function() {
[
// basic

@@ -240,2 +31,28 @@ {str:'{{anything}}', type:handlebarsUtils.ESCAPE_EXPRESSION, result:true, rstr:'{{anything}}'},

{str:'{{any\'thing\'}}', type:handlebarsUtils.ESCAPE_EXPRESSION, result:true, rstr:'{{any\'thing\'}}'},
{str:"{{any'thing'}}", type:handlebarsUtils.ESCAPE_EXPRESSION, result:true, rstr:"{{any'thing'}}"},
{str:"{{~any'thing'~}}", type:handlebarsUtils.ESCAPE_EXPRESSION, result:true, rstr:"{{~any'thing'~}}"},
{str:'{{any"thing"}}', type:handlebarsUtils.ESCAPE_EXPRESSION, result:true, rstr:'{{any"thing"}}'},
// with ~
{str:'{{~anything}}', type:handlebarsUtils.ESCAPE_EXPRESSION, result:true, rstr:'{{~anything}}'},
{str:'{{~anything~}}', type:handlebarsUtils.ESCAPE_EXPRESSION, result:true, rstr:'{{~anything~}}'},
{str:'{{~ anything ~}}', type:handlebarsUtils.ESCAPE_EXPRESSION, result:true, rstr:'{{~ anything ~}}'},
// invalid reserved expression
{str:'{{#anything}}', type:handlebarsUtils.ESCAPE_EXPRESSION, result:false},
{str:'{{~#anything}}', type:handlebarsUtils.ESCAPE_EXPRESSION, result:false},
{str:'{{/anything}}', type:handlebarsUtils.ESCAPE_EXPRESSION, result:false},
{str:'{{~/anything}}', type:handlebarsUtils.ESCAPE_EXPRESSION, result:false},
{str:'{{@anything}}', type:handlebarsUtils.ESCAPE_EXPRESSION, result:false},
{str:'{{~@anything}}', type:handlebarsUtils.ESCAPE_EXPRESSION, result:false},
{str:'{{^anything}}', type:handlebarsUtils.ESCAPE_EXPRESSION, result:false},
{str:'{{~^anything}}', type:handlebarsUtils.ESCAPE_EXPRESSION, result:false},
{str:'{{!anything}}', type:handlebarsUtils.ESCAPE_EXPRESSION, result:false},
{str:'{{~!anything}}', type:handlebarsUtils.ESCAPE_EXPRESSION, result:false},
{str:'{{!--anything--}}', type:handlebarsUtils.ESCAPE_EXPRESSION, result:false},
{str:'{{~!--anything--}}', type:handlebarsUtils.ESCAPE_EXPRESSION, result:false},
{str:'{{>anything}}', type:handlebarsUtils.ESCAPE_EXPRESSION, result:false},
{str:'{{~>anything}}', type:handlebarsUtils.ESCAPE_EXPRESSION, result:false},
// invalid expression

@@ -248,5 +65,6 @@ {str:'{ {anything}}', type:handlebarsUtils.ESCAPE_EXPRESSION, result:false},

{str:'{{ }anything}}', type:handlebarsUtils.ESCAPE_EXPRESSION, result:false},
{str:'{{}}', type:handlebarsUtils.ESCAPE_EXPRESSION, result:false}
];
arr.forEach(function(obj) {
{str:'{{ ~anything}}', type:handlebarsUtils.ESCAPE_EXPRESSION, result:false},
{str:'{{}}', type:handlebarsUtils.ESCAPE_EXPRESSION, result:false},
].forEach(function(obj) {
var r = handlebarsUtils.isValidExpression(obj.str, 0, obj.type);

@@ -257,4 +75,4 @@ utils.testIsValidExpression(r, obj);

it("handlebars-handlebarsUtilss#isValidExpression rawExpressionRegExp test", function() {
var arr = [
it("handlebars-utils#isValidExpression rawExpressionRegExp test", function() {
[
// basic

@@ -268,13 +86,18 @@ {str:'{{{anything}}}', type:handlebarsUtils.RAW_EXPRESSION, result:true, rstr:'{{{anything}}}'},

// with ~
{str:'{{{~anything}}}', type:handlebarsUtils.RAW_EXPRESSION, result:true, rstr:'{{{~anything}}}'},
{str:'{{{~anything~}}}', type:handlebarsUtils.RAW_EXPRESSION, result:true, rstr:'{{{~anything~}}}'},
{str:'{{{~ anything ~}}}', type:handlebarsUtils.RAW_EXPRESSION, result:true, rstr:'{{{~ anything ~}}}'},
// invalid expression
{str:'{ {{anything}}}', type:handlebarsUtils.ESCAPE_EXPRESSION, result:false},
{str:'{{ {anything}}}', type:handlebarsUtils.ESCAPE_EXPRESSION, result:false},
{str:'{{{anything}', type:handlebarsUtils.ESCAPE_EXPRESSION, result:false},
{str:'{{{anything}}', type:handlebarsUtils.ESCAPE_EXPRESSION, result:false},
{str:'{{{anything}}}}', type:handlebarsUtils.ESCAPE_EXPRESSION, result:false},
{str:'{{{ {anything}}}', type:handlebarsUtils.ESCAPE_EXPRESSION, result:false},
{str:'{{{ }anything}}}', type:handlebarsUtils.ESCAPE_EXPRESSION, result:false},
{str:'{{{}}}', type:handlebarsUtils.ESCAPE_EXPRESSION, result:false}
];
arr.forEach(function(obj) {
{str:'{ {{anything}}}', type:handlebarsUtils.RAW_EXPRESSION, result:false},
{str:'{{ {anything}}}', type:handlebarsUtils.RAW_EXPRESSION, result:false},
{str:'{{{anything}', type:handlebarsUtils.RAW_EXPRESSION, result:false},
{str:'{{{anything}}', type:handlebarsUtils.RAW_EXPRESSION, result:false},
{str:'{{{anything}}}}', type:handlebarsUtils.RAW_EXPRESSION, result:false},
{str:'{{{ {anything}}}', type:handlebarsUtils.RAW_EXPRESSION, result:false},
{str:'{{{ }anything}}}', type:handlebarsUtils.RAW_EXPRESSION, result:false},
{str:'{{{ ~anything}}}', type:handlebarsUtils.RAW_EXPRESSION, result:false},
{str:'{{{}}}', type:handlebarsUtils.RAW_EXPRESSION, result:false}
].forEach(function(obj) {
var r = handlebarsUtils.isValidExpression(obj.str, 0, obj.type);

@@ -285,4 +108,4 @@ utils.testIsValidExpression(r, obj);

it("handlebars-handlebarsUtilss#isValidExpression greedy match test", function() {
var arr = [
it("handlebars-utils#isValidExpression greedy match test", function() {
[
// basic

@@ -299,2 +122,7 @@ {str:'{{anything}}{{anything}}', type:handlebarsUtils.ESCAPE_EXPRESSION, result:true, rstr:'{{anything}}'},

// with ~
{str:'{{~anything}}{{anything}}', type:handlebarsUtils.ESCAPE_EXPRESSION, result:true, rstr:'{{~anything}}'},
{str:'{{~anything~}}{{~anything~}}', type:handlebarsUtils.ESCAPE_EXPRESSION, result:true, rstr:'{{~anything~}}'},
{str:'{{~ anything ~}}{{~ anything ~}}', type:handlebarsUtils.ESCAPE_EXPRESSION, result:true, rstr:'{{~ anything ~}}'},
// basic

@@ -310,2 +138,30 @@ {str:'{{{anything}}}{{{anything}}}', type:handlebarsUtils.RAW_EXPRESSION, result:true, rstr:'{{{anything}}}'},

{str:'{{{any\r\nthing}}}{{{anything}}}', type:handlebarsUtils.RAW_EXPRESSION, result:true, rstr:'{{{any\r\nthing}}}'},
// with ~
{str:'{{{~anything}}}{{{anything}}}', type:handlebarsUtils.RAW_EXPRESSION, result:true, rstr:'{{{~anything}}}'},
{str:'{{{~anything~}}}{{{~anything~}}}', type:handlebarsUtils.RAW_EXPRESSION, result:true, rstr:'{{{~anything~}}}'},
{str:'{{{~ anything ~}}}{{{~ anything ~}}}', type:handlebarsUtils.RAW_EXPRESSION, result:true, rstr:'{{{~ anything ~}}}'},
].forEach(function(obj) {
var r = handlebarsUtils.isValidExpression(obj.str, 0, obj.type);
utils.testIsValidExpression(r, obj);
});
});
it("handlebars-utils#isValidExpression partialExpressionRegExp test", function() {
var arr = [
// basic
{str:'{{>anything}}', type:handlebarsUtils.PARTIAL_EXPRESSION, result:true, rstr:'{{>anything}}'},
{str:'{{> anything }}', type:handlebarsUtils.PARTIAL_EXPRESSION, result:true, rstr:'{{> anything }}'},
// with \r and \n
{str:'{{>any\rthing}}', type:handlebarsUtils.PARTIAL_EXPRESSION, result:true, rstr:'{{>any\rthing}}'},
{str:'{{>any\nthing}}', type:handlebarsUtils.PARTIAL_EXPRESSION, result:true, rstr:'{{>any\nthing}}'},
{str:'{{>any\r\nthing}}', type:handlebarsUtils.PARTIAL_EXPRESSION, result:true, rstr:'{{>any\r\nthing}}'},
// with ~
{str:'{{~>anything}}', type:handlebarsUtils.PARTIAL_EXPRESSION, result:true, rstr:'{{~>anything}}'},
{str:'{{~>anything~}}', type:handlebarsUtils.PARTIAL_EXPRESSION, result:true, rstr:'{{~>anything~}}'},
{str:'{{~> anything ~}}', type:handlebarsUtils.PARTIAL_EXPRESSION, result:true, rstr:'{{~> anything ~}}'},
// invalid
{str:'{{@anything}}', type:handlebarsUtils.PARTIAL_EXPRESSION, result:false},
];

@@ -318,12 +174,26 @@ arr.forEach(function(obj) {

it("handlebars-handlebarsUtilss#isReservedChar test", function() {
it("handlebars-utils#isValidExpression dataVarExpressionRegExp test", function() {
[
'#', '/', '>', '@', '^', '!'
].forEach(function(c) {
var r = handlebarsUtils.isReservedChar(c);
expect(r).to.equal(true);
// basic
{str:'{{@anything}}', type:handlebarsUtils.DATA_VAR_EXPRESSION, result:true, rstr:'{{@anything}}'},
{str:'{{@ anything }}', type:handlebarsUtils.DATA_VAR_EXPRESSION, result:true, rstr:'{{@ anything }}'},
// with \r and \n
{str:'{{@any\rthing}}', type:handlebarsUtils.DATA_VAR_EXPRESSION, result:true, rstr:'{{@any\rthing}}'},
{str:'{{@any\nthing}}', type:handlebarsUtils.DATA_VAR_EXPRESSION, result:true, rstr:'{{@any\nthing}}'},
{str:'{{@any\r\nthing}}', type:handlebarsUtils.DATA_VAR_EXPRESSION, result:true, rstr:'{{@any\r\nthing}}'},
// with ~
{str:'{{~@anything}}', type:handlebarsUtils.DATA_VAR_EXPRESSION, result:true, rstr:'{{~@anything}}'},
{str:'{{~@anything~}}', type:handlebarsUtils.DATA_VAR_EXPRESSION, result:true, rstr:'{{~@anything~}}'},
{str:'{{~@ anything ~}}', type:handlebarsUtils.DATA_VAR_EXPRESSION, result:true, rstr:'{{~@ anything ~}}'},
// invalid
{str:'{{>anything}}', type:handlebarsUtils.DATA_VAR_EXPRESSION, result:false},
].forEach(function(obj) {
var r = handlebarsUtils.isValidExpression(obj.str, 0, obj.type);
utils.testIsValidExpression(r, obj);
});
});
it("handlebars-handlebarsUtilss#isBranchExpression test", function() {
it("handlebars-utils#isBranchExpression test", function() {
[

@@ -336,2 +206,13 @@ {str: '{{#if xxx}}', rstr: 'if'},

{str: '{{~#if xxx}}', rstr: 'if'},
{str: '{{~#if xxx}}{{#if xxx}}', rstr: 'if'},
{str: '{{~#if xxx}}x{{#if xxx}}', rstr: 'if'},
{str: '{{~#if xxx}} x {{#if xxx}}', rstr: 'if'},
{str: '{{~# if xxx}}', rstr: 'if'},
{str: '{{~#if xxx~}}', rstr: 'if'},
{str: '{{~#if xxx~}}{{#if xxx}}', rstr: 'if'},
{str: '{{~#if xxx~}}x{{#if xxx}}', rstr: 'if'},
{str: '{{~#if xxx~}} x {{#if xxx}}', rstr: 'if'},
{str: '{{~# if xxx~}}', rstr: 'if'},
{str: '{{#with xxx}}', rstr: 'with'},

@@ -344,2 +225,9 @@ {str: '{{#each xxx}}', rstr: 'each'},

{str: '{{~^msg xxx}}', rstr: 'msg'},
{str: '{{^}}', rstr: false},
{str: '{{~^}}', rstr: false},
{str: '{{~^~}}', rstr: false},
{str: '{{~ ^ ~}}', rstr: false},
// illegal handlebars format

@@ -353,3 +241,3 @@ {str: '{{#t-ag xxx}}', rstr: 't-ag'}

it("handlebars-handlebarsUtilss#isBranchEndExpression test", function() {
it("handlebars-utils#isBranchEndExpression test", function() {
[

@@ -362,2 +250,13 @@ {str: '{{/if}}', rstr: 'if'},

{str: '{{~/if}}', rstr: 'if'},
{str: '{{~/if}}{{/if}}', rstr: 'if'},
{str: '{{~/if}}x{{/if}}', rstr: 'if'},
{str: '{{~/if}} x {{/if}}', rstr: 'if'},
{str: '{{~/ if }}', rstr: 'if'},
{str: '{{~/if~}}', rstr: 'if'},
{str: '{{~/if~}}{{/if}}', rstr: 'if'},
{str: '{{~/if~}}x{{/if}}', rstr: 'if'},
{str: '{{~/if~}} x {{/if}}', rstr: 'if'},
{str: '{{~/ if ~}}', rstr: 'if'},
{str: '{{/with}}', rstr: 'with'},

@@ -378,3 +277,3 @@ {str: '{{/each}}', rstr: 'each'},

it("handlebars-handlebarsUtilss#isElseExpression test", function() {
it("handlebars-utils#isElseExpression test", function() {
[

@@ -385,3 +284,24 @@ {str: '{{else}}', result:true},

{str: '{{else}}x{{else}}', result:true},
{str: '{{else}} x {{else}}', result:true}
{str: '{{else}} x {{else}}', result:true},
{str: '{{~else~}}', result:true},
{str: '{{~ else ~}}', result:true},
{str: '{{~else~}}{{~else~}}', result:true},
{str: '{{~else~}}x{{~else~}}', result:true},
{str: '{{~else~}} x {{~else~}}', result:true},
{str: '{{^}}', result:true},
{str: '{{~^~}}', result:true},
{str: '{{~ ^ ~}}', result:true},
{str: '{{^ }}', result:true},
{str: '{{^ ~}}', result:true},
{str: '{{^}}{{^}}', result:true},
{str: '{{^}}x{{^}}', result:true},
{str: '{{^}} x {{^}}', result:true},
{str: '{{~^~}}{{~^~}}', result:true},
{str: '{{^msg}}', result:false},
{str: '{{^msg ~}}', result:false},
{str: '{{^ msg}}', result:false},
{str: '{{^ msg }}', result:false}
].forEach(function(obj) {

@@ -393,3 +313,3 @@ var r = handlebarsUtils.isElseExpression(obj.str);

it("handlebars-handlebarsUtilss#isBranchExpressions test", function() {
it("handlebars-utils#isBranchExpressions test", function() {
[

@@ -410,3 +330,5 @@ {str: '{{#if xxx}}', result:true},

{str: '{{else}}', result:false},
{str: '{{^}}', result:false},
{str: '{{expression}}', result:false},
{str: '{{@partial}}', result:false},
{str: '{{{expression}}}', result:false},

@@ -419,316 +341,193 @@ ].forEach(function(obj) {

it("handlebars-handlebarsUtilss - extract basic Handlebars {{#if xxx}} statement test", function() {
var s = "{{#if xxx}} a {{/if}} zzzzzzzz";
var r = handlebarsUtils.extractBranchStmt(s, 0, false);
expect(r['stmt']).to.equal('{{#if xxx}} a {{\/if}}');
it("handlebars-utils#isReservedChar test", function() {
[
'#', '/', '>', '@', '^', '!',
'~#', '~/', '~>', '~@', '~^', '~!'
].forEach(function(s) {
var r = handlebarsUtils.isReservedChar(s, 0);
expect(r).to.equal(true);
});
});
it("handlebars-handlebarsUtilss - extract basic Handlebars {{#if xxx}} statement with {{else}} test", function() {
var s = "{{#if xxx}} a {{else}} b {{/if}} zzzzzzzz";
var r = handlebarsUtils.extractBranchStmt(s, 0, false);
expect(r['stmt']).to.equal('{{#if xxx}} a {{else}} b {{\/if}}');
});
it("context-parser-handlebars#_handleCommentExpression test", function() {
var parser = new ContextParserHandlebars();
[
{str: '{{! comment }}', type:handlebarsUtils.COMMENT_EXPRESSION_SHORT_FORM, result:14},
{str: '{{! comment }} }}', type:handlebarsUtils.COMMENT_EXPRESSION_SHORT_FORM, result:14},
{str: '{{!-- comment --}}', type:handlebarsUtils.COMMENT_EXPRESSION_LONG_FORM, result:18},
{str: '{{!-- comment --}} --}}', type:handlebarsUtils.COMMENT_EXPRESSION_LONG_FORM, result:18},
{str: '{{!-- comment }} --}}', type:handlebarsUtils.COMMENT_EXPRESSION_LONG_FORM, result:22},
it("handlebars-handlebarsUtilss - extract nested Handlebars {{#if xxx}} statement test", function() {
var s = "{{#if xxx}} a {{#if}} b {{else}} c {{/if}} d {{else}} e {{#if}} f {{else}} h {{/if}} i {{/if}} zzzzzzzz";
var r = handlebarsUtils.extractBranchStmt(s, 0, false);
expect(r['stmt']).to.equal('{{#if xxx}} a {{#if}} b {{else}} c {{/if}} d {{else}} e {{#if}} f {{else}} h {{/if}} i {{/if}}');
// these cases are guarded against by isCommentExpression
{str: '{{!-- comment }}', type:handlebarsUtils.COMMENT_EXPRESSION_SHORT_FORM, result:16},
{str: '{{! comment --}}', type:handlebarsUtils.COMMENT_EXPRESSION_LONG_FORM, result:16}
].forEach(function(obj) {
var r = parser._handleCommentExpression(obj.str, 0, obj.str.length, obj.type);
expect(r).to.equal(obj.result);
});
});
it("handlebars-handlebarsUtilss - extract basic Handlebars {{#with xxx}} statement test", function() {
var s = "{{#with xxx}} a {{/with}} zzzzzzzz";
var r = handlebarsUtils.extractBranchStmt(s, 0, false);
expect(r['stmt']).to.equal('{{#with xxx}} a {{\/with}}');
it("handlebars-utils - build basic branch AST test", function() {
var s = "{{#if xxx}} a b {{/if}}xxxxxxx";
var ast = handlebarsUtils.buildBranchAst(s, 0);
expect(ast.program[0].type).to.equal('branch');
expect(ast.program[0].content).to.equal('{{#if xxx}}');
expect(ast.program[1].type).to.equal('content');
expect(ast.program[1].content).to.equal(' a b ');
expect(ast.program[2].type).to.equal('branchend');
expect(ast.program[2].content).to.equal('{{/if}}');
expect(ast.inverse).to.deep.equal([]);
expect(ast.index).to.equal(22);
var r = handlebarsUtils.analyseBranchAst(ast, 1);
expect(r.output).to.equal('{{#if xxx}} a b {{/if}}');
});
it("handlebars-handlebarsUtilss - extract basic Handlebars {{#with xxx}} statement with {{else}} test", function() {
var s = "{{#with xxx}} a {{else}} b {{/with}} zzzzzzzz";
var r = handlebarsUtils.extractBranchStmt(s, 0, false);
expect(r['stmt']).to.equal('{{#with xxx}} a {{else}} b {{\/with}}');
it("handlebars-utils - build basic branch with {{expression}} AST test", function() {
var s = "{{#if xxx}} a b {{expression}} {{/if}}xxxxxxx";
var ast = handlebarsUtils.buildBranchAst(s, 0);
expect(ast.program[0].type).to.equal('branch');
expect(ast.program[0].content).to.equal('{{#if xxx}}');
expect(ast.program[1].type).to.equal('content');
expect(ast.program[1].content).to.equal(' a b {{expression}} ');
expect(ast.program[2].type).to.equal('branchend');
expect(ast.program[2].content).to.equal('{{/if}}');
expect(ast.inverse).to.deep.equal([]);
expect(ast.index).to.equal(37);
var r = handlebarsUtils.analyseBranchAst(ast, 1);
expect(r.output).to.equal('{{#if xxx}} a b {{{yd expression}}} {{/if}}');
});
it("handlebars-handlebarsUtilss - extract nested Handlebars {{#with xxx}} statement test", function() {
var s = "{{#with xxx}} a {{#with}} b {{else}} c {{/with}} d {{else}} e {{#with}} f {{else}} h {{/with}} i {{/with}} zzzzzzzz";
var r = handlebarsUtils.extractBranchStmt(s, 0, false);
expect(r['stmt']).to.equal('{{#with xxx}} a {{#with}} b {{else}} c {{/with}} d {{else}} e {{#with}} f {{else}} h {{/with}} i {{/with}}');
});
it("handlebars-utils - build basic branch with {{!comment}} AST test", function() {
var s = "{{#if xxx}} a b {{!--comment {{#if xxx}} abc {{/if}} --}} {{/if}}xxxxxxx";
var ast = handlebarsUtils.buildBranchAst(s, 0);
expect(ast.program[0].type).to.equal('branch');
expect(ast.program[0].content).to.equal('{{#if xxx}}');
expect(ast.program[1].type).to.equal('content');
expect(ast.program[1].content).to.equal(' a b {{!--comment {{#if xxx}} abc {{/if}} --}} ');
expect(ast.program[2].type).to.equal('branchend');
expect(ast.program[2].content).to.equal('{{/if}}');
expect(ast.inverse).to.deep.equal([]);
expect(ast.index).to.equal(65);
var r = handlebarsUtils.analyseBranchAst(ast, 1);
expect(r.output).to.equal('{{#if xxx}} a b {{!--comment {{#if xxx}} abc {{/if}} --}} {{/if}}');
it("handlebars-handlebarsUtilss - extract basic Handlebars {{#each xxx}} statement test", function() {
var s = "{{#each xxx}} a {{/each}} zzzzzzzz";
var r = handlebarsUtils.extractBranchStmt(s, 0, false);
expect(r['stmt']).to.equal('{{#each xxx}} a {{\/each}}');
s = "{{#if xxx}} a b {{!comment {{#if xxx}} abc {{/if}} --}} {{/if}}xxxxxxx";
ast = handlebarsUtils.buildBranchAst(s, 0);
expect(ast.program[0].type).to.equal('branch');
expect(ast.program[0].content).to.equal('{{#if xxx}}');
expect(ast.program[1].type).to.equal('content');
expect(ast.program[1].content).to.equal(' a b {{!comment {{#if xxx}} abc ');
expect(ast.program[2].type).to.equal('branchend');
expect(ast.program[2].content).to.equal('{{/if}}');
expect(ast.inverse).to.deep.equal([]);
expect(ast.index).to.equal(50);
r = handlebarsUtils.analyseBranchAst(ast, 1);
expect(r.output).to.equal('{{#if xxx}} a b {{!comment {{#if xxx}} abc {{/if}}');
});
it("handlebars-handlebarsUtilss - extract basic Handlebars {{#each xxx}} statement with {{else}} test", function() {
var s = "{{#each xxx}} a {{else}} b {{/each}} zzzzzzzz";
var r = handlebarsUtils.extractBranchStmt(s, 0, false);
expect(r['stmt']).to.equal('{{#each xxx}} a {{else}} b {{\/each}}');
});
it("handlebars-utils - build basic branch with inverse AST test", function() {
var s = "{{#if xxx}} a {{else}} b {{/if}}xxxxxxxx";
var ast = handlebarsUtils.buildBranchAst(s, 0);
expect(ast.program[0].type).to.equal('branch');
expect(ast.program[0].content).to.equal('{{#if xxx}}');
expect(ast.program[1].type).to.equal('content');
expect(ast.program[1].content).to.equal(' a ');
it("handlebars-handlebarsUtilss - extract nested Handlebars {{#each xxx}} statement test", function() {
var s = "{{#each xxx}} a {{#each}} b {{else}} c {{/each}} d {{else}} e {{#each}} f {{else}} h {{/each}} i {{/each}} zzzzzzzz";
var r = handlebarsUtils.extractBranchStmt(s, 0, false);
expect(r['stmt']).to.equal('{{#each xxx}} a {{#each}} b {{else}} c {{/each}} d {{else}} e {{#each}} f {{else}} h {{/each}} i {{/each}}');
expect(ast.inverse[0].type).to.equal('branchelse');
expect(ast.inverse[0].content).to.equal('{{else}}');
expect(ast.inverse[1].type).to.equal('content');
expect(ast.inverse[1].content).to.equal(' b ');
expect(ast.inverse[2].type).to.equal('branchend');
expect(ast.inverse[2].content).to.equal('{{/if}}');
expect(ast.index).to.equal(31);
var r = handlebarsUtils.analyseBranchAst(ast, 1);
expect(r.output).to.equal('{{#if xxx}} a {{else}} b {{/if}}');
});
it("handlebars-handlebarsUtilss - extract basic Handlebars {{#list xxx}} statement test", function() {
var s = "{{#list xxx}} a {{/list}} zzzzzzzz";
var r = handlebarsUtils.extractBranchStmt(s, 0, false);
expect(r['stmt']).to.equal('{{#list xxx}} a {{\/list}}');
});
it("handlebars-utils - build nested branch AST test", function() {
var s = "{{#if xxx}} a {{#if yyy}} b {{else}} c {{/if}} d {{else}} e {{#if}} f {{else}} g {{/if}} h {{/if}}xxxxxx";
var ast = handlebarsUtils.buildBranchAst(s, 0);
expect(ast.program[0].type).to.equal('branch');
expect(ast.program[0].content).to.equal('{{#if xxx}}');
expect(ast.program[1].type).to.equal('content');
expect(ast.program[1].content).to.equal(' a ');
expect(ast.program[2].type).to.equal('node');
expect(ast.program[2].content.program[0].type).to.equal('branch');
expect(ast.program[2].content.program[0].content).to.equal('{{#if yyy}}');
expect(ast.program[2].content.program[1].type).to.equal('content');
expect(ast.program[2].content.program[1].content).to.equal(' b ');
expect(ast.program[2].content.inverse[0].type).to.equal('branchelse');
expect(ast.program[2].content.inverse[0].content).to.equal('{{else}}');
expect(ast.program[2].content.inverse[1].type).to.equal('content');
expect(ast.program[2].content.inverse[1].content).to.equal(' c ');
expect(ast.program[2].content.inverse[2].type).to.equal('branchend');
expect(ast.program[2].content.inverse[2].content).to.equal('{{/if}}');
expect(ast.program[3].type).to.equal('content');
expect(ast.program[3].content).to.equal(' d ');
it("handlebars-handlebarsUtilss - extract basic Handlebars {{#list xxx}} statement with {{else}} test", function() {
var s = "{{#list xxx}} a {{else}} b {{/list}} zzzzzzzz";
var r = handlebarsUtils.extractBranchStmt(s, 0, false);
expect(r['stmt']).to.equal('{{#list xxx}} a {{else}} b {{\/list}}');
expect(ast.inverse[0].type).to.equal('branchelse');
expect(ast.inverse[0].content).to.equal('{{else}}');
expect(ast.inverse[1].type).to.equal('content');
expect(ast.inverse[1].content).to.equal(' e ');
expect(ast.inverse[2].type).to.equal('node');
expect(ast.inverse[2].content.program[0].type).to.equal('branch');
expect(ast.inverse[2].content.program[0].content).to.equal('{{#if}}');
expect(ast.inverse[2].content.program[1].type).to.equal('content');
expect(ast.inverse[2].content.program[1].content).to.equal(' f ');
expect(ast.inverse[2].content.inverse[0].type).to.equal('branchelse');
expect(ast.inverse[2].content.inverse[0].content).to.equal('{{else}}');
expect(ast.inverse[2].content.inverse[1].type).to.equal('content');
expect(ast.inverse[2].content.inverse[1].content).to.equal(' g ');
expect(ast.inverse[2].content.inverse[2].type).to.equal('branchend');
expect(ast.inverse[2].content.inverse[2].content).to.equal('{{/if}}');
expect(ast.inverse[3].type).to.equal('content');
expect(ast.inverse[3].content).to.equal(' h ');
expect(ast.inverse[4].type).to.equal('branchend');
expect(ast.inverse[4].content).to.equal('{{/if}}');
expect(ast.index).to.equal(97);
var r = handlebarsUtils.analyseBranchAst(ast, 1);
expect(r.output).to.equal('{{#if xxx}} a {{#if yyy}} b {{else}} c {{/if}} d {{else}} e {{#if}} f {{else}} g {{/if}} h {{/if}}');
});
it("handlebars-handlebarsUtilss - extract nested Handlebars {{#list xxx}} statement test", function() {
var s = "{{#list xxx}} a {{#list yyy}} b {{else}} c {{/list}} d {{else}} e {{#list zzz}} f {{else}} h {{/list}} i {{/list}} zzzzzzzz";
var r = handlebarsUtils.extractBranchStmt(s, 0, false);
expect(r['stmt']).to.equal('{{#list xxx}} a {{#list yyy}} b {{else}} c {{/list}} d {{else}} e {{#list zzz}} f {{else}} h {{/list}} i {{/list}}');
it("handlebars-utils - build parallel branch AST test", function() {
var s = "{{#if xxx}} a {{#if yyy}} b {{else}} c {{/if}} d {{#if}} e {{else}} f {{/if}} g {{/if}}xxxxxxx";
var ast = handlebarsUtils.buildBranchAst(s, 0);
expect(ast.program[0].type).to.equal('branch');
expect(ast.program[0].content).to.equal('{{#if xxx}}');
expect(ast.program[1].type).to.equal('content');
expect(ast.program[1].content).to.equal(' a ');
expect(ast.program[2].type).to.equal('node');
expect(ast.program[2].content.program[0].type).to.equal('branch');
expect(ast.program[2].content.program[0].content).to.equal('{{#if yyy}}');
expect(ast.program[2].content.program[1].type).to.equal('content');
expect(ast.program[2].content.program[1].content).to.equal(' b ');
expect(ast.program[2].content.inverse[0].type).to.equal('branchelse');
expect(ast.program[2].content.inverse[0].content).to.equal('{{else}}');
expect(ast.program[2].content.inverse[1].type).to.equal('content');
expect(ast.program[2].content.inverse[1].content).to.equal(' c ');
expect(ast.program[2].content.inverse[2].type).to.equal('branchend');
expect(ast.program[2].content.inverse[2].content).to.equal('{{/if}}');
expect(ast.program[3].type).to.equal('content');
expect(ast.program[3].content).to.equal(' d ');
expect(ast.program[4].type).to.equal('node');
expect(ast.program[4].content.program[0].type).to.equal('branch');
expect(ast.program[4].content.program[0].content).to.equal('{{#if}}');
expect(ast.program[4].content.program[1].type).to.equal('content');
expect(ast.program[4].content.program[1].content).to.equal(' e ');
expect(ast.program[4].content.inverse[0].type).to.equal('branchelse');
expect(ast.program[4].content.inverse[0].content).to.equal('{{else}}');
expect(ast.program[4].content.inverse[1].type).to.equal('content');
expect(ast.program[4].content.inverse[1].content).to.equal(' f ');
expect(ast.program[4].content.inverse[2].type).to.equal('branchend');
expect(ast.program[4].content.inverse[2].content).to.equal('{{/if}}');
expect(ast.program[5].type).to.equal('content');
expect(ast.program[5].content).to.equal(' g ');
expect(ast.program[6].type).to.equal('branchend');
expect(ast.program[6].content).to.equal('{{/if}}');
expect(ast.index).to.equal(86);
var r = handlebarsUtils.analyseBranchAst(ast, 1);
expect(r.output).to.equal('{{#if xxx}} a {{#if yyy}} b {{else}} c {{/if}} d {{#if}} e {{else}} f {{/if}} g {{/if}}');
});
it("handlebars-handlebarsUtilss - extract basic Handlebars {{#tag xxx}} statement test", function() {
var s = "{{#tag xxx}} a {{/tag}} zzzzzzzz";
var r = handlebarsUtils.extractBranchStmt(s, 0, false);
expect(r['stmt']).to.equal('{{#tag xxx}} a {{\/tag}}');
});
it("handlebars-handlebarsUtilss - extract basic Handlebars {{#tag xxx}} statement with {{else}} test", function() {
var s = "{{#tag xxx}} a {{else}} b {{/tag}} zzzzzzzz";
var r = handlebarsUtils.extractBranchStmt(s, 0, false);
expect(r['stmt']).to.equal('{{#tag xxx}} a {{else}} b {{\/tag}}');
});
it("handlebars-handlebarsUtilss - extract nested Handlebars {{#tag xxx}} statement test", function() {
var s = "{{#tag xxx}} a {{#tag yyy}} b {{else}} c {{/tag}} d {{else}} e {{#tag zzz}} f {{else}} h {{/tag}} i {{/tag}} zzzzzzzz";
var r = handlebarsUtils.extractBranchStmt(s, 0, false);
expect(r['stmt']).to.equal('{{#tag xxx}} a {{#tag yyy}} b {{else}} c {{/tag}} d {{else}} e {{#tag zzz}} f {{else}} h {{/tag}} i {{/tag}}');
});
it("handlebars-handlebarsUtilss - extract basic Handlebars {{^msg}} statement test", function() {
var s = "{{^msg}} a {{/msg}} zzzzzzzz";
var r = handlebarsUtils.extractBranchStmt(s, 0, false);
expect(r['stmt']).to.equal('{{^msg}} a {{\/msg}}');
});
it("handlebars-handlebarsUtilss - extract basic Handlebars {{^msg}} statement with {{else}} test", function() {
var s = "{{^msg}} a {{else}} b {{/msg}} zzzzzzzz";
var r = handlebarsUtils.extractBranchStmt(s, 0, false);
expect(r['stmt']).to.equal('{{^msg}} a {{else}} b {{\/msg}}');
});
it("handlebars-handlebarsUtilss - extract nested Handlebars {{^msg}} statement test", function() {
var s = "{{^msg}} a {{^msg}} b {{else}} c {{/msg}} d {{else}} e {{^msg}} f {{else}} h {{/msg}} i {{/msg}} zzzzzzzz";
var r = handlebarsUtils.extractBranchStmt(s, 0, false);
expect(r['stmt']).to.equal('{{^msg}} a {{^msg}} b {{else}} c {{/msg}} d {{else}} e {{^msg}} f {{else}} h {{/msg}} i {{/msg}}');
});
it("handlebars-handlebarsUtilss - extract basic Handlebars {{#unless xxx}} statement test", function() {
var s = "{{#unless xxx}} a {{/unless}} zzzzzzzz";
var r = handlebarsUtils.extractBranchStmt(s, 0, false);
expect(r['stmt']).to.equal('{{#unless xxx}} a {{\/unless}}');
});
it("handlebars-handlebarsUtilss - extract nested Handlebars {{#unless xxx}} statement test", function() {
var s = "{{#unless xxx}} a {{#unless xxx}} b {{/unless}} c {{/unless}} zzzzzzzz";
var r = handlebarsUtils.extractBranchStmt(s, 0, false);
expect(r['stmt']).to.equal('{{#unless xxx}} a {{#unless xxx}} b {{/unless}} c {{/unless}}');
});
it("handlebars-handlebarsUtilss - extract nested Handlebars {{#unless xxx}} statement exception test", function() {
var s = "{{#if} aaaaaaaa";
try {
var r = handlebarsUtils.extractBranchStmt(s, 0, false);
expect(false).to.equal(true);
} catch (err) {
expect(true).to.equal(true);
}
});
it("handlebars-handlebarsUtilss - parse basic Handlebars {{#if xxx}} statement parseAstTreeState test", function() {
var s = "<a href='{{#if xxx}} a {{/if}}'>";
var obj = handlebarsUtils.extractBranchStmt(s, 9, true);
expect(obj.stmt).to.equal('{{#if xxx}} a {{/if}}');
var ast = handlebarsUtils.parseBranchStmt(obj.stmt);
var r = handlebarsUtils.parseAstTreeState(ast, 39, obj);
// a (1st branch)
// 'empty string' (2nd branch)
expect(r.lastStates[0]).to.equal(39);
expect(r.lastStates[1]).to.equal(39);
});
it("handlebars-handlebarsUtilss - parse basic Handlebars {{#if xxx}} statement with {{else}} parseAstTreeState test", function() {
var s = "<a href='{{#if xxx}} a {{else}} b {{/if}}'>";
var obj = handlebarsUtils.extractBranchStmt(s, 9, true);
expect(obj.stmt).to.equal('{{#if xxx}} a {{else}} b {{/if}}');
var ast = handlebarsUtils.parseBranchStmt(obj.stmt);
var r = handlebarsUtils.parseAstTreeState(ast, 39, obj);
// a (1st branch)
// b (2nd branch)
expect(r.lastStates[0]).to.equal(39);
expect(r.lastStates[1]).to.equal(39);
});
it("handlebars-handlebarsUtilss - parse nested Handlebars {{#if xxx}} statement parseAstTreeState test", function() {
var s = "<a href='{{#if xxx}} a {{#if}} b {{/if}} c {{/if}}'>";
var obj = handlebarsUtils.extractBranchStmt(s, 9, true);
expect(obj.stmt).to.equal('{{#if xxx}} a {{#if}} b {{/if}} c {{/if}}');
var ast = handlebarsUtils.parseBranchStmt(obj.stmt);
var r = handlebarsUtils.parseAstTreeState(ast, 39, obj);
// a b c (1st branch)
// a c (1st branch)
// 'empty string' (2nd branch)
expect(r.lastStates[0]).to.equal(39);
expect(r.lastStates[1]).to.equal(39);
});
it("handlebars-handlebarsUtilss - parse nested Handlebars {{#if xxx}} statement with {{else}} parseAstTreeState test", function() {
var s = "<a href='{{#if xxx}} a {{#if}} b {{else}} c {{/if}} d {{else}} e {{#if}} f {{else}} g {{/if}} h {{/if}}'>";
var obj = handlebarsUtils.extractBranchStmt(s, 9, true);
expect(obj.stmt).to.equal('{{#if xxx}} a {{#if}} b {{else}} c {{/if}} d {{else}} e {{#if}} f {{else}} g {{/if}} h {{/if}}');
var ast = handlebarsUtils.parseBranchStmt(obj.stmt);
var r = handlebarsUtils.parseAstTreeState(ast, 39, obj);
// a b d (1st branch)
// a c d (1st branch)
// e f h (2nd branch)
// e g h (2nd branch)
expect(r.lastStates[0]).to.equal(39);
expect(r.lastStates[1]).to.equal(39);
});
it("handlebars-handlebarsUtilss - parse parallel Handlebars {{#if xxx}} statement parseAstTreeState test", function() {
var s = "<a href='{{#if xxx}} a {{#if}} b {{else}} c {{/if}} d {{#if}} b {{else}} c {{/if}} f {{else}} e {{#if xyz}} f {{else}} g {{/if}} h {{/if}}'>";
var obj = handlebarsUtils.extractBranchStmt(s, 9, true);
expect(obj.stmt).to.equal('{{#if xxx}} a {{#if}} b {{else}} c {{/if}} d {{#if}} b {{else}} c {{/if}} f {{else}} e {{#if xyz}} f {{else}} g {{/if}} h {{/if}}');
var ast = handlebarsUtils.parseBranchStmt(obj.stmt);
var r = handlebarsUtils.parseAstTreeState(ast, 39, obj);
// a b d b f (1st branch)
// a c d b f (1st branch)
// a b d c f (1st branch)
// a c d c f (1st branch)
// e f h (2nd branch)
// e g h (2nd branch)
expect(r.lastStates[0]).to.equal(39);
expect(r.lastStates[1]).to.equal(39);
});
it("handlebars-handlebarsUtilss - parse parallel Handlebars {{#if}} statement with {{else}} parseAstTreeState test", function() {
var s = "<a href='{{#if xxx}} a {{#if}} b {{else}} c {{/if}} d {{#if}} b {{else}} c {{/if}} f {{else}} e {{#if}} f {{else}} g {{/if}} h {{#if}} 1 {{else}} 2 {{/if}} h {{/if}}'>";
var obj = handlebarsUtils.extractBranchStmt(s, 9, true);
expect(obj.stmt).to.equal('{{#if xxx}} a {{#if}} b {{else}} c {{/if}} d {{#if}} b {{else}} c {{/if}} f {{else}} e {{#if}} f {{else}} g {{/if}} h {{#if}} 1 {{else}} 2 {{/if}} h {{/if}}');
var ast = handlebarsUtils.parseBranchStmt(obj.stmt);
var r = handlebarsUtils.parseAstTreeState(ast, 39, obj);
// a b d b f (1st branch)
// a c d b f (1st branch)
// a b d c f (1st branch)
// a c d c f (1st branch)
// e f h 1 h (2nd branch)
// e g h 1 h (2nd branch)
// e f h 2 h (2nd branch)
// e g h 2 h (2nd branch)
expect(r.lastStates[0]).to.equal(39);
expect(r.lastStates[1]).to.equal(39);
});
it("handlebars-handlebarsUtilss - parse basic Handlebars {{^msg}} statement parseAstTreeState test", function() {
var s = "<a href='{{^msg}} a {{/msg}}'>";
var obj = handlebarsUtils.extractBranchStmt(s, 9, true);
expect(obj.stmt).to.equal('{{^msg}} a {{/msg}}');
var ast = handlebarsUtils.parseBranchStmt(obj.stmt);
var r = handlebarsUtils.parseAstTreeState(ast, 39, obj);
// a (1st branch)
// b (2nd branch)
expect(r.lastStates[0]).to.equal(39);
expect(r.lastStates[1]).to.equal(39);
});
it("handlebars-handlebarsUtilss - parse basic Handlebars {{^msg}} statement with {{else}} parseAstTreeState test", function() {
var s = "<a href='{{^msg}} a {{else}} b {{/msg}}'>";
var obj = handlebarsUtils.extractBranchStmt(s, 9, true);
expect(obj.stmt).to.equal('{{^msg}} a {{else}} b {{/msg}}');
var ast = handlebarsUtils.parseBranchStmt(obj.stmt);
var r = handlebarsUtils.parseAstTreeState(ast, 39, obj);
// a (1st branch)
// b (2nd branch)
expect(r.lastStates[0]).to.equal(39);
expect(r.lastStates[1]).to.equal(39);
});
it("handlebars-handlebarsUtilss - parse nested Handlebars {{^msg}} statement parseAstTreeState test", function() {
var s = "<a href='{{^msg}} a {{^msg}} b {{/msg}} c {{/msg}}'>";
var obj = handlebarsUtils.extractBranchStmt(s, 9, true);
expect(obj.stmt).to.equal('{{^msg}} a {{^msg}} b {{/msg}} c {{/msg}}');
var ast = handlebarsUtils.parseBranchStmt(obj.stmt);
var r = handlebarsUtils.parseAstTreeState(ast, 39, obj);
// a b c (1st branch)
// a c (1st branch)
// 'empty string' (2nd branch)
expect(r.lastStates[0]).to.equal(39);
expect(r.lastStates[1]).to.equal(39);
});
it("handlebars-handlebarsUtilss - parse nested Handlebars {{^msg}} statement with {{else}} parseAstTreeState test", function() {
var s = "<a href='{{^msg}} a {{^msg}} b {{else}} c {{/msg}} d {{else}} e {{^msg}} f {{else}} g {{/msg}} h {{/msg}}'>";
var obj = handlebarsUtils.extractBranchStmt(s, 9, true);
expect(obj.stmt).to.equal('{{^msg}} a {{^msg}} b {{else}} c {{/msg}} d {{else}} e {{^msg}} f {{else}} g {{/msg}} h {{/msg}}');
var ast = handlebarsUtils.parseBranchStmt(obj.stmt);
var r = handlebarsUtils.parseAstTreeState(ast, 39, obj);
// a b d (1st branch)
// a c d (1st branch)
// e f h (2nd branch)
// e g h (2nd branch)
expect(r.lastStates[0]).to.equal(39);
expect(r.lastStates[1]).to.equal(39);
});
it("handlebars-handlebarsUtilss - parse parallel Handlebars {{^msg}} statement parseAstTreeState test", function() {
var s = "<a href='{{^msg}} a {{^msg}} b {{else}} c {{/msg}} d {{^msg}} b {{else}} c {{/msg}} f {{else}} e {{^msg}} f {{else}} g {{/msg}} h {{/msg}}'>";
var obj = handlebarsUtils.extractBranchStmt(s, 9, true);
expect(obj.stmt).to.equal('{{^msg}} a {{^msg}} b {{else}} c {{/msg}} d {{^msg}} b {{else}} c {{/msg}} f {{else}} e {{^msg}} f {{else}} g {{/msg}} h {{/msg}}');
var ast = handlebarsUtils.parseBranchStmt(obj.stmt);
var r = handlebarsUtils.parseAstTreeState(ast, 39, obj);
// a b d b f (1st branch)
// a c d b f (1st branch)
// a b d c f (1st branch)
// a c d c f (1st branch)
// e f h (2nd branch)
// e g h (2nd branch)
expect(r.lastStates[0]).to.equal(39);
expect(r.lastStates[1]).to.equal(39);
});
it("handlebars-handlebarsUtilss - parse parallel Handlebars {{^msg}} statement with {{else}} parseAstTreeState test", function() {
var s = "<a href='{{^msg}} a {{^msg}} b {{else}} c {{/msg}} d {{^msg}} b {{else}} c {{/msg}} f {{else}} e {{^msg}} f {{else}} g {{/msg}} h {{^msg}} 1 {{else}} 2 {{/msg}} h {{/msg}}'>";
var obj = handlebarsUtils.extractBranchStmt(s, 9, true);
expect(obj.stmt).to.equal('{{^msg}} a {{^msg}} b {{else}} c {{/msg}} d {{^msg}} b {{else}} c {{/msg}} f {{else}} e {{^msg}} f {{else}} g {{/msg}} h {{^msg}} 1 {{else}} 2 {{/msg}} h {{/msg}}');
var ast = handlebarsUtils.parseBranchStmt(obj.stmt);
var r = handlebarsUtils.parseAstTreeState(ast, 39, obj);
// a b d b f (1st branch)
// a c d b f (1st branch)
// a b d c f (1st branch)
// a c d c f (1st branch)
// e f h 1 h (2nd branch)
// e g h 1 h (2nd branch)
// e f h 2 h (2nd branch)
// e g h 2 h (2nd branch)
expect(r.lastStates[0]).to.equal(39);
expect(r.lastStates[1]).to.equal(39);
});
it("handlebars-handlebarsUtilss - parse basic Handlebars {{#if xxx}} broken statement with {{else}} parseAstTreeState exception test", function() {
var s = "<a href='{{#if xxx}} a {{else}} b'> {{/if}}'>";
var obj = handlebarsUtils.extractBranchStmt(s, 9, true);
var ast = handlebarsUtils.parseBranchStmt(obj.stmt);
try {
var r = handlebarsUtils.parseAstTreeState(ast, 39, obj);
expect(false).to.equal(true);
} catch (err) {
expect(true).to.equal(true);
}
});
});
}());

@@ -17,3 +17,3 @@ /*

exports.testExpression = function(result, obj) {
expect(result.isPrefixWithKnowFilter).to.equal(obj.isPrefixWithKnowFilter);
expect(result.isPrefixWithKnownFilter).to.equal(obj.isPrefixWithKnownFilter);
expect(result.filter).to.equal(obj.filter);

@@ -20,0 +20,0 @@ expect(result.isSingleIdentifier).to.equal(obj.isSingleIdentifier);

Sorry, the diff of this file is not supported yet

SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc