aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorIgor Minar2012-01-24 02:33:35 -0800
committerIgor Minar2012-01-25 16:17:43 -0800
commit39b3297fc34b6b15bb3487f619ad1e93c4480741 (patch)
treee78eee301f3f19add0416482516a1fa95c159fdc
parent1268fc1a44e1359f670a586e6cbc6774736f0a2d (diff)
downloadangular.js-39b3297fc34b6b15bb3487f619ad1e93c4480741.tar.bz2
fix($parse): get rid of $unboundFn
Closes #731
-rw-r--r--src/Angular.js1
-rw-r--r--src/service/parse.js63
-rw-r--r--src/widget/input.js2
-rw-r--r--test/service/parseSpec.js21
-rw-r--r--test/service/scopeSpec.js2
5 files changed, 72 insertions, 17 deletions
diff --git a/src/Angular.js b/src/Angular.js
index 7955ce61..81261ab5 100644
--- a/src/Angular.js
+++ b/src/Angular.js
@@ -686,7 +686,6 @@ function equals(o1, o2) {
return true;
}
}
- if (t1 == 'function' && o1.$unboundFn) return o1.$unboundFn === o2.$unboundFn;
}
return false;
}
diff --git a/src/service/parse.js b/src/service/parse.js
index e8693819..c4a1258a 100644
--- a/src/service/parse.js
+++ b/src/service/parse.js
@@ -144,18 +144,40 @@ function lex(text){
fn:function() {return number;}});
}
function readIdent() {
- var ident = "";
- var start = index;
- var fn;
+ var ident = "",
+ start = index,
+ fn, lastDot, peekIndex, methodName;
+
while (index < text.length) {
var ch = text.charAt(index);
if (ch == '.' || isIdent(ch) || isNumber(ch)) {
+ if (ch == '.') lastDot = index;
ident += ch;
} else {
break;
}
index++;
}
+
+ //check if this is not a method invocation and if it is back out to last dot
+ if (lastDot) {
+ peekIndex = index
+ while(peekIndex < text.length) {
+ var ch = text.charAt(peekIndex);
+ if (ch == '(') {
+ methodName = ident.substr(lastDot - start + 1);
+ ident = ident.substr(0, lastDot - start);
+ index = peekIndex;
+ break;
+ }
+ if(isWhitespace(ch)) {
+ peekIndex++;
+ } else {
+ break;
+ }
+ }
+ }
+
fn = OPERATORS[ident];
tokens.push({
index:start,
@@ -167,6 +189,19 @@ function lex(text){
}
})
});
+
+ if (methodName) {
+ tokens.push({
+ index:lastDot,
+ text: '.',
+ json: false
+ });
+ tokens.push({
+ index: lastDot + 1,
+ text: methodName,
+ json: false
+ });
+ }
}
function readString(quote) {
@@ -490,13 +525,17 @@ function parser(text, json, $filter){
throwError("not a primary expression", token);
}
}
- var next;
+
+ var next, context;
while ((next = expect('(', '[', '.'))) {
if (next.text === '(') {
- primary = functionCall(primary);
+ primary = functionCall(primary, context);
+ context = null;
} else if (next.text === '[') {
+ context = primary;
primary = objectIndex(primary);
} else if (next.text === '.') {
+ context = primary;
primary = fieldAccess(primary);
} else {
throwError("IMPOSSIBLE");
@@ -544,7 +583,7 @@ function parser(text, json, $filter){
});
}
- function _functionCall(fn) {
+ function _functionCall(fn, contextGetter) {
var argsFn = [];
if (peekToken().text != ')') {
do {
@@ -553,14 +592,16 @@ function parser(text, json, $filter){
}
consume(')');
return function(self){
- var args = [];
+ var args = [],
+ context = contextGetter ? contextGetter(self) : self;
+
for ( var i = 0; i < argsFn.length; i++) {
args.push(argsFn[i](self));
}
var fnPtr = fn(self) || noop;
// IE stupidity!
return fnPtr.apply
- ? fnPtr.apply(self, args)
+ ? fnPtr.apply(context, args)
: fnPtr(args[0], args[1], args[2], args[3], args[4]);
};
}
@@ -691,11 +732,7 @@ function getterFn(path) {
code += 'if(!s) return s;\n' +
'l=s;\n' +
's=s' + key + ';\n' +
- 'if(typeof s=="function" && !(s instanceof RegExp)) {\n' +
- ' fn=function(){ return l' + key + '.apply(l, arguments); };\n' +
- ' fn.$unboundFn=s;\n' +
- ' s=fn;\n' +
- '} else if (s && s.then) {\n' +
+ 'if (s && s.then) {\n' +
' if (!("$$v" in s)) {\n' +
' p=s;\n' +
' p.$$v = undefined;\n' +
diff --git a/src/widget/input.js b/src/widget/input.js
index 9f522d18..05390b38 100644
--- a/src/widget/input.js
+++ b/src/widget/input.js
@@ -741,7 +741,7 @@ var inputDirective = ['$defer', '$formFactory', function($defer, $formFactory) {
type = lowercase(type);
TypeController = (loadFromScope
- ? (assertArgFn(modelScope.$eval(loadFromScope[1]), loadFromScope[1])).$unboundFn
+ ? assertArgFn(modelScope.$eval(loadFromScope[1]), loadFromScope[1])
: angularInputType(type)) || noop;
if (!HTML5_INPUTS_TYPES[type]) {
diff --git a/test/service/parseSpec.js b/test/service/parseSpec.js
index 08167843..d83cc050 100644
--- a/test/service/parseSpec.js
+++ b/test/service/parseSpec.js
@@ -110,6 +110,17 @@ describe('parser', function() {
expect(tokens[3].text).toEqual(';');
});
+ it('should tokenize function invocation', function() {
+ var tokens = lex("a()")
+ expect(map(tokens, function(t) { return t.text;})).toEqual(['a', '(', ')']);
+ });
+
+ it('should tokenize method invocation', function() {
+ var tokens = lex("a.b.c (d) - e.f()");
+ expect(map(tokens, function(t) { return t.text;})).
+ toEqual(['a.b', '.', 'c', '(', 'd', ')', '-', 'e', '.', 'f', '(', ')']);
+ });
+
it('should tokenize number', function() {
var tokens = lex("0.5");
expect(tokens[0].text).toEqual(0.5);
@@ -244,6 +255,12 @@ describe('parser', function() {
expect(scope.$eval("add(1,2)")).toEqual(3);
});
+ it('should evaluate function call from a return value', function() {
+ scope.val = 33;
+ scope.getter = function() { return function() { return this.val; }};
+ expect(scope.$eval("getter()()")).toBe(33);
+ });
+
it('should evaluate multiplication and division', function() {
scope.taxRate = 8;
scope.subTotal = 100;
@@ -281,7 +298,7 @@ describe('parser', function() {
expect(toJson(scope.$eval("[{a:[]}, {b:1}]"))).toEqual('[{"a":[]},{"b":1}]');
});
- it('should evaluate multipple statements', function() {
+ it('should evaluate multiple statements', function() {
expect(scope.$eval("a=1;b=3;a+b")).toEqual(4);
expect(scope.$eval(";;1;;")).toEqual(1);
});
@@ -296,6 +313,7 @@ describe('parser', function() {
scope.obj = new C();
expect(scope.$eval("obj.getA()")).toEqual(123);
+ expect(scope.$eval("obj['getA']()")).toEqual(123);
});
it('should evaluate methods in correct context (this) in argument', function() {
@@ -311,6 +329,7 @@ describe('parser', function() {
scope.obj = new C();
expect(scope.$eval("obj.sum(obj.getA())")).toEqual(246);
+ expect(scope.$eval("obj['sum'](obj.getA())")).toEqual(246);
});
it('should evaluate objects on scope context', function() {
diff --git a/test/service/scopeSpec.js b/test/service/scopeSpec.js
index b1b870f4..cc3f93f5 100644
--- a/test/service/scopeSpec.js
+++ b/test/service/scopeSpec.js
@@ -252,7 +252,7 @@ describe('Scope', function() {
module(provideLog);
inject(function($rootScope, log) {
$rootScope.fn = function() {return 'a'};
- $rootScope.$watch('fn', function(scope, fn) {
+ $rootScope.$watch('fn', function(fn) {
log(fn());
});
$rootScope.$digest();