// default.js
// Copyright (C) 2005-2006 H. Adam Mill II
// Copyright (C) 2006-2008 ImarcsGroup.com

// **** General ****

// Extend an object with another objects properties.
var extend = function(target, source)
{
	for(var property in source)
      if(source.hasOwnProperty(property))
         target[property] = source[property];
   return target;
};

// Retrieve values from a GET.
var request = function(key, deft)
{
	if(undefined == request._values)
	{
		var href = window.location.href;
		var idx = href.indexOf("?");
		var values = { };
		if(-1 != idx)
		{
			var params = href.substr(idx+1).split('&');
			for(var loop=0; loop < params.length; loop++)
			{
				var pair = params[loop].split('=');
				pair.push('')
				values[unescape(pair[0])] = unescape(pair[1]);
			}
		}
      request._values = values;
	}
	var value = request._values[key];
	if(undefined === value)
		return deft;
	else
		return value;
}

// **** Type Utilities. ****
var TypeUtils =
{
	isNull : function(value)
	{
		if(null === value)
			return true;
		else
			return false;
	},
	isString : function(value)
	{
		if('string' === typeof value)
			return true;
		else
			return false;
	},
	isBoolean : function(value)
	{
		if(true === value || false === value)
			return true;
		else
			return false;
	},
	isInteger : function(value)
	{
		if(value === parseInt(value))
			return true;
		return false;
	},
	isFloat : function(value)
	{
		if("number" === typeof value)
			return true;
		else
			return false;
	},
	isArray : function(value)
	{
		if(value instanceof Array)
			return true;
		else
			return false;
	},
	isRegExp : function(value)
	{
		if(value instanceof RegExp)
			return true;
		else
			return false;
	},
	isObject : function(value)
	{
		// NOTE: typeof null == 'object'
		if(null === value)
			return false;
		else if('object' === typeof value)
			return true;
		else
			return false;
	},
	isFunction : function(value)
	{
		if('function' == typeof value)
			return true;
		else
			return false;
	},
   isDate : function(value)
   {
      if(value instanceof Date)
      {
         if(value.getFullYear && !isNaN(value.getFullYear()))
            return true;
      }
      return false;
   }
};


// **** String Utilities ****
var StringUtils = 
{
	// Trim whitespace from both ends of a string.
	trim : function(value)
	{
		if(null == value)	
			return null;
		return value.replace(/(^\s+|\s+$)/g, "");
	},
	// Trim whitespace from the right side of a string.
	rtrim : function(value)
	{
		if(null == value)	
			return null;
		return value.replace(/(\s+$)/g, "");
	},
	// Trim whitespace from the left side of a string.
	ltrim : function(value)
	{
		if(null == value)	
			return null;
		return value.replace(/(^\s+)/g, "");
	},	
	// Like String.split, only the seperator can be any of a group of characters 
	//  and the seperators are included in the array as individual items.
	breakup : function(value, chars)
	{
		if(null == value)	
			return null;
		var split = [ ];
		var current = "";
		for(var loop=0; loop < value.length; loop++)
		{
			var chr = value.substr(loop, 1);
			if((-1) == chars.indexOf(chr))
				current += chr;
			else
			{
				if("" != current)
					split.push(current);
				current = "";
				split.push(chr);
			}
		}
		if("" != current)
			split.push(current);
		return split;
	},
	// Count the number of times a string occurs within another string.
	count : function(value, search)
	{
		if(null == value)	
			return 0;
		var count = 0;
		var idx = value.indexOf(search);
		while((-1) != idx)
		{
			count++;
			value = value.substr(idx+1);
			idx = value.indexOf(search);
		}
		return count;
	},
	// Convert the first character of a string to uppercase.
	ucfirst : function(value)
	{
		if(null == value)	
			return null;
		return value.replace(/(^\w)/g,
			function(str)
			{
				return str.toUpperCase();
			});
	},	
	// Convert the first character of each word in a string to uppercase.
	ucwords : function(value)
	{
		if(null == value)	
			return null;
		return value.replace(/(^\w|(\s)(\w))/g,
			function(str)
			{
				return str.toUpperCase();
			});
	},
   padRight : function(value, size, chr)
   {
      return StringUtils._pad(value, size, chr, false);
   },
   padLeft : function(value, size, chr)
   {
      return StringUtils._pad(value, size, chr, true);
   },
   _pad : function(value, size, chr, dir)
   {
		if(null === value || undefined === value)
			return null;
      value = String(value);
      if(!chr)
         chr = " ";
      if(value.length < size)
      {
         for(var loop=0; loop < size-value.length; loop++)
         {
            if(dir) // If true left pad, else right.
               value = chr + value;
            else
               value += chr;
         }
      }
      return value;
   }
};


// **** RegExp Utilities ****
var RegExpUtils = 
{
	// Get a regular expression that finds a search item in a whitespace delimited list.
	getWhiteSpaceListMatcher : function(search)
	{
		return new RegExp("(^|\\s+)" + RegExpUtils.escape(search) + "($|\\s+)", "");
	},
	// Escape regexp control characters in a string.
	escape : function(value)
	{
		// Matches: . ? + * | ! \ / [ ] ( ) { } ^ $
		return value.replace(/([\.\?\+\*\|\!\\\/\[\]\(\)\{\}\^\$])/g,
			function(str)
			{
				return "\\" + str;
			});	
	}
};


// **** Size Utilities. ****
// Reference: http://www.quirksmode.org/viewport/compatibility.html
var SizeUtils =
{
	screen :
	{
		getHeight : function()
		{
			if(window.screen)
				return window.screen.height;
			else
				throw "Unable to determine screen height.";
		},
		getWidth : function()
		{
			if(window.screen)
				return window.screen.width;
			else
				throw "Unable to determine screen height.";
		}
	},
	scroll :
	{
		getVertical : function()
		{
			if(document.all)
				return document.body.scrollTop;
			else
				return window.pageYOffset;
		},
		getHorizontal : function()
		{
			if(document.all)
				return document.body.scrollLeft;
			else
				return window.pageXOffset;				
		}
	},
	window : 
	{	
		getHeight : function()
		{
			if(window.innerHeight)
				return window.innerHeight;
			else if(document.documentElement && document.documentElement.clientHeight)
				return document.documentElement.clientHeight;
			else if(document.body)
				return document.body.offsetHeight;
			else
				throw "Unable to determine window height.";
		},
		getWidth : function()
		{
			if(window.innerWidth)
				return window.innerWidth;
			else if(document.documentElement && document.documentElement.clientWidth)
				return document.documentElement.clientWidth;
			else if(document.body)
				return document.body.offsetWidth;
			else
				throw "Unable to determine window width.";
		},
		getOuterHeight : function()
		{
			if(window.outerHeight)
				return window.outerHeight;
			else
				throw "Unable to determine window outer height.";
		},
		getOuterWidth : function()
		{
			if(window.outerWidth)
				return window.outerWidth;
			else
				throw "Unable to determine window outer width.";
		}
	},
	element :
	{
		getHeight : function(element)
		{
			element = DOMUtils.getElement(element);
			if(element.offsetHeight)
				return element.offsetHeight;
			else
				throw "Unable to determine element height.";
		},
		getWidth : function(element)
		{
			element = DOMUtils.getElement(element);
			if(element.offsetWidth)
				return element.offsetWidth;
			else
				throw "Unable to determine element width.";				
		},
      getTop : function(element)
      {
         element = DOMUtils.getElement(element);
			if(element.offsetTop)
				return element.offsetTop;
			else
				throw "Unable to determine element top.";	
      },
      setHeight : function(element, height, unit)
      {
			element = DOMUtils.getElement(element);
         if(!unit) unit = "px";
         element.style["height"] = height + unit;
      },
      setWidth : function(element, width, unit)
      {
			element = DOMUtils.getElement(element);
         if(!unit) unit = "px";
         element.style["width"] = width + unit;
      }
	}
};

// **** Date Utilities. ****
var DateUtils = 
{
   isEqual : function(right, left)
   {
      if(!(right < left) && !(right > left))
         return true;
      else
         return false;
   },
	toAge : function(year, month, day)
	{
		year = DateUtils.getYear(year);
		month = DateUtils.getMonth(month);
		day = DateUtils.getDay(day);
		var date = new Date(year, month-1, day);
		var today = new Date();
		var age = today.getFullYear() - date.getFullYear();
		date.setFullYear(today.getFullYear())
		if(date > today)
			age--;
		return age;
	},
	getParts : function(year, month, day)
	{
		var date = { year : null, month : null, day : null };
		date['year'] = DateUtils.getYear(year);
		date['month'] = DateUtils.getMonth(month);
		date['day'] = DateUtils.getDay(day);
		if(date['year'] == null)
			return null;
		if(date['month'] == null)
			return null;
		if(date['day'] == null)
			return null;
		return date;
	},
	getYear : function(year)
	{
		year = parseInt(year);
		if(isNaN(year))
			return null;
		if(year < 100)
		{
			if(year > 49)
				year += 1900;
			else
				year += 2000;
		}
		return year;
	},
	getMonth : function(month)
	{
		month = parseInt(month);
		if(isNaN(month) || month < 1 || month > 12)
			return null;
		return month;
	},
	getDay : function(day)
	{
		day = parseInt(day);
		if(isNaN(day) || day < 1 || day > 31)
			return null;
		return day;
	}
};


// **** Array Utilities ****
var ArrayUtils =
{
	// Does the array contain the value.
	contains : function(list, value)
	{
		if(null == list)
			return false;	
		else if(list.indexOf)
			return (list.indexOf(value) != -1);			
		for(var loop=0; loop < list.length; loop++)
			if(value == list[loop])
				return true;
		return false;
	},
	// Convert an array-like object to an array.
	toArray : function(list)
	{
		if(null == list)
			return null;	
		else if(list instanceof Array)
			return list;
		var ary = [ ];
		for(var loop=0; loop < list.length; loop++)
			ary.push(list[loop]);
		return ary;
	},
	// Remove duplicate entries from an array.
	removeDuplicates : function(list)
	{
		if(null == list)
			return null;	
		var ary = [ ];
		for(var loop=0; loop < list.length; loop++)
		{
			if(!ArrayUtils.contains(ary, list[loop]))
				ary.push(list[loop]);
		}
		return ary;
	}	
};


// **** DOM Utilities ****
var DOMUtils = 
{
	// Node Types
	ELEMENT_NODE : 1,
	// Get the previous sibling element (skipping over text and comment nodes).
	getPreviousSiblingElement : function(element)
	{
		element = DOMUtils.getElement(element);
		do
		{
			if(!element.previousSibling)
				return null;
			element = element.previousSibling;
		}
		while(DOMUtils.ELEMENT_NODE != element.nodeType)
		return element;
	},
   appendClassName : function(element, classname)
   {
      element = DOMUtils.getElement(element);
      var fullclass = element.getAttribute("className");
      if(!fullclass)
      {
         fullclass = element.getAttribute("class");
         if(!fullclass)
            fullclass = "";
      }
      var classlist = fullclass.split(" ");
      if(!ArrayUtils.contains(fullclass, classname))
         classlist.push(classname);
      fullclass = classlist.join(" ");
      element.className = fullclass;
   },
	// Get a child element by id.
	getElementById : function(id, parent, directchildren)
	{
		parent = DOMUtils.getElement(parent, document);
		if(parent.getElementById)
			return parent.getElementById(id);
		else
		{
			var itemlist = [ ];
			var children = parent.childNodes;
			if(children && 0 != children.length)
			{
				for (var loop=0; loop < children.length; loop++)
				{
					if(id == children[loop].id)
						return children[loop];
					if(!directchildren)
						if(children[loop].childNodes && 0 != children[loop].childNodes.length)
						{
							var element = DOMUtils.getElementById(id, children[loop]);
							if(null != element)
								return element;
						}
				}
			}
		}
		return null;
	},
	// Get a direct child element by id.
	getChildById : function(id, parent)
	{
		return DOMUtils.getElementById(id, parent, true);
	},
	// Get elements by class name.
	getElementsByClassName : function(className, parent)
	{
		var re_classname = RegExpUtils.getWhiteSpaceListMatcher(className);
		var elementlist = DOMUtils.getElementsByFilter(function(element) {
				if(element.className && re_classname.test(element.className))
						return true;
				return false;
			}, parent);
		return elementlist;
	},
	// Get direct children by class name.
	getChildrenByClassName : function(className, parent)
	{
		var re_classname = RegExpUtils.getWhiteSpaceListMatcher(className);
		return DOMUtils.getChildrenByFilter(function(element) {
				if(element.className && re_classname.test(element.className))
						return true;
				return false;
			}, parent);
	},
	// Get elements by type (text or comment).
	getElementsByType : function(typename, parent)
	{
		var elements = DOMUtils.getElementsByFilter(function(element) {
				if("#"+typename == element.nodeName)
					return true;
				return false;
			}, parent);	
		return elements;
	},
	// Get all child nodes.
	getAllChildren : function(parent)
	{
		parent = DOMUtils.getElement(parent, document);
		if(parent.all)
			return parent.all;		
		else if(parent.getElementsByTagName)
			return parent.getElementsByTagName("*");
		else
			return null;
	},
	// Get the direct children of an element.
	getDirectChildren : function(parent)
	{
		parent = DOMUtils.getElement(parent, document);
		if(parent.childNodes)
			return parent.childNodes;
		else
			return null;
	},
	// Get direct children by tag name.
	getChildrenByTagName : function(nodeName, parent)
	{
		return DOMUtils.getChildrenByFilter(function(element) {
				if(element.nodeName && element.nodeName.toLowerCase() == nodeName)
					return true;
				return false;
			}, parent);
	},
	// Get elements that pass a filter function.
	getElementsByFilter : function(filter, parent, extra, directchildren)
	{
		parent = DOMUtils.getElement(parent, document);
		var itemlist = [ ];
		var children = parent.childNodes;
		if(children && 0 != children.length)
		{
			for (var loop=0; loop < children.length; loop++)
			{
				if(filter(children[loop], extra))
					itemlist.push(children[loop]);
				if(!directchildren)
					if(children[loop].childNodes && 0 != children[loop].childNodes.length)
						itemlist = itemlist.concat(DOMUtils.getElementsByFilter(filter, children[loop], extra));
			}
		}
		return itemlist;
	},
	// Get direct child elements that pass a filter function.
	getChildrenByFilter : function(filter, parent, extra)
	{
		return DOMUtils.getElementsByFilter(filter, parent, extra, true);
	},	
	// Get an element.
	getElement : function(element, deft)
	{
		if(null == element)
			return deft;
		else if("string" == typeof element)
		{
			element = document.getElementById(element)
			if(!element)
				return deft;
		}
		return element;	
	},
	// Is element member of some class?
	isClass : function(element, match)
	{
		element = DOMUtils.getElement(element, null);
		var re_classname = RegExpUtils.getWhiteSpaceListMatcher(match);
		if(element && element.className && re_classname.test(element.className))
				return true;
		return false;
	},
	// Process elements by list.
	processElementsByList : function(elementlist, fn)
	{
		for(var loop=0; loop < elementlist.length; loop++)
			fn(elementlist[loop]);
	},	
	// Concatenate all text nodes that belong to a given element.
	getNodeText : function(element)
	{
		element = DOMUtils.getElement(element, document);
		var value = "";
		DOMUtils.processElementsByList(
			DOMUtils.getElementsByType("text", element),
			function(ele) {
            value += ele.nodeValue;
			});
		return value;
	},
	// Remove any empty (blank or pure whitespace) nodes from the document
	removeEmptyTextNodes : function()
	{
		DOMUtils.processElementsByList(
			DOMUtils.getElementsByType("text", document),
			function(element) {
				if("" == StringUtils.trim(element.nodeValue))
					DOMUtils.removeElement(element);
			});	
	},
	// Remove child text nodes.
	removeChildTextNodes : function(element)
	{
		DOMUtils.processElementsByList(
			DOMUtils.getElementsByType("text", element),
			function(ele) {
            DOMUtils.removeElement(ele);
			});
	},
	// Remove an element.
	removeElement : function(element)
	{
		element = DOMUtils.getElement(element);
		if(element && element.parentNode)
			element.parentNode.removeChild(element);
	}
};


// **** DOMUtils.getElementsBySelector ****
extend(DOMUtils, {
	// Get a list of elements matching a CSS selector.
	getElementsBySelector : function(selector, parent)
	{
		var compiled = DOMUtils.getCompiledSelector(selector);
		return DOMUtils.getElementsByCompiledSelector(parent, compiled);
	},
	// Get a list of elements matching compiled selector.
	getElementsByCompiledSelector : function(parent, compiled, depth)
	{
		if(!compiled || !compiled.length)
			return null;
		if(!parent) parent = document;
		var elementlist = [ ];		
		if(undefined == depth)
		{
			for(var loop=0; loop < compiled.length; loop++)
				elementlist = elementlist.concat(DOMUtils.getElementsByCompiledSelector(parent, compiled[loop], 0));
			elementlist = ArrayUtils.removeDuplicates(elementlist);
		}
		else
		{
			var selector = compiled[depth];
			if(parent.parentNode)
				var grandparent = parent.parentNode;
			else
				var grandparent = null;
			switch(selector.type)
			{
				case "id":
				{
					var element = null;
					if("all" == selector.descend)
						element = DOMUtils.getElementById(selector.data, parent);
					else if("child" == selector.descend)
						element = DOMUtils.getChildById(parent, selector.data);
					else if("self" == selector.descend)
					{
						if(parent.id && parent.id == selector.data)
							element = parent;
					}
					else if("sibling" == selector.descend && grandparent)
					{
						element = DOMUtils.getChildById(grandparent, selector.data);
						if(element && parent != DOMUtils.getPreviousSiblingElement(element))
							element = null;
					}
					if(element)
						elementlist = [ element ];							
					break; 
				}
				case "class":
				{
					if("all" == selector.descend)
						elementlist = DOMUtils.getElementsByClassName(selector.data, parent);
					else if("child" == selector.descend)
						elementlist = DOMUtils.getChildrenByClassName(selector.data, parent);
					else if("self" == selector.descend)
					{
						if(parent.className)
						{
							var re_classname = RegExpUtils.getWhiteSpaceListMatcher(selector.data);
							if(re_classname.test(parent.className))
								elementlist = [ parent ];
						}
					}
					else if("sibling" == selector.descend && grandparent)
					{
						siblinglist = DOMUtils.getChildrenByClassName(grandparent, selector.data);
						elementlist = DOMUtils._getSelectorSiblingMatch(parent, siblinglist);
					}
					break;
				}
				case "tag":
				{
					if("all" == selector.descend)
						elementlist = ArrayUtils.toArray(parent.getElementsByTagName(selector.data));
					else if("child" == selector.descend)
						elementlist = ArrayUtils.toArray(DOMUtils.getChildrenByTagName(parent, selector.data));
					else if("self" == selector.descend)
					{
						if(parent.nodeName && parent.nodeName == selector.data)
								elementlist = [ parent ];				
					}
					else if("sibling" == selector.descend && grandparent)
					{
						siblinglist = DOMUtils.getChildrenByTagName(grandparent, selector.data);
						elementlist = DOMUtils._getSelectorSiblingMatch(parent, siblinglist);
					}
					break;
				}
				case "*":
				{
					if("all" == selector.descend)
						elementlist = ArrayUtils.toArray(DOMUtils.getAllChildren(parent));
					else if("child" == selector.descend)
						elementlist = ArrayUtils.toArray(DOMUtils.getDirectChildren(parent));
					else if("sibling" == selector.descend && grandparent)
					{
						siblinglist = ArrayUtils.toArray(DOMUtils.getAllChildren(grandparent));
						elementlist = DOMUtils._getSelectorSiblingMatch(parent, siblinglist);
					}
					break;
				}
				case "attr":
				{
					var element = null;
					var attrib = parent.getAttribute(selector.data.name);
					if(attrib)
					{
						switch(selector.data.cmp)
						{
							case "*":
							{
								element = parent;
								break;
							}
							case "starts":
							{
								if(selector.data.value == attrib.substr(0, selector.data.value.length))
									element = parent;
								break;
							}
							case "eq":
							{
								if(selector.data.value == attrib)
									element = parent;
								break;
							}
							case "ends":
							{
								if(selector.data.value == attrib.substr(attrib.length-selector.data.value.length))
									element = parent;
								break;
							}						
							case "contains":
							{
								if(-1 != attrib.indexOf(selector.data.value))
									element = parent;
								break;
							}
							case "one-of":
							{										
								var re_oneof = RegExpUtils.getWhiteSpaceListMatcher(selector.data.value);
								if(re_oneof.test(attrib))
									element = parent;
								break;
							}
							case "hyphen":
							{
								var re_hypen = new RegExp("^(" + RegExpUtils.escape(selector.data.value) + ")(-|$)", "");
								if(re_hypen.test(attrib))
									element = parent;
								break;
							}
						}
						if(element)
							elementlist = [ element ];
					}
					break;
				}
				case "psudo":
				{
					if("root" == selector.data && 0 == depth)
					{
						if(document.documentElement)
							elementlist = [ document.documentElement ];
						else
							elementlist = [ document ];
					}
					break;
				}
			}
			if(elementlist && elementlist.length && depth+1 < compiled.length)
			{
				var parentlist = elementlist;
				var elementlist = [ ];
				for(var loop=0; loop < parentlist.length; loop++)
				{
					var eles = DOMUtils.getElementsByCompiledSelector(parentlist[loop], compiled, depth+1);
					if(null != eles && eles.length)
						elementlist = elementlist.concat(eles);
				}
			}
		}
		return elementlist;
	},
	// Compile a CSS selector for use with getElementsByCompiledSelector.
	getCompiledSelector : function(selector)
	{
		var compiledlist = [ ];
		var compiledselector = [ ];
		var nextselector = false;
		var partlist = StringUtils.breakup(StringUtils.trim(selector), " \t.#+>:[],");
		var descend = "all";
		// Compiled object is has the following members: descend, type, data, source.
		var compiled = null;
		for(var loop=0; loop < partlist.length; loop++)
		{
			var part = partlist[loop];
			var source = part;
			switch(part)
			{
				case "+":
				{
					descend = "sibling";
					break;
				}
				case ">":
				{
					descend = "child";
					break;
				}
				case "\t":
				case " ":
				{
					if("self" == descend)
						descend = "all";
					break;
				}
				case ":":
				case "#":
				case ".":
				{
					if(loop+1 >= partlist.length)
						return null;
					if("#" == part)
						type = "id";
					else if(":" == part)
						type = "psudo";
					else
						type = "class";
					loop++;
					part = partlist[loop];
					source += part;
					compiled = { descend : descend, type : type, data : part };
					break;
				}
				case "[":
				{
					var attrib = { };
					source = "";
					loop++;
					// Recombine parts that were broken up until the entire attribute selector is found.
					while(loop < partlist.length)
					{
						part = partlist[loop];
						if("]" == part && !(StringUtils.count(source, "\"")%2)) // TODO: This is ugly, replace it.
							break;
						source += part;
						loop++;
					}
					if("]" != part)
						return null;
					var match = DOMUtils._re_attrib.exec(source);
					if(!match)
						return null;
					attrib.name = match[1];
					if('' == match[2])
					{
						attrib.cmp = "*";
						attrib.value = null;
					}
					else
					{
						switch(match[3])
						{
							case "=":
							{
								attrib.cmp = "eq";
								break;
							}
							case "^=":
							{
								attrib.cmp = "starts";
								break;
							}
							case "$=":
							{
								attrib.cmp = "ends";
								break;
							}
							case "*=":
							{
								attrib.cmp = "contains";
								break;
							}						
							case "~=":
							{
								attrib.cmp = "one-of";
								break;
							}
							case "|=":
							{
								attrib.cmp = "hyphen";
								break;
							}
							default:
								return null;
						}
						attrib.value = match[4];
					}
					source = "[" + source + "]";
					compiled = { descend : "self", type : "attr", data : attrib };
					break;
				}
				case "*":
				{
					compiled = { descend : descend, type : "*", data : part };
					break;
				}
				case ",":
				{
					nextselector = true;
					break;
				}
				default:
				{
					if(DOMUtils._re_tag.test(part))
						compiled = { descend : descend, type : "tag", data : part };
					else
						return null;
				}
			}
			if(compiled)
			{
				compiled.source = source;
				compiledselector.push(compiled);
				compiled = null;
				descend = "self";
			}
			if(nextselector)
			{
				if(!compiledselector.length)
					return null;
				compiledlist.push(compiledselector);
				compiledselector = [ ];
				nextselector = false;
				descend = "all";
			}
		}
		compiledlist.push(compiledselector);
		return compiledlist;
	},
	// Get sibling matches.
	_getSelectorSiblingMatch : function(parent, siblinglist)
	{
		var elementlist = [ ];
		for(var loop=0; loop < siblinglist.length; loop++)
		{
			var element = siblinglist[loop];
			var previousSibling = DOMUtils.getPreviousSiblingElement(element);
			if(element != parent && parent == previousSibling)
				elementlist.push(element);
		}
		return elementlist;
	},
	// Regular expression for parsing selector with attributes.
	_re_attrib 	: /^([A-Za-z]+)(|([~\^\*\$\|]?=)\"(.*)\")$/,
		// /^																Start
		// ([A-Za-z]+)				href								The attribute
		// (|																	Empty or...
		//		([~\^\*\$\|]?=)		^=									The comparison type ( ~= ^= *= $= |= = )
		//		\"(.*)\"					"http://"						Quoted string
		// )$/																End
	// Regular expression for matching tag names.
	_re_tag		: /^[A-Za-z1-9]+$/
});


// **** Form Utilities. ****
var FormUtils = 
{
	// Get field value.
	getFieldValue : function(form, field)
	{
		field = FormUtils.getFormField(form, field);
		if(!field)
			return null;
		if(null == field.type)
		{
			var type = field[0].type;
			if("radio" == type)
			{
				for(var loop=0; loop < field.length; loop++)
					if(true == field[loop].checked)
						return field[loop].value;
			}
		}
		else
		{
			var type = field.type;
			if((type == "text") || (type == "password") || (type == "textarea") || (type == "hidden") || (type == "file"))
				return field.value;
			else if(type == "checkbox")
			{
				if(true == field.checked)
					return "Y";
				else
					return "";
			}
			else if("radio" == type)
			{
				if(true == field.checked)
					return field.value;
			}		
			else if(type == "select-one")
			{
				for(var loop=0; loop < field.options.length; loop++)
				{
					if(true == field.options[loop].selected)
						return field.options[loop].value;
				}
				return false;
			}
			else if(type == "select-multiple")
			{
				var value = "";
				for(var loop=0; loop < field.options.length; loop++)
				{
					if(true == field.options[loop].selected)
					{
						if("" != value)
							values += ",";
						values += field.options[loop].value;
					}
				}
				return value;
			}
			// NOTE: Ignore button, image, and submit types
		}
		return null;
	},
	// Set field value.
	setFieldValue : function(field, value, parent)
	{
		field = DOMUtils.getElement(field, parent);
		if(!field)
			return;
		var type = field.type;
		if(null == type)
		{
			type = field[0].type;
			if(type == "radio")
			{
				for(var loop=0; loop < field.length; loop++)
				{
					if(value == field[loop].value)
						field[loop].checked = true;
					else
						field[loop].checked = false;
				}
			}
		}
		else
		{
			if((type == "text") || (type == "password") || (type == "textarea") || (type == "hidden") || (type == "file"))
				field.value = value;
			else if(type == "checkbox")
			{
				if(value == field.value)
					field.checked = true;
				else
					field.checked = false;
			}
			else if(type == "select-one")
			{
				for(var loop=0; loop < field.options.length; loop++)
				{
					if(value == field.options[loop].value)
						field.options[loop].selected = true;
					else
						field.options[loop].selected = false;
				}
			}
			else if(type == "select-multiple")
			{
				for(var loop=0; loop < field.options.length; loop++)
						field.options[loop].selected = false;
				var values = unescape(value).split(",");
				for(var loop=0; loop < values.length; loop++)
				{
					for(var loopB=0; loopB < field.options.length; loopB++)
						if(values[loop] == field.options[loopB].value)
							field.options[loopB].selected = true;
				}
			}
			// NOTE: Ignore button, image, and submit types
		}
	},   
	getFormField : function(form, field)
	{
		form = DOMUtils.getElement(form, null);
		if(!form)
			return null;
		return form[field];
	},
	// Get child field.
	getFields : function(parent, exclude)
	{
      if(!exclude)
         exclude = "button";
		return DOMUtils.getElementsByFilter(function(element) {
				if(element.nodeName)
				{
					switch(element.nodeName.toLowerCase())
					{
						case "select":
						case "textarea":
						{
							if('!button' == exclude)			
								return false;
							return true;
						}
						case "input":
						{
							switch(element.type)
							{
								case "button":
								case "submit":
								case "image":
								{
									if('button' == exclude)
										return false;
									else
										return true;
									break;
								}
							}
							if('!button' == exclude)			
								return false;							
							return true;
						}
						case "button":
						{
							if('button' == exclude)
								return false;
							return true;
						}
					}
				}
				return false;
			}, parent);
	}
};

// **** Event Utilities ****
var EventUtils = {
   listen : function(type, listener, element)
   {
		if(element instanceof Array)
		{
			for(var loop=0; loop < element.length; loop++)
				EventUtils.listen(type, listener, element[loop]);
			return;
		}	
		element = DOMUtils.getElement(element, window);
      if("beforeunload" == type && window == element)
         window.onbeforeunload = listener;
      else
      {
			if(element.addEventListener)
				element.addEventListener(type, listener, false);
			else if(element.attachEvent)
				element.attachEvent("on" + type, listener);
			else
				throw "No method found for adding event listeners to '" + String(element) + "'.";
		}
   },
	// Remove an event listener.
	unlisten : function(type, listener, element)
	{
		var useCapture = false;
		element = DOMUtils.getElement(element, window);
		if(element.removeEventListener)
			element.removeEventListener(type, listener, useCapture);
		else if(window.detachEvent)
			element.detachEvent("on" + type, listener);
		else
			throw "No method found for removing event listeners.";			
	},   
   getEvent : function(evt)
   {
		if (!evt)
			evt = window.event;	
		return evt;
   },
	getEventTarget : function(evt)
	{
		var target = null;
		if(evt.target)
			target = evt.target;
		else if(evt.srcElement)
			target = evt.srcElement;
		if(target && target.nodeType == 3)
			target = target.parentNode;
		return target;
	},
   cancelEvent : function(evt)
   {
		evt = EventUtils.getEvent(evt);
		evt.cancelBubble = true;
		if(evt.preventDefault)
			evt.preventDefault();
		return false;
   }
};


// **** Behavior ****
// Based on Behaviour v1.0 by Ben Nolan, June 2005.
var Behavior =
{
	list : [ ],

   add : function(value)
   {
      Behavior.list.push(value);
   },

   unlisten : function()
   {
      for(var loop=0; group=Behavior.list[loop]; loop++)
      {
			for(var selector in group)
			{
				var item = group[selector];
				var elementlist = null
				if(selector == ':window')
					elementlist = [ window ];
				else
					elementlist = DOMUtils.getElementsBySelector(selector);
				if (!elementlist)
					continue;
				for(var eleloop=0; ele=elementlist[eleloop]; eleloop++)
				{
					if('function' != typeof item)
					{
						for(var member in group[selector])
						{
							if('behavior' != member && 'load' != member)
								EventUtils.unlisten(member, item[member], ele);
						}
					}
				}
			}
      }
   },
	
	reapply : function()
	{
		Behavior.unlisten();
		Behavior.apply();
	},

	apply : function(load,evt)
	{
		if(null == load || undefined == load)
			load = false;
		if(load)
			evt = EventUtils.getEvent(evt);
		if(evt == null)
			evt = { };
		for(var loop=0; group=Behavior.list[loop]; loop++)
		{
			for(var selector in group)
			{
				var item = group[selector];
				var elementlist = null
				if(selector == ':window')
					elementlist = [ window ];
				else
					elementlist = DOMUtils.getElementsBySelector(selector);
				if (!elementlist)
					continue;
				for(var eleloop=0; ele=elementlist[eleloop]; eleloop++)
				{
					if('function' == typeof item)
						item(ele);
					else
					{
						for(var member in group[selector])
						{
							if(('behavior' == member) || (load && 'load' == member))
								item[member](evt);
							else
								EventUtils.listen(member, item[member], ele);
						}
					}
				}
			}
		}
	}
};

var TriggerEvent = {
    change : function(element)
    {
        return TriggerEvent._triggerHTMLEvent(element, "change");
    },
    _triggerHTMLEvent : function(element, type)
    {
        element = DOMUtils.getElement(element, window);
        if(document.createEvent)
        {
            var evt = element.ownerDocument.createEvent("HTMLEvents");
            evt.initEvent(type, true, false);
            return element.dispatchEvent(evt);
        }
        else if(document.createEventObject)
        {
            var evt = element.ownerDocument.createEventObject();
            element.fireEvent('on'+type, evt);
        }
        else
            throw "No method found for triggering event.";
    }
}; 

// Backwards compatibilty.
extend(Behavior, {
		register : Behavior.add
	});
var Behaviour = Behavior;


// **** On load... ****
EventUtils.listen('load', function(evt)
	{
		DOMUtils.removeEmptyTextNodes();
		Behavior.apply(true,evt);
	});

document.write("");