/*
  Copyright (c) 1999-2008 - All Rights Reserved
    Author: Dave Clark Consulting
    E-mail: webmaster@daveclarkconsulting.com
   Website: www.DaveClarkConsulting.com
  Location: Dayton, OH, USA

  Object: ClientProxy

  Title: Generic and Simple XMLHttpRequest Usage

  This external JavaScript library document provides two functions for generic
  use of server-side calls in the background of a JavaScript client browser.  This
  combined use of technologies is now being referred to as "AJAX" (Asynchronous
  Javascript And Xml -- though XML use is not actually required and, in fact, this
  function does not support or accept the use of XML).  As provided for by this
  code, there are three uses that may be made of this code -- all involving the
  specification of a url (with or without a query string attachment).

  The first use is to make GET-type document requests from a server just as the
  browser address bar or any HTML hyperlink may do.  The difference is that the
  asynchronous server response is returned to the optional JavaScript function you
  provide as a pointer argument to this code.  If the optional user function
  argument is omitted, then the synchronous server response is returned directly
  from the call of this XMLHttpRequest function.  The limits of what you may
  accomplish via this simple interface is bounded only by your imagination.

  The second use is similar to the first except this is for POST-type requests to a
  server document -- just as an HTML FORM may do.  For this request, a fourth
  argument is accepted which represents the POST data to be sent to the server.

  The final use goes one step further in that you may make HEAD-type requests and
  the server will return just the headers of a document (e.g., containing the
  document last-modified date) and/or whether that document even exists (yes, the
  infamous 404-error page).

  The first function is for instantiating the Client Proxy object itself.  The
  second function is included for your convenience.  You may choose to interact with
  the instantiated Client Proxy object directly.  If you don't wish to do this, you
  may use the second function and it will coordinate the use of the Client Proxy
  objhect for you.  Note that a combined use of both is also possible -- for truly
  knowledgeable users.  To use the second function, you will pass:

  1)  the Client Proxy object you've previously instantiated;
  2)  the url of the server-side document to call (with or without a query string
      attachment -- according to your needs);
  3)  an optional pointer to your function which will receive the asynchronous
      response to your request (if omitted, the synchronous response is returned
      directly from the call of this XMLHttpRequest function);
  4)  an optional send-type argument of "GET", "POST", or "HEAD" -- if omitted,
      "GET" is the default;
  5)  an optional argument representing POST-type data to be sent to the server --
      this argument is required only if the third argument is specified as "POST";
  6)  an optional boolean argument specifying whether Netscape security privileges
      are required or not (the user's browser must support this feature).

  The following is an example of a function to receive an asynchronous server
  request.  The example concludes with an example call to the XMLHttpRequest
  function provided by this code.
  
  var proxy = ClientProxy();
  function myResponse() {
    if(proxy.readyState == 4
    || proxy.readyState == 'complete') {
      alert(proxy.responseText);
    }
    return true;
  }
  ClientSubmit(proxy, 'mypage.asp?cell=A1&value=327', myResponse);

  For other uses of this code, see: http://jibbering.com/2002/4/httprequest.html

  Note that, for the benefit of the server-side code you will be calling, the second
  function also employs a marker on all requests which such server-side code may
  detect for the purpose of distinguishing between requests made via this method and
  normal browser requests.  For example, in ASP/VBScript parlance, you could use the
  following code to test for requests from this method:

      If Request.ServerVariables("REQUEST_METHOD") = "GET" Then
          cell = Request.QueryString("cell")
          value = Request.QueryString("value")
          str1 = Request.ServerVariables("HTTP_ACCEPT")
          If str1 = "message/x-jl-formresult") Then
              Response.Write cell & "=" & value
          Else
              ' other responses
          End If
      End If
  
  Likewise, in PHP parlance, the same thing would look like this:

      if ($_SERVER['REQUEST_METHOD'] == "GET"):
          $cell = $_GET['cell'];
          $value = $_GET['value'];
          $str1 = $_SERVER['HTTP_ACCEPT'];
          if ($str1 == 'message/x-jl-formresult'):
              echo $cell .'='. $value;
          else:
              # other responses
          endif;
      endif;
  
  Note the testing for the special HTTP_ACCEPT header specification.

  In addition, this JavaScript Library also adds three useful methods to the String
  object, as prototype methods, such that all string variables will inherit these
  methods.  They are:
  
  string.appendURLTimeStamp()
       This method has no arguments.  It merely adds a timestamp to the string as an
       additional querystring argument name/value pair.  This is useful for forcing
       the browser to bypass local cache when retrieving the requested document.

  string.trimEnds()
       This method has no arguments.  It merely trims white space (blanks, tabs,
       etc.) off of both ends of the content of the object string variable.

  string.parsePairs(pair_delim = "&", name_delim = "=")
       This method has two optional arguments.  It parses the name/value pairs from
       the content of the object string variable and returns them as an array.  This
       array will also contains hash entries with the "name" as the hash key and the
       "value" as the hash key's value.  The optional arguments indicate the delim-
       iter between name/value pairs (default is "&") and the delimiter between the
       name and its associated value (default is "=").
*/
//
//	Do not touch the following unless you know what you're doing.
//
function ClientProxy(privilege)
{
	var obj = null, errstr;
	if (typeof XMLHttpRequest == 'undefined')		// if ECMA version of object not available
	{
		var msobj = [ "Msxml2.XMLHTTP.7.0", "Msxml2.XMLHTTP.6.0", "Msxml2.XMLHTTP.5.0",
						"Msxml2.XMLHTTP.4.0", "Msxml2.XMLHTTP.3.0", "Msxml2.XMLHTTP",
						"Microsoft.XMLHTTP" ];
		var x, len = msobj.length;
		for (x=0; x<len; ++x)						// loop on each MS ActiveX object
		{
			try										// try each MS ActiveX object in turn
			{
				obj = new ActiveXObject(msobj[x]);	// instantiate it
				break;
			}
			catch (err)								// trap error and try again
			{
				if (typeof err != 'object') errstr = err;
				else errstr = (err.number & 0xFFFF) + ': ' + err.description;
			}
		}
		if (obj == null) alert("Client Proxy instatiation error:\n" + err);	//	show error
	} else {										// else assume this is IE and
		try
		{
			if (window.Event && privilege)
			{
				netscape.security.PrivilegeManager.enablePrivilege("UniversalBrowserRead");
				netscape.security.PrivilegeManager.enablePrivilege("UniversalXPConnect");
			}
			obj = new XMLHttpRequest();				// instantiate it
		}
		catch (err)									// trap possible error
		{
			if (typeof err != 'object') errstr = err;
			else errstr = (err.number & 0xFFFF) + ': ' + err.description;
			alert("Client Proxy instatiation error:\n" + errstr);	//	show error
		}
	}
	return obj;
}
//
function ClientSubmit(objClient, url, func, type, post, privilege)
{
	var opt = (func) ? true : false;			//	set up for optional arguments
	type = (typeof type == 'string') ? type.toUpperCase() : "GET";
	if(type != 'HEAD'
	&& type != 'POST')							//	if not known values
	{
		type = 'GET';							//		set default request type
		post = null;							//		which has no post data
	}
	if(type == 'POST' && !post)					//	if missing required argument
	{
		alert("POST data omitted.");			//		show error
		return null;							//		and exit
	}
	var errstr;
	try
	{
		if (window.Event && privilege)
		{
			netscape.security.PrivilegeManager.enablePrivilege("UniversalBrowserRead");
			netscape.security.PrivilegeManager.enablePrivilege("UniversalXPConnect");
		}
		objClient.open(type, url, opt);				//	open server interface
	}
	catch (err)									//	if error occurs
	{
		if (typeof err != 'object') errstr = err;
		else errstr = (err.number & 0xFFFF) + ': ' + err.description;
		alert("Client Proxy open failure:\n" + errstr);	//	show error
		return null;
	}											//	else
	if (opt) objClient.onreadystatechange = func;	//	assign function for response
	else objClient.onreadystatechange = function() { return true };
	objClient.setRequestHeader('Accept', 'message/x-jl-formresult');
	if (type == 'POST')							//	set standard header for POST requests
	{
		objClient.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded;');
	}
	try
	{
		if (window.Event && privilege)
		{
			netscape.security.PrivilegeManager.enablePrivilege("UniversalBrowserRead");
			netscape.security.PrivilegeManager.enablePrivilege("UniversalXPConnect");
		}
		objClient.send(post);						//	send the request
		if (!opt) return objClient.responseText;		//	return response
	}
	catch (err)									//	if error occurs
	{
		if (typeof err != 'object') errstr = err;
		else errstr = (err.number & 0xFFFF) + ': ' + err.description;
		alert("Client Proxy send failure:\n" + errstr);	//	show error
	}
	return null;								//	exit
}
//
String.prototype.appendURLTimeStamp = function()
{
	var url = this.toString();
	return url += ((url.indexOf('?') < 0)?'?':'&') + 'tstmp=' + (new Date()).getTime();
}
String.prototype.trimEnds = function ()		// trim white-space off both ends of object string
{
	return this.replace(/(^\s+)|(\s+$)/g, ''); // return trimmed string
}
String.prototype.parsePairs = function ()	// parse delimited string of name/value pairs
{
	if (this.length == 0) return null;			// return null for zero-length string
	var p_delim = (arguments[0]) ? arguments[0] : '&';	// set default pair delimiter
	var n_delim = (arguments[1]) ? arguments[1] : '=';	// set default name delimiter
	var items = this.split(p_delim);			// split argument string into an array
	var x, len = items.length;					// get number of name/value pairs
	for (x=0; x<len; ++x)						// loop on name/value pairs
	{
		items[x] = items[x].split(n_delim);		// split them and re-add to array
		items[x][0] = unescape(items[x][0].replace(/\+/g,' ')).trimEnds(); // massage and trim name value
		items[x][1] = (items[x][1]) ? unescape(items[x][1].replace(/\+/g,' ')).trimEnds() : ""; // massage and trim data value
		if (items[x][0].length > 0) {			// add hash entry, also
			if (typeof items[items[x][0]] == 'undefined') {
				items[items[x][0]] = items[x][1];
			} else {							// use array for duplicate hash entries
				if (typeof items[items[x][0]] != 'object') {
					items[items[x][0]] = new Array(items[items[x][0]]);
				}
				items[items[x][0]].push(items[x][1]);
			}
		}
	}											// end loop
	return items;
}
//
//	Do not touch the preceding unless you know what you're doing.
//

