From 849a05b5a578f19ddc3d24dc9fbd304e0e07612a Mon Sep 17 00:00:00 2001
From: Misko Hevery
Date: Thu, 22 Jul 2010 11:18:32 -0700
Subject: added jsonp to resources
---
example/buzz/buzz.css | 0
example/buzz/buzz.html | 30 ++++++++++++++++++++++++++++++
example/buzz/buzz.js | 19 +++++++++++++++++++
src/AngularPublic.js | 5 ++++-
src/Browser.js | 48 +++++++++++++++++++++++++++++++-----------------
src/Resource.js | 3 ++-
test/BrowserSpecs.js | 28 ++++++++++++++++++++++++++--
test/ResourceSpec.js | 12 ++++++++++++
test/angular-mocks.js | 1 +
9 files changed, 125 insertions(+), 21 deletions(-)
create mode 100644 example/buzz/buzz.css
create mode 100644 example/buzz/buzz.html
create mode 100644 example/buzz/buzz.js
diff --git a/example/buzz/buzz.css b/example/buzz/buzz.css
new file mode 100644
index 00000000..e69de29b
diff --git a/example/buzz/buzz.html b/example/buzz/buzz.html
new file mode 100644
index 00000000..ee2b2bb9
--- /dev/null
+++ b/example/buzz/buzz.html
@@ -0,0 +1,30 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/example/buzz/buzz.js b/example/buzz/buzz.js
new file mode 100644
index 00000000..871982d7
--- /dev/null
+++ b/example/buzz/buzz.js
@@ -0,0 +1,19 @@
+angular.service('myApplication', function($resource){
+ this.Activity = $resource(
+ 'https://www.googleapis.com/buzz/v1/activities/:userId/:visibility/:activityId/:comments',
+ {alt:'json', callback:'JSON_CALLBACK'},
+ {
+ get: {method:'JSON', params:{visibility:'@self'}},
+ replies: {method:'JSON', params:{visibility:'@self', comments:'@comments'}}
+ });
+}, {inject:['$resource']});
+
+function BuzzController(){
+ this.$watch('$location.hashPath', this.userChange);
+}
+BuzzController.prototype = {
+ userChange: function(){
+ this.userId = this.$location.hashPath;
+ this.activities = this.Activity.get({userId:this.userId});
+ }
+};
diff --git a/src/AngularPublic.js b/src/AngularPublic.js
index e2e576fd..7b093f88 100644
--- a/src/AngularPublic.js
+++ b/src/AngularPublic.js
@@ -1,7 +1,10 @@
var browserSingleton;
angularService('$browser', function browserFactory(){
if (!browserSingleton) {
- browserSingleton = new Browser(window.location, window.document);
+ browserSingleton = new Browser(
+ window.location,
+ jqLite(window.document),
+ jqLite(window.document.getElementsByTagName('head')[0]));
browserSingleton.startUrlWatcher();
browserSingleton.bind();
}
diff --git a/src/Browser.js b/src/Browser.js
index 0552b3ae..3299540c 100644
--- a/src/Browser.js
+++ b/src/Browser.js
@@ -2,7 +2,7 @@
// Browser
//////////////////////////////
-function Browser(location, document) {
+function Browser(location, document, head) {
this.delay = 50;
this.expectedUrl = location.href;
this.urlListeners = [];
@@ -21,8 +21,9 @@ function Browser(location, document) {
};
this.location = location;
- this.document = jqLite(document);
- this.body = jqLite(document.body);
+ this.document = document;
+ this.head = head;
+ this.idCounter = 0;
}
Browser.prototype = {
@@ -58,21 +59,34 @@ Browser.prototype = {
callback = post;
post = null;
}
- var xhr = new this.XHR(),
- self = this;
- xhr.open(method, url, true);
- this.outstandingRequests.count ++;
- xhr.onreadystatechange = function() {
- if (xhr.readyState == 4) {
- try {
- callback(xhr.status || 200, xhr.responseText);
- } finally {
- self.outstandingRequests.count--;
- self.processRequestCallbacks();
+ if (lowercase(method) == 'json') {
+ var callbackId = "angular_" + Math.random() + '_' + (this.idCounter++);
+ callbackId = callbackId.replace(/\d\./, '');
+ var script = this.document[0].createElement('script');
+ script.type = 'text/javascript';
+ script.src = url.replace('JSON_CALLBACK', callbackId);
+ this.head.append(script);
+ window[callbackId] = function(data){
+ delete window[callbackId];
+ callback(200, data);
+ };
+ } else {
+ var xhr = new this.XHR(),
+ self = this;
+ xhr.open(method, url, true);
+ this.outstandingRequests.count ++;
+ xhr.onreadystatechange = function() {
+ if (xhr.readyState == 4) {
+ try {
+ callback(xhr.status || 200, xhr.responseText);
+ } finally {
+ self.outstandingRequests.count--;
+ self.processRequestCallbacks();
+ }
}
- }
- };
- xhr.send(post || '');
+ };
+ xhr.send(post || '');
+ }
},
processRequestCallbacks: function(){
diff --git a/src/Resource.js b/src/Resource.js
index 1279dc54..f4f26ebd 100644
--- a/src/Resource.js
+++ b/src/Resource.js
@@ -28,6 +28,7 @@ Route.prototype = {
query.push(encodeURI(key) + '=' + encodeURI(value));
}
});
+ url = url.replace(/\/*$/, '');
return url + (query.length ? '?' + query.join('&') : '');
}
};
@@ -88,7 +89,7 @@ ResourceFactory.prototype = {
throw "Expected between 0-3 arguments [params, data, callback], got " + arguments.length + " arguments.";
}
- var value = action.isArray ? [] : new Resource(data)
+ var value = action.isArray ? [] : new Resource(data);
self.xhr(
action.method,
route.url(extend({}, action.params || {}, extractParams(data), params)),
diff --git a/test/BrowserSpecs.js b/test/BrowserSpecs.js
index 3ce158b4..a9f61a6b 100644
--- a/test/BrowserSpecs.js
+++ b/test/BrowserSpecs.js
@@ -1,10 +1,15 @@
describe('browser', function(){
- var browser, location;
+ var browser, location, head;
beforeEach(function(){
location = {href:"http://server", hash:""};
- browser = new Browser(location, {});
+ document = jqLite(window.document);
+ head = {
+ scripts: [],
+ append: function(node){head.scripts.push(node);}
+ };
+ browser = new Browser(location, jqLite(window.document), head);
browser.setTimeout = noop;
});
@@ -45,4 +50,23 @@ describe('browser', function(){
});
});
+ describe('xhr', function(){
+ describe('JSON', function(){
+ it('should add script tag for request', function() {
+ var log = "";
+ browser.xhr('JSON', 'http://example.org/path?cb=JSON_CALLBACK', function(code, data){
+ log += code + ':' + data + ';';
+ });
+ expect(head.scripts.length).toEqual(1);
+ var url = head.scripts[0].src.split('?cb=');
+ expect(url[0]).toEqual('http://example.org/path');
+ expect(typeof window[url[1]]).toEqual('function');
+ window[url[1]]('data');
+ expect(log).toEqual('200:data;');
+ expect(typeof window[url[1]]).toEqual('undefined');
+
+ });
+ });
+ });
+
});
diff --git a/test/ResourceSpec.js b/test/ResourceSpec.js
index 4882e70e..6e32ce18 100644
--- a/test/ResourceSpec.js
+++ b/test/ResourceSpec.js
@@ -28,6 +28,18 @@ describe("resource", function() {
resource.route('URL').query();
});
+ it('should ignore slashes of undefinend parameters', function(){
+ var R = resource.route('/Path/:a/:b/:c');
+ xhr.expectGET('/Path').respond({});
+ xhr.expectGET('/Path/1').respond({});
+ xhr.expectGET('/Path/2/3').respond({});
+ xhr.expectGET('/Path/4/5/6').respond({});
+ R.get({});
+ R.get({a:1});
+ R.get({a:2, b:3});
+ R.get({a:4, b:5, c:6});
+ });
+
it("should build resource with default param", function(){
xhr.expectGET('/Order/123/Line/456.visa?minimum=0.05').respond({id:'abc'});
var LineItem = resource.route('/Order/:orderId/Line/:id:verb', {orderId: '123', id: '@id.key', verb:'.visa', minimum:0.05});
diff --git a/test/angular-mocks.js b/test/angular-mocks.js
index 8838b2cd..bac2e800 100644
--- a/test/angular-mocks.js
+++ b/test/angular-mocks.js
@@ -66,6 +66,7 @@ function MockBrowser() {
self.xhr.expectPOST = angular.bind(self, self.xhr.expect, 'POST');
self.xhr.expectDELETE = angular.bind(self, self.xhr.expect, 'DELETE');
self.xhr.expectPUT = angular.bind(self, self.xhr.expect, 'PUT');
+ self.xhr.expectJSON = angular.bind(self, self.xhr.expect, 'JSON');
self.xhr.flush = function() {
while(requests.length) {
requests.pop()();
--
cgit v1.2.3