// 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.5
 */

//////////////////////////////////////////////////////////////////
// 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"
//////////////////////////////////////////////////////////////////

grvTraceCmp("grvAJAX.js: Begin");

Class(grvXMLreq,["url"]);
/**
 * @class Constructor for grvXMLreq "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.5
 */
function grvXMLreq()
{
	/**
	 * @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 grvXMLreq 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
	 * @throws ErrMsg if missing required parameter(s)
	 */
	this.konstructor = function( url, userCallbackFunction, optPostData, optWaitFlag, optSuppressLaunchFlag )
	{
		var self = this; //needed for closure

		// initialize instance data
		this.name   = "grvXMLreq" + grvTimestamp(); //mostly unique name
		this.local  = false; //starting assumption
		this.url    = url;
		this.post   = optPostData ? optPostData : null;
		this.async  = optWaitFlag ? false : true;
		this.usercb = userCallbackFunction || function(){ grvError("Unhandled Reply=["+this.getResponseText()+"]"); };
		this.cb     = function(){ self.cbwrapper(); };
		if (!optSuppressLaunchFlag) this.launch();
	}

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

	/** method to launch "this" request */
	this.launch = function()
	{
		this.xhr = grvGetXHR();
		this.xhr.onreadystatechange = this.cb;
		this.xhr.open( this.post?"POST":"GET", this.url, this.async );
		// if POST, we tell the server to expect form-type parameters
		if (this.post) this.xhr.setRequestHeader("Content-Type","application/x-www-form-urlencoded;");
		this.xhr.send( this.post );
	}

	/** callback wrapper method to relieve user callback from gory details of 
	 * XMLHttpRequest state transition handling.
	 */ 
	this.cbwrapper = function()
	{
	  if (this.xhr.readyState == 4)
	  {
		switch( this.xhr.status )
		{
		  case 0:
			// local files return 0 on Mozilla and work...
			// but on IE it doesn't work, so retry as local file
			if (grvIs_IE_AJAX())
			{
				this.xhr = grvIELoadXmlDOM( this.url );
				this.local = true;
			}
		  case 200:
			this.usercb( this );		//support both method and subroutine calls
			break;
		  default:
			if (!_kGrvAjaxTestData)	//suppress error msg on "Save" if we are using dummy xml files instead of real server
				grvError( "Bad XML retrieve status["+this.xhr.status+"]["+ this.xhr.statusText +"] for ["+this.name+"]");
		}
	  }
	}

	/** get XML returned in the reply to "this" xml request
	 * @return XML DOM @type Object
	 */
	this.getResponseXML = function()
	{
		return this.local ? this.xhr : this.xhr.responseXML;
	}

	/** get text returned in the reply to "this" xml request
	 * @type String
	 */
	this.getResponseText = function()
	{
		return this.local ? null : this.xhr.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...
//////////////////////////////////////////////////////////////

function grvIs_FF_AJAX(){ return document.implementation && document.implementation.createDocument; }
function grvIs_IE_AJAX(){ return grvIs_IE_Pre7() || !grvIs_FF_AJAX(); }

/** method to create new XMLHttpRequest object
 * @return "XMLHttpRequest object" @type Object
 * @throws ErrMsg if XML Request Objects arent supported by Browser
 */
function grvGetXHR()
{
	 try { return new ActiveXObject("Msxml2.XMLHTTP"   ); } catch (e){ }
	 try { return new ActiveXObject("Microsoft.XMLHTTP"); } catch (e){ }
	 try { return new XMLHttpRequest();                   } catch (e){ }
	 grvLoudThrow("XML Request Objects are not supported by this Browser.");
}

/** SYNCHRONOUS retrieve XSL DOM from given URL
 * @param {String} url the URL to request XSL from
 * @return XSL DOM @type Object
 */
function grvLoadXslDOM( xmlURL )
{
	//IE needs Msxml2.DOMDocument load to do XSL tranforms
	if (grvIs_IE_AJAX()) return grvIELoadXmlDOM( xmlURL );
	return grvLoadXmlDOM( xmlURL );
}

/** SYNCHRONOUS non-IE-specific retrieve XML DOM from given URL
 * @param {String} url the URL to request XML from
 * @return XML DOM @type Object
 */
function grvLoadXmlDOM( xmlURL )
{
    var xhr = grvGetXHR();
    xhr.open( "GET", xmlURL, false );
    xhr.send( null );
    switch( xhr.status )
    {
	  default:
		  grvLoudThrow( "Bad XML retrieve status["+xhr.status+"]["+ xhr.statusText +"] for ["+xmlURL+"]");
      case 0:
    	  // local files return 0 on Mozilla and work...
    	  // but on IE it doesn't work, so retry as local file
    	  if (grvIs_IE_AJAX()) return grvIELoadXmlDOM( xmlURL );
      case 200:
    }
	return xhr.responseXML;
}


/** SYNCHRONOUS IE-specific retrieve XML DOM from given URL
 * @param {String} url the URL to request XML from
 * @return XML DOM @type Object
 */
function grvIELoadXmlDOM( xmlURL )
{
    var xmlDoc = new ActiveXObject("Msxml2.DOMDocument");
    	xmlDoc.async = false;
    	xmlDoc.load( xmlURL );
	if (xmlDoc.parseError.errorCode != 0)
		grvLoudThrow("Error loading["+xmlURL+"]: " + xmlDoc.parseError.reason);
	return xmlDoc;
}

/** transform given XML DOM using given XSLT DOM
 * @return transformed XML @type String
 * @throws ErrMsg on fatal error
 */
function grvXformDOM( xslDOM, xmlDOM )
{
	if (grvIs_IE_AJAX()) return xmlDOM.transformNode( xslDOM );

	// code for Mozilla, Firefox, Opera, etc.
	var    xsltProcessor = new XSLTProcessor();
	       xsltProcessor.importStylesheet   ( xslDOM );
	return xsltProcessor.transformToDocument( xmlDOM ).documentElement.textContent;
}

/** 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( grvLoadXslDOM(xslURL), xmlDOM );
}

grvTraceCmp("grvAJAX.js: End");