From c0d5b2610400de8701b43e78c31e170cf59208c7 Mon Sep 17 00:00:00 2001 From: Nera Liu Date: Sat, 13 Jun 2015 16:25:35 +0800 Subject: [PATCH 1/5] add the parseEscapeExpression function to fix singleID issue. --- src/handlebars-utils.js | 44 ++++++++++++++++++++++++++++++++--------- tests/test-patterns.js | 10 ++++++++++ 2 files changed, 45 insertions(+), 9 deletions(-) diff --git a/src/handlebars-utils.js b/src/handlebars-utils.js index 105bf1b..ca3f444 100644 --- a/src/handlebars-utils.js +++ b/src/handlebars-utils.js @@ -35,32 +35,33 @@ 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 '}' */ HandlebarsUtils.ESCAPE_EXPRESSION = 2; // {{expression}} -HandlebarsUtils.escapeExpressionRegExp = /^\{\{~?\s*@?\s*([^\s\}\{~]+)\s*([^\}\{~]*)~??\}\}(?!})/; +HandlebarsUtils.escapeExpressionRegExp1 = /^\{\{~?\s*@?\s*([^\s\}\{~]+)\s*([^\}\{~]*)~?\}\}(?!})/; +HandlebarsUtils.escapeExpressionRegExp2 = /^\{\{~?\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; // {{!--.*--}} @@ -120,8 +121,13 @@ HandlebarsUtils.isValidExpression = function(input, i, type) { re = HandlebarsUtils.rawExpressionRegExp.exec(s); break; case HandlebarsUtils.ESCAPE_EXPRESSION: - re = HandlebarsUtils.escapeExpressionRegExp.exec(s); + re = HandlebarsUtils.escapeExpressionRegExp2.exec(s); if (re !== null && re[1] !== undefined) { + // 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; @@ -177,6 +183,26 @@ HandlebarsUtils.isValidExpression = function(input, i, type) { return re; }; +// @function HandlebarsUtils.parseEscapeExpression +HandlebarsUtils.parseEscapeExpression = function(str) { + var j=0, inSquareBracket = false, + fstr = '', re = []; + + str = str.trim(); // the new regexp will capture the tailing space. + while(j Date: Sat, 13 Jun 2015 22:10:18 +0800 Subject: [PATCH 2/5] add more test cases for singleID issue. --- tests/test-patterns.js | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/tests/test-patterns.js b/tests/test-patterns.js index 890bd79..d42514a 100644 --- a/tests/test-patterns.js +++ b/tests/test-patterns.js @@ -287,9 +287,13 @@ var escapeExpressionTestPatterns = [ { syntax: '{{}} ', type: handlebarsUtils.ESCAPE_EXPRESSION, rstr: false, isSingleID: false, result: [ false, false, 11 ]}, { syntax: '{{article.[a b].[c d]}} ', - type: handlebarsUtils.ESCAPE_EXPRESSION, rstr: 'article.[a b].[c d]', isSingleID: true, result: [ 'MustacheStatement', true, 11 ]}, + 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, 11 ]}, + 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; From 535a4ad1aa959211c716e2c74760cf61de87ce8c Mon Sep 17 00:00:00 2001 From: Nera Liu Date: Mon, 15 Jun 2015 11:05:59 +0800 Subject: [PATCH 3/5] remove the variable escapeExpressionRegExp2 --- src/handlebars-utils.js | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/handlebars-utils.js b/src/handlebars-utils.js index ca3f444..4c36dbc 100644 --- a/src/handlebars-utils.js +++ b/src/handlebars-utils.js @@ -39,8 +39,7 @@ HandlebarsUtils.rawExpressionRegExp = /^\{\{\{~?\s*@?\s*([^\s\}\{~]+)\s*([^\}\{~ /* '{{' '~'? 'space'* '@'? 'space'* ('not {}~'+) 'space'* ('not {}~'+) '~'? non-greedy '}}' and not follow by '}' */ HandlebarsUtils.ESCAPE_EXPRESSION = 2; // {{expression}} -HandlebarsUtils.escapeExpressionRegExp1 = /^\{\{~?\s*@?\s*([^\s\}\{~]+)\s*([^\}\{~]*)~?\}\}(?!})/; -HandlebarsUtils.escapeExpressionRegExp2 = /^\{\{~?\s*@?\s*([^\}\{~]+)~?\}\}(?!})/; +HandlebarsUtils.escapeExpressionRegExp = /^\{\{~?\s*@?\s*([^\}\{~]+)~?\}\}(?!})/; /* '{{' '~'? '>' '\s'* ('not \s, special-char'+) '\s'* 'not ~{}'* non-greedy '}}' and not follow by '}' */ HandlebarsUtils.PARTIAL_EXPRESSION = 3; // {{>.*}} @@ -121,7 +120,7 @@ HandlebarsUtils.isValidExpression = function(input, i, type) { re = HandlebarsUtils.rawExpressionRegExp.exec(s); break; case HandlebarsUtils.ESCAPE_EXPRESSION: - re = HandlebarsUtils.escapeExpressionRegExp2.exec(s); + re = HandlebarsUtils.escapeExpressionRegExp.exec(s); if (re !== null && re[1] !== undefined) { // NOTE: the re.index field is never been used. var r = HandlebarsUtils.parseEscapeExpression(re[1]); From e8b471f9a300fdc7b0db23040b4e84f7e4a0a87f Mon Sep 17 00:00:00 2001 From: Nera Liu Date: Mon, 15 Jun 2015 11:41:56 +0800 Subject: [PATCH 4/5] add NOTE to to unit tests and clean up --- tests/test-patterns.js | 47 +++++++++++++++++++++--------------------- 1 file changed, 24 insertions(+), 23 deletions(-) diff --git a/tests/test-patterns.js b/tests/test-patterns.js index d42514a..73693f9 100644 --- a/tests/test-patterns.js +++ b/tests/test-patterns.js @@ -15,6 +15,7 @@ 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', '', '' ]}, @@ -243,48 +244,48 @@ 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, '' ]}, From 5dac988445f833f624ca43a7beffbd7c3dda002a Mon Sep 17 00:00:00 2001 From: Nera Liu Date: Mon, 15 Jun 2015 13:48:58 +0800 Subject: [PATCH 5/5] remove trim() for browser compatibility --- src/handlebars-utils.js | 19 ++++++++++++------- tests/test-patterns.js | 4 ++-- 2 files changed, 14 insertions(+), 9 deletions(-) diff --git a/src/handlebars-utils.js b/src/handlebars-utils.js index 4c36dbc..a6b6bef 100644 --- a/src/handlebars-utils.js +++ b/src/handlebars-utils.js @@ -37,7 +37,7 @@ HandlebarsUtils.RAW_EXPRESSION = 1; // {{{expression}}} // HandlebarsUtils.rawExpressionRegExp = /^\{\{\{\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*([^\}\{~]+)~?\}\}(?!})/; @@ -121,7 +121,7 @@ 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]; @@ -187,18 +187,23 @@ HandlebarsUtils.parseEscapeExpression = function(str) { var j=0, inSquareBracket = false, fstr = '', re = []; - str = str.trim(); // the new regexp will capture the tailing space. + // the new regexp will capture the tailing space. + str = str.replace(/\s+$/, ''); while(j