aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--src/Angular.js19
-rw-r--r--src/AngularPublic.js1
-rw-r--r--src/Browser.js3
-rw-r--r--src/JSON.js2
-rw-r--r--src/Resource.js12
-rw-r--r--src/Scope.js4
-rw-r--r--src/services.js71
-rw-r--r--test/AngularSpec.js8
-rw-r--r--test/JsonTest.js10
-rw-r--r--test/ResourceSpec.js9
-rw-r--r--test/angular-mocks.js11
-rw-r--r--test/servicesSpec.js9
-rw-r--r--test/testabilityPatch.js1
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,