	// c3o.org ajax library
	// version 3


	var req, reqTimeout, reqCursorBusy;
	reqQueue = new Array();
	reqProperties = new Array();

	function ajax(url, onsend, onreceive, onsuccess, onfail, onempty) {

		if(ajaxInProgress(req)) {	//busy

			if(c3o_debug) { c3o_debug('AJAX is busy, retrying '+url+' in 0.5 secs'); }

			reqQueue[reqQueue.length] = window.setTimeout('ajax("'+url+'", "'+onsend+'", "'+onreceive+'", "'+onsuccess+'", "'+onfail+'", "'+onempty+'")', 500);
	
		} else {

			if(c3o_debug) { c3o_debug('AJAX &rarr; '+url); }
	
			req = null;
			if(document.all) { req = new ActiveXObject("Microsoft.XMLHTTP"); }
						else { req = new XMLHttpRequest(); }	

			reqProperties = new Array(); //clean out last req's props
			if(typeof(onreceive) == 'function') { reqProperties['onreceive'] = onreceive;}
			if(typeof(onsuccess) == 'function') { reqProperties['onsuccess'] = onsuccess;}
			if(typeof(onfail)    == 'function') { reqProperties['onfail']    = onfail;	 }
			if(typeof(onempty)   == 'function') { reqProperties['onempty']   = onempty;  }
			if(typeof(onsend)    == 'function') { onsend(); }
			reqProperties['starttime'] = new Date();
			reqProperties['url'] = url;
			reqProperties['variables'] = parseQueryString(url);

			req.onreadystatechange = ajaxResponse;
			req.open("GET", url, true);
			req.send(null);

			reqTimeout = window.setTimeout(function() {		//TODO make this an array
				if(ajaxInProgress(req)) {
					if(c3o_debug) { c3o_debug('AJAX &mdash; request timed out, aborted'); }
					req.abort(); document.body.style.cursor='auto';
				}
			}, 12000); // 12 seconds max

			//only display a 'busy' cursor if this takes longer than half a second		
			reqCursorBusy = setTimeout('document.body.style.cursor="wait";',500);
			
		}

	}
	


	function ajaxResponse() {
		//if(c3o_debug) { c3o_debug('AJAX statechange '+req.readyState); }
		if (req.readyState == 4) { 

			window.clearTimeout(reqTimeout);	//this didn't time out
			window.clearTimeout(reqCursorBusy);	//if havent even started cursor yet
			document.body.style.cursor='auto';	//not loading anymore

			if(c3o_debug) { c3o_debug('AJAX &larr; '+req.status+' ('+Math.round((new Date()-reqProperties['starttime'])/10)/100+'s)'); }

			if(reqProperties['onreceive']) { reqProperties['onreceive'](); }

			if (req.status == 200 && reqProperties['onsuccess']) {		//results
				reqResponse = req.responseText;
				reqProperties['onsuccess']();
			} else if (req.status == 204 && reqProperties['onempty']) {	//empty
				reqProperties['onempty']();
			} else if(reqProperties['onfail']) { 						//failed
				reqProperties['onfail']();
			}

		}
	}



	function ajaxInProgress(thereq) {
		if(thereq) { 
			switch (thereq.readyState) {
				case 1, 2, 3:	return true; 	break;
		    	default: 		return false;	break;		// case 4 and 0
	    	}
		} else {
			return false;
		}
	}


	function form2Url(o) {
		//todo improve this
		// filter querystring, hash
		// allow /..

		var action = o.getAttribute('action');
		var url;
		if(action.indexOf('http://')) {
			url = action;	
		} else if (action.substr(0, 1) == '/') {
			url = 'http://'+document.location.href.split('/')[2]+action;
		} else {
			url = document.location.href + action;
		}
		url += '?';
		var inputs = o.getElementsByTagName('input');
		for(var i=0; i<inputs.length; i++) {
			url += inputs[i].getAttribute('name')+'='+escape(inputs[i].value)+'&';
		}
	
		return url;
	}

	
	function ajaxLink(o) {
		ajax(o.href, o.getAttribute('onsend'), o.getAttribute('onreceive'), o.getAttribute('onsuccess'), o.getAttribute('onfail'), o.getAttribute('onempty'));
	}


	//////

	function parseQueryString(url) {

	    var obj = new Object();
		var nvpairs = url.substring(url.indexOf('?')+1).split("&");

		for (var idx = 0; idx < nvpairs.length; idx++) {
	        var tokens = nvpairs[idx].split("=");
        	obj[unescape(tokens[0])] = tokens.length == 2 ?
    	        unescape(tokens[1]) : undefined;
	    }
    	
		return obj;
	}


