// JavaScript library
// http://www.optimalworks.net/

// convert a value to an integer
Object.prototype.toInt = function() {
	var str = String(this);
	str = str.replace(/[^0-9-.]/g, "");
	var ret = parseInt(str, 10);
	if (isNaN(ret)) ret = 0;
	return ret;
}

// string trimming
String.prototype.Trim = function() { return this.replace(/^\s*|\s*$/g, ""); }

// string cleaning
String.prototype.Clean = function() { return this.replace(/[^\w|\s|@|&|.|,|!|%|(|)|+|-]/g, "").replace(/_/g, " ").replace(/\s+/g, " ").trim(); }

// string padding
String.prototype.Pad = function(length, padChar) {
	var str = String(this);
	length = length.toInt();
	if (typeof padChar == 'undefined' || padChar.length < 1) padChar = " ";
	while (str.length < length) str = padChar + str;
	return str;
}

// convert a serial YYYYMMDD string to a date
String.prototype.SerialToDate = function() {
	var d = String(this);
	d = d.Trim();
	var date = (d.length >= 8 ? new Date(d.substr(0,4).toInt(), d.substr(4,2).toInt() - 1, d.substr(6,2).toInt()) : new Date());
	return date;
}

// convert a date to serial YYYYMMDD format
Date.prototype.DateToSerial = function() {
	return String(this.getFullYear()).Pad(4,"0")+String(this.getMonth()+1).Pad(2,"0")+String(this.getDate()).Pad(2,"0");
}


// array stack push (if unsupported)
if (!Array.prototype.push) { Array.prototype.push = function(element) { this[this.length] = element; } }

// array stack pop (if unsupported)
if (!Array.prototype.pop) {
	Array.prototype.pop = function() {
		var ret;
		if (this.length > 0) {
			ret = this[this.length-1];
			this.length--;
		}
		return ret;
	}
}


// does associative array item exist?
Array.prototype.Exists = function(key) {
	var type = typeof(this[key]);
	return (type != 'undefined' && type != 'function');
}

// save array to cookie
Array.prototype.StoreAll = function(name, minutes) {
	var values = "";
	for (var key in this) if (typeof(this[key]) != 'function') values += (values == "" ? "" : "[:]") + key + "[=]" + String(this[key]);
	CookieSet(name, values, minutes);
}

// load array from cookie
Array.prototype.LoadAll = function(name) {
	var allValues = CookieGet(name);
	var values = allValues.split("[:]");
	var thisValue;
	for (var i = 0; i < values.length; i++) {
		thisValue = values[i].split("[=]");
		if (thisValue.length == 2) {
			if (thisValue[1] == "true" || thisValue[1] == "false") this[thisValue[0]] = (thisValue[1] == "true"); // boolean values
			else this[thisValue[0]] = thisValue[1]; // other values
		}
	}
}


// parse querystring arguments
function HTTParguments() {
	var args = new Array();
	var arglist = location.search.Trim();
	if (arglist.charAt(0) == '?') arglist = arglist.substr(1);
	var argsep = arglist.split('&');
	var thisValue;
	for (var i = 0; i < argsep.length; i++) {
		thisValue = argsep[i].split("=");
		if (thisValue.length == 2) args[unescape(thisValue[0])] = unescape(thisValue[1]);
	}
	return args;
}


// set a cookie value (path set to root)
function CookieSet(name, value, minutes) {
	if (minutes) {
		var date = new Date();
		date.setTime(date.getTime()+(minutes*60000));
		var expires = "; expires="+date.toGMTString();
	}
	else expires = "";
	document.cookie = name+"="+String(value)+expires+"; path="+location.pathname.substr(0, location.pathname.indexOf("/",1)+1);
}

// read a cookie value
function CookieGet(name) {
	name += "=";
	var allCookies = document.cookie.split(';');
	var thisCookie;
	for(var i = 0; i < allCookies.length; i++) {
		thisCookie = allCookies[i].Trim();
		if (thisCookie.indexOf(name) == 0) return thisCookie.substring(name.length).replace(/\|/g, "\n");
	}
	return "";
}

// static DOM library
var DOM = new function() {

	// common node types
	this.ElementNode = 1;
	this.AttributeNode = 2;
	this.TextNode = 3;
	this.CommentNode = 8;


	// DOM enabled browser?
	this.Enabled = (document.getElementById && document.getElementsByTagName);


	// locate an element by ID (starting at rootElement)
	this.Id = function(id, rootElement) {
		var element = null;
		if (DOM.Enabled) {
			if (typeof rootElement == 'string') rootElement = this.Id(rootElement);
			if (!rootElement) rootElement = document;
			element = rootElement.getElementById(String(id));
		}
		return element;
	}


	// return an array of elements identified by tag name (starting at rootElement)
	this.Tags = function(tag, rootElement) {
		var elements = new Array(0);
		if (DOM.Enabled) {
			if (typeof rootElement == 'string') rootElement = this.Id(rootElement);
			if (!rootElement) rootElement = document;
			elements = rootElement.getElementsByTagName(String(tag));
		}
		return elements;
	}


	// returns array of child elements (ignores whitespace and comments)
	this.ChildElements = function(element) {
		var ce = new Array();
		if (typeof element == 'string') element = this.Id(element);
		if (element) {
			for (var i=0; i<element.childNodes.length; i++) {
				if (element.childNodes[i].nodeType == this.ElementNode && element.childNodes[i].nodeName != "!") ce.push(element.childNodes[i]);
			}
		}
		return ce;
	}


	// finds a child node of required type
	this.FindNodeType = function(element, ntype) {
		var found = null;
		if (typeof element == 'string') element = this.Id(element);
		if (element) {
			var thisNode;
			for (var i =0; i < element.childNodes.length && found == null; i++) {
				thisNode = element.childNodes[i];
				if (thisNode.nodeType == ntype) found = thisNode;
				else found = this.FindNodeType(thisNode, ntype);
			}
		}
		return found;
	}


	// find the value of the first text node found
	this.Text = function(element) {
		var text = "";
		if (typeof element == 'string') element = this.Id(element);
		if (element) {
			var tNode = this.FindNodeType(element, this.TextNode);
			if (tNode) text = tNode.nodeValue;
		}
		return text;
	}


	// changes a text node (will append one if necessary)
	this.SetText = function(element, text) {
		if (typeof element == 'string') element = this.Id(element);
		if (element) {
			var tNode = this.FindNodeType(element, this.TextNode);
			if (tNode) tNode.nodeValue = text;
			else element.appendChild(document.createTextNode(text));
		}
	}


	// copies (clones) children of fromNode to children of toNode
	this.Copy = function(fromNode, toNode) {
		if (typeof fromNode == 'string') fromNode = this.Id(fromNode);
		if (typeof toNode == 'string') toNode = this.Id(toNode);
		if (fromNode && toNode) {
			for (var i = 0; i < fromNode.childNodes.length; i++) toNode.appendChild(fromNode.childNodes[i].cloneNode(true));
		}
	}


	// removes all node children
	this.RemoveChildren = function(element) {
		if (typeof element == 'string') element = this.Id(element);
		if (element) while (element.lastChild) element.removeChild(element.lastChild);
	}


	// find absolute x co-ordinate of element
	this.AbsoluteX = function(element) {
		var pos = 0;
		if (typeof element == 'string') element = this.Id(element);
		if (element && typeof element.offsetLeft != 'undefined') {
			pos = element.offsetLeft;
			while ((element = element.offsetParent)) pos += element.offsetLeft;
		}
		return pos;
	}


	// find absolute y co-ordinate of element
	this.AbsoluteY = function(element) {
		var pos = 0;
		if (typeof element == 'string') element = this.Id(element);
		if (element && typeof element.offsetTop != 'undefined') {
			pos = element.offsetTop;
			while ((element = element.offsetParent)) pos += element.offsetTop;
		}
		return pos;
	}
}


// event handler
function Event(element, eventType, eventHandler) {

	// reference to this object
	var thisEvent = this;

	// event object
	this.Raised = null;

	// handler function (parses event and passes reference to this)
	var handler = function(e) { thisEvent.Raised = new EventInformation(e); eventHandler(thisEvent); }

	// attach event
	try {
		if (element.addEventListener) element.addEventListener(eventType, handler, false);
		else if (element.attachEvent) var evt = element.attachEvent("on"+eventType, handler);
		else element["on"+eventType] = handler;
	}
	catch (e) {}

	// add to event manager (if not a window unload)
	if (element !== window || eventType != "unload") EventManager.Add(this);

	// detach event method
	this.Detach = function() {
		try {
			if (element.removeEventListener) element.removeEventListener(eventType, handler, false);
			else if (element.detachEvent) var evt = element.detachEvent("on"+eventType, handler);
			else element["on"+eventType] = null;
		}
		catch(e) {}
	}

}


// Event information object (cross-browser)
function EventInformation(event, stopPropagation, stopDefault) {

	// store event
	this.Event = (event ? event : window.event);

	// default values
	this.Type = "";
	this.Element = null;
	this.Key = "";
	this.ControlKey = "";
	this.Shift = false;
	this.Ctrl = false;
	this.Alt = false;
	this.MouseX = 0;
	this.MouseY = 0;

	if (this.Event) {
		// event type
		this.Type = String(event.type).toLowerCase();

		// source element
		this.Element = (event.target ? event.target : event.srcElement);

		// standard key press
		var keyCode = (this.Event.keyCode ? this.Event.keyCode : this.Event.charCode);
		if (this.Event.charCode != 0 && keyCode >= 32) this.Key = String.fromCharCode(keyCode);

		// control key press
		if (this.Key == "") {
			this.ControlKey = ControlKeys[keyCode];
			if (!this.ControlKey) this.ControlKey = "";
		}

		// action keys
		this.Ctrl = event.ctrlKey;
		this.Alt = event.altKey;
		this.Shift = event.shiftKey;

		// mouse co-ordinates
		var mre = /mouse|click/i;
		if (mre.test(this.Type)) {
			this.MouseX = (this.Event.pageX ? this.Event.pageX : this.Event.clientX + Math.max(document.documentElement.scrollLeft, document.body.scrollLeft));
			this.MouseY = (this.Event.pageY ? this.Event.pageY : this.Event.clientY + Math.max(document.documentElement.scrollTop, document.body.scrollTop));
		}

		// default actions
		if (stopPropagation) this.StopPropagation();
		if (stopDefault) this.StopDefaultAction();
	}
}


// stop event propagation
EventInformation.prototype.StopPropagation = function() {
	if (this.Event) {
		try { this.Event.stopPropagation(); } catch(e) {}
		this.Event.cancelBubble = true;
	}
}


// stop event default action
EventInformation.prototype.StopDefaultAction = function() {
	if (this.Event) {
		try { this.Event.preventDefault(); } catch(e) {}
		this.Event.returnValue = false;
	}
}


// control key definitions
var ControlKeys = []; ControlKeys[8] = "backspace"; ControlKeys[9] = "tab"; ControlKeys[13] = "enter"; ControlKeys[27] = "esc"; ControlKeys[33] = "pageup"; ControlKeys[34] = "pagedown"; ControlKeys[35] = "end"; ControlKeys[36] = "home"; ControlKeys[37] = "left"; ControlKeys[38] = "up"; ControlKeys[39] = "right"; ControlKeys[40] = "down"; ControlKeys[45] = "insert"; ControlKeys[46] = "delete"; ControlKeys[112] = "f1"; ControlKeys[113] = "f2"; ControlKeys[114] = "f3"; ControlKeys[115] = "f4"; ControlKeys[116] = "f5"; ControlKeys[117] = "f6"; ControlKeys[118] = "f7"; ControlKeys[119] = "f8"; ControlKeys[120] = "f9"; ControlKeys[121] = "f10"; ControlKeys[122] = "f11"; ControlKeys[123] = "f12"; ControlKeys[144] = "numlock"; ControlKeys[145] = "scrolllock";


// event manager object
var EventManager = new function() {

	// initialise event stack
	var eStack = null;
	this.Initialise = function() {
		if (eStack == null) { eStack = new Array(); new Event(window, "unload", this.CleanUp); }
	}

	// add an event to the stack (except window unload events)
	this.Add = function(event) { this.Initialise(); eStack.push(event); }

	// detach all stacked events
	this.CleanUp = function(e) { while (eStack.length > 0) eStack.pop().Detach(); }
}

// style handler
var StyleHandler = new function() {

	var DefaultTitle = "default style"; // default style title (if not defined)
	var AlternateTitle = "alternate style"; // alternate style title (if not defined)

	var Styles; // stylesheet array
	var Initialised = false; // true when StyleHandler has been initialised

	// initialise StyleHandler
	this.Initialise = function() {

		if (!Initialised) {
			Initialised = true; // StyleHandler initialised

			// find style declarations
			var slist = DOM.Tags("link");
			var title, rel, media;

			// examine styles
			for (var i = 0; i < slist.length; i++) {
				title = slist[i].getAttribute("title");
				rel = slist[i].getAttribute("rel");
				media = slist[i].getAttribute("media");

				// set default titles
				if (!title && (!media || media == "screen")) {
					if (rel && rel.indexOf("style") >= 0 && rel.indexOf("alt") < 0) title = DefaultTitle; else title = AlternateTitle;
					slist[i].setAttribute("title", title);
				}

				// set screen media
				if (!media) slist[i].setAttribute("media", "screen");
			}
		}
	}

	// returns current stylesheet
	this.Current = function() {
		this.Initialise();
		var slist = DOM.Tags("link");
		var current = "";
		for (var i = 0; i < slist.length && current == ""; i++) if (!slist[i].disabled) current = slist[i].getAttribute("title");
		return current;
	}

	// return an array of style titles
	this.Titles = function() {
		this.Initialise();
		var slist = DOM.Tags("link");
		var titles = new Array();
		var title, media, exists, i, j;

		for (i = 0; i < slist.length; i++) {
			title = slist[i].getAttribute("title");
			media = slist[i].getAttribute("media").Trim().toLowerCase();

			if (title != "" && media == "screen") {
				exists = false;
				for (j = 0; j < titles.length && !exists; j++) if (titles[j] == title) exists = true;
				if (!exists) titles.push(title);
			}
		}
		return titles;
	}

	// activate a style
	this.Activate = function(styletitle) {
		this.Initialise();
		var found = null;
		var currentstyle = null;
		var firststyle = null;
		var current = this.Current();
		var slist = DOM.Tags("link");
		var title, media, i;

		for (i = 0; i < slist.length; i++) {

			title = slist[i].getAttribute("title");
			media = slist[i].getAttribute("media").Trim().toLowerCase();

			if (title != "" && media == "screen") {
				if (found == null && title == styletitle) found = i;
				if (currentstyle == null && title == current) currentstyle = i;
				if (firststyle == null) firststyle = i;
				slist[i].disabled = true; // disable style
			}
		}

		// enable style (if not found, use current or first defined style)
		i = (found != null ? found : (currentstyle != null ? currentstyle : firststyle));
		if (i != null) { slist[i].disabled = false; slist[i].disabled = true; slist[i].disabled = false; }
		this.SavePreference();
	}

	// load user preference
	this.LoadPreference = function() {
		this.Initialise();
		this.Activate(CookieGet("stylepref"));
	}

	// save user preference
	this.SavePreference = function() {
		this.Initialise();
		CookieSet("stylepref", this.Current(), 10080); // store preference for 7 days
	}
}

// initialise StyleHandler
new Event(window, "unload", function(evt) { StyleHandler.SavePreference(); evt.Detach(); });
new Event(window, "load", function(evt) { StyleHandler.LoadPreference(); evt.Detach(); });

// ________________________________________________________
// Site-specific script
new Event(window, "load", function(evt) { evt.Detach(); Initialise(); });

// Initialise page
function Initialise() {
	DefineSwitcher("defaultswitch", "default style");
	DefineSwitcher("accessibleswitch", "accessible style");
}


// define style switching link
function DefineSwitcher(id, stylesheet) {
	var ss = DOM.Tags("a", id);
	if (ss.length == 1) {
		ss[0].setAttribute("href", "#");
		new Event(ss[0], "click", function(evt) { StyleHandler.Activate(stylesheet); });
	}
}