/* Sam's AJAX tool. (Jan. 2008) */

// Foundational Data structure
var AJAX 		= new Object();
AJAX.StatusMessage 	= new AJAXDisplayElement(); 	// 'Loading' message and such.

// Direct update of a URL to a target zone
AJAX.Update 	= function(url, target){
	// AJAX.request
	var r = this.request(url, null, 'get');
	r.setHandler('complete', AJAX.replaceContentCallback(target));
	r.submit();
	return r;
}
// Assimilate a form into the AJAX structure so it can parse and submit it
AJAX.Form 	= function(o){
	return new AJAXFormHandler(this.getObject(o));	
}
AJAX.request 	= function(url, query, method){
	return new AJAXRequest(url,query,method, this.StatusMessage);
}

AJAX.replaceContentCallback = function (id){
	return function(r){ AJAX.getObject(id).innerHTML = r.response.text};
}
AJAX.getObject	= function(o){
	if(DOM)	o = DOM.Obj.get(o);
	else if(typeof(o) == 'string') o = document.getElementById(o);
	return o;
}

///////////////////////////////////////////////////////////////////////////////
///////////////////////////* Form handling *///////////////////////////////////
///////////////////////////////////////////////////////////////////////////////

function AJAXFormHandler(o){
	this.obj	= o;
	this.method	= o.method || 'get';

	if(Form)	this.query	= Form.Query(o, true);
	else		alert('Form integration not supported!');
}
AJAXFormHandler.prototype.request = function(callback){
	var m, r; 
	if(!this.query) return false;
	m = this.obj.method || 'get';
	r = AJAX.request(this.obj.action, this.query, m);
	r.setHandler('complete', callback);
	r.submit();
	return r;
}
AJAXFormHandler.prototype.Update = function(t){
	// This function returns false so that forms won't submit independent of AJAX
	return (this.request(AJAX.replaceContentCallback(t)) && false);
}

///////////////////////////////////////////////////////////////////////////////
////////////////////////* Request handling methods *///////////////////////////
///////////////////////////////////////////////////////////////////////////////
function AJAXRequest(url, query, method, disp){
	this.url	= url;
	this.method	= method.toUpperCase();
	this.query	= query;
	this.response	= false;
	this.status	= false;
	this.handler	= (window.XMLHttpRequest)
		? new XMLHttpRequest() 
		: new ActiveXObject("MSXML2.XMLHTTP");
	this.display	= disp;
	this.callback	= this.defaultCallbacks;
}

AJAXRequest.prototype.defaultCallbacks = {0:null,1:null,2:null,3:null,4:null};

AJAXRequest.prototype.submit = function(async){
	r = this; async = (typeof(async) == 'boolean') ? async : true;
	// GET VARIATIONS
	if(this.method == 'GET'){ this.url += '?' + this.query; }
	this.handler.open(this.method, this.url, async);
	this.handler.onreadystatechange = function(){
		switch(r.handler.readyState){
			case 0: break;	// uninitialized
			case 1: break;	// loading
			case 2: break;	// loaded
			case 3: break;	// interactive
			case 4:		// complete
				r.response = {
					text	: r.handler.responseText,
					xml	: r.handler.responseXML
				}
				r.status = {
					code	: r.handler.status,
					text	: r.handler.statusText
				}
				if(r.display) setTimeout("r.display.Off()", r.display.timeout);
			default: break;
		}
		return r.runCallback(r.handler.readyState, r);
	}
	// POST VARIATIONS
	if(this.method == 'POST'){
		this.handler.setRequestHeader('Content-type', 'application/x-www-form-urlencoded');
		this.handler.setRequestHeader('Content-length', this.query.length);
		this.handler.setRequestHeader('Connection', 'close');	
	}
	
	this.handler.send(this.query);
	return false; // Allows a form to submit without throwing an exception when the AJAX query proceeds. 
}

AJAXRequest.prototype.setHandler = function(state, callback){
	if(typeof(state) != 'number'){
		switch(state.toLowerCase()){
		case 'complete':	state = 4;	break;
		case 'interactive':	state = 3;	break;
		case 'loading':		state = 2;	break;
		case 'loaded':		state = 1;	break;
		case 'uninitialized':	state = 0;	break;
		default:		state = 'invalid';
		}
	}
	if(typeof(state) == 'number') this.callback[state] = callback; 
	else return false;

	return true;
}
AJAXRequest.prototype.runCallback = function(state, args){
	var r = true;
	if(this.callback[state])
		r = this.callback[state](args) && r;
	if(this.display) this.display.State(state);
	return r;
}



////////////////////////////////////////////////////////////////////////////////
/////////////////////////* Status display message handler. *////////////////////
////////////////////////////////////////////////////////////////////////////////
function AJAXDisplayElement(o, r){
	if(o){
		this.element = o;
		this.msgReplace = (false || r);
	}else{
		this.element = document.createElement('div');
		this.element.setAttribute('class', 'ajaxStatus');
		this.msgReplace = true;
	}
	this.timeout = 700;
}
AJAXDisplayElement.prototype.On = function(msg){
	// It didn't like me integrating the status message before document.body was created, so I moved it here...
	// Not the most efficient thing, but it works nicely.
	this.element = document.body.appendChild(this.element);
	if(typeof(msg) != 'string') msg = '';
	if(Display) Display.handler(this.element, 'block');
	if(this.msgReplace) this.element.innerHTML = msg;
	return true;
}
AJAXDisplayElement.prototype.Off = function(msg){
	if(typeof(msg) != 'string') msg = '';
	if(Display) Display.handler(this.element, 'none');
	if(this.msgReplace) this.element.innerHTML = msg;
	return true;
}
AJAXDisplayElement.prototype.State = function(s){
	switch(s){
	case 0: return this.On('Uninitialized');
	case 1: return this.On('Loading');
	case 2: return this.On('Loaded');
	case 3: return this.On('Interactive');
	case 4: return this.On('Done');
	case 5: return this.Off('');
	default: return null;
	}
	return false;
}
