Skip to content
This repository was archived by the owner on Apr 12, 2024. It is now read-only.

Commit 199715f

Browse files
committed
fix($interpolate): use the first end symbol that forms a valid expression
When an interpolation string has multiple end symbol choices, pick the occurrence that forms a valid expression Closes #8642
1 parent 8ac9035 commit 199715f

File tree

2 files changed

+46
-3
lines changed

2 files changed

+46
-3
lines changed

src/ng/interpolate.js

+37-2
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,8 @@ var $interpolateMinErr = minErr('$interpolate');
4141
function $InterpolateProvider() {
4242
var startSymbol = '{{';
4343
var endSymbol = '}}';
44+
var quoteChars = '\'"';
45+
var brackets = {"(": ")", "[": "]", "{": "}"}
4446

4547
/**
4648
* @ngdoc method
@@ -193,17 +195,42 @@ function $InterpolateProvider() {
193195
textLength = text.length,
194196
exp,
195197
concat = [],
196-
expressionPositions = [];
198+
expressionPositions = [],
199+
quoteChar,
200+
bracketCounts = {},
201+
ch;
197202

203+
forEach(brackets, function(start, end) {
204+
bracketCounts[start] = 0;
205+
bracketCounts[end] = 0;
206+
});
198207
while(index < textLength) {
199208
if ( ((startIndex = text.indexOf(startSymbol, index)) != -1) &&
200209
((endIndex = text.indexOf(endSymbol, startIndex + startSymbolLength)) != -1) ) {
201210
if (index !== startIndex) {
202211
concat.push(unescapeText(text.substring(index, startIndex)));
203212
}
213+
index = startIndex + startSymbolLength;
214+
while (index < endIndex || quoteChar !== undefined || !bracketsMatch(bracketCounts)) {
215+
if (index >= endIndex) {
216+
endIndex = text.indexOf(endSymbol, endIndex + 1);
217+
if (endIndex === -1) {
218+
break;
219+
}
220+
}
221+
ch = text[index];
222+
if (quoteChar) {
223+
if (quoteChar === ch) quoteChar = undefined;
224+
else if (ch === '\\') index++;
225+
} else {
226+
if (ch in bracketCounts) bracketCounts[ch]++;
227+
else if (quoteChars.indexOf(ch) != -1) quoteChar = ch;
228+
}
229+
index++;
230+
}
204231
exp = text.substring(startIndex + startSymbolLength, endIndex);
205-
expressions.push(exp);
206232
parseFns.push($parse(exp, parseStringifyInterceptor));
233+
expressions.push(exp);
207234
index = endIndex + endSymbolLength;
208235
expressionPositions.push(concat.length);
209236
concat.push('');
@@ -312,6 +339,14 @@ function $InterpolateProvider() {
312339
$exceptionHandler(newErr);
313340
}
314341
}
342+
343+
function bracketsMatch(bracketCounts) {
344+
var match = true;
345+
forEach(brackets, function(start, end) {
346+
match &= bracketCounts[start] === bracketCounts[end];
347+
});
348+
return match;
349+
}
315350
}
316351

317352

test/ng/interpolateSpec.js

+9-1
Original file line numberDiff line numberDiff line change
@@ -105,7 +105,7 @@ describe('$interpolate', function() {
105105
expect(function() {
106106
$interpolate('{{\\{\\{foo\\}\\}}}')(obj);
107107
}).toThrowMinErr('$parse', 'lexerr',
108-
'Lexer Error: Unexpected next character at columns 0-0 [\\] in expression [\\{\\{foo\\}\\]');
108+
'Lexer Error: Unexpected next character at columns 0-0 [\\] in expression [\\{\\{foo\\}\\}]');
109109
}));
110110

111111

@@ -116,6 +116,14 @@ describe('$interpolate', function() {
116116
it('should evaluate expressions between escaped start/end symbols', inject(function($interpolate) {
117117
expect($interpolate('\\{\\{Hello, {{bar}}!\\}\\}')(obj)).toBe('{{Hello, World!}}');
118118
}));
119+
120+
it('should pick the first end symbol location that forms a valid expression', inject(function($interpolate) {
121+
expect($interpolate('{{{}}}')(obj)).toBe('{}');
122+
expect($interpolate('{{"{{ }}"}}')(obj)).toBe('{{ }}');
123+
expect($interpolate('{{{foo: "bar"}}}')(obj)).toBe('{"foo":"bar"}');
124+
expect($interpolate('{{{foo: {"bar": "baz"}}}}')(obj)).toBe('{"foo":{"bar":"baz"}}');
125+
expect($interpolate('{{[{foo: {"bar": "baz"}}]}}')(obj)).toBe('[{"foo":{"bar":"baz"}}]');
126+
}));
119127
});
120128

121129

0 commit comments

Comments
 (0)