function cache_manager ()
{
	this.cache = new Object();

	this.get = cache_manager_get;
	this.set = cache_manager_set;
}

function cache_manager_set (primary_key, secondary_key, value)
{
	var cache = false;

	if (typeof (this.cache[primary_key] == 'undefined'))
		this.cache[primary_key] = new Object();

	cache = this.cache[primary_key];
	cache[secondary_key] = value;
}

function cache_manager_get (primary_key, secondary_key, default_value)
{
	if (typeof (this.cache[primary_key]) == 'undefined')
		return default_value;
	else if (typeof (this.cache[primary_key][secondary_key]) == 'undefined')
		return default_value;
	else
		return this.cache[primary_key][secondary_key];
}

// Does this browser support try-catch?
function test_try_catch()
{
	var tc = false;

	try 
	{ 
		tc = true; 
	} 
	catch (f) 
	{ 
		tc = false; 
	}

	return tc;
}

function getRequestObject() 
{
	// Adapted from http://www.expertsrt.com/tutorials/Rod/JSreadPart2.php
	var objRequest = false;

	if (window.ActiveXObject) 
	{
		if (test_try_catch) 
		{
			try 
			{
				objRequest = new ActiveXObject('Msxml2.XMLHTTP');  // IE 6+
			}
			catch(e) 
			{
				try 
				{
					objRequest = new ActiveXObject('Microsoft.XMLHTTP');  // IE 5.5
				}
				catch(f) 
				{ 
				}
			}
		} 
		else 
		{
			objRequest = new ActiveXObject('Microsoft.XMLHTTP');  // ? IE 5.0 ?
		}
	} 
	else if (window.XMLHttpRequest) 
	{
		objRequest = new XMLHttpRequest();  // All other browsers, inc. IE 7 and DOM 3
	}

	return objRequest;
}

function load_url_contents (my_url, debug)
{
	var req_obj 	= getRequestObject();  
	var str 	= "";

	if (debug)
		alert ('load_url_contents: ' + my_url);

	// Sanity-check the request object before proceeding
	if (typeof(req_obj) == 'object') 
	{
		try
		{
			if (req_obj.readyState >= 0) 
			{
				req_obj.open('GET', my_url, false);
				req_obj.send(null);

				// Attempt to sanity check return value
				// Not sure how this will work with relative urls...
				if (my_url.indexOf('http://') == 0 || my_url.indexOf('https://') == 0)
				{
				}

				str = req_obj.responseText;
			}
			else
			{
				alert('XML HTTP Request Object not ready');
			}
		}
		catch (e)
		{
			alert ('load_url_contents: Failed to get ' + my_url);
		}
	}
	else
	{
		alert('XML HTTP Request Object unavailable');
	}

	return str;
}

function split_args(my_url, strip_link)
{
	strip_link = (typeof (strip_link) == 'undefined' ? true: strip_link);

	var args = "";
	var arg_list = new Array();
	var i,j = 0;
	var ts = "";

	// strip off any sub-section links (e.g. index.html#REF1)
	if (strip_link)
	{
		i = my_url.lastIndexOf('#');

		if (i != -1)
			my_url = my_url.substring (0, i);
	}


	// Assume the string has already been split if no "?" present
	i = my_url.indexOf('?');
	if (i != -1)
		args = my_url.substring (i + 1, my_url.length);
	else
		args = my_url;

	// Make sure we handle any encoded characters!
	args = decodeURI(args);

	if (args.length > 0) 
	{
		var elements = args.split("&");

		for (i = 0; i < elements.length; i++) 
		{
			ts = elements[i];

			if (ts.length > 0)
			{
				j = ts.indexOf("=");

				// Try to handle empty values - e.g. "page.html?fred"
				if (j != -1)
					arg_list[ts.substring(0, j)] = ts.substring(j+1, ts.length);
				else
					arg_list[ts] = "";

				// Further processing to handle encoded characters is left as
				// an exercise for the reader...
			}
		}
	}

	return arg_list;
}

function fn_text_expand (str)
{
	// Cheesy function to expand a string
	// e.g. "test" to "t e s t"

	var i = 0;
	var retval = '';

	for (i = 0; i < str.length; i++)
		retval += str.charAt(i) + ' ';

	retval = retval.substring(0, retval.length - 1);
	retval = retval.replace (/\s+/, ' ');

	return retval;
}

function uppercase_words (str, first_only)
{
	var i		= 0;
	var t_str 	= '';
	var word_list	= false;
	var f_str	= '';
	var l_str	= '';

	// Split by whitespace
	// We assume we don't need to restore the same whitespace values
	// after processing...
	word_list = str.split(/\s+/g);
	for (i = 0; i < word_list.length; i ++) 
	{
		t_str = word_list[i];

		f_str = t_str.charAt(0).toUpperCase();
		l_str = t_str.substring(1, t_str.length).toLowerCase();

		word_list[i] = f_str + l_str;

		if (first_only)
			break;
	}

	t_str = word_list.join(' ');

	return t_str;
}

function fn_generate_header_text (str)
{
	var retval = '';
	retval = '<center><b>-- ' + fn_text_expand(str) + ' --</b></center>';
	retval = retval.toLowerCase();

	return retval;
}

// Trims from 0..N or N..length, based on the regex and boolean flags passed
function trim_string (str, re, trim_front)
{
	var matches	= false;
	var match_str 	= '';
	var match_pos 	= 0;
	var match_len 	= 0;
	var retval	= '';

	matches	= re.exec(str);

	if (matches != null)
	{
		match_pos = matches.index;

		if (trim_front)
			retval = str.substring (match_pos + matches[0].length, str.length);
		else
			retval = str.substring (0, match_pos);
	}
	else
	{
		alert ('trim_string: unable to find match in string for RE: ' + re + ' - string: \n' + str);
		retval = str;
	}

	return retval;
}

function timer_manager (auto_start)
{
	this.start	= timer_start;
	this.stop	= timer_stop;
	this.mark	= timer_mark;
	this.reset	= timer_reset;
	this.show	= timer_show;

	this.get_time	= timer_get_time;

	this.timings	= new Array();
	this.start_time	= 0;
	this.end_time	= 0;

	if (auto_start)
		this.start();
}

function sanitise_str (my_name)
{
	var str = my_name.toLowerCase();
	return str.replace(/\W/g, '');
}

function timer_start ()
{
	this.start_time = this.get_time();
}

function timer_stop ()
{
	this.end_time = this.get_time();

	return this.end_time - this.start_time;
}

function timer_get_time ()
{
	// The Date() object returns a static object and therefore needs to be reloaded on each request
	var my_date = new Date();
	return my_date.getTime();
}

function timer_mark (mark_name)
{
	var i 		= this.timings.length;
	var my_obj 	= new Object();

	my_obj.name 		= mark_name;
	my_obj.timestamp	= this.get_time();

	this.timings[i] = my_obj;
}

function timer_reset ()
{
	this.timings = new Array();
}

function timer_show (show_total, write_html)
{
	var i 			= 0;
	var j			= 0;
	var total		= 0;
	var tl			= 0;
	var s_time	 	= 0;
	var e_time	 	= 0;
	var str 		= '';
	var html_str		= (write_html ? '<br />' : '');
	var time_obj		= false;

	tl = this.timings.length;

	if (tl > 1)
	{
		time_obj = this.timings[0];
		str += time_obj.name + ': ' + '0ms' + html_str + '\n';

		for (i = 1; i < tl; i++)
		{
			time_obj = this.timings[i];
			s_time = this.timings[i-1].timestamp;
			e_time = time_obj.timestamp;

			j = e_time - s_time;

			str += time_obj.name + ': ' + (e_time - s_time) + 'ms' + html_str + '\n';

			if (show_total)
				total += j;
		}

		if (show_total)
			str += '===================\n' + 'Total: ' + total + 'ms' + html_str + '\n';
	}
	else
	{
		alert ('Insufficient timing marks recorded?');
	}

	return str;
}

// Borrowed from http://www.quirksmode.org/js/detect.html
var BrowserDetect = {
	init: function () {
		this.browser = this.searchString(this.dataBrowser) || "An unknown browser";
		this.version = this.searchVersion(navigator.userAgent)
			|| this.searchVersion(navigator.appVersion)
			|| "an unknown version";
		this.OS = this.searchString(this.dataOS) || "an unknown OS";
	},
	searchString: function (data) {
		for (var i=0;i<data.length;i++)	{
			var dataString = data[i].string;
			var dataProp = data[i].prop;
			this.versionSearchString = data[i].versionSearch || data[i].identity;
			if (dataString) {
				if (dataString.indexOf(data[i].subString) != -1)
					return data[i].identity;
			}
			else if (dataProp)
				return data[i].identity;
		}
	},
	searchVersion: function (dataString) {
		var index = dataString.indexOf(this.versionSearchString);
		if (index == -1) return;
		return parseFloat(dataString.substring(index+this.versionSearchString.length+1));
	},
	dataBrowser: [
		{
			string: navigator.userAgent,
			subString: "Chrome",
			identity: "Chrome"
		},
		{ 	string: navigator.userAgent,
			subString: "OmniWeb",
			versionSearch: "OmniWeb/",
			identity: "OmniWeb"
		},
		{
			string: navigator.vendor,
			subString: "Apple",
			identity: "Safari",
			versionSearch: "Version"
		},
		{
			prop: window.opera,
			identity: "Opera"
		},
		{
			string: navigator.vendor,
			subString: "iCab",
			identity: "iCab"
		},
		{
			string: navigator.vendor,
			subString: "KDE",
			identity: "Konqueror"
		},
		{
			string: navigator.userAgent,
			subString: "Firefox",
			identity: "Firefox"
		},
		{
			string: navigator.vendor,
			subString: "Camino",
			identity: "Camino"
		},
		{		// for newer Netscapes (6+)
			string: navigator.userAgent,
			subString: "Netscape",
			identity: "Netscape"
		},
		{
			string: navigator.userAgent,
			subString: "MSIE",
			identity: "Explorer",
			versionSearch: "MSIE"
		},
		{
			string: navigator.userAgent,
			subString: "Gecko",
			identity: "Mozilla",
			versionSearch: "rv"
		},
		{ 		// for older Netscapes (4-)
			string: navigator.userAgent,
			subString: "Mozilla",
			identity: "Netscape",
			versionSearch: "Mozilla"
		}
	],
	dataOS : [
		{
			string: navigator.platform,
			subString: "Win",
			identity: "Windows"
		},
		{
			string: navigator.platform,
			subString: "Mac",
			identity: "Mac"
		},
		{
			   string: navigator.userAgent,
			   subString: "iPhone",
			   identity: "iPhone/iPod"
	    },
		{
			string: navigator.platform,
			subString: "Linux",
			identity: "Linux"
		}
	]

};

