var isProcessing = false;
var req; // hold request object (is it wise to reinitialize each time, or can we reuse)

/// basic structure of a request (via XMLHttpRequest.send())
function requestChange(target, args, callback, async, curs) {
	if (isProcessing) {
		//alert("Already a transaction underway. Killing that previous transaction now.");
	}
	removeCursor();
	if (curs == null) {
		curs = true;
	}
	try {
		req = new XMLHttpRequest();
	} catch (e) {
		req = false;
	}
	if (req) { // we were able to instantiate
		if (async == null) {
			async = true; // if we want to default to synchronous, this should be false
		}
		req.onreadystatechange = callback; // assign response handler (processRequest should be function object)
//		req.open('POST', target, true);
		req.open('POST', target, async); // this way we can parametrize whether to behave synchronously
		req.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded');
		req.send(args);

		if (curs) {
			makeCursor();
		}
		isProcessing = true;
		return true; // could dispatch
	} else {
		removeCursor();
		return false; // could not dispatch
	}
}

function cancelRequest() {
	if (req) {
		req.abort();
		removeCursor();
		isProcessing = false;
	}
}

function showErrors(errs) {
	// there were errors in processing
	var errMesg = '';
	for (var e=0; e<errs.length; e++) {
		var mesg = errs[e].getElementsByTagName("mesg");
		for (var m=0; m<mesg.length; m++) {
			errMesg += mesg[m].firstChild.nodeValue + "\n";
		}
	}
	alert("Response: " + errMesg);
}	

function genericRespond(fn, contain, errfn) { // contain should be id of current content container
	try {
		if (req.readyState == 4 && req.status == 200) { // readyState == 4 -> complete; status == 200 -> good response
//			alert("back now");
			var errs = req.responseXML.getElementsByTagName("err");
				// if we can find a cursor (anything with id #busyCursor), toggle visibility
			isProcessing = false;
			removeCursor();
			if (errs.length > 0) { // there were errors in processing
				if (errfn) {
					errfn.call(null);
				}
				showErrors(errs);
			} else {
				var newContents = fn.apply(); // assume this returns DocumentFragment that can be inserted into new div#details (NB: apply() with no 'this' argument uses global object as target, which is what we want here)
				if (contain) { // only swap containers when given an old one
					var theNewContainer = document.createElement("div");
					theNewContainer.appendChild(newContents);
					var theOrigContainer = (isElement(contain)) ? contain : document.getElementById(contain);
					var theDetailContainer = theOrigContainer.parentNode;
					theDetailContainer.replaceChild(theNewContainer, theOrigContainer);
					if (!isElement(contain)) {
						theNewContainer.setAttribute("id", contain); // replace id, because we've removed original node with this id
					}
				}
			}
		}
	} catch (e) {
		handleError(e);
		removeCursor();
		isProcessing = false;
	}
}

function isElement(nd) {
	try {
		return (nd && nd.nodeType && nd.nodeType == Node.ELEMENT_NODE);
	} catch (e) { return false }
}

function makeCursor() {
	if (!document.getElementById("busyCursor")) {
	//	var curs = document.createElement("img");
	//		curs.setAttribute("src", "http://www.macupdate.com/watch/bigSpinner.gif");
		//	document.body.appendChild(curs);
	}
}

function removeCursor() {
	var theCurs = document.getElementById("busyCursor");
	if (theCurs) {
		var theContain = theCurs.parentNode;
		theContain.removeChild(theCurs);
	}
}

function handleError(err) {
	var errstr = "Error Information:\n";
	for (var i in err) {
		errstr += "["+i+"]: " + err[i] + "\n";
	}
	return errstr;
}

function ajaxStatus() {
	var statString = "Req object readyState: " + req.readyState + " | ";
	statString += "Status: " + req.status + " | ";
	statString += "Status text: " + req.statusText + " | ";
	statString += "Response: " + req.responseText  +"\n";
	var statusContainer = document.getElementById("ajaxStatus");
	if (statusContainer) {
		var now = new Date;
		statusContainer.appendChild(document.createTextNode("["+now.toString()+"] " + statString));
	} else {
		alert(statString);
	}
}

function dummy() {
	alert("If you can read this, I was invoked successfully.");
}
