This tips demonstrates how to call the API in JavaScript without the need of jQuery or other frameworks.
In a lot of cases you will find that the JSONP implementations provided in various frameworks are just not quite right for the job and require a lot of massaging.
Here is a JSONP implementation I designed specifically for use with the API.
I have tested on many browsers on windows and have yet to encounter a failure.
It may be easy to miss, but a packed version is at the bottom of this post and weighs in at 696 bytes.
Usage Example
<script src="scripts/DevTips.js" type="text/javascript"></script>
<script type="text/javascript">
var myApiKey = "";
var url = "http://api.stackapps.com/0.9/questions/unanswered";
var parameters = {
"sort": "votes",
"min": "5",
"order": "asc"
};
DevTips.jsonp(url, parameters, myApiKey,
function success(data) {
alert(data.total);
},
function failure(error) {
alert(error.message);
});
</script>
The Code:
var DevTips = DevTips || {};
(function(dt) {
var __callbackId = 0;
var maxQueryStringLengthErrorMessage = "Query string length exceeds maximum recommended value of ";
var maxPathLengthErrorMessage = "Url path length exceeds maximum recommended value of ";
// expose these for reference
var maxQueryStringLength = dt.maxQueryStringLength = 1000;
var maxPathLength = dt.maxPathLength = 240;
var jsEncode = dt.jsEncode = function(value) {
// properly encode arrays and dates
// NOTE: use this to turn arrays of ids into
// semicolon delimited string.
if (value instanceof Date) {
return new Date(value).getTime();
};
if (value instanceof Array) {
var values = [];
for (var i = 0; i < value.length; i++) {
values.push(encodeURIComponent(value[i]));
};
// vectorize arrays
return values.join(";");
};
return encodeURIComponent(value);
};
var jsonp = dt.jsonp = function(url, parameters, apiKey, success, error, timeout) {
/// <summary>
/// Sets up a JSONP call to the api.
/// </summary>
/// <param name="url" type="String"></param>
/// <param name="parameters" type="Map">Optional</param>
/// <param name="apiKey" type="String">Optional</param>
/// <param name="success" type="Function(data)">Handle successful api call. data = api JSOB</param>
/// <param name="error" type="Function(error)">Handle and error condition. error = API error JSOB.
/// Optional, if you are crazy or would like to be driven that way.</param>
/// <param name="timeout" type="Number">Timeout in ms. Optional. Defaults to 10,000</param>
if (url.length > maxPathLength) {
throw new Error(maxPathLengthErrorMessage + maxPathLength);
};
var callBackName = "_callback" + __callbackId++;
var queryString = "?jsonp=DevTips.jsonp." + callBackName;
if (parameters) {
for (var name in parameters) {
if (parameters.hasOwnProperty(name)) {
queryString = queryString + "&" + name + "=" + jsEncode(parameters[name]);
};
};
};
if (queryString.length > maxQueryStringLength) {
throw new Error(maxQueryStringLengthErrorMessage + maxQueryStringLength);
};
// setup the callback
jsonp[callBackName] = function(data) {
delete jsonp[callBackName];
if (data.error) {
if (error) {
data.error.callback = callBackName;
error(data.error);
};
}
else {
success(data);
};
};
// send the request
var scr = document.createElement("script");
scr.type = "text/javascript";
scr.src = url + queryString;
var head = document.getElementsByTagName("head")[0];
head.insertBefore(scr, head.firstChild);
// default to 10 second timeout
timeout = timeout || 10000;
window.setTimeout(function() {
if (typeof jsonp[callBackName] == "function") {
// replace success with null callback in case the request is just very latent.
jsonp[callBackName] = function(data) {
delete jsonp[callBackName];
};
// call the error callback
error({ code: 408, message: "Request Timeout", callback: callBackName });
// set a longer timeout to safely clean up the unused callback.
window.setTimeout(function() {
if (typeof jsonp[callBackName] == "function") {
delete jsonp[callBackName];
};
}, 60000);
};
}, timeout);
};
})(DevTips);
The Code Packed with Dojo ShrinkSafe (696 bytes)
var DevTips=DevTips||{}; (function(dt){ var _2=0; var _3="Query string length exceeds maximum recommended value of "; var _4="Url path length exceeds maximum recommended value of "; var _5=dt.maxQueryStringLength=1000; var _6=dt.maxPathLength=240; var _7=dt.jsEncode=function(_8){ if(_8 instanceof Date){ return new Date(_8).getTime(); } if(_8 instanceof Array){ var _9=[]; for(var i=0;i<_8.length;i++){ _9.push(encodeURIComponent(_8[i])); } return _9.join(";"); } return encodeURIComponent(_8); }; var _b=dt.jsonp=function(_c,_d,_e,_f,_10,_11){ if(_c.length>_6){ throw new Error(_4+_6); } var _12="_callback"+_2++; var _13="?jsonp=DevTips.jsonp."+_12; if(_d){ for(var _14 in _d){ if(_d.hasOwnProperty(_14)){ _13=_13+"&"+_14+"="+_7(_d[_14]); } } } if(_13.length>_5){ throw new Error(_3+_5); } _b[_12]=function(_15){ delete _b[_12]; if(_15.error){ if(_10){ _15.error.callback=_12; _10(_15.error); } }else{ _f(_15); } }; var scr=document.createElement("script"); scr.type="text/javascript"; scr.src=_c+_13; var _17=document.getElementsByTagName("head")[0]; _17.insertBefore(scr,_17.firstChild); _11=_11||10000; window.setTimeout(function(){ if(typeof _b[_12]=="function"){ _b[_12]=function(_18){ delete _b[_12]; }; _10({code:408,message:"Request Timeout",callback:_12}); window.setTimeout(function(){ if(typeof _b[_12]=="function"){ delete _b[_12]; } },60000); } },_11); }; })(DevTips);