JSON

Developing the next generation of open data interchange

« Previous

The Future Ajax Wrapper

8 April 2008

With IE8’s new XDomainRequest feature, a new API is added for cross-site requests, instead of using the W3C cross-site access proposal. Just for fun, I thought I would provide a little glimpse of what the classic Ajax request wrapper function may look like for the next era of web developers. Just a few simple calls to XMLHttpRequest would be way too easy, so instead we get do this:

function doRequest(method,url,async,onLoad,onProgress) {
    var xhr;
    if ((onProgress || isXDomainUrl(url)) && window.XDomainRequest) {
        // if it is x-domain or streaming/incremental updates are needed we will use IE's XDomainRequest for IE
        // streaming/interactive mode is broken in IE's XHR, but for some reason works in XDR (with onprogress), so we will
        // need to use XDR if incremental updates are necessary
        // see bug https://connect.microsoft.com/IE/feedback/ViewFeedback.aspx?FeedbackID=334813
         if (url.match(/^https:/) && !onProgress) {
            // XDR doesn’t work for secure https communication
            // see bug https://connect.microsoft.com/IE/feedback/ViewFeedback.aspx?FeedbackID=333380
            loadUsingScriptTag(url); // script tag insertion can be more secure than XDR
				// in some situations because it supports https
            return;
	}
        xhr = new XDomainRequest;
        // relative paths don’t work in XDomainRequest, see bug https://connect.microsoft.com/IE/feedback/ViewFeedback.aspx?FeedbackID=333275
	if (!url.match(/^http:/)) { // test to see if it is an absolute url
            url = absoluteUrl(location.href,url); // must have a function to turn it into an absolute url
	}
	if (!(method == “GET” || method == “POST”)) {
	    // XDomainRequest does not support methods besides GET and POST
	    // see bug https://connect.microsoft.com/IE/feedback/ViewFeedback.aspx?FeedbackID=334809
	    // We will try to add the method as a parameter and hope the server will understand… good luck :/
	    url += “&method=” + method;
	    method = “POST”;
	}
	function xdrLoad() {
	   if (xhr.contentType.match(/\/xml/)){
		// there is no responseXML in XDomainRequest, so we have to create it manually
		var dom = new ActiveXObject(”Microsoft.XMLDOM”);
		dom.async = false;
		dom.loadXML(xhr.responseText,200);
		onLoad(dom);
	   }
	   else {
	   	onLoad(xhr.responseText,200); // we will assume that the status code is 200, XDomainRequest rejects all other successful status codes
		// see bug https://connect.microsoft.com/IE/feedback/ViewFeedback.aspx?FeedbackID=334804
	   }
	}
	if (async === false) {
	    // XDomainRequest does not support synchronous requests
	    // see bug https://connect.microsoft.com/IE/feedback/ViewFeedback.aspx?FeedbackID=336031
	    // so we will try to block execution on our own (which is not really possible in any reasonable manner)
	    var loaded;
	    xhr.onload = function() {
		loaded = true;
		xdrLoad();
	    }
            xhr.open(method,url);
	    xhr.send(null);
	    while(!loaded) { // try to block until the response is received
		// I am sure the user won’t mind just clicking OK so we can block execution
		alert(”Waiting for the response, please click OK because it probably is here now”);
	    }
 	    return;
	}
	else {	// do an asynchronous request with XDomainRequest
            xhr.onload = xdrLoad;
            xhr.open(method,url);
            xhr.onprogress = onProgress;
	}
    }
    // we will mercifully skip all the branches for ActiveXObject(”Microsoft.XMLHTTP”) to accomodate IE6 and lower
    else {
        xhr = new XMLHttpRequest; // use the standard XHR for same origin and browsers that implement cross-site
			// W3C requests and streaming
        xhr.open(method,url,async);
        xhr.onreadystatechange = function() {
            if (xhr.readyState == 3) // interactive mode
                onProgress(xhr.responseText);
            if (xhr.readyState == 4) // finished
                onLoad(xhr.responseText,xhr.status);
        }
    }
    xhr.send(null); // finally send the request whether it be XDR or XHR

	// and supporting functions
	function absoluteUrl : function(baseUrl, relativeUrl) {
		// This takes a base url and a relative url and resolves the target url.
		// For example:
		// resolveUrl(”http://www.domain.com/path1/path2″,”../path3″) ->”http://www.domain.com/path1/path3″
		//
		if (relativeUrl.match(/\w+:\/\//))
			return relativeUrl;
		if (relativeUrl.charAt(0)==’/') {
			baseUrl = baseUrl.match(/.*\/\/[^\/]+/)
			return (baseUrl ? baseUrl[0] : ”) + relativeUrl;
		}
			//TODO: handle protocol relative urls:  ://www.domain.com
		baseUrl = baseUrl.substring(0,baseUrl.length - baseUrl.match(/[^\/]*$/)[0].length);// clean off the trailing path
		if (relativeUrl == ‘.’)
			return baseUrl;
		while (relativeUrl.substring(0,3) == ‘../’) {
			baseUrl = baseUrl.substring(0,baseUrl.length - baseUrl.match(/[^\/]*\/$/)[0].length);
			relativeUrl = relativeUrl.substring(3);
		}
		return baseUrl + relativeUrl;
	}
	function loadUsingScriptTag(url) {
		… do JSONP here if we want
	}
}

Disclaimer: This is completely untested code, just a glimpse of the issues and divergences I am already aware of. I am sure the working code with be even more complicated. I didn’t get into the code branches for errors (XDomainRequest uses a new “onerror” handler), and or discuss the server side branching code for two different sets of headers, or the lack of support for basic HTTP headers.

I do appreciate IE’s effort to provide some mechanism for cross-site requests. But, do we really need a new API? Also, don’t get me wrong, I think the W3C cross-site XHR has it’s warts as well, for example I think XML processing instructions do not belong in an XHR specification.

Posted in Ajax | Trackback | del.icio.us | Top Of Page

    6 Responses to “The Future Ajax Wrapper”

  1. Ric Johnson Says:

    OMG!
    I am a MS.NET developer, and I know that they do not want to be constrained by the W3’s ridiculous meeting politics and time lines, but does that mean they have to re-invent EVERYTHING?

  2. henrah Says:

    Here is another possible implementation of absoluteUrl():

    var tempDiv = document.createElement('div');

    function absoluteURL(url) {
    tempDiv.innerHTML = '‘;
    return tempDiv.firstChild.href;
    };

    A little cheeky, I know. But very short!

    Also, there is a problem with your comment form: it won’t accept email addresses containing a plus sign ( ). They’re in the spec!

  3. red Says:

    Thank you M$… Soon we’ll have the IE6/7/8 Nightmare! Is 6 and 7 not enough?

  4. henrah Says:

    Wow, my code block got mangled beyond recognition.

    Here is my alternate implementation of absoluteUrl(), exploiting the html parser: http://dpaste.com/hold/45665/

  5. Kris Zyp Says:

    henrah, that is very cool. Is that x-browser, or just for IE? It is only needed for IE in this code, but it would be cool to use that elsewhere.

  6. Anne van Kesteren Says:

    Kris, Access Control can also be used for XSLT, XBL, et cetera. There a processing instruction makes a lot of sense for ease of authoring. I agree that for typical XMLHttpRequest scenarios you probably do not need it, but it is not just for XMLHttpRequest.

Leave a Reply