Supplementary - On-Demand Loaders
Three Functions for Loading JavaScript and CSS Code Dynamically
T
ypically, web pages import JavaScript code statically by means of script elements within the head element. However, code can also be loaded dynamically, either by inserting script elements explicitly into the page's DOM hierarchy, or by using XMLHTTPRequest. These approaches are far more efficient, as applications need import code only when required, thus consuming bandwidth and platform resources only when necessary, and this page presents a function that automates the XHR-based technique.
Critically, this function can be used in conjunction with AspectJS, and this is significant because the intersection of the two technologies permits library downloads on demand but entirely transparently at the point of need. This is explored in the AspectJS Applications page entitled On-Demand Loading.
Contents
LoadLib_XHR
LoadLib_XHR is presented in Listing 1. It is an alternative to LoadLib_STH in that it also loads JavaScript code dynamically, but uses XMLHTTPRequest to link to the server (hence the 'XHR' suffix).
This means that, unlike LoadLib_STH, it can download code only from the originating domain, but that it can do so synchronously. While this blocks further execution until the transaction has completed (successfully or not), it obviates the call-back technique described in the page that covers LoadLib_STH, thus simplifying design, and allowing certain applications that would otherwise be impracticable.
However, it also means that a tardy response from the server could yield an unacceptable delay for users. LoadLib_XHR will therefore abort the transaction with the server after a (caller definable) period; callers can also provide a reference to a client-code method that the function will call in this event.
Do note also that the catch clauses in LoadLib_XHR are present because the function is designed to work with AspectJS. The function-interception mechanism generated by that product swallows all exceptions thrown by affix functions, therefore LoadLib_XHR catches any exceptions thrown by the XHR library, and reports them explicitly to the caller through the OnError handler.

 // Listing 1

 function LoadLib_XHR (Args)
    {
    var XHRWrapper = null;
    var Timer      = null;

    function OnTimeout ()
       {
       if (XHRWrapper)     { XHRWrapper.Abort (); }
       if (Args.OnTimeout) { Args.OnTimeout   (); }
       }

    function OnError (E)
       {
       clearTimeout (Timer);

       if (Args.OnError) { Args.OnError (E); }
       else              { throw E; }

       }

    function OnLibLoaded (XHRObj)
       {
       clearTimeout (Timer);

       try
          {
          if (window.execScript) { window.execScript     (XHRObj.responseText); }
          else                   { this.eval ? this.eval (XHRObj.responseText)
                                             : eval      (XHRObj.responseText); }
          }

       catch (E)
          {
          if (Args.OnError) { Args.OnError (E); }
          else              { throw E; }
          }

       }

    try
       {
       XHRWrapper = XHRFactory.CreateXHRWrapper ();

       if (Args.TimeoutDelay) { Timer = setTimeout (OnTimeout, Args.TimeoutDelay); }
       else                   { Timer = setTimeout (OnTimeout, 3000); }

       XHRWrapper.DoTxn (Args.LibURL, "GET", false, OnLibLoaded, null, null, OnError);

       }

    catch (E) { OnError (E); }

    }
   
Using LoadLib_XHR
Using LoadLib_XHR is more involved than using LoadLib_STH or LoadStyleSheet for two reasons:
First, it relies upon the XMLHTTPRequest library that is also available freely on this site, a copy of which you should acquire and make available in any page that uses LoadLib_XHR. Alternatively, you should provide an XMLHTTPRequest library that exposes an identical API, or edit the code in LoadLib_XHR to call a library with a different API.
The second consideration when using LoadLib_XHR is that arguments must be passed in an indirect fashion as members of an object. This may seem strange and a little awkward, but this is also because the function is designed to be used in conjunction with AspectJS. See Part Three of the AspectJS tutorial for more information on the single-argument signature requirement that AspectJS imposes on affix functions.
An example of such a 'parameter object' is shown in Example One. Note that it does not have to be called 'Args', nor are TimeoutDelay, OnTimeout or OnError necessary. Additionally, the members do not have to be defined in the order shown, but such objects must have at least one member called LibURL, which denotes the URL of the desired library.
If included in the object, TimeoutDelay denotes the time in milliseconds that should elapse before the abort method is called on the XMLHTTPRequest object that is used to communicate with the server. If this member is omitted, a default delay of 3000 milliseconds is used. To set a different default, change the value of 3000 that appears within the try block in the code that makes up LoadLib_XHR.
OnTimeout, if included, must refer to a function that LoadLib_XHR will call if the connection with the server times out. If it is absent then LoadLib_XHR calls the abort method on the XMLHTTPRequest object, but does nothing else. To change this default behaviour, edit the code within the inner function called OnTimeout that resides within LoadLib_XHR.
Finally, if OnError is included, it must refer to a user-defined function that is called if the HTTP transaction fails. If it is absent then LoadLib_XHR simply cancels the timer, but does nothing else. To change this default behaviour, edit the code in the inner function called OnError that resides within LoadLib_XHR. Note that the exception object thrown by the underlying XHR object is of the JavaScript Error type, and is passed to this call-back. Accessing the message member of this object thus allows diagnosis of what went wrong.
Note that, while the call-back references must have the names that LoadLib_XHR expects, they do not have to refer to functions of the same name.

 // Example 1: Structure of object that must be passed to LoadLib_XHR

 var Args =             // Does not have to be called 'Args'
    {
    LibURL       : ...  // Mandatory member. Denotes the URL of the library

    TimeoutDelay : ...  // Optional member. Must be an integer if included.
                        // Overrides the default delay of 3000 milliseconds

    OnTimeout    : ...  // Optional member. If included, must refer to a
                        // call-back that is invoked if the XHR transaction
                        // times out

    OnError      : ...  // Optional member. If included, must refer to an
                        // error-handler call-back that is invoked if the HTTP
                        // transaction fails
    };
            
Stipulating a URL
Example Two shows the simplest way of calling LoadLib_XHR, where just the URL of the desired library is passed. JavaScript allows object-literal syntax to form an argument to a function, therefore the argument object can be defined and passed in a single statement.

 // Example 2: Passing just the URL

 LoadLib_XHR ( { LibURL : "MyLib.js" } );
            
Setting a Timeout Delay
Example Three demonstrates passing the URL of the desired library, along with a Timeout delay.

 // Example 3: Passing the URL and a Timeout delay

 LoadLib_XHR ( { LibURL : "MyLib.js", TimeoutDelay : 2000 } );
            
Passing Timeout and Error Handlers
Example Four extends the previous example and passes a reference to a Timeout call-back and an error handling call-back. These functions are defined outside of the object-literal definition that is passed to LoadLib_XHR, although they could, in principle, be defined within the object-literal definition (assuming you like tortuous code).
Note that in the event of a time-out or error, the respective call-backs should throw an exception in order to prevent LoadLib_XHR's caller from attempting to proceed normally.

 // Example 4: Passing the URL, Timeout and Error Callbacks

 function OnLibLoadTimeout ()  { alert ("Library-load timed out"); }
 function OnLibLoadError   (E) { alert (E.message); }

 LoadLib_XHR ( { LibURL       : "MyLib.js",
                 TimeoutDelay : 2000,
                 OnTimeout    : OnLibLoadTimeout,
                 OnError      : OnLibLoadError } );
            
Alternative Argument-Syntax
Obviously, argument objects can be defined outside of the call to LoadLib_XHR, which makes calling that function a little neater, at the expense of code volume, and Example Five illustrates this.

 // Example 5: Out-of-line argument-object definition

 function OnLibLoadTimeout ()  { alert ("Library-load timed out"); }
 function OnLibLoadError   (E) { alert (E.message); }

 var Args =
    {
    LibURL       : "MyLib.js",
    TimeoutDelay : 5000,
    OnTimeout    : OnLibLoadTimeout,
    OnError      : OnLibLoadError
    };

 LoadLib_XHR (Args);
            
Finally, argument objects can also be constructed dynamically using a constructor function, rather than by using object-literal syntax, and this is demonstrated in Example Six.

 // Example 6: Constructor-based argument-object creation

 function OnLibLoadTimeout ()  { alert ("Library-load timed out"); }
 function OnLibLoadError   (E) { alert (E.message); }

 function LoadLibArgs (LibURL, TimeoutDelay, OnTimeout, OnError)
    {
    this.LibURL       = LibURL;
    this.TimeoutDelay = TimeoutDelay;
    this.OnTimeout    = OnTimeout;     // Call back functions could
    this.OnError      = OnError;       // be defined anonymously here
    };

 var Args = new LibLoadArgs ("MyLib.js", 2000, OnLibLoadTimeout, OnLibLoadError);

 LoadLib_XHR (Args);
            
Copyright © Dodeca Technologies Ltd. 2007