// this class provides a simple means to make ajax calls and hides all the messy 
// code requried to set up the XMLHttpRequest object prior to a call. 

//var gaAjaxCache = new Array;



function AjaxCache() {
	// this function is used to manage caching. It can store requests by url but
	// also track time of caching and limit on how many items to cache
	
	// PUBLIC FUNCTIONS
	
	// accessor methods
	
	this.get = function(sName) {
		// method to get a property, returns property value
		// - sName: property name
		
		return _get(sName);
	};
	
	this.set = function(sName, value) {
		// method to get a property, return true if property found and set
		// - sName: property name
		// - value: new property value to set
		
		return _set(sName, value);
	};
	
	// custom methods
	
	this.getCached = function(sUrl, iTime) {
		// will look for data stored in cache, if none found will return
		// false
		// - sUrl: this is the url that may have been cached
		// - iTime (optional): only return if cached within this time (ms)
		
		return _getCached(sUrl, iTime);
	};
	
	this.appendCache = function(sUrl, sData) {
		// this will store the item in the cache
		// - sUrl: this is the url that may have been cached
		
		_appendCache(sUrl, sData);
	};
	
	// PRIVATE VARS / FUNCTIONS
	
	// this is the variable that will store each item. I have opted to 
	// use numerically indexed array to store each cached item as it
	// allows us to delete old items
	var _aAjaxCache = new Array;
	
	var _iLimitCount = 50; // this is the maximum number of cached items to store
	
	function _get(sName) {
		// method to get a property, returns property value
		// - sName: property name
		
		switch(sName) {
		default:
			return false;
		}
	}
	
	function _set(sName, value) {
		// method to get a property, return true if property found and set
		// - sName: property name
		// - value: new property value to set
		
		switch(sName) {
		case "iLimitCount":
			_iLimitCount = value;
			break;
		default:
			return false;
		}
		
		return true;
	}
	
	function _getCached(sUrl, iTime) {
		// will look for data stored in cache, if none found will return
		// false
		// - sUrl: this is the url that may have been cached
		// - iTime (optional): only return if cached within this time (ms)
		
		var aCached;
		
		// loop through cache and see if a match is found
		for(var i=0; i<_aAjaxCache.length; i++) {
			if(_aAjaxCache[i]["url"] == sUrl) {
				aCached = _aAjaxCache[i];
			}
		}
		
		// check if we have something
		if(aCached) {
			// ok, we have an item in cache for this url
			
			// check if we have a iTime so we can check the date diff
			if(iTime) {
				
				var iNow = (new Date()).getTime();
				
				// date comparison requested
				if((iNow - aCached["timestamp"]) <= iTime) {
					return aCached;
				}
			} else {
				// no date comparison, return cached item
				return aCached;
			}
		}
		
		return false;
	}
	
	function _appendCache(sUrl, sData, oXML) {
		// this will store the item in the cache
		// - sUrl: this is the url that may have been cached
		// - sData: string data to cache
		// - oXML (optional): if we want to cache xml too
		
		// here we want to check if the item exists already. If
		// so then we will remove it from the stack an push it
		// to the top, this allows us to keep everything in proper
		// order
		for(var i=0; i<_aAjaxCache.length; i++) {
			if(_aAjaxCache[i]["url"] == sUrl) {
				// we have found this item in cache, so splice this one as
				// we will create a new entry on top
				_aAjaxCache.splice(i, 1);
			}
		}
		
		
		// now lets create our new entry as we know that url does exist anymore
		var aCached = new Array;
		
		aCached["url"] = sUrl;
		aCached["timestamp"] = (new Date()).getTime();
		aCached["responseText"] = sData;
		aCached["responseXML"] = oXML;
		
		_aAjaxCache.push(aCached);
		
		// perform a tidy up
		_tidyCache();
	}
	
	function _tidyCache() {
		// this will, using limit variables, clean up cache items
		
		// check we are not over the count limit
		if(_iLimitCount) {
			// calculate how many items to remove
			var iSplice = _aAjaxCache.length - _iLimitCount;
			
			// remove oldest items
			_aAjaxCache.splice(0, iSplice);
		}
	}
}

var goAjaxCache = new AjaxCache;

function AjaxWrapper() {
	
	// PROPERTIES
	
	this.method = "GET";
	this.url = "";
	this.responseText = null;
	this.responseXML = null;
	this.variables = new Array; // allows variables to be passed from the containing object to the ajax wrapper for onSuccess()
	
	var _obj = this; // reference to object
	var _sStatusText; // when the call is done this is set, used in event of error notification
	
	// METHODS
	
	this.onSuccess = function() {
		// this is the function that will handle the response. It is intended to be 
		// defined after instantiation. It cannot be passed any params but instead 
		// use this.variables array to pass params when setting object up
		
		
	};
	
	this.onFail = function() {
		// this is the function that will handle the response where it has failed
		
		//alert("Problem retrieving XML data:" + _sStatusText + "("+_obj.url+")");
	};
	
	this.post = function(sData, sContentType, bUseCache, iUseCacheTimeRange) {
		// post data to the server and handle reponse
		// - sData: this is the data that will get post to the server    
		// - sContentType: is the post data in name/value pairs or raw data? define the 
		//                 content type here 
		
		this.method = "POST";
		_send(sData, sContentType, bUseCache, iUseCacheTimeRange);
	};
	
	this.get = function(bUseCache, iUseCacheTimeRange) {
		// get data from the server and handle response   
		
		this.method = "GET";
		_send(null, null, bUseCache, iUseCacheTimeRange);
	};
	
	function _send(sData, sContentType, bUseCache, iUseCacheTimeRange) {
		// get data from the server. 
		// - sData: only requried for posts 
		// - sContentType: only required for posts
		// - bUseCache: set to true to check the cache
		// - iUseCacheTimeRange: this extends cache to use a time range also. If not passed, any cached item will be used regardless of how old
		
		var oXmlhttp = null;
		
		// maybe we can use the cache object to fetch the data rather than
		// calling the file on the server
		if(bUseCache) {
			
			// fetch cached item if exists but also check it is within a given
			// time range. We will check passing a given time range
			var aCached = goAjaxCache.getCached(_obj.url, iUseCacheTimeRange); // (60*60*1000) = 1hr
			
			if(aCached) {
				_obj.responseText = aCached["responseText"];
				_obj.responseXML = aCached["responseXML"];
				_obj.onSuccess();
				
				return true;
			}
		}
		
		// no cache, fresh call
		if (window.XMLHttpRequest) {// code for IE7, Firefox, Mozilla, etc.
			oXmlhttp = new XMLHttpRequest();
		} else if (window.ActiveXObject) {// code for IE5, IE6
			oXmlhttp = new ActiveXObject("Microsoft.XMLHTTP");
		}
		if (oXmlhttp==null) {
			alert("Your browser does not support XMLHTTP.");
		}
		
		// create url with time stamp //
		var tmpUrl = _obj.url;
		if(tmpUrl.indexOf("?") >= 0) {
			tmpUrl = tmpUrl.replace("?", "?t="+(new Date()).getTime()+"&"); // sneak a timestamp inbetween ? and name/value pairs
		} else {
			tmpUrl = tmpUrl + "?t="+(new Date()).getTime();
		}
		
		oXmlhttp.open(_obj.method, tmpUrl, true);
		
		oXmlhttp.onreadystatechange = function() {
			if (oXmlhttp.readyState==4) { // 4 = "loaded"
				
				// set status text
				_sStatusText = oXmlhttp.statusText;
				
				if (oXmlhttp.status==200) { // 200 = "OK"
					
					// only cache if this requests that we are to use the cache as from that
					// we can decide whether or not to store it. If we store every request then
					// if may affect performance
					if(bUseCache) {
						
						// add entry to cache object
						goAjaxCache.appendCache(_obj.url, oXmlhttp.responseText, oXmlhttp.responseXML);
						
					}
					
					// update vars
					_obj.responseText = oXmlhttp.responseText;
					_obj.responseXML = oXmlhttp.responseXML;
					
					// call handler
					_obj.onSuccess();
					
				} else {
					
					// call handler
					_obj.onFail();
					
				}
			}
		};
		
		// post also requires that additional headers are setup
		if(_obj.method == "POST") {
			// this installation of AjaxHandler POSTs raw data as defined in the content-type
			oXmlhttp.setRequestHeader("Content-type", sContentType);
			oXmlhttp.setRequestHeader("Connection", "close");
		}
		
		// send the data with the XHR->send() method
		oXmlhttp.send(sData);
		
	}

}
