diff options
| -rw-r--r-- | src/Angular.js | 19 | ||||
| -rw-r--r-- | src/AngularPublic.js | 1 | ||||
| -rw-r--r-- | src/Browser.js | 3 | ||||
| -rw-r--r-- | src/JSON.js | 2 | ||||
| -rw-r--r-- | src/Resource.js | 12 | ||||
| -rw-r--r-- | src/Scope.js | 4 | ||||
| -rw-r--r-- | src/services.js | 71 | ||||
| -rw-r--r-- | test/AngularSpec.js | 8 | ||||
| -rw-r--r-- | test/JsonTest.js | 10 | ||||
| -rw-r--r-- | test/ResourceSpec.js | 9 | ||||
| -rw-r--r-- | test/angular-mocks.js | 11 | ||||
| -rw-r--r-- | test/servicesSpec.js | 9 | ||||
| -rw-r--r-- | test/testabilityPatch.js | 1 | 
13 files changed, 112 insertions, 48 deletions
diff --git a/src/Angular.js b/src/Angular.js index 3af21ced..2df6bbef 100644 --- a/src/Angular.js +++ b/src/Angular.js @@ -231,13 +231,14 @@ function isLeafNode (node) {  function copy(source, destination){    if (!destination) { -    if (isArray(source)) { -      return copy(source, []); -    } else if (isObject(source)) { -      return copy(source, {}); -    } else { -      return source; +    if (source) { +      if (isArray(source)) { +        return copy(source, []); +      } else if (isObject(source)) { +        return copy(source, {}); +      }      } +    return source;    } else {      if (isArray(source)) {        while(destination.length) { @@ -249,7 +250,11 @@ function copy(source, destination){        });      }      foreach(source, function(value, key){ -      destination[key] = isArray(value) ? copy(value, []) : (isObject(value) ? copy(value, {}) : value); +      destination[key] = value ? +              ( isArray(value) ? +                  copy(value, []) : +                  (isObject(value) ? copy(value, {}) : value)) : +              value;      });      return destination;    } diff --git a/src/AngularPublic.js b/src/AngularPublic.js index 1739ac4b..7230c3e5 100644 --- a/src/AngularPublic.js +++ b/src/AngularPublic.js @@ -22,6 +22,7 @@ extend(angular, {    'isDefined': isDefined,    'isString': isString,    'isFunction': isFunction, +  'isObject': isObject,    'isNumber': isNumber,    'isArray': isArray  }); diff --git a/src/Browser.js b/src/Browser.js index 6ec083fa..2a90b63f 100644 --- a/src/Browser.js +++ b/src/Browser.js @@ -8,6 +8,7 @@ function Browser(location, document) {    this.expectedUrl = location.href;    this.urlListeners = [];    this.hoverListener = noop; +  this.isMock = false;    this.XHR = window.XMLHttpRequest || function () {      try { return new ActiveXObject("Msxml2.XMLHTTP.6.0"); } catch (e1) {} @@ -64,7 +65,7 @@ Browser.prototype = {          callback(xhr.status || 200, xhr.responseText);        }      }; -    xhr.send(''); +    xhr.send(post || '');    },    watchUrl: function(fn){ diff --git a/src/JSON.js b/src/JSON.js index 5c3e1043..340b075a 100644 --- a/src/JSON.js +++ b/src/JSON.js @@ -74,7 +74,7 @@ function toJsonArray(buf, obj, pretty, stack){        var childPretty = pretty ? pretty + "  " : false;        var keys = [];        for(var k in obj) { -        if (k.indexOf('$$') === 0) +        if (k.indexOf('$$') === 0 || obj[k] === undefined)            continue;          keys.push(k);        } diff --git a/src/Resource.js b/src/Resource.js index 34ad1c5d..c9bad0c0 100644 --- a/src/Resource.js +++ b/src/Resource.js @@ -21,7 +21,7 @@ Route.prototype = {      });      url = url.replace(/\/?#$/, '');      var query = []; -    foreach(params, function(value, key){ +    foreachSorted(params, function(value, key){        if (!self.urlParams[key]) {          query.push(encodeURI(key) + '=' + encodeURI(value));        } @@ -69,14 +69,18 @@ ResourceFactory.prototype = {          switch(arguments.length) {          case 3: callback = a3;          case 2: -          if (typeof a2 == 'function') { +          if (isFunction(a2)) {              callback = a2;            } else {              params = a1;              data = a2;              break;            } -        case 1: if (isPost) data = a1; else params = a1; break; +        case 1: +          if (isFunction(a1)) callback = a1; +          else if (isPost) data = a1; +          else params = a1; +          break;          case 0: break;          default:            throw "Expected between 0-3 arguments [params, data, callback], got " + arguments.length + " arguments."; @@ -109,7 +113,7 @@ ResourceFactory.prototype = {            case 1: if (typeof a1 == 'function') callback = a1; else params = a1;            case 0: break;            default: -            throw "Expected between 1-3 arguments [params, data, callback], got " + arguments.length + " arguments."; +            throw "Expected between 1-2 arguments [params, callback], got " + arguments.length + " arguments.";            }            var self = this;            Resource[name](params, this, function(response){ diff --git a/src/Scope.js b/src/Scope.js index 1b93418f..9a20c214 100644 --- a/src/Scope.js +++ b/src/Scope.js @@ -97,7 +97,7 @@ function createScope(parent, services, existing) {      $set: bind(instance, setter, instance),      $eval: function $eval(exp) { -      if (isDefined(exp)) { +      if (exp) {          return expressionCompile(exp).apply(instance, slice.call(arguments, 1, arguments.length));        } else {          foreach(evalLists.sorted, function(list) { @@ -129,8 +129,8 @@ function createScope(parent, services, existing) {        function watcher(){          var value = watch.call(instance);          if (last !== value) { -          instance.$tryEval(listener, exceptionHandler, value, last);            last = value; +          instance.$tryEval(listener, exceptionHandler, value, last);          }        }        instance.$onEval(PRIORITY_WATCH, watcher); diff --git a/src/services.js b/src/services.js index 2cf0e4ad..195cd6b3 100644 --- a/src/services.js +++ b/src/services.js @@ -64,6 +64,12 @@ angularService("$location", function(browser){    return location;  }, {inject: ['$browser']}); +angularService("$log", function(){ +  return { +    error: noop +  }; +}); +  angularService("$hover", function(browser) {    var tooltip, self = this, error, width = 300, arrowWidth = 10;    browser.hover(function(element, show){ @@ -152,6 +158,7 @@ angularService('$route', function(location, params){        onChange = [],        matcher = angularWidget('NG:SWITCH').route,        parentScope = this, +      dirty = 0,        $route = {          routes: routes,          onChange: bind(onChange, onChange.push), @@ -160,7 +167,7 @@ angularService('$route', function(location, params){            var route = routes[path];            if (!route) route = routes[path] = {};            if (params) angular.extend(route, params); -          if (matcher(location.hashPath, path)) updateRoute(); +          dirty++;            return route;          }        }; @@ -185,7 +192,7 @@ angularService('$route', function(location, params){        parentScope.$tryEval(childScope.init);      }    } -  this.$watch(function(){return location.hash;}, updateRoute); +  this.$watch(function(){return dirty + location.hash;}, updateRoute);    return $route;  }, {inject: ['$location']}); @@ -221,32 +228,49 @@ angularService('$xhr.bulk', function($xhr){        callback = post;        post = null;      } -    requests.push({method: method, url: url, data:post}); -    callbacks.push(callback); +    var currentQueue; +    foreach(bulkXHR.urls, function(queue){ +      if (isFunction(queue.match) ? queue.match(url) : queue.match.exec(url)) { +        currentQueue = queue; +      } +    }); +    if (currentQueue) { +      if (!currentQueue.requests) currentQueue.requests = []; +      if (!currentQueue.callbacks) currentQueue.callbacks = []; +      currentQueue.requests.push({method: method, url: url, data:post}); +      currentQueue.callbacks.push(callback); +    } else { +      $xhr(method, url, post, callback); +    }    } -  bulkXHR.url = "/bulk"; +  bulkXHR.urls = {};    bulkXHR.flush = function(callback){ -    var currentRequests = requests, -        currentCallbacks = callbacks; -    requests = []; -    callbacks = []; -    $xhr('POST', bulkXHR.url, {requests:currentRequests}, function(code, response){ -      foreach(response, function(response, i){ -        try { -          (currentCallbacks[i] || noop)(response.status, response.response); -        } catch(e) { -          self.$log.error(e); -        } -      }); -      (callback || noop)(); +    foreach(bulkXHR.urls, function(queue, url){ +      var currentRequests = queue.requests, +          currentCallbacks = queue.callbacks; +      if (currentRequests && currentRequests.length) { +        queue.requests = []; +        queue.callbacks = []; +        $xhr('POST', url, {requests:currentRequests}, function(code, response){ +          foreach(response, function(response, i){ +            try { +              (currentCallbacks[i] || noop)(response.status, response.response); +            } catch(e) { +              scope.$log.error(e); +            } +          }); +          (callback || noop)(); +        }); +        scope.$eval(); +      }      }); -    scope.$eval();    }; +  this.$onEval(PRIORITY_LAST, bulkXHR.flush);    return bulkXHR;  }, {inject:['$xhr']});  angularService('$xhr.cache', function($xhr){ -  var inflight = {}; +  var inflight = {}, self = this;;    function cache(method, url, post, callback){      if (isFunction(post)) {        callback = post; @@ -263,14 +287,15 @@ angularService('$xhr.cache', function($xhr){          cache.delegate(method, url, post, function(status, response){            if (status == 200)              cache.data[url] = { value: response }; -          foreach(inflight[url].callbacks, function(callback){ +          var callbacks = inflight[url].callbacks; +          delete inflight[url]; +          foreach(callbacks, function(callback){              try {                (callback||noop)(status, copy(response));              } catch(e) {                self.$log.error(e);              }            }); -          delete inflight[url];          });        }      } else { @@ -281,7 +306,7 @@ angularService('$xhr.cache', function($xhr){    cache.data = {};    cache.delegate = $xhr;    return cache; -}, {inject:['$xhr']}); +}, {inject:['$xhr.bulk']});  angularService('$resource', function($xhr){    var resource = new ResourceFactory($xhr); diff --git a/test/AngularSpec.js b/test/AngularSpec.js index 60079c47..de724f03 100644 --- a/test/AngularSpec.js +++ b/test/AngularSpec.js @@ -41,4 +41,12 @@ describe("copy", function(){      assertEquals(src.a, dst.a);      assertNotSame(src.a, dst.a);    }); + +  it("should copy primitives", function(){ +    expect(copy(null)).toEqual(null); +    expect(copy('')).toEqual(''); +    expect(copy(123)).toEqual(123); +    expect(copy([{key:null}])).toEqual([{key:null}]); +  }); +  }); diff --git a/test/JsonTest.js b/test/JsonTest.js index 9b275248..1ed56da8 100644 --- a/test/JsonTest.js +++ b/test/JsonTest.js @@ -63,9 +63,9 @@ JsonTest.prototype.testItShouldEscapeUnicode = function () {  JsonTest.prototype.testItShouldUTCDates = function() {    var date = angular.String.toDate("2009-10-09T01:02:03Z"); -  assertEquals('"2009-10-09T01:02:03Z"', toJson(date));   -  assertEquals(date.getTime(),  -      fromJson('"2009-10-09T01:02:03Z"').getTime());   +  assertEquals('"2009-10-09T01:02:03Z"', toJson(date)); +  assertEquals(date.getTime(), +      fromJson('"2009-10-09T01:02:03Z"').getTime());  };  JsonTest.prototype.testItShouldPreventRecursion = function () { @@ -78,3 +78,7 @@ JsonTest.prototype.testItShouldSerializeSameObjectsMultipleTimes = function () {    var obj = {a:'b'};    assertEquals('{"A":{"a":"b"},"B":{"a":"b"}}', angular.toJson({A:obj, B:obj}));  }; + +JsonTest.prototype.testItShouldNotSerializeUndefinedValues = function () { +  assertEquals('{}', angular.toJson({A:undefined})); +}; diff --git a/test/ResourceSpec.js b/test/ResourceSpec.js index f0bb6770..d2d52d47 100644 --- a/test/ResourceSpec.js +++ b/test/ResourceSpec.js @@ -81,6 +81,15 @@ describe("resource", function() {      expect(callback).wasCalledWith(ccs);    }); +  it("should have all arguments optional", function(){ +    xhr.expectGET('/CreditCard').respond([{id:1}]); +    var log = ''; +    var ccs = CreditCard.query(function(){ log += 'cb;'; }); +    xhr.flush(); +    nakedExpect(ccs).toEqual([{id:1}]); +    expect(log).toEqual('cb;'); +  }); +    it('should delete resource', function(){      xhr.expectDELETE("/CreditCard/123").respond({}); diff --git a/test/angular-mocks.js b/test/angular-mocks.js index 715b4d75..6ae91596 100644 --- a/test/angular-mocks.js +++ b/test/angular-mocks.js @@ -26,16 +26,17 @@ function MockBrowser() {    var self = this,        expectations = {},        requests = []; +  this.isMock = true;    self.url = "http://server";    self.watches = [];    self.xhr = function(method, url, data, callback) { -    if (isFunction(data)) { +    if (angular.isFunction(data)) {        callback = data;        data = null;      } -    if (data && isObject(data)) data = angular.toJson(data); -    if (data && isString(data)) url += "|" + data; +    if (data && angular.isObject(data)) data = angular.toJson(data); +    if (data && angular.isString(data)) url += "|" + data;      var expect = expectations[method] || {};      var response = expect[url];      if (!response) { @@ -48,8 +49,8 @@ function MockBrowser() {    self.xhr.expectations = expectations;    self.xhr.requests = requests;    self.xhr.expect = function(method, url, data) { -    if (data && isObject(data)) data = angular.toJson(data); -    if (data && isString(data)) url += "|" + data; +    if (data && angular.isObject(data)) data = angular.toJson(data); +    if (data && angular.isString(data)) url += "|" + data;      var expect = expectations[method] || (expectations[method] = {});      return {        respond: function(response) { diff --git a/test/servicesSpec.js b/test/servicesSpec.js index 112fc374..794d1120 100644 --- a/test/servicesSpec.js +++ b/test/servicesSpec.js @@ -149,6 +149,7 @@ describe("service", function(){        expect(scope.$route.current).toEqual(null);        scope.$route.when('/NONE', {template:'instant update'}); +      scope.$eval();        expect(scope.$route.current.template).toEqual('instant update');      });    }); @@ -187,7 +188,7 @@ describe("service", function(){      describe('bulk', function(){        it('should collect requests', function(){ -        scope.$xhr.bulk.url = "/"; +        scope.$xhr.bulk.urls["/"] = {match:/.*/};          scope.$xhr.bulk('GET', '/req1', null, callback);          scope.$xhr.bulk('POST', '/req2', {post:'data'}, callback); @@ -225,7 +226,11 @@ describe("service", function(){        });        it('should keep track of in flight requests and request only once', function(){ -        cache.delegate = scope.$xhr.bulk; +        scope.$xhr.bulk.urls['/bulk'] = { +          match:function(url){ +            return url == '/url'; +          } +        };          xhr.expectPOST('/bulk', {            requests:[{method:'GET',  url:'/url', data: null}]          }).respond([ diff --git a/test/testabilityPatch.js b/test/testabilityPatch.js index 572e6a36..4d129f60 100644 --- a/test/testabilityPatch.js +++ b/test/testabilityPatch.js @@ -19,6 +19,7 @@ extend(angular, {    'identity':identity,    'isUndefined': isUndefined,    'isDefined': isDefined, +  'isObject': isObject,    'isString': isString,    'isFunction': isFunction,    'isNumber': isNumber,  | 
