Friday, April 18th, 2008

100 Line Ajax Wrapper

Category: Browsers, IE, XmlHttpRequest

<>p>Kris Zyp gives us a glimpse at a potential future with his 100 line Ajax wrapper that tries to do the right thing cross browser for the various cross-domain models:

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:

javascript
< view plain text >
  1. function doRequest(method,url,async,onLoad,onProgress) {
  2.     var xhr;
  3.     if ((onProgress || isXDomainUrl(url)) && window.XDomainRequest) {
  4.         // if it is x-domain or streaming/incremental updates are needed we will use IE's XDomainRequest for IE
  5.         // streaming/interactive mode is broken in IE's XHR, but for some reason works in XDR (with onprogress), so we will
  6.         // need to use XDR if incremental updates are necessary
  7.         // see bug https://connect.microsoft.com/IE/feedback/ViewFeedback.aspx?FeedbackID=334813
  8.          if (url.match(/^https:/) && !onProgress) {
  9.             // XDR doesn’t work for secure https communication
  10.             // see bug https://connect.microsoft.com/IE/feedback/ViewFeedback.aspx?FeedbackID=333380
  11.             loadUsingScriptTag(url); // script tag insertion can be more secure than XDR
  12.                 // in some situations because it supports https
  13.             return;
  14.     }
  15.         xhr = new XDomainRequest;
  16.         // relative paths don’t work in XDomainRequest, see bug https://connect.microsoft.com/IE/feedback/ViewFeedback.aspx?FeedbackID=333275
  17.     if (!url.match(/^http:/)) { // test to see if it is an absolute url
  18.             url = absoluteUrl(location.href,url); // must have a function to turn it into an absolute url
  19.     }
  20.     if (!(method == “GET” || method == “POST”)) {
  21.         // XDomainRequest does not support methods besides GET and POST
  22.         // see bug https://connect.microsoft.com/IE/feedback/ViewFeedback.aspx?FeedbackID=334809
  23.         // We will try to add the method as a parameter and hope the server will understand… good luck :/
  24.         url += “&method=” + method;
  25.         method = “POST”;
  26.     }
  27.     function xdrLoad() {
  28.        if (xhr.contentType.match(/\/xml/)){
  29.         // there is no responseXML in XDomainRequest, so we have to create it manually
  30.         var dom = new ActiveXObject(”Microsoft.XMLDOM”);
  31.         dom.async = false;
  32.         dom.loadXML(xhr.responseText,200);
  33.         onLoad(dom);
  34.        }
  35.        else {
  36.         onLoad(xhr.responseText,200); // we will assume that the status code is 200, XDomainRequest rejects all other successful status codes
  37.         // see bug https://connect.microsoft.com/IE/feedback/ViewFeedback.aspx?FeedbackID=334804
  38.        }
  39.     }
  40.     if (async === false) {
  41.         // XDomainRequest does not support synchronous requests
  42.         // see bug https://connect.microsoft.com/IE/feedback/ViewFeedback.aspx?FeedbackID=336031
  43.         // so we will try to block execution on our own (which is not really possible in any reasonable manner)
  44.         var loaded;
  45.         xhr.onload = function() {
  46.         loaded = true;
  47.         xdrLoad();
  48.         }
  49.             xhr.open(method,url);
  50.         xhr.send(null);
  51.         while(!loaded) { // try to block until the response is received
  52.         // I am sure the user won’t mind just clicking OK so we can block execution
  53.         alert(”Waiting for the response, please click OK because it probably is here now”);
  54.         }
  55.         return;
  56.     }
  57.     else {  // do an asynchronous request with XDomainRequest
  58.             xhr.onload = xdrLoad;
  59.             xhr.open(method,url);
  60.             xhr.onprogress = onProgress;
  61.     }
  62.     }
  63.     // we will mercifully skip all the branches for ActiveXObject(”Microsoft.XMLHTTP”) to accomodate IE6 and lower
  64.     else {
  65.         xhr = new XMLHttpRequest; // use the standard XHR for same origin and browsers that implement cross-site
  66.             // W3C requests and streaming
  67.         xhr.open(method,url,async);
  68.         xhr.onreadystatechange = function() {
  69.             if (xhr.readyState == 3) // interactive mode
  70.                 onProgress(xhr.responseText);
  71.             if (xhr.readyState == 4) // finished
  72.                 onLoad(xhr.responseText,xhr.status);
  73.         }
  74.     }
  75.     xhr.send(null); // finally send the request whether it be XDR or XHR
  76.  
  77.     // and supporting functions
  78.     function absoluteUrl : function(baseUrl, relativeUrl) {
  79.         // This takes a base url and a relative url and resolves the target url.
  80.         // For example:
  81.         // resolveUrl(”http://www.domain.com/path1/path2″,”../path3″) ->”http://www.domain.com/path1/path3″
  82.         //
  83.         if (relativeUrl.match(/\w+:\/\//))
  84.             return relativeUrl;
  85.         if (relativeUrl.charAt(0)==’/') {
  86.             baseUrl = baseUrl.match(/.*\/\/[^\/]+/)
  87.             return (baseUrl ? baseUrl[0] : ”) + relativeUrl;
  88.         }
  89.             //TODO: handle protocol relative urls:  ://www.domain.com
  90.         baseUrl = baseUrl.substring(0,baseUrl.length - baseUrl.match(/[^\/]*$/)[0].length);// clean off the trailing path
  91.         if (relativeUrl == ‘.’)
  92.             return baseUrl;
  93.         while (relativeUrl.substring(0,3) == ‘../’) {
  94.             baseUrl = baseUrl.substring(0,baseUrl.length - baseUrl.match(/[^\/]*\/$/)[0].length);
  95.             relativeUrl = relativeUrl.substring(3);
  96.         }
  97.         return baseUrl + relativeUrl;
  98.     }
  99.     function loadUsingScriptTag(url) {
  100.         … do JSONP here if we want
  101.     }
  102. }

I think that says…. please just implement the standard IE team! :)

Related Content:

Posted by Dion Almaer at 6:45 am
2 Comments

+++--
3.6 rating from 17 votes

2 Comments »

Comments feed TrackBack URI

“With IE8’s new XDomainRequest feature, a new API is added for cross-site requests, instead of using the W3C cross-site access proposal”

Gee, I see they are back to avoiding standards. They never learn… :(

Comment by Steve — April 18, 2008

Oh no, Steve — they learned. They learned a long time ago how to Embrace, Extend, and Extinguish.

But, seriously, let them do what they want — we’ll make all wrappers cross-browser like we’ve been doing, and then Microsoft will just have to put up with their scripts running as slow as everyone else’s.

Comment by mdmadph — April 18, 2008

Leave a comment

You must be logged in to post a comment.