Overview | File | Class | Tree | Index | Help
| The Gravey 2.0 Framework
PREV NEXT | FRAMES NO FRAMES

## grvAJAX.js

#### Summary

Collection of AJAX support routines.
Version: 2.0
Requires:

- grvUtils.js
- grvValidate.js

Author: Bruce Wallace (PolyGlotInc.com)

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

Method Summary
`static Object` | `grvGetXslDOM( xslURL )` retrieve XSL DOM from given URL
`static String` | `grvXformDOM( xslDOM, xmlDOM )` transform given XML DOM using given XSLT DOM
`static String` | `grvXformURL( xslURL, xmlDOM )` transform given XML DOM using an XSLT retrieved from given URL

    // This file uses JSDoc-friendly comments [ http://jsdoc.sourceforge.net/ ]

    /**
     * @file         grvAJAX.js
     * @fileoverview Collection of AJAX support routines.
     * @author       Bruce Wallace  (PolyGlotInc.com)
     * @requires     grvUtils.js
     * @requires     grvValidate.js
     * @version      2.0
     */

    //////////////////////////////////////////////////////////////////
    // GRAVEY LEXICAL CODING CONVENTIONS:
    // (*) All private variables or functions start with "_"
    // (*) All variables and functions start with a lowercase letter
    // (*) All Classes start with an uppercase letter
    // (*) All Class methods and instance variables start with lowercase
    // (*) All Class "static" methods and variables start with uppercase
    // (*) All constants start with "k"
    // (*) All global variables start with "g"
    // (*) All event handler functions start with "on"
    //
    // (*) All Gravey utility global variables start with "gGrv"
    // (*) All Gravey MVC     global variables start with "gMVC"
    // (*) All Gravey EDO     global variables start with "gEDO"
    // (*) All Gravey utility functions start with "grv"
    // (*) All Gravey MVC     functions start with "mvc"
    // (*) All Gravey MVC event handler functions start with "onMVC"
    // (*) All Gravey EDO event handler functions start with "onEDO"
    // (*) All Gravey MVC classes start with "MVC"
    // (*) All Gravey EDO classes start with "EDO"
    //////////////////////////////////////////////////////////////////

    Class(XMLreq,["url"]);
    /**
     * @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.
     * @author Bruce Wallace  (PolyGlotInc.com)
     * @version 2.0
     */
    function XMLreq()
    {
    	/**
    	 * @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)
    	 */
    	this.konstructor = function( url, userCallbackFunction, optPostData, optWaitFlag, optTestFlag, optSuppressLaunchFlag )
    	{
    		// initialize instance data
    		this.name   = "XMLreq" + grvTimestamp(); //mostly unique name
    		this.url    = url;
    		this.post   = optPostData;
    		this.test   = grvIsUndefined(optTestFlag) ? _kGrvAjaxTestData : optTestFlag;
    		this.async  = optWaitFlag?false:true;
    		this.usercb = userCallbackFunction || function(){ grvError("Unhandled Reply=["+this.getResponseText()+"]"); };
    		this.cb     = new Function( "grvGetGlobalVar(\""+ this.name +"\").cbwrapper()" );

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

    	/** method to return "this" as a human-readable string @type String */
    	this.toString = function(){ return grvObjectToString(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
    	 */
    	this.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.
    	 */
    	this.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
    			grvClearGlobalVar( this.name ); //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)
    	    	 grvBreak("DEBUG:zero xml status! msg["+ this.obj.statusText +"] for ["+this.name+"]");
    		else grvError(   "Bad XML retrieve status["+ this.obj.statusText +"] for ["+this.name+"]");
    	  }
    	}

    	/** 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:
    	 *    this.open("GET", "/query.cgi?name=Bob&email=bob&#64;example.com");
    	 *    this.send(null);
    	 * To send CGI variables using the POST request method, we have to
    	 * pass the variables to the send() method:
    	 *    this.open("POST", "/query.cgi");
    	 *    this.send("name=Bob&email=bob&#64;example.com");
    	 *</pre>
    	 */
    	this.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:
    		//              xmlReq.open("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.
    		//
    		this.obj.open( this.post?"POST":"GET", this.url, this.async );

    		// if POST, we tell the server to expect form-type parameters
    		if (this.post)
    			this.obj.setRequestHeader("Content-Type","application/x-www-form-urlencoded;");

    		// 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( this.post ? this.post : null );
    	}

    	/** get XML returned in the reply to "this" xml request
    	 * @return XML DOM @type Object
    	 */
    	this.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
    	 */
    	this.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
    	 * @throws ErrMsg on fatal error
    	 */
    	this.xform =  function( xslURL ) {
    		return grvXformURL( xslURL, this.getResponseXML() );
    	}

    	/** return the output from transforming the response XML
    	 * with the XSL in the specified XSL DOM
    	 * @type String
    	 * @throws ErrMsg on fatal error
    	 */
    	this.xform2 = function( xslDOM ) {
    		return grvXformDOM( xslDOM, this.getResponseXML() );
    	}
    }

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

    /** retrieve XSL DOM from given URL
     * @return XSL DOM @type Object
     */
    function grvGetXslDOM( xslURL )
    {
        // load XSLT stylesheet document
        var xslDOM = new ActiveXObject("Msxml2.DOMDocument");
            xslDOM.async = false;
            xslDOM.load( xslURL );
    	if (xslDOM.parseError.errorCode != 0) {
    		var myErr = xmlDoc.parseError;
    		grvError("Error loading["+xslURL+"]: " + myErr.reason);
    	}
    	return xslDOM;
    }

    /** transform given XML DOM using given XSLT DOM
     * @return transformed XML @type String
     * @throws ErrMsg on fatal error
     */
    function grvXformDOM( xslDOM, xmlDOM ){
    	if (xslDOM.parseError.errorCode != 0)
    	  throw "XSL was not successfully loaded; errcode="+xslDOM.parseError.errorCode;
    	return xmlDOM.transformNode( xslDOM );
    }

    /** transform given XML DOM using an XSLT retrieved from given URL
     * @return transformed XML @type String
     * @throws ErrMsg on fatal error
     */
    function grvXformURL( xslURL, xmlDOM ){
      return grvXformDOM( grvGetXslDOM(xslURL), xmlDOM );
    }

Overview | File | Class | Tree | Index | Help
| The Gravey 2.0 Framework
PREV NEXT | FRAMES NO FRAMES

Documentation generated by JSDoc on Sun Feb 11 13:28:59 2007