Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse files

JSONP support

  • Loading branch information...
commit 00ca962fae4ac0c2644bb915e13dda5ccad1e97b 1 parent 3d3c2d2
@scothis scothis authored
View
2  .jshintrc
@@ -28,7 +28,7 @@
// ECMAScript 5.
- "es5" : true, // Allow ECMAScript 5 syntax.
+ "es5" : false, // Allow ECMAScript 5 syntax.
"strict" : false, // Require `use strict` pragma in every file.
"globalstrict" : false, // Allow global "use strict" (also enables 'strict').
View
80 src/rest/client/jsonp.js
@@ -0,0 +1,80 @@
+(function (global, document, define) {
+
+ define(['when', '../UrlBuilder'], function (when, UrlBuilder) {
+ "use strict";
+
+ // consider abstracting this into a util module
+ function clearProperty(scope, propertyName) {
+ try {
+ delete scope[propertyName];
+ }
+ catch (e) {
+ // IE doesn't like to delete properties on the window object
+ if (propertyName in scope) {
+ scope[propertyName] = undefined;
+ }
+ }
+ }
+
+ function registerCallback(prefix, resolver) {
+ var name;
+
+ do {
+ name = prefix + Math.floor(new Date().getTime() * Math.random());
+ }
+ while (name in global);
+
+ global[name] = function jsonpCallback(data) {
+ resolver.resolve({ entity: data });
+ clearProperty(global, name);
+ };
+
+ return name;
+ }
+
+ /**
+ * Executes the request as JSONP.
+ *
+ * @param {String} request.path the URL to load
+ * @param {Object} [request.params] parameters to bind to the path
+ * @param {String} [request.callback.param='callback'] the parameter name for which the callback function name is the value
+ * @param {String} [request.callback.prefix='jsonp'] prefix for the callback function, as the callback is attached to the window object, a unique, unobtrusive prefix is desired
+ *
+ * @returns {Promise<Response>}
+ */
+ function jsonp(request) {
+ var d, callbackParams, script, firstScript;
+
+ d = when.defer();
+
+ try {
+ request.callback = request.callback || {};
+ callbackParams = {};
+ callbackParams[request.callback.param || 'callback'] = registerCallback(request.callback.prefix || 'jsonp', d.resolver);
+
+ script = document.createElement('script');
+ script.type = 'text/javascript';
+ script.async = true;
+ script.src = new UrlBuilder(request.path, request.params).build(callbackParams);
+ firstScript = document.getElementsByTagName('script')[0];
+ firstScript.parentNode.insertBefore(script, firstScript);
+ }
+ catch (e) {
+ d.reject(e);
+ }
+
+ return d.promise;
+ }
+
+ return jsonp;
+
+ });
+
+}(
+ this,
+ this.document,
+ typeof define === 'function' ? define : function (deps, factory) {
+ module.exports = factory.apply(this, deps.map(require));
+ }
+ // Boilerplate for AMD and Node
+));
View
46 src/rest/interceptor/jsonp.js
@@ -0,0 +1,46 @@
+(function (define) {
+
+ define(['../client/jsonp'], function (defaultClient) {
+ "use strict";
+
+ /**
+ * Allows common configuration of JSONP clients.
+ *
+ * Values provided to this interceptor are added to the request, if the
+ * request dose not already contain the property.
+ *
+ * The rest/client/jsonp client is used by default instead of the
+ * common default client for the platform.
+ *
+ * @param {Client} [client=rest/client/jsonp] custom client to wrap
+ * @param {String} [config.callback.param] the parameter name for which the callback function name is the value
+ * @param {String} [config.callback.prefix] prefix for the callback function, as the callback is attached to the window object, a unique, unobtrusive prefix is desired
+ *
+ * @returns {Client}
+ */
+ return function (client, config) {
+ if (typeof client === 'object') {
+ config = client;
+ }
+ if (typeof client !== 'function') {
+ client = defaultClient;
+ }
+ config = config || {};
+ config.callback = config.callback || {};
+
+ return function (request) {
+ request.callback = request.callback || {};
+ request.callback.param = request.callback.param || config.callback.param;
+ request.callback.prefix = request.callback.prefix || config.callback.prefix;
+ return client(request);
+ };
+ };
+
+ });
+
+}(
+ typeof define === 'function' ? define : function (deps, factory) {
+ module.exports = factory.apply(this, deps.map(require));
+ }
+ // Boilerplate for AMD and Node
+));
View
3  test/buster.js
@@ -6,8 +6,7 @@ config['rest:node'] = {
tests: [
'test/**/*-test.js',
'test/**/*-test-node.js'
- ],
-
+ ]
};
config['rest:browser'] = {
View
58 test/client/jsonp-test-browser.js
@@ -0,0 +1,58 @@
+(function (buster, define) {
+
+ var jsonp, jsonpInterceptor, rest, assert, refute;
+
+ assert = buster.assert;
+ refute = buster.refute;
+ buster.testRunner.timeout = 500;
+
+ function never(done) {
+ return function () {
+ assert(false, 'this method should never be invoked');
+ done();
+ };
+ }
+
+ buster.testCase('rest/client/jsonp', {
+ setUp: function (done) {
+ if (jsonp) { return done(); }
+ define('rest/client/jsonp-test', ['rest/client/jsonp', 'rest/interceptor/jsonp', 'rest'], function (jpc, jpi, r) {
+ jsonp = jpc;
+ jsonpInterceptor = jpi;
+ rest = r;
+ done();
+ });
+ },
+
+ 'should make a GET by default': function (done) {
+ var request = { path: 'http://ajax.googleapis.com/ajax/services/search/web?v=1.0', params: { q: 'javascript' } };
+ jsonp(request).then(
+ function (response) {
+ assert(response.entity.responseData);
+ done();
+ },
+ never(done)
+ );
+ },
+ 'should use the jsonp client from the jsonp interceptor by default': function (done) {
+ var request = { path: 'http://ajax.googleapis.com/ajax/services/search/web?v=1.0', params: { q: 'html5' } };
+ jsonpInterceptor()(request).then(
+ function (response) {
+ assert(response.entity.responseData);
+ done();
+ },
+ never(done)
+ );
+ },
+ 'should not be the default client': function () {
+ refute.same(jsonp, rest);
+ }
+ });
+
+}(
+ this.buster || require('buster'),
+ typeof define === 'function' ? define : function (id, deps, factory) {
+ factory(require('../../src/rest/client/jsonp'), require('../../src/rest/interceptor/jsonp'), require('../../src/rest'));
+ }
+ // Boilerplate for AMD and Node
+));
View
51 test/interceptor/jsonp-test.js
@@ -0,0 +1,51 @@
+(function (buster, define) {
+
+ var jsonp, jsonpClient, when, assert, refute;
+
+ assert = buster.assertions.assert;
+ refute = buster.assertions.refute;
+
+ buster.testCase('rest/interceptor/jsonp', {
+ setUp: function (done) {
+ if (jsonp) { return done(); }
+ define('rest/interceptor/jsonp-test', ['rest/interceptor/jsonp', 'rest/client/jsonp', 'when'], function (jpi, jpc, w) {
+ jsonp = jpi;
+ jsonpClient = jpc;
+ when = w;
+ done();
+ });
+ },
+
+ 'should include callback info from config in request by default': function () {
+ var client = jsonp(
+ function (request) { return { request: request }; },
+ { callback: { param: 'callback', prefix: 'jsonp' } }
+ );
+ when(client({})).then(
+ function (response) {
+ assert.equals('callback', response.request.callback.param);
+ assert.equals('jsonp', response.request.callback.prefix);
+ }
+ );
+ },
+ 'should include callback info from request overridding config values': function () {
+ var client = jsonp(
+ function (request) { return { request: request }; },
+ { callback: { param: 'callback', prefix: 'jsonp' } }
+ );
+ when(client({ callback: { param: 'customCallback', prefix: 'customPrefix' } })).then(
+ function (response) {
+ assert.equals('customCallback', response.request.callback.param);
+ assert.equals('customPrefix', response.request.callback.prefix);
+ }
+ );
+ }
+ });
+
+}(
+ this.buster || require('buster'),
+ typeof define === 'function' ? define : function (id, deps, factory) {
+ factory(require('../../src/rest/interceptor/jsonp'), require('../../src/rest/client/jsonp'), require('when'));
+ }
+ // Boilerplate for AMD and Node
+));
View
2  test/mime/registry-test.js
@@ -54,7 +54,7 @@
done();
}
);
- },
+ }
});
}(
Please sign in to comment.
Something went wrong with that request. Please try again.