diff --git a/src/ng/parse.js b/src/ng/parse.js index 2dd4336ade34..b869f8666945 100644 --- a/src/ng/parse.js +++ b/src/ng/parse.js @@ -276,6 +276,16 @@ Lexer.prototype = { this.index++; } + //check if the identifier ends with . and if so move back one char + if (lastDot && ident[ident.length - 1] === '.') { + this.index--; + ident = ident.slice(0, -1); + lastDot = ident.lastIndexOf('.'); + if (lastDot === -1) { + lastDot = undefined; + } + } + //check if this is not a method invocation and if it is back out to last dot if (lastDot) { peekIndex = this.index; diff --git a/test/ng/parseSpec.js b/test/ng/parseSpec.js index 3bd3c40f1b11..265f12c67b54 100644 --- a/test/ng/parseSpec.js +++ b/test/ng/parseSpec.js @@ -71,6 +71,13 @@ describe('parser', function() { expect(tokens[i].string).toEqual('d"e'); }); + it('should tokenize identifiers with spaces after dots', function () { + var tokens = lex('foo. bar'); + expect(tokens[0].text).toEqual('foo'); + expect(tokens[1].text).toEqual('.'); + expect(tokens[2].text).toEqual('bar'); + }); + it('should tokenize undefined', function() { var tokens = lex("undefined"); var i = 0; @@ -349,6 +356,28 @@ describe('parser', function() { expect(scope.$eval("x.y.z", scope)).not.toBeDefined(); }); + it('should handle white-spaces around dots in paths', function () { + scope.a = {b: 4}; + expect(scope.$eval("a . b", scope)).toEqual(4); + expect(scope.$eval("a. b", scope)).toEqual(4); + expect(scope.$eval("a .b", scope)).toEqual(4); + expect(scope.$eval("a . \nb", scope)).toEqual(4); + }); + + it('should throw syntax error exception for identifiers ending with a dot', function () { + scope.a = {b: 4}; + + expect(function() { + scope.$eval("a.", scope); + }).toThrowMinErr('$parse', 'syntax', + "Token 'null' is an unexpected token at column 2 of the expression [a.] starting at [.]."); + + expect(function() { + scope.$eval("a .", scope); + }).toThrowMinErr('$parse', 'syntax', + "Token 'null' is an unexpected token at column 3 of the expression [a .] starting at [.]."); + }); + it('should resolve deeply nested paths (important for CSP mode)', function() { scope.a = {b: {c: {d: {e: {f: {g: {h: {i: {j: {k: {l: {m: {n: 'nooo!'}}}}}}}}}}}}}; expect(scope.$eval("a.b.c.d.e.f.g.h.i.j.k.l.m.n", scope)).toBe('nooo!');