Skip to content
This repository has been archived by the owner on Sep 5, 2018. It is now read-only.

Commit

Permalink
Merge pull request #67 from neraliu/fix-id-parsing-issue
Browse files Browse the repository at this point in the history
add the parseEscapeExpression function to fix singleID issue.
  • Loading branch information
adon-at-work committed Jun 15, 2015
2 parents 79e10ed + 5dac988 commit b435da1
Show file tree
Hide file tree
Showing 2 changed files with 78 additions and 33 deletions.
50 changes: 40 additions & 10 deletions src/handlebars-utils.js
Original file line number Diff line number Diff line change
Expand Up @@ -35,32 +35,32 @@ HandlebarsUtils.rawEndBlockRegExp = /^\{\{\{\{\/([^\s!"#%&'\(\)\*\+,\.\/;<=>@\[\
/* '{{{' '~'? 'space'* '@'? 'space'* ('not {}~'+) 'space'* ('not {}~'+) '~'? non-greedy '}}}' and not follow by '}' */
HandlebarsUtils.RAW_EXPRESSION = 1; // {{{expression}}}
// HandlebarsUtils.rawExpressionRegExp = /^\{\{\{\s*([^\s!"#%&'\(\)\*\+,\.\/;<=>\[\\\]\^`\{\|\}\~]+)\s*?\}\}\}(?!})/;
HandlebarsUtils.rawExpressionRegExp = /^\{\{\{~?\s*@?\s*([^\s\}\{~]+)\s*([^\}\{~]*)~??\}\}\}(?!})/;
HandlebarsUtils.rawExpressionRegExp = /^\{\{\{~?\s*@?\s*([^\s\}\{~]+)\s*([^\}\{~]*)~?\}\}\}(?!})/;

/* '{{' '~'? 'space'* '@'? 'space'* ('not {}~'+) 'space'* ('not {}~'+) '~'? non-greedy '}}' and not follow by '}' */
/* '{{' '~'? 'space'* '@'? 'space'* ('not {}~'+) '~'? non-greedy '}}' and not follow by '}' */
HandlebarsUtils.ESCAPE_EXPRESSION = 2; // {{expression}}
HandlebarsUtils.escapeExpressionRegExp = /^\{\{~?\s*@?\s*([^\s\}\{~]+)\s*([^\}\{~]*)~??\}\}(?!})/;
HandlebarsUtils.escapeExpressionRegExp = /^\{\{~?\s*@?\s*([^\}\{~]+)~?\}\}(?!})/;

/* '{{' '~'? '>' '\s'* ('not \s, special-char'+) '\s'* 'not ~{}'* non-greedy '}}' and not follow by '}' */
HandlebarsUtils.PARTIAL_EXPRESSION = 3; // {{>.*}}
HandlebarsUtils.partialExpressionRegExp = /^\{\{~?>\s*([^\s!"#%&'\(\)\*\+,\.\/;<=>@\[\\\]\^`\{\|\}\~]+)\s*[^~\}\{]*~??\}\}(?!})/;
HandlebarsUtils.partialExpressionRegExp = /^\{\{~?>\s*([^\s!"#%&'\(\)\*\+,\.\/;<=>@\[\\\]\^`\{\|\}\~]+)\s*[^~\}\{]*~?\}\}(?!})/;

/* '{{' '~'? '&' '\s'* ('not \s, special-char'+) '\s'* 'not ~{}'* non-greedy '}}' and not follow by '}' */
HandlebarsUtils.REFERENCE_EXPRESSION = 11; // {{&.*}}
HandlebarsUtils.referenceExpressionRegExp = /^\{\{~?&\s*([^\s!"#%&'\(\)\*\+,\.\/;<=>@\[\\\]\^`\{\|\}\~]+)\s*[^~\}\{]*~??\}\}(?!})/;
HandlebarsUtils.referenceExpressionRegExp = /^\{\{~?&\s*([^\s!"#%&'\(\)\*\+,\.\/;<=>@\[\\\]\^`\{\|\}\~]+)\s*[^~\}\{]*~?\}\}(?!})/;

/* '{{' '~'? '# or ^' '\s'* ('not \s, special-char'+) '\s'* 'not {}~'* '~'? non-greedy '}}' and not follow by '}' */
HandlebarsUtils.BRANCH_EXPRESSION = 4; // {{#.*}}, {{^.*}}
HandlebarsUtils.branchExpressionRegExp = /^\{\{~?[#|\^]\s*([^\s!"#%&'\(\)\*\+,\.\/;<=>@\[\\\]\^`\{\|\}\~]+)\s*[^\}\{~]*~??\}\}(?!})/;
HandlebarsUtils.branchExpressionRegExp = /^\{\{~?[#|\^]\s*([^\s!"#%&'\(\)\*\+,\.\/;<=>@\[\\\]\^`\{\|\}\~]+)\s*[^\}\{~]*~?\}\}(?!})/;
/* '{{' '~'? '/' '\s'* ('not \s, special-char'+) '\s'* 'not {}~'* '~'? non-greedy '}}' and not follow by '}' */
HandlebarsUtils.BRANCH_END_EXPRESSION = 5; // {{/.*}}
HandlebarsUtils.branchEndExpressionRegExp = /^\{\{~?\/\s*([^\s!"#%&'\(\)\*\+,\.\/;<=>@\[\\\]\^`\{\|\}\~]+)\s*[^\}\{~]*~??\}\}(?!})/;
HandlebarsUtils.branchEndExpressionRegExp = /^\{\{~?\/\s*([^\s!"#%&'\(\)\*\+,\.\/;<=>@\[\\\]\^`\{\|\}\~]+)\s*[^\}\{~]*~?\}\}(?!})/;

/* '{{' '~'? '\s'* 'else' '\s'* 'not {}~'* '~'? non-greedy '}}' and not follow by '}' */
HandlebarsUtils.ELSE_EXPRESSION = 6; // {{else}}, {{^}}
HandlebarsUtils.elseExpressionRegExp = /^\{\{~?\s*else\s*[^\}\{~]*~??\}\}(?!})/;
HandlebarsUtils.elseExpressionRegExp = /^\{\{~?\s*else\s*[^\}\{~]*~?\}\}(?!})/;
/* '{{' '~'? '^'{1} '~'? non-greedy '}}' and not follow by '}' */
HandlebarsUtils.elseShortFormExpressionRegExp = /^\{\{~?\^{1}~??\}\}(?!})/;
HandlebarsUtils.elseShortFormExpressionRegExp = /^\{\{~?\^{1}~?\}\}(?!})/;

/* '{{' '~'? '!--' */
HandlebarsUtils.COMMENT_EXPRESSION_LONG_FORM = 7; // {{!--.*--}}
Expand Down Expand Up @@ -121,7 +121,12 @@ HandlebarsUtils.isValidExpression = function(input, i, type) {
break;
case HandlebarsUtils.ESCAPE_EXPRESSION:
re = HandlebarsUtils.escapeExpressionRegExp.exec(s);
if (re !== null && re[1] !== undefined) {
if (re) {
// NOTE: the re.index field is never been used.
var r = HandlebarsUtils.parseEscapeExpression(re[1]);
re[1] = r[1];
re[2] = r[2];

if (HandlebarsUtils.isReservedChar(re[1], 0)) {
re.tag = false;
re.isSingleID = false;
Expand Down Expand Up @@ -177,6 +182,31 @@ HandlebarsUtils.isValidExpression = function(input, i, type) {
return re;
};

// @function HandlebarsUtils.parseEscapeExpression
HandlebarsUtils.parseEscapeExpression = function(str) {
var j=0, inSquareBracket = false,
fstr = '', re = [];

// the new regexp will capture the tailing space.
str = str.replace(/\s+$/, '');
while(j<str.length) {
// space is defined as \s in the handlebars lex
if (str[j].match(/\s/) && !inSquareBracket) {
break;
} else if (str[j] === '[') {
inSquareBracket = true;
} else if (str[j] === ']') {
inSquareBracket = false;
}
fstr += str[j];
++j;
}

re[1] = fstr;
re[2] = str.substring(j).replace(/^\s+/, '').replace(/\s+$/, '');
return re;
};

// @function HandlebarsUtils.isReservedChar
HandlebarsUtils.isReservedChar = function(input, i) {
var ch = input[i];
Expand Down
61 changes: 38 additions & 23 deletions tests/test-patterns.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,12 @@ var handlebarsUtils = require('../src/handlebars-utils'),

// for handlebars-3.0-spec test only
var expressionTestPatterns = [

// NOTE: result[0]: it is being used in run-handlebars-3.0-spec.js for the AST object type from Handlebars 3.0
// result[1]: it is being used in run-utils-spec.js for isValidExpression test.
// result[2]: it is being used in run-cph-spec.js for consumeExpression test.
// Empty string represents not being used in the unit tests testing.

// valid syntax
{ syntax: '{{escapeexpression}} ', type: '', rstr: '', result: [ 'MustacheStatement', '', '' ]},
{ syntax: '{{&escapeexpression}} ', type: '', rstr: '', result: [ 'MustacheStatement', '', '' ]},
Expand Down Expand Up @@ -238,48 +244,57 @@ exports.rawEndBlockTestPatterns = rawEndBlockTestPatterns;

var escapeExpressionTestPatterns = [
// valid syntax
{ syntax: '{{expression}} ', type: handlebarsUtils.ESCAPE_EXPRESSION, rstr: 'expression', isSingleID: true, result: [ 'MustacheStatement', true, 11 ]},
{ syntax: '{{expression}} ', type: handlebarsUtils.ESCAPE_EXPRESSION, rstr: 'expression', isSingleID: true, result: [ 'MustacheStatement', true, '' ]},
// it is fine to have space in the expression
{ syntax: '{{ expression }} ', type: handlebarsUtils.ESCAPE_EXPRESSION, rstr: 'expression', isSingleID: true, result: [ 'MustacheStatement', true, 11 ]},
{ syntax: '{{ expression }} ', type: handlebarsUtils.ESCAPE_EXPRESSION, rstr: 'expression', isSingleID: true, result: [ 'MustacheStatement', true, '' ]},
// it is fine to have whitespace control in the expression.
{ syntax: '{{~ expression ~}} ', type: handlebarsUtils.ESCAPE_EXPRESSION, rstr: 'expression', isSingleID: true, result: [ 'MustacheStatement', true, 11 ]},
{ syntax: '{{~ expression ~}} ', type: handlebarsUtils.ESCAPE_EXPRESSION, rstr: 'expression', isSingleID: true, result: [ 'MustacheStatement', true, '' ]},
// private variable
{ syntax: '{{@expression}} ', type: handlebarsUtils.ESCAPE_EXPRESSION, rstr: 'expression', isSingleID: true, result: [ 'MustacheStatement', true, 11 ]},
{ syntax: '{{@expression}} ', type: handlebarsUtils.ESCAPE_EXPRESSION, rstr: 'expression', isSingleID: true, result: [ 'MustacheStatement', true, '' ]},
// private variable with space before
{ syntax: '{{@ expression }} ', type: handlebarsUtils.ESCAPE_EXPRESSION, rstr: 'expression', isSingleID: true, result: [ 'MustacheStatement', true, 11 ]},
{ syntax: '{{@ expression }} ', type: handlebarsUtils.ESCAPE_EXPRESSION, rstr: 'expression', isSingleID: true, result: [ 'MustacheStatement', true, '' ]},
// private variable with space after/before
{ syntax: '{{ @ expression }} ', type: handlebarsUtils.ESCAPE_EXPRESSION, rstr: 'expression', isSingleID: true, result: [ 'MustacheStatement', true, 11 ]},
{ syntax: '{{ @ expression }} ', type: handlebarsUtils.ESCAPE_EXPRESSION, rstr: 'expression', isSingleID: true, result: [ 'MustacheStatement', true, '' ]},
// dot as the ID
{ syntax: '{{.}} ', type: handlebarsUtils.ESCAPE_EXPRESSION, rstr: '.', isSingleID: true, result: [ 'MustacheStatement', true, 11 ]},
{ syntax: '{{.}} ', type: handlebarsUtils.ESCAPE_EXPRESSION, rstr: '.', isSingleID: true, result: [ 'MustacheStatement', true, '' ]},
// with / as separator
{ syntax: '{{../name}} ', type: handlebarsUtils.ESCAPE_EXPRESSION, rstr: '../name', isSingleID: true, result: [ 'MustacheStatement', true, 11 ]},
{ syntax: '{{../name ../name}} ', type: handlebarsUtils.ESCAPE_EXPRESSION, rstr: '../name', isSingleID: false, result: [ 'MustacheStatement', true, 11 ]},
{ syntax: '{{../name}} ', type: handlebarsUtils.ESCAPE_EXPRESSION, rstr: '../name', isSingleID: true, result: [ 'MustacheStatement', true, '' ]},
{ syntax: '{{../name ../name}} ', type: handlebarsUtils.ESCAPE_EXPRESSION, rstr: '../name', isSingleID: false, result: [ 'MustacheStatement', true, '' ]},
// with dot as separator
{ syntax: '{{article.title}} ', type: handlebarsUtils.ESCAPE_EXPRESSION, rstr: 'article.title', isSingleID: true, result: [ 'MustacheStatement', true, 11 ]},
{ syntax: '{{article.title}} ', type: handlebarsUtils.ESCAPE_EXPRESSION, rstr: 'article.title', isSingleID: true, result: [ 'MustacheStatement', true, '' ]},
// with / as separator
{ syntax: '{{article/title}} ', type: handlebarsUtils.ESCAPE_EXPRESSION, rstr: 'article/title', isSingleID: true, result: [ 'MustacheStatement', true, 11 ]},
{ syntax: '{{article/title}} ', type: handlebarsUtils.ESCAPE_EXPRESSION, rstr: 'article/title', isSingleID: true, result: [ 'MustacheStatement', true, '' ]},
// with dot as separator and index
{ syntax: '{{article.[10].[#comments]}} ', type: handlebarsUtils.ESCAPE_EXPRESSION, rstr: 'article.[10].[#comments]', isSingleID: true, result: [ 'MustacheStatement', true, 11 ]},
{ syntax: '{{article.[10].[#comments]}} ', type: handlebarsUtils.ESCAPE_EXPRESSION, rstr: 'article.[10].[#comments]', isSingleID: true, result: [ 'MustacheStatement', true, '' ]},
// 2 expressions
{ syntax: '{{exp1 exp2}} ', type: handlebarsUtils.ESCAPE_EXPRESSION, rstr: 'exp1', isSingleID: false, result: [ 'MustacheStatement', true, 11 ]},
{ syntax: '{{exp1 exp2}} ', type: handlebarsUtils.ESCAPE_EXPRESSION, rstr: 'exp1', isSingleID: false, result: [ 'MustacheStatement', true, '' ]},
// 3 expressions
{ syntax: '{{exp1 exp2 exp3}} ', type: handlebarsUtils.ESCAPE_EXPRESSION, rstr: 'exp1', isSingleID: false, result: [ 'MustacheStatement', true, 11 ]},
{ syntax: '{{exp1 exp2 exp3}} ', type: handlebarsUtils.ESCAPE_EXPRESSION, rstr: 'exp1', isSingleID: false, result: [ 'MustacheStatement', true, '' ]},
// expression with param
{ syntax: '{{exp1 (param1)}} ', type: handlebarsUtils.ESCAPE_EXPRESSION, rstr: 'exp1', isSingleID: false, result: [ 'MustacheStatement', true, 11 ]},
{ syntax: '{{exp1 (param1)}} ', type: handlebarsUtils.ESCAPE_EXPRESSION, rstr: 'exp1', isSingleID: false, result: [ 'MustacheStatement', true, '' ]},
// expression with data param
{ syntax: '{{exp1 (@param1)}} ', type: handlebarsUtils.ESCAPE_EXPRESSION, rstr: 'exp1', isSingleID: false, result: [ 'MustacheStatement', true, 11 ]},
{ syntax: '{{exp1 (@param1)}} ', type: handlebarsUtils.ESCAPE_EXPRESSION, rstr: 'exp1', isSingleID: false, result: [ 'MustacheStatement', true, '' ]},
// expression with 2 params
{ syntax: '{{exp1 (param1 param2)}} ', type: handlebarsUtils.ESCAPE_EXPRESSION, rstr: 'exp1', isSingleID: false, result: [ 'MustacheStatement', true, 11 ]},
{ syntax: '{{exp1 (param1 param2)}} ', type: handlebarsUtils.ESCAPE_EXPRESSION, rstr: 'exp1', isSingleID: false, result: [ 'MustacheStatement', true, '' ]},

// reserved char
{ syntax: '{{/exp1}} ', type: handlebarsUtils.ESCAPE_EXPRESSION, rstr: false, isSingleID: false, result: [ false, false, 11 ]},
{ syntax: '{{#exp1}} ', type: handlebarsUtils.ESCAPE_EXPRESSION, rstr: false, isSingleID: false, result: [ false, false, 11 ]},
{ syntax: '{{>exp1}} ', type: handlebarsUtils.ESCAPE_EXPRESSION, rstr: false, isSingleID: false, result: [ 'PartialStatement', false, 11 ]},
{ syntax: '{{!exp1}} ', type: handlebarsUtils.ESCAPE_EXPRESSION, rstr: false, isSingleID: false, result: [ 'CommentStatement', false, 11 ]},
{ syntax: '{{/exp1}} ', type: handlebarsUtils.ESCAPE_EXPRESSION, rstr: false, isSingleID: false, result: [ false, false, '' ]},
{ syntax: '{{#exp1}} ', type: handlebarsUtils.ESCAPE_EXPRESSION, rstr: false, isSingleID: false, result: [ false, false, '' ]},
{ syntax: '{{>exp1}} ', type: handlebarsUtils.ESCAPE_EXPRESSION, rstr: false, isSingleID: false, result: [ 'PartialStatement', false, '' ]},
{ syntax: '{{!exp1}} ', type: handlebarsUtils.ESCAPE_EXPRESSION, rstr: false, isSingleID: false, result: [ 'CommentStatement', false, '' ]},
// it is fine to pass util test, as Handlebars parser will fails the second &exp2
{ syntax: '{{exp1 &exp2}} ', type: handlebarsUtils.ESCAPE_EXPRESSION, rstr: 'exp1',isSingleID: false, result: [ false, true, 11 ]},
{ syntax: '{{exp1 &exp2}} ', type: handlebarsUtils.ESCAPE_EXPRESSION, rstr: 'exp1',isSingleID: false, result: [ false, true, '' ]},
// we skip this pattern
{ syntax: '{{}} ', type: handlebarsUtils.ESCAPE_EXPRESSION, rstr: false, isSingleID: false, result: [ false, false, 11 ]},
{ syntax: '{{}} ', type: handlebarsUtils.ESCAPE_EXPRESSION, rstr: false, isSingleID: false, result: [ false, false, '' ]},

{ syntax: '{{article.[a b].[c d] }} ',
type: handlebarsUtils.ESCAPE_EXPRESSION, rstr: 'article.[a b].[c d]', isSingleID: true, result: [ 'MustacheStatement', true, '' ]},
{ syntax: '{{article.[a b].[c d] something }} ',
type: handlebarsUtils.ESCAPE_EXPRESSION, rstr: 'article.[a b].[c d]', isSingleID: false, result: [ 'MustacheStatement', true, '' ]},
{ syntax: '{{article/[a b]}} ',
type: handlebarsUtils.ESCAPE_EXPRESSION, rstr: 'article/[a b]', isSingleID: true, result: [ 'MustacheStatement', true, '' ]},
{ syntax: '{{article.[a\rb]}} ',
type: handlebarsUtils.ESCAPE_EXPRESSION, rstr: 'article.[a\rb]', isSingleID: true, result: [ 'MustacheStatement', true, '' ]},
];
exports.escapeExpressionTestPatterns = escapeExpressionTestPatterns;

Expand Down

0 comments on commit b435da1

Please sign in to comment.