12

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);

You must log in to answer this question.

Browse other questions tagged .