The Gravy Framework



Collection of AJAX support routines.

Author: Bruce Wallace (

Version: 1.0

Class Summary
XMLreq Constructor for XMLreq "object" which encapsulates a REST-style request/reply transaction that returns XML or Text.

Method Summary
static Object GetXslDOM( xslURL )
           retrieve XSL DOM from given URL
static String XformDOM( xslDOM, xmlDOM )
           transform given XML DOM using given XSLT DOM
static String XformURL( xslURL, xmlDOM )
           transform given XML DOM using an XSLT retrieved from given URL

// This file uses JSDoc-friendly comments [ ]

 * @file         ajax.js
 * @fileoverview Collection of AJAX support routines.
 * @author       Bruce Wallace  (
 * @requires     utils.js
 * @version      1.0

 * @class Constructor for XMLreq "object" which encapsulates a REST-style
 * request/reply transaction that returns XML or Text. This class makes
 * multiple simultaneous requests possible, however, a separate instance
 * of this class is required for each request. This constructor will
 * automatically cause this request to be immediately launched unless
 * launch is suppressed.<p>
 * If post data is defined then this request is made via a POST (otherwise
 * via a GET) HTTP request.<p>
 * Any instance of this class can be created in "test mode" that reads
 * XML directly from an XML file rather than making a POST/GET request for XML.
 * This class has helper methods that will transform the response XML via
 * an XSL document specified via filename or URL.
 * @param {String} url the URL to request XML from (or XML file path in test mode)
 * @param {Function} userCallbackFunction the callback to handle the XML when it arrives
 * [note: The callback will have this XMLreq object passed to it via
 *  both "this", and, as a single function argument.]
 * @param {String} optPostData optional post data string (should be "undefined" if not a POST req.)
 * @param {boolean} optWaitFlag optional "wait for reply" flag (i.e. DONT do this async if true)
 * @param {boolean} optSuppressLaunchFlag optional "suppress launch" flag
 * @param {boolean} optTestFlag optional "read test files" flag (i.e. read XML file rather than ask server)
 * @throws ErrMsg if missing required parameter(s)
 * @author Bruce Wallace  (
 * @version 1.0
function XMLreq( url, userCallbackFunction, optPostData, optWaitFlag, optTestFlag, optSuppressLaunchFlag )
	// validate parameters
	if (url==undefined) throw "missing required parameter: URL";

	// initialize instance data   = "XMLreq" + timestamp(); //mostly unique name
	this.url    = url;   = optPostData;
	this.test   = optTestFlag;
	this.async  = optWaitFlag?false:true;
	this.usercb = userCallbackFunction || function(){ Error("Unhandled Reply=["+this.getResponseText()+"]"); };
	this.cb     = new Function( "GetGlobalVar(\""+ +"\").cbwrapper()" );

	// save reference to "this" in page-level variable (to be retrieved in callback)
	SetGlobalVar(, this );
	if (!optSuppressLaunchFlag) this.launch();

// ----------------------------------------------------------------------------
// define/override methods
// ----------------------------------------------------------------------------

/** method to return "this" as a human-readable string @type String */
XMLreq.prototype.toString = function(){ return ObjectToString(this); }

/** method to create new XMLHttpRequest object for "this" request
 * @return "XMLHttpRequest object" @type Object
 * @throws ErrMsg if XML Request Objects arent supported by Browser
XMLreq.prototype.newRequestObj = function()
	function getObj( xmlReq )
		// if in test mode, return an XML document
		if (xmlReq.test){
			var XMLDoc = new ActiveXObject("MSXML2.DOMDocument");
			XMLDoc.async = xmlReq.async;
			return XMLDoc;

	    // has native XMLHttpRequest object?
	    if (window.XMLHttpRequest) return new XMLHttpRequest();
	    // has IE/Windows ActiveX version?
	    if (window.ActiveXObject)
	         try      { return new ActiveXObject("Msxml2.XMLHTTP"   ); }
	         catch(e) { return new ActiveXObject("Microsoft.XMLHTTP"); }
		throw "XML Request Objects are not supported by this Browser.";

	var o = getObj( this );
	o.onreadystatechange = this.cb;
	return o;

/** callback wrapper method to relieve user callback from gory details of 
 * XMLHttpRequest state transition handling.
XMLreq.prototype.cbwrapper = function()
  // Once send() has been called, XMLHttpRequest will contact the server
  // and retrieve the data we requested; however, this process takes an
  // indeterminate amount of time. In order to find out when the object
  // has finished retrieving data, we must use an event listener. In the
  // case of an XMLHttpRequest object, we need to listen for changes in
  // its readyState variable. This variable specifies the status of the
  // object's connection, and can be any of the following:
  // 0 - Uninitialised 
  // 1 - Loading 
  // 2 - Loaded 
  // 3 - Interactive 
  // 4 - Completed
  if (this.obj.readyState == 4)
     // readyState increments from 0 to 4, and the onreadystatechange
     // event is triggered for each increment, but we really only want
     // to know when the connection has completed (4), so our handling
     // function needs to realise this. Upon the connection's completion,
     // we also have to check whether the XMLHttpRequest object success-
     // fully retrieved the data, or was given an error code, such as
     // 404: "Page not found". This can be determined from the object's
     // status property, which contains an integer code. "200" denotes
     // a successful completion, but this value can be any of the HTTP
     // codes that servers may return. If the request was not successful,
     // we must specify a course of action.
	if (this.test || this.obj.status == 200)
		this.usercb( this ); //support both method and subroutine calls
		ClearGlobalVar( ); //we are done so clean up persistence store
	// IE returns a status code of 0 on some occasions, so ignore this case
	else if (this.obj.status == 0)
    	 Break("DEBUG:zero xml status! msg["+ this.obj.statusText +"] for [""]");
	else Error(   "Bad XML retrieve status["+ this.obj.statusText +"] for [""]");

/** method to launch "this" request using the following logic;<pre>
 * To send CGI variables using the GET request method, we have to
 * hardcode the variables into the open URL parameter:
 *"GET", "/query.cgi?name=Bob&email=bob&#64;"); 
 *    this.send(null);
 * To send CGI variables using the POST request method, we have to
 * pass the variables to the send() method:
 *"POST", "/query.cgi");
 *    this.send("name=Bob&email=bob&#64;");
XMLreq.prototype.launch = function()
	// Even though the XMLHttpRequest object allows us to call the open()
	// method multiple times, each object can really only be used for one
	// call, as the onreadystatechange event doesn't update again once
	// readyState changes to "4" (in Mozilla). Therefore, we have to create
	// a new XMLHttpRequest object every time we want to make a remote call.
	this.obj = this.newRequestObj();
	if (this.test) { this.obj.load( this.url ); return; }

	// open() initialises the connection we wish to make, and takes two
	// arguments, with several optionals. The first argument is the type
	// of request we want to send; the second argument identifies the
	// location from which we wish to request data. For instance, if we
	// wanted to use a GET request to access feed.xml at the root of our
	// server, we'd initialise the XMLHttpRequest object like this:
	//    "GET", "/feed.xml");
	// The URL can be either relative or absolute, but due to cross-domain
	// security concerns, the target must reside on the same domain as the
	// page that requests it.
	// The open() method also takes an optional third boolean argument that
	// specifies whether the request is made asynchronously (true, the default)
	// or synchronously (false). With a synchronous request, the browser will
	// freeze, disallowing any user interaction, until the object has completed.
	// An asynchronous request occurs in the background, allowing other scripts
	// to run and letting the user continue to access their browser. It's
	// recommended that you use asynchronous requests; otherwise, we run the
	// risk of a user's browser locking up while they wait for a request that
	// went awry. open()'s optional fourth and fifth arguments are a username
	// and password for authentication when accessing a password-protected URL.
	//"POST":"GET", this.url, this.async );

	// if POST, we tell the server to expect form-type parameters
	if (

	// Once open() has been used to initialise a connection, the send() method
	// activates the connection and makes the request. send() takes one argument,
	// allowing us to send extra data, such as CGI variables, along with the
	// call. Internet Explorer treats it as optional, but Mozilla will return
	// an error if no value is passed, so it's safest to call it using:
	//                xmlReq.send(null);
	this.obj.send( ? : null );

/** get XML returned in the reply to "this" xml request
 * @return XML DOM @type Object
XMLreq.prototype.getResponseXML = function()
	// return a DOM-structured object of any XML data that was
	// retrieved by the request. This object is navigable using
	// the standard JavaScript DOM access methods and properties,
	// such as getElementsByTagName(), childNodes[] and parentNode. 
	return this.test ? this.obj : this.obj.responseXML;

/** get text returned in the reply to "this" xml request
 * @type String
XMLreq.prototype.getResponseText = function()
	// return the data as one complete string. If the content
	// type of the data supplied by the server was text/plain
	// or text/html, then this is the only property that will
	// contain data. A copy of any text/xml data will be flattened
	// and placed here as an alternative to responseXML.  
	return this.test ? null : this.obj.responseText;

/** return the output from transforming the response XML
 * with the XSL in the specified URL/filename
 * @type String
XMLreq.prototype.Xform = function( xslURL ) {
	return XformURL( xslURL, this.getResponseXML() );

// Process XML via browser-side XSL...

/** retrieve XSL DOM from given URL
 * @return XSL DOM @type Object
function GetXslDOM( xslURL )
    // load XSLT stylesheet document
    var xslDOM = new ActiveXObject("Msxml2.DOMDocument");
        xslDOM.async = false;
        xslDOM.load( xslURL );
 return xslDOM;

/** transform given XML DOM using given XSLT DOM
 * @return transformed XML @type String
function XformDOM( xslDOM, xmlDOM ){
	return xmlDOM.transformNode( xslDOM );

/** transform given XML DOM using an XSLT retrieved from given URL
 * @return transformed XML @type String
function XformURL( xslURL, xmlDOM ){
  return XformDOM( GetXslDOM(xslURL), xmlDOM );

The Gravy Framework

Documentation generated by JSDoc on Fri Mar 17 06:40:21 2006