Supplementary - XMLHTTPRequest Library
A Small JavaScript-Library that Automates working with XMLHTTPRequest
Overview
The XHRFactory object is a singleton factory-object that is implemented using JavaScript's object-literal notation. While it is capable of creating raw XMLHTTPRequest objects that are appropriate to the platform in question, its strength lies in its ability to create 'wrapper' objects that subsume an XHR object, and which automate the process of opening the connection with the server and sending the request.
XHR wrappers also absolve client code of managing the (completely needless, and thoroughly annoying) bugs, inconsistencies and anomalies that the various XMLHTTPRequest-implementations exhibit. These incude:
  • The Firefox synchronous-call anomaly, where the response handler must be executed explicitly after the send call
  • The disappearance of the Firefox synchronous-call anomaly in the presence of Firebug (guaranteed to stump you completely when testing on arbitrary Firefox installations)
  • The bug that some Gecko browsers exhibit, where a call to an XHR object's abort method causes a call to the response handler
  • The XHR-object reuse problem that Internet Explorer exhibits with regard to when the response-handler reference is set
  • The caching problem that Internet Explorer exhibits (although the fix is still fairly crude, and will be addressed in a future release)
It does not address the in-page and inter-page memory-leak bugs that occur in Internet Explorer up to (apparently) version six.
Given these points, XHRFactory exposes just two methods that are meaningful for client code to invoke. These are CreateXHR and CreateXHRWrapper. It does support other methods, but it is not profitable for client code to call these, as they are simply support functions that CreateXHR and CreateXHRWrapper use. It would be possible to 'privatise' these by the use of closures, but the overhead incurred would offset the efficiencies that the product seeks to exploit. Note that XHRWrappers do not permit control of content headers currently, and this will be addressed in a future release.
Using CreateXHR
XHRFactory.CreateXHR creates an XMLHTTPRequest object that is appropriate for the platform on which the application is running and, in this respect, incorporates the browser detection tests that are necessary to achieve this.
Importantly, this function configures the XHRFactory object such that the browser tests are performed only once when it is first called. This means that subsequent calls to CreateXHR are extremely efficient and considerably faster than the alternative, which would be to perform browser detection redundantly every time it was called. CreateXHR also configures the XHRFactory object in other ways such that the code contained within the DoTxn method of XHRWrapper objects is simpler, faster and more efficient.
Callers of CreateXHR must furnish a full response-handler for each XHR object created, and are responsible for all other aspects of using XHR objects such as calling their open, and send methods etc. These points are documented on other sites, and in a number of books, and the logic that handles these is also to be found in the listing for the XHRFactory object, therefore Example One gives only a minimal illustration of calling and using CreateXHR.
Note that should you use this method in preference to creating and using a wrapper object, you will gain a performance advantage in terms of XHR object-creation, but you will have to negotiate the minefield of bugs and inconsistencies that XHR exhibits on the various platforms.

 // Example 1: Minimal illustration of using CreateXHR

 try
    {
    var XHRObj = XHRFactory.CreateXHR ();

    XHRObj.open (...);

    XHRObj.onreadystatechange = ...

    XHRObj.send (...);

    }

 catch (E) { ... }
            
Using CreateXHRWrapper
Given this, the considerably easier route is to use XHRFactory.CreateXHRWrapper, which creates an object that contains a reference to an XMLHTTPRequest object, and which contains a method called DoTxn that manages communication with the server.
Example Two illustrates creating an XHRWrapper object.

 // Example 2: Using CreateXHRWrapper

 try
    {
    var XHRWrapper = XHRFactory.CreateXHRWrapper ();

    ...

    }

 catch (E) { ... }
            
Understanding DoTxn
The DoTxn method of an XHRWrapper object calls the open and send methods of the XHR object that the XHRWrapper contains, and manages the finer details that surround this process.
Critically, DoTxn also provides a 'partial' or 'base' response handler that comes into play whenever data is returned by the server. Normally, when working with XMLHTTPRequest, processing any data returned from the server requires each response handler to contain logic that tests the readystate and status attributes of the object concerned. Example Three demonstrates this.
However, repetition of this code in every response handler within an application creates unwanted clutter that impinges on readability, and increases the amount of code that must be downloaded onto the client, thus harming efficiency.
DoTxn circumvents this by implementing an inner function that performs the testing, and by assigning a reference to that inner function to the onreadystatechange attribute of the XHR object concerned. Callers of DoTxn that expect data to be returned from the server should pass a reference to a function that can process that data such that, when DoTxn executes, the assignation of its inner function to the onreadystatechange attribute forms a closure that preserves that reference.

 // Example 3: Typical onreadystatechange event handler

 var XHRObj = ...

 XHRObj.onreadystatechange = function ()
    {
    if (XHRObject.readyState === 4)
       {
       if (ObjRef.status === 200)
          {
          // Process data returned from server

          alert (XHRObj.responseText);

          }

       else
          {
          // Report error to user

          ...

          }

       }

    }
            
When the server responds the XHR object invokes the inner function, which performs the necessary tests before calling the function that is on the other end of the caller-supplied reference. This allows all response handlers implemented in client code to remain free of redundant logic. This is illustrated in Example Four, and demonstrated in the examples in the next section.

 // Example 4: onreadystatechange event-handler required
 //            when using DoTxn. Note the absence of the
 //            typical conditional logic

 function MyOnReadyStateChangeHandler (XHRObj)
    {

    alert (XHRObj.responseText);

    }
            
Using XHRWrappers
Once an XHRWrapper object has been created using XHRFactory.CreateXHRWrapper, it can be put to use simply by calling DoTxn.
Callers of DoTxn must, at the least, supply an HTTP method-string, a string denoting the URL to communicate with, and a boolean value denoting whether the transaction should be synchronous or asynchronous.
Callers may also supply a string containing any data that is to be sent to the server, a reference to a response-handler function (if the HTTP method-string is "GET"), a string denoting the mime-type (if the default is inappropriate) and a reference to an error-handler function that will be called if the transaction fails. Note that DoTxn returns a reference to the wrapper object in question, meaning that a wrapper object can be created and put to use in a single statement, as Example Five shows.

 // Example 5: Using an XHRWrapper - Single-stage creation/dispatch

 function OnXHRError   (XHRObj) { alert (XHRObj.status); }
 function OnXHRSuccess (XHRObj)
    {

    alert (XHRObj.responseText); // Note: no testing for a successful
                                 // response is required here - all
                                 // of that is performed in DoTxn
    }

 try
    {
    XHRFactory.CreateXHRWrapper ().DoTxn (".../somedata.txt", "GET",
                                           true, OnXHRSuccess, null,
                                           null, OnXHRError);

    }

 catch (E) { alert (E.message); }
            
An alternative is to create the XHRWrapper and then call DoTxn in two steps, as illustrated in Example Six.
Note that when communicating with the server synchronously, it is important to guard against the server being unavailable (for whatever reason). In this case, the XHRWrapper should be created, after which the setTimeout function should be called and passed an appropriate delay, and a reference to a function that will call the abort method on the underlying XHR object.

 // Example 6: Using an XHRWrapper - Two-stage creation and dispatch

 function OnXHRError   (XHRObj) { }
 function OnXHRSuccess (XHRObj) { }

 try
    {
    var XHRWrapper = XHRFactory.CreateXHRWrapper ();

    ...

    XHRWrapper.DoTxn (".../someservice.php", "GET",
                       true, OnXHRSuccess, null,
                       null, OnXHRError);

    }

 catch (E) { alert (E.message); }
            
Reusing an XHRWrapper
The fact that an XHRWrapper can be created and used in two steps allows a wrapper to be re-used indefinitely, and Example Seven demonstrates this.

 // Example 7: Reusing an XHRWrapper

 function OnXHRError    (XHRObj) { }
 function OnXHRSuccess1 (XHRObj) { }
 function OnXHRSuccess2 (XHRObj) { }

 try       { XHRWrapper = XHRFactory.CreateXHRWrapper (); }
 catch (E) { alert (E.message); }

 function MyEventHandler ()
    {
    var XHRWrapper = XHRFactory.CreateXHRWrapper ();


    ...

    XHRWrapper.DoTxn (".../getservice1.php", "GET",
                       true, OnXHRSuccess1, null,
                       null, OnXHRError);

    ...

    XHRWrapper.DoTxn (".../putservice.php", "PUT",
                       true, null, "data_val = 1234",
                       null, OnXHRError);


    ...

    XHRWrapper.DoTxn (".../getservice2.php", "GET",
                       true, OnXHRSuccess2, null,
                       null, OnXHRError);

    }
            
Using Multiple XHRWrappers
The XHRFactory object allows multiple XHR objects and XHRWrappers to be created and manipulated independently of each other. Example Eight demonstrates this, where two XHRWrappers are created and communicate with the server.

 // Example 8: Creating and using multiple XHRWrapper objects

 function OnXHRError   (XHRObj) { }
 function OnXHRSuccess (XHRObj) { }

 try
    {
    XHRFactory.CreateXHRWrapper ().DoTxn ("GET", "somedata.xml",
                                           true, OnXHRSuccess, null
                                           null, OnXHRError);

    XHRFactory.CreateXHRWrapper ().DoTxn ("GET", "someotherdata.xml",
                                           true, OnXHRSuccess, null,
                                           null, OnXHRError);

    }

 catch (E) { alert (E.message); }
            
Aborting Transactions
To abort a transaction, simply call the Abort method of the XHRWrapper object in question. Example Nine illustrates this through the use of a timer when connecting asynchronously.
Here, the function called OnXHRSuccess is executed if the server responds within three seconds. This function cancels the timer, and (if appropriate to the transaction in question) processes any data returned by the server.
If, however, the server does not respond within that interval then the OnTimeout function is invoked automatically, and it is here that the Abort method of the XHRWRapper is called. This terminates the transaction with the server, and reports the problem to the user.
Note that calling the abort method of the underlying XHR object will also cancel a transaction, but on browsers such as Firefox this will cause execution of the response handler. Using an XHRWrapper's Abort method guards against this.

 // Example 9: Aborting a transaction

 // OnXHRError not shown

 var Timer = null;

 function OnXHRSuccess ()
    {
    clearTimeout (Timer);

    // Do something here with data
    // returned from server (if appropriate)

    }

 function OnTimeout ()
    {
    XHRWrapper.Abort ();
    alert            ("XHR transaction timed-out");
    }

 ...

 try
    {
    var XHRWrapper = XHRFactory.CreateXHRWrapper ();

    ...

    XHRWrapper.DoTxn (".../someservice.php", "GET",
                       false, OnXHRSuccess, null,
                       null,  OnXHRError);

    Timer = setTimeout (OnTimeout, 3000);

    ...

    }

 catch (E) { alert (E.message); }
            
Copyright © Dodeca Technologies Ltd. 2007-2008