Activate your free membership today | Log-in

Friday, April 18th, 2008

100 Line Ajax Wrapper

Category: Browsers, IE, XmlHttpRequest

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

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

Posted by Dion Almaer at 6:45 am

+++--
3.9 rating from 9 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.