js/NativeJsonpDataProvider.js
"use strict";
/*global window */
/**
* @class circuits.NativeJsonpDataProvider
*
* Data provider implementation that implements JSONP.
*/
define([
"./declare",
"./DataProvider",
"./Request",
"./log"
], function (
declare,
DataProvider,
Request,
logger
) {
var module = declare(DataProvider, {
constructor: function (config) {
var that = this;
this.hitchedInvoke = function () {
that.invokeJsonpRequest.apply(that, arguments);
};
},
/**
* @param {object} params
* @return {object} request
*/
read: function (params) {
var jsonpCallback = 'jsonp' + new Date().getTime(),
request = new Request({
callbackName: params.jsonpCallbackParam,
load: params.handler,
error: params.handler,
jsonpCallback: jsonpCallback,
jsonpUrl: params.url,
timeout: params.timeout
}, this.hitchedInvoke);
return request;
},
create: function (params) {
throw new Error("Can not do create via JSONP");
},
update: function (params) {
throw new Error("Can not do updates via JSONP");
},
del: function (params) {
throw new Error("Can not do deletes via JSONP");
},
/**
* @param {string} transport
* @return {boolean}
* @overrides
*/
supportsTransport: function (transport) {
return transport === 'JSONP';
},
/**
* Adds script tag to header of page to make jsonp request and invokes the callback.
* @param {object} params
*/
invokeJsonpRequest: function (params) {
var element = document.createElement('script'),
headElement = document.getElementsByTagName('head')[0],
load = params.load,
error = params.error,
jsonpCallback = params.jsonpCallback,
timeout = params.timeout || 10000,
timeoutId,
handleError = function (err) {
window.clearTimeout(timeoutId);
delete window[jsonpCallback];
headElement.removeChild(element);
if (error) {
error("500", {message: err});
}
};
window[jsonpCallback] = function (data) {
window.clearTimeout(timeoutId);
// TODO: add response validation here
load("200", data);
delete window[jsonpCallback];
headElement.removeChild(element);
};
// Error handlers fall back to timeout.
element.onerror = handleError;
element.onreadystatechange = function () {
var readyState = element.readyState;
if (readyState !== 'loaded') {
handleError({type: 'error'});
}
};
timeoutId = window.setTimeout(handleError, timeout, 'timeout');
element.type = 'text/javascript';
element.src = this.updateQueryString(params.jsonpUrl, params.callbackName, jsonpCallback);
element.id = jsonpCallback;
element.async = true;
element.charset = 'utf-8';
headElement.appendChild(element);
},
/**
* Appends the callback parameter to the existing url
* @param {string} url
* @param {string} key
* @param {string} value
* @returns {string}
*/
updateQueryString: function (url, key, value) {
var regex = new RegExp("([?|&])" + key + "=.*?(&|#|$)(.*)", "gi"),
separator,
hash;
if (regex.test(url)) {
url = url.replace(regex, '$1' + key + "=" + value + '$2$3');
} else {
separator = url.indexOf('?') !== -1 ? '&' : '?';
hash = url.split('#');
url = hash[0] + separator + key + '=' + value;
}
return url;
}
});
return module;
});