/*****************************************************************************************
 *
 *	Areeba Javascript Library
 *	Author: Eric Orton
 *
 ****************************************************************************************/
// $$Revision: 1 $

// ---------------------------------------------------------------------
// 	arb
// 	This provides us with a namespace.  Everything in this file is under arb.XXX
//  so as not to interfere with the operation of any other included scripts.
// ---------------------------------------------------------------------
var arb = { };

arb.prefs = {
	promptText: true,
	focusHighlighting: true,
	popupHelp: false,
	tableStriping: true
}

arb.init = function() {
	arb.element.addClass(document.body.parentNode, "js");
	if (arb.prefs.promptText) arb.form.initPromptText();	
	if (arb.prefs.focusHighlighting) arb.form.initFocusHighlighting();	
	if (arb.prefs.popupHelp) arb.form.initPopupHelp();
	if (arb.prefs.tableStriping) arb.functions.stripeTables();
};

arb.event = {
	/**
	 *	@name 	arb.event.addHandler
	 * 	@desc 	add the given event handler, preserving existing event handler functions.   The event handler
	 *			is passed the event object as it's only argument, and within the event handler "this" refers to the element 
	 *			being acted on.
	 *	@param	(DOM Node) elem, the element to add the event handler to
	 *	@param	(String) eventName, the name of the event, eg/ "load", "mouseover"
	 *	@param	(function) func, the function to call when the event is fired.
	 *	@author	Eric Orton <eric.orton@areeba.com.au>
	 */
	addHandler: function (elem, eventName, func) {
		// make a function which lets 'this' be used in our handlers and fixes e to point to an event
		var handlerFunc = function(e) {
			e = e ? e : window.event;
			elem.__f = func;
			var s = elem.__f(e);
			elem.__f = null;
			return s;
		}
		
		var currentHandler = elem['on' + eventName];
		if (typeof(currentHandler) == 'function') { // not first handler
			elem['on' + eventName] = function(e) {
				var x = currentHandler(e);
				var y = handlerFunc(e);
				return x && y;
			}	
		} else { // first handler
			elem['on' + eventName] = handlerFunc;
		}
	 }
};


arb.event.standardHandler = {

	/**
	 *	@name 	arb.event.standardHandler.hover
	 * 	@desc 	Adds a classname on mouseover and removes it on mouseout.
	 *	@param	(DOM Node) elem, the element to add the event handlers to
	 *	@param	(String) optional hoverClass, the class to apply when hovering, defaults to "arb-hover"
	 *	@author	Eric Orton <eric.orton@areeba.com.au>
	 */
	hover: function(element, hoverClass) {
		hoverClass = (hoverClass != null) ? hoverClass : 'arb-hover'; 
		arb.event.addHandler(element, 'mouseover', function() { arb.element.addClass(this, 'arb-hover'); } );
		arb.event.addHandler(element, 'mouseout', function() { arb.element.removeClass(this, 'arb-hover'); } );
	},

	/**
	 *	@name 	arb.event.standardHandler.hoverFns
	 * 	@desc 	add event handlers for mouseover and mouseout which are only called when the event applies to the given 
	 *			DOM node, and not when the target is a contained node.
	 *	@param	(DOM Node) element, the element to add the event handlers to
	 *	@param	(Function) f, the mouseover event handler
	 *	@param	(Function) g, the mouseout event handler
	 *	@author	Eric Orton <eric.orton@areeba.com.au>
	 */
	hoverFns: function(element, f, g) {
		// A private function for handling mouse 'hovering'
		function handleHover(e) {
			// Check if mouse(over|out) are still within the same parent element
			var p = (e.type == "mouseover" ? e.fromElement : e.toElement) || e.relatedTarget;
	
			// Traverse up the tree
			while ( p && p != element ) p = p.parentNode;
			
			// If we actually just moused on to a sub-element, ignore it
			if ( p == element ) return false;
			
			// Execute the right function
			element.__g = (e.type == "mouseover" ? f : g);
			var s = element.__g(e);
			element.__g = null;
			return s;
		}
		
		// Bind the function to the two event listeners
		arb.event.addHandler(element, 'mouseover', handleHover);
		arb.event.addHandler(element, 'mouseout', handleHover);
	}
};


arb.element = {
	/**
	 *	@name 	arb.event.addClass
	 * 	@desc 	Add a class to a DOM node.
	 *	@param	(DOM Node) theNode, the element to add the class to
	 *	@param	(String) theClass, the class name to add
	 *	@author	Eric Orton <eric.orton@areeba.com.au>
	 */
	addClass: function(theNode, theClass) {
		if (theNode.className == '') {
			theNode.className = theClass;
		} else {
			theNode.className += ' ' + theClass;
		}
	},
	
	/**
	 *	@name 	arb.event.removeClass
	 * 	@desc 	Removes the specified class from the given element
	 *	@param	(DOM Node) theNode, the element to remove the class from
	 *	@param	(String) theClass, the class name to remove
	 *	@author	Eric Orton <eric.orton@areeba.com.au>
	 */
	removeClass: function(theNode, theClass) {
		var oldClass = theNode.className;
		var regExp = new RegExp('\\s?\\b'+theClass+'\\b');
		if (oldClass.match(regExp) != null) {
			theNode.className = oldClass.replace(regExp,'');
		}
	},
	
	/**
	 *	@name 	arb.event.hasClass
	 * 	@desc 	Checks whether the DOM node has a specified class
	 *	@param	(DOM Node) elem, the element to check
	 *	@param	(String) theClass, the class name to check for
	 *	@type	Boolean, true if it has the class
	 *	@author	Eric Orton <eric.orton@areeba.com.au>
	 */
	hasClass: function(elem, theClass) {
		var regExp = new RegExp('\\b'+theClass+'\\b');
		return (elem.className.match(regExp) != null);
	},
	
	/**
	 *	@name 	arb.event.getCSSProperty
	 * 	@desc 	Read the value of the style for the given element.
	 *	@param	(DOM Node) oNode, the element to query
	 *	@param	(String) sProperty, the css property to retrieve the value for
	 *	@type	(String), the property value, or null if it can't be retrieved
	 *	@author	Eric Orton <eric.orton@areeba.com.au>
	 */
	getCSSProperty: function(oNode, sProperty) {
		if(document.defaultView) {
			return document.defaultView.getComputedStyle(oNode, null).getPropertyValue(sProperty);
		} else if(oNode.currentStyle) {
			for(var reExp = /-([a-z])/; reExp.test(sProperty); sProperty = sProperty.replace(reExp, RegExp.$1.toUpperCase())) {
				;
			}
			return oNode.currentStyle[sProperty];
		}
		else {
			return null;
		}
	},
	
	/**
	 *	@name 	arb.event.moveChildren
	 * 	@desc 	Move a DOM nodes children to be children of another node
	 *	@param	(DOM Node) fromElem, the DOM node to move the children from
	 *	@param	(DOM Node) fromElem, the DOM node to move the children to
	 *	@author	Eric Orton <eric.orton@areeba.com.au>
	 */
	moveChildren: function(fromElem, toElem) {
		var nextChild;
		for(var child = fromElem.childNodes[0]; child != null; child = nextChild) {
			var nextChild = child.nextSibling;
			fromElem.removeChild(child);
			toElem.appendChild(child);
		}
	}
};

arb.element.position = {
	/**
	 *	@name 	arb.element.position.left
	 * 	@desc 	Return the absolute distance of the left of the element to the left of the document
	 *	@param	(DOM Node) el, the DOM node to get the position of
	 *	@type	(Number), the distance in pixels
	 *	@author	Eric Orton <eric.orton@areeba.com.au>
	 */
	left: function(el){
		var l=el.offsetLeft;
		while((el=el.parentNode) && el!=document)
			l+=el.offsetLeft;
		return l;
	},
	
	/**
	 *	@name 	arb.element.position.top
	 * 	@desc 	Return the absolute distance of the top of the element to the top of the document
	 *	@param	(DOM Node) el, the DOM node to get the position of
	 *	@type	(Number), the distance in pixels
	 *	@author	Eric Orton <eric.orton@areeba.com.au>
	 */
	top: function(el){
		var t=el.offsetTop;
		while((el=el.parentNode) && el!=document)
			t+=el.offsetTop || 0;
		return t;
	}		
};


arb.form = {
	/**
	 *	@name 	arb.form.promptText
	 * 	@desc 	Add event handlers to any <input> on the page with a 'promptValue' attribute to 
	 *  		display the 'promptValue' as the 'Value' when the 'Value' is empty, and to clear the 'Value'
	 *  		when the input is focus'ed.  While the <input> is displaying the 'promptValue', it has 
	 *  		a class of 'displayingPromptValue' applied to it.
	 *	@author	Eric Orton <eric.orton@areeba.com.au>
	 */
	initPromptText: function() {
		/* 
		 * local function definitions 
		 */
		function _cleanupPromptTextOnSubmit(e) {
			var inputs = this.getElementsByTagName('input');
			for(var j=0; j < inputs.length; j++) {
				_clearPromptText(inputs[j]);
			}			
		}	
		function _clearPromptText(elem) {
			if(elem.value == elem.getAttribute("promptvalue")) {
				elem.value = "";
				arb.element.removeClass(elem, 'displayingPromptValue');
			}
		}
		function _addPromptText(elem) {
			/* only add the prompt text if this item isn't disabled */
			if((elem.getAttribute('disabled') == null || elem.getAttribute('disabled') == false) 
				&& (!elem.value || elem.value == "" || elem.value == elem.getAttribute("promptValue"))) {
				elem.value = elem.getAttribute("promptValue");
				arb.element.addClass(elem, 'displayingPromptValue');
			}
		}

		function _clearPromptTextHandler(e) { _clearPromptText(this); }
		function _replacePromptText(e) { _addPromptText(this); } 			

		/* 
		 * Start of function code 
		 */
		if(!document.getElementsByTagName) return false;
		var forms = document.getElementsByTagName('form');
		
		for(var i = (forms.length - 1); i > -1; i--) { 
			var inputs = forms[i].getElementsByTagName('input');
			arb.event.addHandler(forms[i], 'submit', _cleanupPromptTextOnSubmit);
			for(var j=0; j < inputs.length; j++) {
				var theInput = inputs[j];
				if(theInput.getAttribute("promptvalue")) {
					var selectedNode;
					// IE only, check if we're already focused
					if (typeof document.selection != "undefined" && document.selection != null && typeof window.opera == "undefined") {
						/* find the currently focused page element */
						selectedNode = document.selection.createRange().parentElement();
					}
					/* only insert prompt text at start if not focused in input */
					if(selectedNode != theInput) {
						_addPromptText(theInput);
					}
					/* add the focus and blur handlers to add/remove prompt text */
					arb.event.addHandler(theInput, 'focus', _clearPromptTextHandler);
					arb.event.addHandler(theInput, 'blur', _replacePromptText);
				}
			}
		}		
	},
				
	/**
	 *	@name 	arb.form.initFocusHighlighting
	 * 	@desc 	Add event handlers to any <input> on the page with an associated <label> which will
	 *			add a class of 'arbHasFocus' to both items when the input is focused.
	 *	@author	Eric Orton <eric.orton@areeba.com.au>
	 */
	initFocusHighlighting: function() {
		/* 
		 * local function definitions 
		 */
		function _focus(e) {
			if(arb.element.hasClass(this, 'arbHasFocus')) return true;
			arb.element.addClass(this, 'arbHasFocus');
			for(var i = 0; i < this._labels.length; i++) {
				arb.element.addClass(this._labels[i], 'arbHasFocus');
			}
		}
		
		function _blur(e) {
			arb.element.removeClass(this, 'arbHasFocus');
			for(var i = 0; i < this._labels.length; i++) {
				arb.element.removeClass(this._labels[i], 'arbHasFocus');			
			}
		}

		/* 
		 * Start of function code 
		 */
		if(!document.getElementsByTagName) return true;
		
		var labels = document.getElementsByTagName('label');
		
		for(var i = (labels.length -1); i > -1; i--) {
			var inputID = labels[i].getAttribute('for') ? labels[i].getAttribute('for') : labels[i].getAttribute('htmlFor');
			if(inputID) {
				var input = document.getElementById(inputID);
				if(!input._labels) input._labels = new Array();
				input._labels.push(labels[i]);
				$(input).bind( 'focus', _focus);
				$(input).bind( 'blur', _blur);
			}
		}
	}
};

arb.debug = {
	/**
	 *	@name 	arb.debug.log
	 * 	@desc 	Print passed messages to an onscreen box.
	 *	@param	(String), the message to print
	 *	@author	Eric Orton <eric.orton@areeba.com.au>
	 */
	debugCounter: 0,
	
	log: function(strMessage) {
		var debugBox = document.getElementById('debugBox');
		var msgP = document.createElement('DIV');
		msgP.appendChild(document.createTextNode(arb.debug.debugCounter + ': ' + strMessage));
		arb.debug.debugCounter++;
		
		if(!debugBox) {
			debugBox = document.createElement('DIV');
			debugBox.style.position = 'absolute';
			debugBox.style.zIndex = '3000';
			debugBox.style.width = '280px';
			debugBox.style.height = '280px';
			debugBox.style.top = '0';
			debugBox.style.right = '0';
			debugBox.style.backgroundColor = "#dddddd";
			debugBox.style.border = '1px solid #aaaaaa';
			debugBox.style.overflow = 'auto';
			debugBox.onclick = function(){this.style.display = 'none';};
			debugBox.id = 'debugBox';
			//document.body.appendChild(debugBox);
		}
		debugBox.style.display = 'block';
		debugBox.insertBefore(msgP, debugBox.firstChild);
	}
};

arb.url = {
	/**
	 *	@name 	arb.url.equal
	 * 	@desc 	Test if the two URL's refer to the same resource.
	 *	@param	(String) left, the first URL
	 *	@param	(String) right, the second URL
	 *	@author	Eric Orton <eric.orton@areeba.com.au>
	 */
	equal: function(left, right) {
		// normalise our url's
		left = arb.url.normalise(left);
		right = arb.url.normalise(right);

		// compare them, simple when they're normalised
		return left == right;
	},
	
	/**
	 *	@name 	arb.url.normalise
	 * 	@desc 	Return a normalised URL.  The URL includes no query
	 *			string or page anchor and has the default.aspx or index.htm(l) stripped off.
	 *	@param	(String) url, the URL to normalise
	 *	@type	(String), the normalised URL
	 *	@author	Eric Orton <eric.orton@areeba.com.au>
	 */
	normalise: function(url) {
		// Lowercase our URL.  Very, very few web servers out there are case sensitive
		url = url.toLowerCase();
		
		// Chop off any query string
		url = url.replace(/^(.*)\?.*$/, "$1");

		// Chop off any page anchor
		url = url.replace(/^(.*)#.*$/, "$1");

		// Chop off any "default.aspx etc."
		url = url.replace(/^(.*)\/(default|index)\.(aspx|htm(l?))$/i, "$1");

		// Chop off any "/"
		url = url.replace(/^(.*)\/$/i, "$1");

		return url;
	},
	
	/**
	 *	@name 	arb.url.parentPage
	 * 	@desc 	Return the parent page of the given page in the form of a normalised URL.
	 *	@param	(String) url, the URL to normalise
	 *	@type	(String), the normalised URL of the parent page
	 *	@author	Eric Orton <eric.orton@areeba.com.au>
	 */
	parentPage: function(url) {
			// normalise the url (no default.aspx etc.)
			url = arb.url.normalise(url);
			
			// If the path ends in .aspx or .html or .htm, chop the filename to
			// try matching our parent
			if (url.match(/^.*\/.*?\.(aspx|htm(l?))$/i)) 
			{
				return url.replace(/^(.*)\/.*?$/i, "$1");
			}
			// Else, if it's a directory reference, chop off a path component and return that
			else if (url.match(/^http(s)?:\/\/.+\/.*$/)) 
			{
				return url.replace(/^(http(s)?:\/\/.+)\/.*?$/i, "$1");
			}
			
			// else, we're outta luck
			return "";
	},
	
	/**
	 *	@name 	arb.url.getParam
	 * 	@desc 	Get the value of a given query string parameter
	 *	@param	(String) variable, the name of the parameter to retrieve
	 *	@type	(String), parameter value
	 *	@author	Eric Orton <eric.orton@areeba.com.au>
	 */
	getParam: function(variable) {
		var query = window.location.search.substring(1);
		var vars = query.split("&");
		for (var i = 0; i < vars.length; i++) {
			var pair = vars[i].split("=");
			if (pair[0] == variable) {
				return pair[1];
			}
		} 
		return null;
	}
};

arb.functions = {
	/**
	 *	@name 	arb.functions.expandMenu
	 * 	@desc 	Traverses a ul/li/a menu structure under an element with the passed id
	 *			and determines which item is the current page.
	 *			Marks the current page's item with a class of 'active', and all it's 
	 *			parents with a class of 'expanded'.
	 *	@param	(String) id, id of the element containing the navigation menu
	 *	@author	Eric Orton <eric.orton@areeba.com.au>
	 */
	expandMenu: function(id) {
		if(!document.getElementById) return;
		
		var menu = document.getElementById(id);
		if(!menu) return;
		
		var menuItems = menu.getElementsByTagName("a");
		var activeMenuItem;
		var searchLocation = document.location.href;
		
		// set the class of "menus-expanded" on a new child of the top level menu
		// containing all the previous children.
		var innerDiv = document.createElement('div');
		arb.element.addClass(innerDiv, 'menus-expanded');	
		arb.element.moveChildren(menu, innerDiv)
		menu.appendChild(innerDiv);
		
		// find the currently active menu item.  If the page exists in more than one place
		// in the navigation heirarchy, this will only find one.  Which one depends on the
		// order of links returned by getElementsByTagName("a")
		do {
			for (var i=0; i<menuItems.length; i++){
					if (menuItems[i].href && arb.url.equal(menuItems[i].href, searchLocation)) {
					activeMenuItem = menuItems[i];
					break;
				}
			}
		
			// If we've exited the above loop without a match, adjust our searchLocation and try again.
			if (!activeMenuItem) {
				searchLocation = arb.url.parentPage(searchLocation);
			}
		} while (!activeMenuItem && searchLocation);
		
		// deal with the active menu item
		// put a class of 'active' on the active menu item, and 'expanded' on it's parent items
		if(activeMenuItem) {
			var parent = activeMenuItem.parentNode;  // our enclosing 'li'
			arb.element.addClass(parent, 'active');
			while(parent != menu) {
				if(parent.nodeName == "LI" || parent.nodeName == "UL") {
					arb.element.addClass(parent, 'expanded');
					if (parent.id != "") {
						arb.element.addClass(parent, parent.id + "-expanded");
					}
				}
				parent = parent.parentNode;
			}
		}
	},
	
	/**
	 *	@name 	arb.functions.dropdownMenus
	 * 	@desc 	Applies "drop down" functionality to a nested <ul>/<li>/<a> menu list.
	 *	@param	(String) id, id of the element containing the navigation menu
	 *	@author	Eric Orton <eric.orton@areeba.com.au>
	 */
	dropdownMenus: function(id) {
		function _showMenu(e) {
			arb.element.addClass(this, "arb-hover");
			if (typeof(jQuery) != "undefined") {
				$("ul", this).slideDown('fast');		
			}
		}
		function _hideMenu(e) { 
			arb.element.removeClass(this, "arb-hover");
		}
		var elements = document.getElementById(id).getElementsByTagName("LI");
		for (var i=0; i<elements.length; i++) {
			arb.event.standardHandler.hoverFns(elements[i], _showMenu, _hideMenu);
			arb.event.addHandler(elements[i], "focus", _showMenu);
			arb.event.addHandler(elements[i], "blur", _hideMenu);
		}
	},
	
	/**
	 *	@name 	arb.functions.stripeTables
	 * 	@desc 	Scans for every table in the page and adds a class of "arb-even" to the <tr> element of each even table row to allow 
	 *			styling of alternate rows.  Also add a class of "arb-hover" to the <tr> element currently being hovered over to allow
	 *			higlighting.
	 *	@author	Eric Orton <eric.orton@areeba.com.au>
	 */
	 stripeTables: function() {
		if (typeof(jQuery) != "undefined") {
			$(document).ready(function(){
			$("#content tr").hover(function() {$(this).addClass("arb-hover");}, function() {$(this).removeClass("arb-hover");});
			$("#content tr:even").addClass("arb-even");
			
			});		
		}
	},
		
	/**
	 *	@name 	arb.functions.linkButtons
	 * 	@desc 	Replaces selected button inputs with links that call the given callback when clicked.
	 *	@param	(String) selector, a jquery selector that returns the button inputs to be replaced.
	 *	@param	(Function) callback - optional, the function that will be executed when the link is clicked.
	 *					The function is called as "callback(link, button)" where link is the jquery link
	 *					object, and button is the jquery button object.
	 *					If no callback is specified a default callback which submits the original button
	 *					will be used.
	 *	@author	Eric Orton <eric.orton@areeba.com.au>
	 */
	linkButtons: function(selector, classifdisabled, callback) {
		callback = (callback == null) ? defaultCallback : callback;
		
		$(selector).each(function() {
			var button = $(this);
			var text = button.val();
			var link = $("<a href=\"#\">" + text + "</a>");
			button.before(link).hide();
			if (!button.is(":disabled")) {
				link.click(function() { callback(link, button); return false; });
			}
			else {
				link.click(function() { return false; }).attr("href", "").attr("disabled", "disabled").attr("readonly", "readonly").attr("class", "disabled");
			}		
		});	
		
		function defaultCallback(link, button) {
			button.click(); 
		}
	},
	
	enhanceTextareas: function () {
		$('textarea:not([@wysiwyg-textarea=true])').each(function() {
			$(this)
				.wrap("<div class=\"enhanced-textarea\"></div>")
				.parent()
				.append($("<div class=\"textarea-footer\"></div>"));
			
			arb.functions.expandableTextarea($(this).parent());
			arb.functions.maxlengthTextarea($(this).parent());
		});
	},
	
	expandableTextarea: function (textareaWrapper) {
		var staticOffset = null;
		var textarea = $("textarea", textareaWrapper);
		
		$(".textarea-footer", textareaWrapper)
			.addClass("grippie")
			.mousedown(startDrag);
		
		var grippie = $(".textarea-footer", textareaWrapper)[0];
		
		$(grippie).css("margin-right", grippie.offsetWidth - textarea[0].offsetWidth + "px");

		function startDrag(e) {
			staticOffset = textarea.height() - arb.functions.mousePosition(e).y;
			textarea.css("opacity", 0.25);
			$(document).bind("mousemove", performDrag).bind("mouseup", endDrag);
			return false;
		}


		function performDrag(e) {
			textarea.height(Math.max(32, staticOffset + arb.functions.mousePosition(e).y) + "px");
			return false;
		}


		function endDrag(e) {
			$(document).unbind("mousemove", performDrag).unbind("mouseup", endDrag);
			textarea.css("opacity", 1);
		}
	},

	mousePosition: function (e) {
		return {x:e.clientX + document.documentElement.scrollLeft, y:e.clientY + document.documentElement.scrollTop};
	},
	
	maxlengthTextarea: function (textareaWrapper) {
		$("textarea[@maxlength]", textareaWrapper)
			// set the max chars
			.each(function(){
				var maxLength  = $(this).attr('maxlength');
				var currentLength = $(this).val().length;
				var html_counter = $("<div class=\"textarea-length-counter\"><span>" + currentLength + "</span>/" + maxLength +" characters</div>");
				$(".textarea-footer", textareaWrapper).append(html_counter);
				this.relatedElement = $('span', html_counter);
			})
			// check the max chars
			.keyup(function(){
				var maxLength     = $(this).attr('maxlength');
				var currentLength = $(this).val().length;
				if(currentLength >= maxLength) {
					this.value = this.value.substring(0, maxLength - 1);
				}
				this.relatedElement.html(currentLength);
			});
	}
};

arb.cookie = {
	/**
	 *	@name 	arb.cookie.set
	 * 	@desc 	Set a cookie
	 *	@param 	String name The name of the cookie.
	 *	@param 	String value The value of the cookie.
	 *	@param 	Hash options A set of key/value pairs for optional cookie parameters.
	 *	@option	Number|Date expires Either an integer specifying the expiration date from now on in days or a Date object.
	 *                             If a negative value is specified (e.g. a date in the past), the cookie will be deleted.
	 *                             If set to null or omitted, the cookie will be a session cookie and will not be retained
	 *                             when the the browser exits.
	 *	@option	String path The value of the path atribute of the cookie (default: path of page that created the cookie).
	 *	@option	String domain The value of the domain attribute of the cookie (default: domain of page that created the cookie).
	 * 	@option	Boolean secure If true, the secure attribute of the cookie will be set and the cookie transmission will
	 *                        require a secure protocol (like HTTPS).
	 *	@author	Eric Orton <eric.orton@areeba.com.au>
	 */
	set: function(name, value, options) {
        options = options || {};
        var expires = '';
		
		options.expires = options.expires || 356;
		
		var date = new Date();
		date.setTime(date.getTime()+(options.expires*24*60*60*1000));
		var expires = "; expires="+date.toGMTString();
		
		var path = options.path ? '; path=' + options.path : '';
        var domain = options.domain ? '; domain=' + options.domain : '';
        var secure = options.secure ? '; secure' : '';
        document.cookie = [name, '=', encodeURIComponent(value), expires, path, domain, secure].join('');
    },
	
	/**
	 *	@name 	arb.cookie.get
	 * 	@desc 	Get the value of a cookie
	 *	@param 	String name The name of the cookie.
 	 *	@return	The value of the cookie.
 	 *	@type 	String
	 *	@author	Eric Orton <eric.orton@areeba.com.au>
	 */
	get: function(name) {
        var cookieValue = null;
        if (document.cookie && document.cookie != '') {
            var cookies = document.cookie.split(';');
            for (var i = 0; i < cookies.length; i++) {
                var cookie = cookies[i].trim();
                // Does this cookie string begin with the name we want?
                if (cookie.substring(0, name.length + 1) == (name + '=')) {
                    cookieValue = decodeURIComponent(cookie.substring(name.length + 1));
                    break;
                }
            }
        }
        return cookieValue;
    },
	
	/**
	 *	@name 	arb.cookie.remove
	 * 	@desc 	Delete a cookie
	 *	@param 	String name The name of the cookie.
	 *	@author	Eric Orton <eric.orton@areeba.com.au>
	 */
	remove: function(name) {
		arb.cookie.set(name, "", {expires: -1});
	}
};	
	
arb.event.addHandler(window, 'load', arb.init);



// ---------------------------------------------------------------------
//                      array.push (if unsupported)
// ---------------------------------------------------------------------
if(Array.prototype.push == null) {
  Array.prototype.push = function(item) {
    this[this.length] = item;
    return this.length;
  };
};


// ---------------------------------------------------------------------
//                      string.trim (our own language addition)
// ---------------------------------------------------------------------
if (String.prototype.trim == null) {
	String.prototype.trim = function() { 
		return this.replace(/^\s+|\s+$/, ''); 
	};
}
// ---------------------------------------------------------------------
// kill dotnets sucky validation rendering and replaces with our own
// ---------------------------------------------------------------------
$(function() {
	if (typeof(ValidatorUpdateDisplay) == 'function') {
		ValidatorUpdateDisplay = function(val) {
			if (typeof(val.display) == "string") {
				if (val.display == "Dynamic") {
					if (val.isvalid && $(val).is(':visible')) {
						$(val).animate({height: 'hide', opacity: 'hide'},'slow', function() {
							if (!$(val).siblings('.error-message').is(':visible')) {
								$(val).parent('.error-messages').parent('.form-item').removeClass('error-form-item');
							}
						});
					}
					else if (!val.isvalid && !$(val).is(':visible')) {
						$(val).animate({height: 'show', opacity: 'show'},'slow').parent('.error-messages').parent('.form-item').addClass('error-form-item');
					}
				}
			}
		}
	}
});

$(document).ready(function(){
initFontSizeControls();
function initFontSizeControls() {
			var maxFontSize = 180; // %
			var minFontSize = 70; // %
			function incrementFontSize(sizeDelta) {
				var newSize = parseFloat(arb.cookie.get("font-size")) + sizeDelta;
				if (newSize > minFontSize && newSize < maxFontSize) {
					setBodyFontSize(newSize);
					arb.cookie.set("font-size", newSize, {path: "/"});
				}
			}
			
			function setBodyFontSize(size) {
				$("#content").css("font-size", size + "%");
			}
			
			var fontSize = arb.cookie.get("font-size");
			var fontIncrement = 8.33; // %
			
			if (!fontSize) {
				arb.cookie.set("font-size", "100", {path: "/"}); // set the default if there' no value
				fontSize = arb.cookie.get("font-size");  // read it back to make sure cookies are supported
			}
			
			// check we can support this functionality
			if (window.print && fontSize) {
				$("#textresize").append("<ul id='page-controls'><li id='font-size-down-control' title='Decrease font size' class='image-replaced'>Decrease font size<span></span></li><li id='font-size-up-control' title='Increase font size' class='image-replaced'>Increase font size<span></span></li></ul>");
				
				setBodyFontSize(fontSize);
				
				// attach print handler
				$("#print-control").click(function() {
					window.print();
				});
				
				// attach font down handler
				$("#font-size-down-control").click(function() {
					incrementFontSize(-fontIncrement);
				});
		
				// attach font up handler
				$("#font-size-up-control").click(function() {
					incrementFontSize(fontIncrement);
				});
				
				// attach hover handler so we can style cursor with a hand on mouseover
				$("#page-controls li").hover(
					function() { $(this).addClass("arb-hover"); },
					function() { $(this).removeClass("arb-hover"); }
				);
			}
}
});