diff options
| author | Igor Minar | 2012-01-24 02:33:35 -0800 | 
|---|---|---|
| committer | Igor Minar | 2012-01-25 16:17:43 -0800 | 
| commit | 39b3297fc34b6b15bb3487f619ad1e93c4480741 (patch) | |
| tree | e78eee301f3f19add0416482516a1fa95c159fdc | |
| parent | 1268fc1a44e1359f670a586e6cbc6774736f0a2d (diff) | |
| download | angular.js-39b3297fc34b6b15bb3487f619ad1e93c4480741.tar.bz2 | |
fix($parse): get rid of $unboundFn
Closes #731
| -rw-r--r-- | src/Angular.js | 1 | ||||
| -rw-r--r-- | src/service/parse.js | 63 | ||||
| -rw-r--r-- | src/widget/input.js | 2 | ||||
| -rw-r--r-- | test/service/parseSpec.js | 21 | ||||
| -rw-r--r-- | test/service/scopeSpec.js | 2 | 
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(); | 
