//###########################################################################
// Functions utilizing XMLHttpRequest() functionality to get dynamic data   #
// without page reloading                                                   #
//###########################################################################


// Performs a HTTP POST of certain enctype to a server page using XMLHTTPRequest.
// Takes and passes to a server a caller name, an enctype, a function-to-call name, and optional parameters (might be arrays);
// surl="URL of server page to be called", fenctype="enctype" ('' means default value of 'application/x-www-form-urlencoded'),
// cname="Name of calling page", fname="Function to call".
// Gets a text (string) response from a server and finally returns an object with the following properties:
// - return value (retval), - success or failure (berr), - error message (errmsg), - error value (errval)
// A set of xmlhhtp constants must be predefined, see 'var globals_set' below.
//
// (Unfortunately I found it impossible to get a cross-browser solution with enctype='',
// so do not use this function to upload files. Just leave parameter fenctype='multipart/form-data'.)
//
// See a lot of related links here http://www.foto-group/xmlhttp/about-xmlhttp.htm 
// Copyright © 2004, 2005 Vladimir Kelman.
// You are permitted to use this function for free but I ask you to provide a link to this page.
//

function postXMLHTTP(surl, fenctype, cname, fname)
{
	function itrim(s) {
		var _str = (s != null)? "" + s : "";
		
		while ((_str.length > 1) && ((_str.charAt(0) == ' ') || (_str.charAt(0) == '\t')))
			_str = _str.substring(1);
		while ((_str.length > 1) && ((_str.charAt(0) == ' ') || (_str.charAt(0) == '\t')))
			_str = _str.substring(0, _str.length - 1);
		if ((_str == " ") || (_str == "\t")) _str = "";
		
		return _str;
	}

	function Retobj (retval) //, berr, errmsg, errval)
	{
		this.retval = retval;
		this.berr   = (arguments.length > 1)? arguments[1] : false;
		this.errmsg = (arguments.length > 2)? arguments[2] : '';
		this.errval = (arguments.length > 3)? arguments[3] : 0;
		this.logout = (arguments.length > 4)? arguments[4] : false;
	}

	function extractXMLHTTPErrResponse(strInp)  // check if server returned a specified error response
	{
	//alert('strInp=' + strInp);
		var res = false;
		
		if (strInp != null) {
//			var strmatch = new RegExp(xmlhttperrstrt + '(([\n]|.)*)' + xmlhttperrend);
			var strmatch = new RegExp(xmlhttperrstrt + '((\n|\r|.)*)' + xmlhttperrend);  // there is no 's' modifier for multi-line match in JavaScript's RegExp
			var resarr = strmatch.exec(strInp);
			
			try { if (resarr.length > 1) { res =  resarr[1]; }}
			catch (e) {}
		}
		return res;
	}

	function extractXMLHTTPResponse(strInp)
	{
	//	alert('strInp={' + strInp + '}');
		var res = false;
		
		if (strInp != null) {
//			var strmatch = new RegExp(xmlhttpstrt + '(([\n]|.)*)' + xmlhttpend);
			var strmatch = new RegExp(xmlhttpstrt + '((\n|\r|.)*)' + xmlhttpend);  // there is no 's' modifier for multi-line match in JavaScript's RegExp
			var resarr = strmatch.exec(strInp);
	//		alert('resarr=' + resarr);
			
			try { if (resarr.length > 1) { res =  resarr[1]; }}
			catch (e) {}
		}
		return res;
	}
	
///////////
	var globals_set = false;
	if ((typeof(xmlhttpurlencode) != 'undefined') && (typeof(xmlhttpmultipart) != 'undefined') &&
	    (typeof(xmlhttpstrt) != 'undefined') && (typeof(xmlhttpend) != 'undefined') &&
	    (typeof(xmlhttperrstrt) != 'undefined') && (typeof(xmlhttperrend) != 'undefined') &&
	    (typeof(xmlhttpclr) != 'undefined')  && (typeof(xmlhttpfnc) != 'undefined') &&
	    (typeof(xmlhttppar) != 'undefined') &&
	    (typeof(xmlhttpiniterrval) != 'undefined') && (typeof(xmlhttpiniterrmsg) != 'undefined') &&
		(typeof(xmlhttpconnerrval) != 'undefined') && (typeof(xmlhttpconnerrmsg) != 'undefined') &&
		(typeof(xmlhttpsrvnovalerrval) != 'undefined') && (typeof(xmlhttpsrvnovalerrmsg) != 'undefined') &&
		(typeof(xmlhttpsrvspecerrval) != 'undefined')  &&
		(typeof(xmlhttpsrvlogoutval) != 'undefined')   && (typeof(xmlhttpsrvlogoutmsg) != 'undefined')) { globals_set = true; }
	else {
		return new Retobj('', true, 'Incorrect call of postXMLHTTP()! Globals aren\'t set.');
	} // these 'global' constants should be set in advance by calling makeXMLHTTPJavaScriptConstants() function
///////////

	var parval;
	var strall = '';
	var j, k;
	var httperrresp, httpresp;
	var objres;

	fenctype = (fenctype.toLowercase != xmlhttpmultipart)? xmlhttpurlencode : xmlhttpmultipart;
	
	strall += xmlhttpclr + '=' + encodeURIComponent(cname) + '&';
	strall += xmlhttpfnc + '=' + encodeURIComponent(fname) + '&';
	
	j = 1;
	for (var i = 4; i < arguments.length; i++) {
		parval = arguments[i];
		if (typeof(parval) != 'object') {
			strall += xmlhttppar + j + '=' + encodeURIComponent(itrim(parval)) + '&';
			j++;
		}
		else {
			for (k = 0; k < parval.length; k++) {
				strall += parval[k][0] + '=' + encodeURIComponent(parval[k][1]) + '&';
				j++;
			}
		}
	}
	strall = strall.substr(0, strall.length - 1);
	
	var xmlhttp = getXMLHTTP();	 // Prepare the XMLHTTP object for a HTTP POST to a server script page
	
	if (! xmlhttp) { return new Retobj('', true, 'Err: ' + xmlhttpiniterrmsg, xmlhttpiniterrval); }
	
	if (document && document.body && document.body.style) {	document.body.style.cursor='wait'; }   // it might be not yet loaded
	
	try {
		
		xmlhttp.open("POST", surl, false);
		
		xmlhttp.setRequestHeader('Content-Type', fenctype);  // Set "form enctype attribute" header to be able to POST properly
//alert(strall);		
		xmlhttp.send(strall); // Execute the request
		
		if (xmlhttp.status == 200) {
			httperrresp = extractXMLHTTPErrResponse(xmlhttp.responseText);
			if (httperrresp === false) {   // server didn't return an error
				httpresp = extractXMLHTTPResponse(xmlhttp.responseText);
//alert("xmlhttp.responseText = " + xmlhttp.responseText);		
				if (httpresp !== false) { objres = new Retobj(httpresp); }  // OK
				else { objres = new Retobj('', true, 'Err: ' + xmlhttpsrvnovalerrmsg, xmlhttpsrvnovalerrval); }  // Server didn't return any value
			}
			else {                        // server returned an error
				switch (httperrresp) {
					case xmlhttpsrvlogoutval:
						objres = new Retobj('', true, 'Err: ' + xmlhttpsrvlogoutmsg, xmlhttpsrvlogoutval, true);
						break;
					default:
						objres = new Retobj('', true, 'Err: ' + httperrresp, xmlhttpsrvspecerrval);    // error is not predefined but error message returned by server
				}
			}
		}
		else { objres = new Retobj('', true, 'Err: ' + xmlhttp.statusText + ' (' + xmlhttp.status + ')', xmlhttp.status); }  // Server returned HTTP error
			
	}
	catch(e) { objres = new Retobj('', true, 'Err: ' + xmlhttpconnerrmsg, xmlhttpconnerrval); }  // Probably some server connectivity error

	finally {
		if (document && document.body && document.body.style) {	document.body.style.cursor='auto'; }   // it might be not yet loaded
	}
	
	return objres;
}

// Initiate a XMLHTTPRequest object and return it or false.
function getXMLHTTP()
{
	var xmlhttp;
    
    try { xmlhttp = new XMLHttpRequest(); }
    catch (e) { //alert('Built-in XMLHttpRequest doesn't work; let's try ActiveX (for older IE).');
        try { xmlhttp = new ActiveXObject("Msxml2.XMLHTTP"); }
        catch (e) { //alert('Not IE... Maybe IE with older XMLHttp?');
            try { xmlhttp = new ActiveXObject("Microsoft.XMLHTTP"); }
            catch (e) { //alert('No, something else, not supporting XMLHttpRequest');
                xmlhttp = false;
            }
        }
    }
	return xmlhttp;
}


// A convenience wrapper to post a form through XMLHTTPRequest. First parameters are of the same meaning as for pstXMLHTTP().
// frmid - is the form name. fenctype will be taken from form itself with xmlhttpurlencode as default.
// fields indicate what to post. If it is an array - it's an array of field names. 'name' means all named fields,
// 'somecustomatrr' means all fieldswith this custom attributes set.
function frmPostXMLHTTP(surl, cname, fname, frmid, fields)
{
	var i, curelem;
	var frm = document.getElementById(frmid);
	var fenctype = frm.enctype;
	var paramarr = new Array();
	
	if(typeof(fields) == 'object') {
		for (var i = 0; i < fields.length; i++) {
			curelem = document.getElementsByName(fields[i]);   // we might have HTML control arrays on a page
			if (typeof(curelem) != 'object') continue;
//			paramarr.push(new Array(curelem.name, curelem.value));
			for (var j = 0; j < curelem.length; j++) { makeElementVal(paramarr, curelem[j]); }
		}
	}
	else {
		fields = fields.toLowerCase();
		if (fields == 'name') {
			for (var i = 0; i < frm.elements.length; i++) {
				curelem = frm.elements[i];
				if (! curelem.getAttribute('name')) continue;
//				paramarr.push(new Array(curelem.name, curelem.value));
				makeElementVal(paramarr, curelem);
			}
		}
		else {
			for (var i = 0; i < frm.elements.length; i++) {
				curelem = frm.elements[i];
				if (! curelem.getAttribute('name') || ! curelem.getAttribute(fields)) continue;
//				paramarr.push(new Array(curelem.name, curelem.value));
				makeElementVal(paramarr, curelem);
			}
		}
	}
	if (paramarr.length > 0) { return postXMLHTTP(surl, fenctype, cname, fname, paramarr); }
	else return false;
}

function makeElementVal(paramarr, curelem)
{
	if (((curelem.tagName).toLowerCase() == 'select') && (curelem.getAttribute('multiple') == true)) {
/*		$bra_pos = (curelem.name).indexOf('[');
		if ((typeof(server_language) != 'undefined') && (server_language.toLowerCase() == 'php')) {
			curelemname = ($bra_pos < 0)? curelem.name + '[]' : curelem.name;  // this is necessary for PHP but should be always 'curelem.name' for ASP!
		}
		else { curelemname = curelem.name; }*/
		
		for (i = 0; i < curelem.options.length; i++) {
			if ((curelem.options[i]).selected) {
				paramarr.push(new Array(curelem.name, (curelem.options[i]).value));  // unfortunately it's hard (and even impossible with IE) to distinguish if option.value parameter wasn't specified (and browser would supply option.text instead) and was specified as an empty string. So we cannot mimique browser here
			}
		}
	}
	else {
		paramarr.push(new Array(curelem.name, curelem.value));
	}
	
	return paramarr;
}


// See a lot of related links here http://www.foto-group/xmlhttp/about-xmlhttp.htm 
//

