
//------------------------------------------------------------------------
// Package: Structures
// The various structures used by the system.
// 
// Topic: Dependencies
// - <DOM Library>
//------------------------------------------------------------------------

//------------------------------------------------------------------------
// Class: Legato_Structure_Color
// Handles a single color.
//------------------------------------------------------------------------

//------------------------------------------------------------------------
// Constructor: Legato_Structure_Color()
// Class constructor.
//
// Parameters:
//     RGB - A six character string of Red, Green and Blue values, eg: "FF2345".
//     
//     OR
//
//     R - Red value, 0 - 255.
//     G - Green value, 0 - 255.
//     B - Blue value, 0 - 255.
//------------------------------------------------------------------------
function Legato_Structure_Color()
{

	// What type of argument was passed in?
	if ( arguments.length == 1 && (typeof arguments[0] == "string" || arguments[0] instanceof String) )
	{

		this.R = parseInt( arguments[0].substring( 0, 2 ), 16 );
		this.G = parseInt( arguments[0].substring( 2, 4 ), 16 );
		this.B = parseInt( arguments[0].substring( 4, 6 ), 16 );

	}  // End if hex string.
	else if ( arguments.length == 3 )
	{

		this.R = arguments[0];
		this.G = arguments[1];
		this.B = arguments[2];

	}  // End if numbers passed in.
	else
	{

		this.R = null;
		this.G = null;
		this.B = null;

	}  // End if null passed in.

}


//------------------------------------------------------------------------
// Public Member Functions
//------------------------------------------------------------------------

//------------------------------------------------------------------------
// Function: toHexString()
// Returns the color as a string of hexidecimal values.
//------------------------------------------------------------------------
Legato_Structure_Color.prototype.toHexString = function()
{

	var R = (this.R < 16) ? "0" + this.R.toString( 16 ) : this.R.toString( 16 );
	var G = (this.G < 16) ? "0" + this.G.toString( 16 ) : this.G.toString( 16 );
	var B = (this.B < 16) ? "0" + this.B.toString( 16 ) : this.B.toString( 16 );

	return R + G + B;

}


//------------------------------------------------------------------------
// Class: Legato_Structure_Dimensions
// Handles a single set of dimensions.
//------------------------------------------------------------------------

//------------------------------------------------------------------------
// Constructor: Legato_Structure_Dimensions()
// Class constructor.
//
// Parameters:
//     width - The width to store.
//     height - The height to store.
//
//     OR
//
//     element - A DOM element, in which case it will store the dimensions
//               of that element at the time this structure was created.
//------------------------------------------------------------------------
function Legato_Structure_Dimensions()
{
	
	// What type of arguments passed in?
	if ( arguments.length == 2 )
	{

		// Store the passed in parameters.
		this.width   = arguments[0];
		this.height  = arguments[1];
		
	}  // End if a width and height.
	else if ( arguments.length == 1 )
	{
		
		var element = $( arguments[0] );

		// Get the element's dimensions.
		var dimensions = element.dimensions();
		this.width = dimensions[0];
		this.height = dimensions[1];
		
	}  // End if element passed in.

}


//------------------------------------------------------------------------
// Class: Legato_Structure_Position
// Handles a single position. For positioning objects relative to its
// containing element.
//------------------------------------------------------------------------

//------------------------------------------------------------------------
// Constructor: Legato_Structure_Position()
// Class constructor.
//
// Parameters:
//     top - The value from the top of the containing element.
//     right - The value from the right of the containing element.
//     bottom - The value from the bottom of the containing element.
//     left - The value from the left of the containing element.
//------------------------------------------------------------------------
function Legato_Structure_Position( top, right, bottom, left )
{

	// Store the passed in parameters.
	this.top     = top;
	this.right   = right;
	this.bottom  = bottom;
	this.left    = left;

}


//------------------------------------------------------------------------
// Class: Legato_Structure_Point
// Handles a single point.
//------------------------------------------------------------------------

//------------------------------------------------------------------------
// Constructor: Legato_Structure_Point()
// Class constructor.
//
// Parameters:
//     X - The X value of the point.
//     Y - The Y value of the point.
//------------------------------------------------------------------------
function Legato_Structure_Point( X, Y )
{

	// Store the passed in parameters.
	this.X = X;
	this.Y = Y;

}


//------------------------------------------------------------------------
// Class: Legato_Structure_Region
// Handles a single region.
//------------------------------------------------------------------------

//------------------------------------------------------------------------
// Constructor: Legato_Structure_Region()
// Class constructor.
//
// Parameters:
//     min_point - The <Legato_Structure_Point> of the top left corner.
//     max_point - The <Legato_Structure_Point> of the bottom right corner.
//
//     OR
//
//     element - A DOM element, in which case the region will be the
//               containing region of the element.
//------------------------------------------------------------------------
function Legato_Structure_Region()
{

	// What was passed in?
	if ( arguments.length == 2 )
	{

		// Store the passed in parameters.
	  this.min_point  = arguments[0];
	  this.max_point  = arguments[1];

	}  // End if points passed in.
	else if ( arguments.length == 1 )
	{

		var element = $( arguments[0] );

		// Get the element's position for the region's min point.
		this.min_point = this.element.position();
		this.min_point = new Legato_Structure_Point( this.min_point[0], this.min_point[1] );

		// Get the max point for the region.
		var dimensions = element.dimensions();
		this.max_point = new Legato_Structure_Point( (this.min_point.X + dimensions[0]), (this.min_point.Y + dimensions[1]) );

	}  // End if HTML element passed in.

}


//------------------------------------------------------------------------
// Public Member Functions
//------------------------------------------------------------------------

//------------------------------------------------------------------------
// Function: intersectRegion()
// Used to test if two <Legato_Structure_Region> objects are intersecting
// each other.
//
// Parameters:
//     region - The <Legato_Structure_Region> that you'd like to test for
//              intersection against this object.
//     in_test - (Optional) If this is set to true, the region passed in
//               must be fully contained by this object.
//     epsilon - (Optional) This is the allowable mistake that can happen
//               between the tests, eg: if this is 1, then if the region
//               is 1 pixel away from intersecting, this function will
//               still return true.
//------------------------------------------------------------------------
Legato_Structure_Region.prototype.intersectRegion = function( region, in_test, epsilon )
{

	// Get the epsilon.
	epsilon = (epsilon) ? epsilon : 0;

	// Only do this if we are testing if it's completely contained.
	if ( in_test )
	{

	  var contained = true;

		// Is the region completely contained in this region?
		if ( region.min_point.X < (this.min_point.X + epsilon) || region.min_point.X > (this.max_point.X + epsilon) ) contained = false;
		else if ( region.min_point.Y < (this.min_point.Y + epsilon) || region.min_point.Y > (this.max_point.Y + epsilon) ) contained = false;
		else if ( region.max_point.X < (this.min_point.X + epsilon) || region.max_point.X > (this.max_point.X + epsilon) ) contained = false;
		else if ( region.max_point.Y < (this.min_point.Y + epsilon) || region.max_point.Y > (this.max_point.Y + epsilon) ) contained = false;

		// Return.
		return contained;

	}  // End if completely contained test.
	else
	{

		// Perform the intersection test.
		return ((this.min_point.X + epsilon) <= region.max_point.X) &&
						((this.min_point.Y + epsilon) <= region.max_point.Y) &&
						((this.max_point.X + epsilon) >= region.min_point.X) &&
						((this.max_point.Y + epsilon) >= region.min_point.Y);

	}  // End normal touching test.

}


//------------------------------------------------------------------------
// Function: containsPoint()
// Used to test if there is an <Legato_Structure_Point> object within the
// bounds of this region.
//
// Parameters:
//     point - The <Legato_Structure_Point> object.
//------------------------------------------------------------------------
Legato_Structure_Region.prototype.containsPoint = function( point )
{

	var contained = true;

	// Is the point completely contained in this region?
	if ( this.min_point.X > point.X || this.max_point.X < point.X ) contained = false;
	if ( this.min_point.Y > point.Y || this.max_point.Y < point.Y ) contained = false;

	// Return.
	return contained;

}


// Developed by Robert Nyman/DOMAssistant team
// code/licensing: http://code.google.com/p/domassistant/
// documentation: http://www.domassistant.com/documentation
// version 2.7.1.1
// Developed by Robert Nyman/DOMAssistant team, code/licensing: http://domassistant.googlecode.com/, documentation: http://www.domassistant.com/documentation, version 2.8
var DOMAssistant = function () {
	var HTMLArray = function () {
		// Constructor
	},
	isIE = /*@cc_on!@*/false,
	isIE5 = isIE && parseFloat(navigator.appVersion) < 6,
	tagCache = {}, lastCache = {}, useCache = true,
	slice = Array.prototype.slice,
	camel = {
		"accesskey": "accessKey",
		"class": "className",
		"colspan": "colSpan",
		"for": "htmlFor",
		"maxlength": "maxLength",
		"readonly": "readOnly",
		"rowspan": "rowSpan",
		"tabindex": "tabIndex",
		"valign": "vAlign",
		"cellspacing": "cellSpacing",
		"cellpadding": "cellPadding"
	},
	regex = {
		rules: /\s*(,)\s*/g,
		selector: /^(\w+)?(#[\w\u00C0-\uFFFF\-\_]+|(\*))?((\.[\w\u00C0-\uFFFF\-_]+)*)?((\[\w+\s*(\^|\$|\*|\||~)?(=\s*([\w\u00C0-\uFFFF\s\-\_\.]+|"[^"]*"|'[^']*'))?\]+)*)?(((:\w+[\w\-]*)(\((odd|even|\-?\d*n?((\+|\-)\d+)?|[\w\u00C0-\uFFFF\-_\.]+|"[^"]*"|'[^']*'|((\w*\.[\w\u00C0-\uFFFF\-_]+)*)?|(\[#?\w+(\^|\$|\*|\||~)?=?[\w\u00C0-\uFFFF\s\-\_\.\'\"]+\]+)|(:\w+[\w\-]*\(.+\)))\))?)*)?(>|\+|~)?/,
		selectorSplit: /(?:\[.*\]|\(.*\)|[^\s\+>~\[\(])+|[\+>~]/g,
		id: /^#([\w\u00C0-\uFFFF\-\_]+)$/,
		tag: /^(\w+)/,
		relation: /^(>|\+|~)$/,
		pseudo: /^:(\w[\w\-]*)(\((.+)\))?$/,
		pseudos: /:(\w[\w\-]*)(\((.+)\))?/g,
		attribs: /\[(\w+)\s*(\^|\$|\*|\||~)?(=)?\s*([^\[\]]*|"[^\"]*"|'[^\']*')?\](?=$|\[|\:|\s)/g,
		classes: /\.([\w\u00C0-\uFFFF\-_]+)/g,
		quoted: /^["'](.*)["']$/,
		nth: /^((odd|even)|([1-9]\d*)|((([1-9]\d*)?)n([\+\-]\d+)?)|(\-(([1-9]\d*)?)n\+(\d+)))$/,
		special: /(:check|:enabl|\bselect)ed\b/
	},
	navigate = function (node, direction, checkTagName) {
		var oldName = node.tagName;
		while ((node = node[direction + "Sibling"]) && (node.nodeType !== 1 || (checkTagName? node.tagName !== oldName : node.tagName === "!"))) {}
		return node;
	},
	def = function (obj) {
		return typeof obj !== "undefined";
	},
	sortDocumentOrder = function (elmArray) {
		return (sortDocumentOrder = elmArray[0].compareDocumentPosition? function (elmArray) { return elmArray.sort( function (a, b) { return 3 - (a.compareDocumentPosition(b) & 6); } ); } :
			isIE? function (elmArray) { return elmArray.sort( function (a, b) { return a.sourceIndex - b.sourceIndex; } ); } :
			function (elmArray) { return elmArray.sort( function (a, b) {
				var range1 = document.createRange(), range2 = document.createRange();
				range1.setStart(a, 0);
				range1.setEnd(a, 0);
				range2.setStart(b, 0);
				range2.setEnd(b, 0);
				return range1.compareBoundaryPoints(Range.START_TO_END, range2);
			} ); })(elmArray);
	};
	var pushAll = function (set1, set2) {
		set1.push.apply(set1, slice.apply(set2));
		return set1;
	};
	if (isIE) {
		pushAll = function (set1, set2) {
			if (set2.slice) {
				return set1.concat(set2);
			}
			var i=0, item;
			while ((item = set2[i++])) {
				set1[set1.length] = item;
			}
			return set1;
		};
	}
	return {
		isIE : isIE,
		camel : camel,
		def : def,
		allMethods : [],
		publicMethods : [
			"prev",
			"next",
			"hasChild",
			"cssSelect",
			"elmsByClass",
			"elmsByAttribute",
			"elmsByTag"
		],

		initCore : function () {
			this.applyMethod.call(window, "$", this.$);
			this.applyMethod.call(window, "$$", this.$$);
			window.DOMAssistant = this;
			if (isIE) {
				HTMLArray = Array;
			}
			HTMLArray.prototype = [];
			(function (H) {
				H.each = function (fn, context) {
					for (var i=0, il=this.length; i<il; i++) {
						if (fn.call(context || this[i], this[i], i, this) === false) {
							break;
						}
					}
					return this;
				};
				H.first = function () {
					return def(this[0])? DOMAssistant.addMethodsToElm(this[0]) : null;
				};
				H.end = function () {
					return this.previousSet;
				};
				H.indexOf = H.indexOf || function (elm) {
					for (var i=0, il=this.length; i<il; i++) {
						if (i in this && this[i] === elm) {
							return i;
						}
					}
					return -1;
				};
				H.map = function (fn, context) {
					var res = [];
					for (var i=0, il=this.length; i<il; i++) {
						if (i in this) {
							res[i] = fn.call(context || this[i], this[i], i, this);
						}
					}
					return res;
				};
				H.filter = function (fn, context) {
					var res = new HTMLArray();
					res.previousSet = this;
					for (var i=0, il=this.length; i<il; i++) {
						if (i in this && fn.call(context || this[i], this[i], i, this)) {
							res.push(this[i]);
						}
					}
					return res;
				};
				H.every = function (fn, context) {
					for (var i=0, il=this.length; i<il; i++) {
						if (i in this && !fn.call(context || this[i], this[i], i, this)) {
							return false;
						}
					}
					return true;
				};
				H.some = function (fn, context) {
					for (var i=0, il=this.length; i<il; i++) {
						if (i in this && fn.call(context || this[i], this[i], i, this)) {
							return true;
						}
					}
					return false;
				};
			})(HTMLArray.prototype);
			this.attach(this);
		},

		addMethods : function (name, method) {
			if (!def(this.allMethods[name])) {
				this.allMethods[name] = method;
				this.addHTMLArrayPrototype(name, method);
			}
		},

		addMethodsToElm : function (elm) {
			for (var method in this.allMethods) {
				if (def(this.allMethods[method])) {
					this.applyMethod.call(elm, method, this.allMethods[method]);
				}
			}
			return elm;
		},

		applyMethod : function (method, func) {
			if (typeof this[method] !== "function") {
				this[method] = func;
			}
		},

		attach : function (plugin) {
			var publicMethods = plugin.publicMethods;
			if (!def(publicMethods)) {
				for (var method in plugin) {
					if (method !== "init" && def(plugin[method])) {
						this.addMethods(method, plugin[method]);
					}
				}
			}
			else if (publicMethods.constructor === Array) {
				for (var i=0, current; (current=publicMethods[i]); i++) {
					this.addMethods(current, plugin[current]);
				}
			}
			if (typeof plugin.init === "function") {
				plugin.init();
			}
		},

		addHTMLArrayPrototype : function (name, method) {
			HTMLArray.prototype[name] = function () {
				var elmsToReturn = new HTMLArray();
				elmsToReturn.previousSet = this;
				for (var i=0, il=this.length; i<il; i++) {
					elmsToReturn.push(method.apply(DOMAssistant.$$(this[i]), arguments));
				}
				return elmsToReturn;
			};
		},

		clearHandlers : function () {
			var children = this.all || this.getElementsByTagName("*");
			for (var i=0, child, attr; (child=children[i++]);) {
				if (child.uniqueHandlerId && (attr = child.attributes)) {
					for (var att, j=attr.length; j--;) {
						att = attr[j].nodeName.toLowerCase();
						if (typeof child[att] === "function") {
							child[att] = null;
						}
					}
				}
			}
		},

		setCache : function (cache) {
			useCache = cache;
		},

		$ : function () {
			var obj = arguments[0];
			if (arguments.length === 1 && (typeof obj === "object" || (typeof obj === "function" && !!obj.nodeName))) {
				return DOMAssistant.$$(obj);
			}
			var elm = !!obj? new HTMLArray() : null;
			for (var i=0, arg, idMatch; (arg=arguments[i]); i++) {
				if (typeof arg === "string") {
					arg = arg.replace(/^[^#]*(#)/, "$1");
					if (regex.id.test(arg)) {
						if ((idMatch = DOMAssistant.$$(arg.substr(1), false))) {
							elm.push(idMatch);
						}
					}
					else {
						var doc = (document.all || document.getElementsByTagName("*")).length;
						elm = (!document.querySelectorAll && useCache && lastCache.rule && lastCache.rule === arg && lastCache.doc === doc)? lastCache.elms : pushAll(elm, DOMAssistant.cssSelection.call(document, arg));
						lastCache = { rule: arg, elms: elm, doc: doc };
					}
				}
			}
			return elm;
		},

		$$ : function (id, addMethods) {
			var elm = (typeof id === "object" || typeof id === "function" && !!id.nodeName)? id : document.getElementById(id),
				applyMethods = addMethods || true,
				getId = function(el) { var eid = el.id; return typeof eid !== "object"? eid : el.attributes.id.nodeValue; };
			if (typeof id === "string" && elm && getId(elm) !== id) {
				elm = null;
				for (var i=0, item; (item=document.all[i]); i++) {
					if (getId(item) === id) {
						elm = item;
						break;
					}
				}
			}
			if (elm && applyMethods && !elm.next) {
				DOMAssistant.addMethodsToElm(elm);
			}
			return elm;
		},

		prev : function () {
			return DOMAssistant.$$(navigate(this, "previous"));
		},

		next : function () {
			return DOMAssistant.$$(navigate(this, "next"));
		},

		hasChild: function (elm) {
			return this === document || this !== elm && (this.contains? this.contains(elm) : !!(this.compareDocumentPosition(elm) & 16));
		},

		getSequence : function (expression) {
			var start, add = 2, max = -1, modVal = -1,
				pseudoVal = regex.nth.exec(expression.replace(/^0n\+/, "").replace(/^2n$/, "even").replace(/^2n+1$/, "odd"));
			if (!pseudoVal) {
				return null;
			}
			if (pseudoVal[2]) {	// odd or even
				start = (pseudoVal[2] === "odd")? 1 : 2;
				modVal = (start === 1)? 1 : 0;
			}
			else if (pseudoVal[3]) {	// single digit
				start = max = parseInt(pseudoVal[3], 10);
				add = 0;
			}
			else if (pseudoVal[4]) {	// an+b
				add = pseudoVal[6]? parseInt(pseudoVal[6], 10) : 1;
				start = pseudoVal[7]? parseInt(pseudoVal[7], 10) : 0;
				while (start < 1) {
					start += add;
				}
				modVal = (start >= add)? (start - add) % add : start;
			}
			else if (pseudoVal[8]) {	// -an+b
				add = pseudoVal[10]? parseInt(pseudoVal[10], 10) : 1;
				start = max = parseInt(pseudoVal[11], 10);
				while (start > add) {
					start -= add;
				}
				modVal = (max >= add)? (max - add) % add : max;
			}
			return { start: start, add: add, max: max, modVal: modVal };
		},

		cssByDOM : function (cssRule) {
			var prevParents, currentRule, cssSelectors, childOrSiblingRef, nextTag, nextRegExp, current, previous, prevParent, notElm, addElm, iteratorNext, childElm, sequence,
				elm = new HTMLArray(), index = elm.indexOf, prevElm = [], matchingElms = [], cssRules = cssRule.replace(regex.rules, "$1").split(",");
			function clearAdded (elm) {
				elm = elm || prevElm;
				for (var n=elm.length; n--;) {
					elm[n].added = null;
					elm[n].removeAttribute("added");
				}
			}
			function clearChildElms () {
				for (var n=prevParents.length; n--;) {
					prevParents[n].childElms = null;
				}
			}
			function subtractArray (arr1, arr2) {
				for (var i=0, src1; (src1=arr1[i]); i++) {
					var found = false;
					for (var j=0, src2; (src2=arr2[j]); j++) {
						if (src2 === src1) {
							found = true;
							arr2.splice(j, 1);
							break;
						}
					}
					if (found) {
						arr1.splice(i--, 1);
					}
				}
				return arr1;
			}
			function getAttr (elm, attr) {
				return (isIE || regex.special.test(attr))? elm[camel[attr.toLowerCase()] || attr] : elm.getAttribute(attr, 2);
			}
			function attrToRegExp (attrVal, substrOperator) {
				attrVal = attrVal? attrVal.replace(regex.quoted, "$1").replace(/(\.|\[|\])/g, "\\$1") : null;
				return {
					"^": "^" + attrVal,
					"$": attrVal + "$",
					"*": attrVal,
					"|": "^" + attrVal + "(\\-\\w+)*$",
					"~": "\\b" + attrVal + "\\b"
				}[substrOperator] || (attrVal !== null? "^" + attrVal + "$" : attrVal);
			}
			function getTags (tag, context) {
				return isIE5? (tag === "*"? context.all : context.all.tags(tag)) : context.getElementsByTagName(tag);
			}
			function getElementsByTagName (tag, parent) {
				tag = tag || "*";
				parent = parent || document;
				return (parent === document || parent.lastModified)? tagCache[tag] || (tagCache[tag] = getTags(tag, document)) : getTags(tag, parent);
			}
			function getElementsByPseudo (previousMatch, pseudoClass, pseudoValue) {
				prevParents = [];
				var pseudo = pseudoClass.split("-"), matchingElms = [], idx = 0, checkNodeName = /\-of\-type$/.test(pseudoClass), recur,
				match = {
					first: function(el) { return !navigate(el, "previous", checkNodeName); },
					last: function(el) { return !navigate(el, "next", checkNodeName); },
					empty: function(el) { return !el.firstChild; },
					enabled: function(el) { return !el.disabled && el.type !== "hidden"; },
					disabled: function(el) { return el.disabled; },
					checked: function(el) { return el.checked; },
					contains: function(el) { return (el.innerText || el.textContent || "").indexOf(pseudoValue.replace(regex.quoted, "$1")) > -1; },
					other: function(el) { return getAttr(el, pseudoClass) === pseudoValue; }
				};
				function basicMatch(key) {
					while ((previous=previousMatch[idx++])) {
						if (match[key](previous)) {
							matchingElms[matchingElms.length] = previous;
						}
					}
					return matchingElms;
				}
				var word = pseudo[0] || null;
				if (word && match[word]) {
					return basicMatch(word);
				}
				switch (word) {
					case "only":
						var kParent;
						while ((previous=previousMatch[idx++])) {
							prevParent = previous.parentNode;
							if (prevParent !== kParent) {
								if (match.first(previous) && match.last(previous)) {
									matchingElms[matchingElms.length] = previous;
								}
								kParent = prevParent;
							}
						}
						break;
					case "nth":
						if (/^n$/.test(pseudoValue)) {
							matchingElms = previousMatch;
						}
						else {
							var direction = (pseudo[1] === "last")? ["lastChild", "previousSibling"] : ["firstChild", "nextSibling"];
							sequence = DOMAssistant.getSequence(pseudoValue);
							if (sequence) {
								while ((previous=previousMatch[idx++])) {
									prevParent = previous.parentNode;
									if (!prevParent.childElms) {
										var childCount = 0, p = previous.nodeName;
										iteratorNext = sequence.start;
										childElm = prevParent[direction[0]];
										while (childElm && (sequence.max < 0 || iteratorNext <= sequence.max)) {
											var c = childElm.nodeName;
											if ((checkNodeName && c === p) || (!checkNodeName && childElm.nodeType === 1 && c !== "!")) {
												if (++childCount === iteratorNext) {
													if (c === p) { matchingElms[matchingElms.length] = childElm; }
													iteratorNext += sequence.add;
												}
											}
											childElm = childElm[direction[1]];
										}
										prevParent.childElms = true;
										prevParents[prevParents.length] = prevParent;
									}
								}
								clearChildElms();
							}
						}
						break;
					case "target":
						var hash = document.location.hash.slice(1);
						if (hash) {
							while ((previous=previousMatch[idx++])) {
								if (getAttr(previous, "name") === hash || getAttr(previous, "id") === hash) {
									matchingElms[matchingElms.length] = previous;
									break;
								}
							}
						}
						break;
					case "not":
						if ((recur = regex.pseudo.exec(pseudoValue))) {
							matchingElms = subtractArray(previousMatch, getElementsByPseudo(previousMatch, recur[1]? recur[1].toLowerCase() : null, recur[3] || null));
						}
						else {
							for (var re in regex) {
								if (regex[re].lastIndex) {
									regex[re].lastIndex = 0;
								}
							}
							pseudoValue = pseudoValue.replace(regex.id, "[id=$1]");
							var notTag = regex.tag.exec(pseudoValue);
							var notClass = regex.classes.exec(pseudoValue);
							var notAttr = regex.attribs.exec(pseudoValue);
							var notRegExp = new RegExp(notAttr? attrToRegExp(notAttr[4], notAttr[2]) : "(^|\\s)" + (notTag? notTag[1] : notClass? notClass[1] : "") + "(\\s|$)", "i");
							while ((notElm=previousMatch[idx++])) {
								addElm = null;
								if (notTag && !notRegExp.test(notElm.nodeName) || notClass && !notRegExp.test(notElm.className)) {
									addElm = notElm;
								}
								else if (notAttr) {
									var att = getAttr(notElm, notAttr[1]);
									if (!def(att) || att === false || typeof att === "string" && !notRegExp.test(att)) {
										addElm = notElm;
									}
								}
								if (addElm && !addElm.added) {
									addElm.added = true;
									matchingElms[matchingElms.length] = addElm;
								}
							}
						}
						break;
					default: return basicMatch("other");
				}
				return matchingElms;
			}
			function pushUnique(set1, set2) {
				var i=0, s=set1, item;
				while ((item = set2[i++])) {
					if (!s.length || s.indexOf(item) < 0) {
						set1.push(item);
					}
				}
				return set1;
			}
			function notComment() {
				return this.tagName !== "!";
			}
			for (var a=0, tagBin=[]; (currentRule=cssRules[a]); a++) {
				if (!(cssSelectors = currentRule.match(regex.selectorSplit)) || a && index.call(cssRules.slice(0, a), currentRule) > -1) { continue; }
				prevElm = [this];
				for (var i=0, rule; (rule=cssSelectors[i]); i++) {
					matchingElms = [];
					if (i > 0 && regex.relation.test(rule)) {
						if ((childOrSiblingRef = regex.relation.exec(rule))) {
							var idElm = null, nextWord = cssSelectors[i+1];
							if ((nextTag = regex.tag.exec(nextWord))) {
								nextTag = nextTag[1];
								nextRegExp = new RegExp("(^|\\s)" + nextTag + "(\\s|$)", "i");
							}
							else if (regex.id.test(nextWord)) {
								idElm = DOMAssistant.$(nextWord) || null;
							}
							for (var j=0, prevRef; (prevRef=prevElm[j]); j++) {
								switch (childOrSiblingRef[0]) {
									case ">":
										var children = idElm || getElementsByTagName(nextTag, prevRef);
										for (var k=0, child; (child=children[k]); k++) {
											if (child.parentNode === prevRef) {
												matchingElms[matchingElms.length] = child;
											}
										}
										break;
									case "+":
										if ((prevRef = navigate(prevRef, "next"))) {
											if ((idElm && idElm[0] === prevRef) || (!idElm && (!nextTag || nextRegExp.test(prevRef.nodeName)))) {
												matchingElms[matchingElms.length] = prevRef;
											}
										}
										break;
									case "~":
										while ((prevRef = prevRef.nextSibling) && !prevRef.added) {
											if ((idElm && idElm[0] === prevRef) || (!idElm && (!nextTag || nextRegExp.test(prevRef.nodeName)))) {
												prevRef.added = true;
												matchingElms[matchingElms.length] = prevRef;
											}
										}
										break;
								}
							}
							prevElm = matchingElms;
							clearAdded();
							rule = cssSelectors[++i];
							if (/^\w+$/.test(rule) || regex.id.test(rule)) {
								continue;
							}
							prevElm.skipTag = true;
						}
					}
					var cssSelector = regex.selector.exec(rule),
					splitRule = {
						tag : (!cssSelector[1] || cssSelector[3] === "*")? "*" : cssSelector[1],
						id : (cssSelector[3] !== "*")? cssSelector[2] : null,
						allClasses : cssSelector[4],
						allAttr : cssSelector[6],
						allPseudos : cssSelector[11]
					};
					if (splitRule.id) {
						var u = 0, DOMElm = document.getElementById(splitRule.id.slice(1));
						if (DOMElm) {
							while (prevElm[u] && !DOMAssistant.hasChild.call(prevElm[u], DOMElm)) { u++; }
							matchingElms = (u < prevElm.length && (splitRule.tag === "*" || splitRule.tag === DOMElm.tagName.toLowerCase()))? [DOMElm] : [];
						}
						prevElm = matchingElms;
					}
					else if (splitRule.tag && !prevElm.skipTag) {
						if (i===0 && !matchingElms.length && prevElm.length === 1) {
							prevElm = matchingElms = pushAll([], getElementsByTagName(splitRule.tag, prevElm[0]));
						}
						else {
							for (var l=0, ll=prevElm.length, tagCollectionMatches, tagMatch; l<ll; l++) {
								tagCollectionMatches = getElementsByTagName(splitRule.tag, prevElm[l]);
								for (var m=0; (tagMatch=tagCollectionMatches[m]); m++) {
									if (!tagMatch.added) {
										tagMatch.added = true;
										matchingElms[matchingElms.length] = tagMatch;
									}
								}
							}
							prevElm = matchingElms;
							clearAdded();
						}
					}
					if (!matchingElms.length) {
						break;
					}
					prevElm.skipTag = false;
					if (splitRule.allClasses) {
						var n = 0, matchingClassElms = [], allClasses = splitRule.allClasses.split(".").slice(1);
						while ((current = prevElm[n++])) {
							var matchCls = true, elmClass = current.className;
							if (elmClass && elmClass.length) {
								elmClass = elmClass.split(" ");
								for (var o=allClasses.length; o--;) {
									if (elmClass.indexOf(allClasses[o]) < 0) {
										matchCls = false;
										break;
									}
								}
								if (matchCls) {
									matchingClassElms[matchingClassElms.length] = current;
								}
							}
						}
						prevElm = matchingElms = matchingClassElms;
					}
					if (splitRule.allAttr) {
						var r = 0, regExpAttributes = [], matchingAttributeElms = [], allAttr = splitRule.allAttr.match(regex.attribs);
						for (var q=0, ql=allAttr.length, attributeMatch, attrVal; q<ql; q++) {
							regex.attribs.lastIndex = 0;
							attributeMatch = regex.attribs.exec(allAttr[q]);
							attrVal = attrToRegExp(attributeMatch[4], attributeMatch[2] || null);
							regExpAttributes[q] = [(attrVal? new RegExp(attrVal) : null), attributeMatch[1]];
						}
						while ((current = matchingElms[r++])) {
							for (var s=0, sl=regExpAttributes.length; s<sl; s++) {
								var matchAttr = true, attributeRegExp = regExpAttributes[s][0], currentAttr = getAttr(current, regExpAttributes[s][1]);
								if (!attributeRegExp && currentAttr === true) { continue; }
								if ((!attributeRegExp && (!currentAttr || typeof currentAttr !== "string" || !currentAttr.length)) || (!!attributeRegExp && !attributeRegExp.test(currentAttr))) {
									matchAttr = false;
									break;
								}
							}
							if (matchAttr) {
								matchingAttributeElms[matchingAttributeElms.length] = current;
							}
						}
						prevElm = matchingElms = matchingAttributeElms;
					}
					if (splitRule.allPseudos) {
						var allPseudos = splitRule.allPseudos.match(regex.pseudos);
						for (var t=0, tl=allPseudos.length; t<tl; t++) {
							regex.pseudos.lastIndex = 0;
							var pseudo = regex.pseudos.exec(allPseudos[t]);
							var pseudoClass = pseudo[1]? pseudo[1].toLowerCase() : null;
							var pseudoValue = pseudo[3] || null;
							matchingElms = getElementsByPseudo(matchingElms, pseudoClass, pseudoValue);
							clearAdded(matchingElms);
						}
						prevElm = matchingElms;
					}
				}
				elm = ((tagBin.length && (splitRule.tag === "*" || index.call(tagBin, splitRule.tag) >= 0 || index.call(tagBin, "*") >= 0))? pushUnique : pushAll)(elm, prevElm);
				tagBin.push(splitRule.tag);
				if (isIE && /\*$/.test(currentRule)) { elm = elm.filter(notComment); }
			}
			return (elm.length && cssRules.length > 1)? sortDocumentOrder(elm) : elm;
		},

		cssByXpath : function (cssRule) {
			var ns = { xhtml: "http://www.w3.org/1999/xhtml" },
				prefix = (document.documentElement.namespaceURI === ns.xhtml)? "xhtml:" : "",
				nsResolver = function lookupNamespaceURI (prefix) {
					return ns[prefix] || null;
				};
			DOMAssistant.cssByXpath = function (cssRule) {
				var currentRule, cssSelectors, xPathExpression, cssSelector, splitRule, sequence,
					elm = new HTMLArray(), cssRules = cssRule.replace(regex.rules, "$1").split(",");
				function attrToXPath (wrap) {
					var pre = wrap? "[" : "", post = wrap? "]" : "";
					return function (match, p1, p2, p3, p4) {
						p4 = (p4 || "").replace(regex.quoted, "$1");
						return pre + ({
							"^": "starts-with(@" + p1 + ", \"" + p4 + "\")",
							"$": "substring(@" + p1 + ", (string-length(@" + p1 + ") - " + (p4.length - 1) + "), " + p4.length + ") = \"" + p4 + "\"",
							"*": "contains(concat(\" \", @" + p1 + ", \" \"), \"" + p4 + "\")",
							"|": "@" + p1 + "=\"" + p4 + "\" or starts-with(@" + p1 + ", \"" + p4 + "-\")",
							"~": "contains(concat(\" \", @" + p1 + ", \" \"), \" " + p4 + " \")"
						}[p2] || ("@" + p1 + (p3? "=\"" + p4 + "\"" : ""))) + post;
					};
				}
				function pseudoToXPath (tag, pseudoClass, pseudoValue) {
					tag = /\-child$/.test(pseudoClass)? "*" : tag;
					var pseudo = pseudoClass.split("-"), position = ((pseudo[1] === "last")? "(count(following-sibling::" : "(count(preceding-sibling::") + tag + ") + 1)", recur, hash;
					switch (pseudo[0]) {
						case "nth": return (pseudoValue !== "n" && (sequence = DOMAssistant.getSequence(pseudoValue)))? ((sequence.start === sequence.max)? position + " = " + sequence.start : position + " mod " + sequence.add + " = " + sequence.modVal + ((sequence.start > 1)? " and " + position + " >= " + sequence.start : "") + ((sequence.max > 0)? " and " + position + " <= " + sequence.max: "")) : "";
						case "not": return "not(" + ((recur = regex.pseudo.exec(pseudoValue))? pseudoToXPath(tag, recur[1]? recur[1].toLowerCase() : null, recur[3] || null) : pseudoValue.replace(regex.id, "[id=$1]").replace(regex.tag, "self::$1").replace(regex.classes, "contains(concat(\" \", @class, \" \"), \" $1 \")").replace(regex.attribs, attrToXPath())) + ")";
						case "first": return "not(preceding-sibling::" + tag + ")";
						case "last": return "not(following-sibling::" + tag + ")";
						case "only": return "not(preceding-sibling::" + tag + " or following-sibling::" + tag + ")";
						case "empty": return "count(child::*) = 0 and string-length(text()) = 0";
						case "contains": return "contains(., \"" + pseudoValue.replace(regex.quoted, "$1") + "\")";
						case "enabled": return "not(@disabled) and not(@type=\"hidden\")";
						case "disabled": return "@disabled";
						case "target": return "@name=\"" + (hash = document.location.hash.slice(1)) + "\" or @id=\"" + hash + "\"";
						default: return "@" + pseudoClass + "=\"" + pseudoValue + "\"";
					}
				}
				for (var i=0; (currentRule=cssRules[i]); i++) {
					if (!(cssSelectors = currentRule.match(regex.selectorSplit)) || i && elm.indexOf.call(cssRules.slice(0, i), currentRule) > -1) { continue; }
					xPathExpression = xPathExpression? xPathExpression + " | ." : ".";
					for (var j=0, jl=cssSelectors.length; j<jl; j++) {
						cssSelector = regex.selector.exec(cssSelectors[j]);
						splitRule = {
							tag: prefix + ((!cssSelector[1] || cssSelector[3] === "*")? "*" : cssSelector[1]),
							id: (cssSelector[3] !== "*")? cssSelector[2] : null,
							allClasses: cssSelector[4],
							allAttr: cssSelector[6],
							allPseudos: cssSelector[11],
							tagRelation: cssSelector[23]
						};
						xPathExpression +=
							(splitRule.tagRelation? ({ ">": "/", "+": "/following-sibling::*[1]/self::", "~": "/following-sibling::" }[splitRule.tagRelation] || "") : ((j > 0 && regex.relation.test(cssSelectors[j-1]))? splitRule.tag : ("//" + splitRule.tag))) +
							(splitRule.id || "").replace(regex.id, "[@id = \"$1\"]") +
							(splitRule.allClasses || "").replace(regex.classes, "[contains(concat(\" \", @class, \" \"), \" $1 \")]") +
							(splitRule.allAttr || "").replace(regex.attribs, attrToXPath(true));
						if (splitRule.allPseudos) {
							var allPseudos = splitRule.allPseudos.match(regex.pseudos);
							for (var k=0, kl=allPseudos.length; k<kl; k++) {
								regex.pseudos.lastIndex = 0;
								var pseudo = regex.pseudos.exec(allPseudos[k]),
									pseudoClass = pseudo[1]? pseudo[1].toLowerCase() : null,
									pseudoValue = pseudo[3] || null,
									xpath = pseudoToXPath(splitRule.tag, pseudoClass, pseudoValue);
								if (xpath.length) {
									xPathExpression += "[" + xpath + "]";
								}
							}
						}
					}
				}
				try {
					var xPathNodes = document.evaluate(xPathExpression, this, nsResolver, 7, null), node, p=0;
					while ((node = xPathNodes.snapshotItem(p++))) { elm.push(node); }
				} catch (e) {}
				return elm;
			};
			return DOMAssistant.cssByXpath.call(this, cssRule);
		},

		cssSelection : function (cssRule) {
			if (!cssRule) { return null; }
			var special = regex.special.test(cssRule);
			try {
				if (document.querySelectorAll && !special) {
					return pushAll(new HTMLArray(), this.querySelectorAll(cssRule));
				}
			} catch (e) {}
			return ((document.evaluate && !special)? DOMAssistant.cssByXpath : DOMAssistant.cssByDOM).call(this, cssRule);
		},

		cssSelect : function (cssRule) {
			return DOMAssistant.cssSelection.call(this, cssRule);
		},

		elmsByClass : function (className, tag) {
			var cssRule = (tag || "") + "." + className;
			return DOMAssistant.cssSelection.call(this, cssRule);
		},

		elmsByAttribute : function (attr, attrVal, tag, substrMatchSelector) {
			var cssRule = (tag || "") + "[" + attr + ((attrVal && attrVal !== "*")? ((substrMatchSelector || "") + "=" + attrVal + "]") : "]");
			return DOMAssistant.cssSelection.call(this, cssRule);
		},

		elmsByTag : function (tag) {
			return DOMAssistant.cssSelection.call(this, tag);
		}
	};
}();
DOMAssistant.initCore();
DOMAssistant.Storage = function () {
	var uniqueId = 1, data = [], expando = "_da" + +new Date();
	return {
		retrieve : function (key) {
			if (!DOMAssistant.def(key)) {
				return this[expando] || (this[expando] = uniqueId++);
			}
			if (!this[expando] || !data[this[expando]]) { return; }
			return data[this[expando]][key];
		},

		store : function (key, val) {
			var uid = this[expando] || (this[expando] = uniqueId++);
			data[uid] = data[uid] || {};
			if (typeof key === "object") {
				for (var i in key) {
					if (typeof i === "string") {
						data[uid][i] = key[i];
					}
				}
			}
			else {
				data[uid][key] = val;
			}
			return this;
		},

		unstore : function (key) {
			var uid = this[expando] || (this[expando] = uniqueId++);
			if (data[uid]) {
				if (DOMAssistant.def(key)) {
					delete data[uid][key];
				}
				else {
					data[uid] = null;
				}
			}
		}
	};
}();
DOMAssistant.attach(DOMAssistant.Storage);
DOMAssistant.CSS = function () {
	var def = DOMAssistant.def;
	return {
		addClass : function (className) {
			if (!this.hasClass(className)) {
				var currentClass = this.className;
				this.className = currentClass + (currentClass.length? " " : "") + className;
			}
			return this;
		},

		removeClass : function (className) {
			return this.replaceClass(className);
		},

		replaceClass : function (className, newClass) {
			var classToRemove = new RegExp(("(^|\\s)" + className + "(\\s|$)"), "i");
			this.className = this.className.replace(classToRemove, function (match, p1, p2) {
				return newClass? (p1 + newClass + p2) : " ";
			}).replace(/^\s+|\s+$/g, "");
			return this;
		},

		hasClass : function (className) {
			return (" " + this.className + " ").indexOf(" " + className + " ") > -1;
		},

		setStyle : function (style, value) {
			var css = this.style;
			if ("filters" in this && (typeof style === "string"? /opacity/i.test(style) : def(style.opacity))) {
				css.zoom = 1;
				css.filter = (css.filter || "").replace(/alpha\([^)]*\)/, "") + "alpha(opacity=" + (def(style.opacity)? style.opacity : value) * 100 + ")";
			}
			if (def(css.cssText)) {
				var styleToSet = css.cssText;
				if (typeof style === "object") {
					for (var i in style) {
						if (typeof i === "string") {
							styleToSet += ";" + i + ":" + style[i];
						}
					}
				}
				else {
					styleToSet += ";" + style + ":" + value;
				}
				css.cssText = styleToSet;
			}
			return this;
		},

		getStyle : function (cssRule) {
			var val = "", f;
			cssRule = cssRule.toLowerCase();
			if (document.defaultView && document.defaultView.getComputedStyle) {
				val = document.defaultView.getComputedStyle(this, "").getPropertyValue(cssRule);
			}
			else if (this.currentStyle) {
				if ("filters" in this && cssRule === "opacity") {
					val = (f = this.style.filter || this.currentStyle.filter) && f.indexOf("opacity=") >= 0? parseFloat(f.match(/opacity=([^)]*)/)[1]) / 100 : 1;
				}
				else {
					cssRule = cssRule.replace(/^float$/, "styleFloat").replace(/\-(\w)/g, function (match, p1) {
						return p1.toUpperCase();
					});
					val = this.currentStyle[cssRule];
				}
				if (val === "auto" && /^(width|height)$/.test(cssRule) && this.currentStyle.display !== "none") {
					val = this["offset" + cssRule.charAt(0).toUpperCase() + cssRule.substr(1)] + "px";
				}
			}
			return val;
		}
	};
}();
DOMAssistant.attach(DOMAssistant.CSS);
DOMAssistant.Content = function () {
	var $$ = DOMAssistant.$$;
	return {
		init : function () {
			DOMAssistant.setCache(false);
		},

		create : function (name, attr, append, content) {
			var elm = $$(document.createElement(name));
			if (attr) {
				elm = elm.setAttributes(attr);
			}
			if (DOMAssistant.def(content)) {
				elm.addContent(content);
			}
			if (append) {
				this.appendChild(elm);
			}
			return elm;
		},

		setAttributes : function (attr) {
			if (DOMAssistant.isIE) {
				var setAttr = function (elm, att, val) {
					var attLower = att.toLowerCase();
					switch (attLower) {
						case "name":
						case "type":
							return $$(document.createElement(elm.outerHTML.replace(new RegExp(attLower + "=[a-zA-Z]+"), " ").replace(">", " " + attLower + "=" + val + ">")));
						case "style":
							elm.style.cssText = val;
							return elm;
						default:
							elm[DOMAssistant.camel[attLower] || att] = val;
							return elm;
					}
				};
				DOMAssistant.Content.setAttributes = function (attr) {
					var elem = this;
					var parent = this.parentNode;
					for (var i in attr) {
						if (typeof attr[i] === "string" || typeof attr[i] === "number") {
							var newElem = setAttr(elem, i, attr[i]);
							if (parent && /(name|type)/i.test(i)) {
								if (elem.innerHTML) {
									newElem.innerHTML = elem.innerHTML;
								}
								parent.replaceChild(newElem, elem);
							}
							elem = newElem;
						}
					}
					return elem;
				};
			}
			else {
				DOMAssistant.Content.setAttributes = function (attr) {
					for (var i in attr) {
						if (/class/i.test(i)) {
							this.className = attr[i];
						}
						else {
							this.setAttribute(i, attr[i]);
						}
					}
					return this;
				};
			}
			return DOMAssistant.Content.setAttributes.call(this, attr);
		},

		addContent : function (content) {
			var type = typeof content;
			if (type === "string" || type === "number") {
				if (!this.firstChild) {
					this.innerHTML = content;
				}
				else {
					var tmp = document.createElement("div");
					tmp.innerHTML = content;
					for (var i=tmp.childNodes.length-1, last=null; i>=0; i--) {
						last = this.insertBefore(tmp.childNodes[i], last);
					}
				}
			}
			else if (type === "object" || (type === "function" && !!content.nodeName)) {
				this.appendChild(content);
			}
			return this;
		},

		replaceContent : function (content) {
			if (!!this.firstChild) {
				DOMAssistant.clearHandlers.apply(this);
				this.innerHTML = "";
			}
			return this.addContent(content);
		},

		replace : function (content, returnNew) {
			var type = typeof content;
			if (type === "string" || type === "number") {
				var parent = this.parentNode;
				var tmp = DOMAssistant.Content.create.call(parent, "div", null, false, content);
				for (var i=tmp.childNodes.length; i--;) {
					parent.insertBefore(tmp.childNodes[i], this.nextSibling);
				}
				content = this.nextSibling;
				parent.removeChild(this);
			}
			else if (type === "object" || (type === "function" && !!content.nodeName)) {
				this.parentNode.replaceChild(content, this);
			}
			return returnNew? content : this;
		},

		remove : function () {
			this.parentNode.removeChild(this);
			return null;
		}
	};
}();
DOMAssistant.attach(DOMAssistant.Content);


/*
	Class: Legato_DOM_Library
	Provides a plugin to DOMAssistant to allow extra features for working with the DOM.
*/
Legato_DOM_Library = {};

Legato_DOM_Library.DOMAssistantPlugIn = function ()
{

	return {

		/*
			Function: dimensions()
			Sets/gets the dimension's of the element.
			If no dimensions passed in, will return the element's dimensions.

			Syntax:
				*Getting Dimensions*

				array dimensions()

				*Setting Dimensions*

				object dimensions( int width, int height )

			Parameters:
				*Setting Dimensions*

				int width - The new width you'd like the element to have. Pass in null if you would like the width to stay the same.
				int height - The new height you'd like the element to have. Pass in null if you would like the height to stay the same.

			Returns:
				*Getting Dimensions*

				Returns an array of the dimensions, with the first item being the width and the second item being the height.

				*Setting Dimensions*

				Returns the element the dimensions were set on.

			Examples:
			(begin code)
				var dimensions = $$( 'container' ).dimensions();
				alert( dimensions[0] )  // Show the width of the container.
			(end)

			(begin code)
				// Set the height of the container to 300 pixels.
				$$( 'container' ).dimensions( null, 300 );
			(end)
		*/
		dimensions: function()
		{

			if ( this.window == window )
			{

				var width = window.innerWidth || (window.document.documentElement.clientWidth || window.document.body.clientWidth);
		        var height = window.innerHeight || (window.document.documentElement.clientHeight || window.document.body.clientHeight);

		        return [ width, height ];

			}
			else if ( arguments.length == 0 )
			{

				return [ this.offsetWidth, this.offsetHeight ];

			}
			else
			{

				if ( arguments[0] !== null ) this.setStyle( 'width', arguments[0] + 'px' );
				if ( arguments[1] !== null ) this.setStyle( 'height', arguments[1] + 'px' );
				return this;

			}

		},


		/*
			Function: position()
			Sets/gets the position of an element.
			If no position passed in, will return the current position of the element.

			Syntax:
				*Getting Position*

				array position()

				*Setting Position*

				object position( int X, int Y )

			Parameters:
				*Setting Position*

				int X - The new X value that you'd like the element to have. Pass in null if you would like the X position to stay the same.
				int Y - The new Y value that you'd like the element to have. Pass in null if you would like the Y position to stay the same.

			Returns:
				*Getting Position*

				Returns an array of the position, with the first item being the X value and the second item being the Y value.

				*Setting Position*

				Returns the element the position was set on.

			Notes:
				This function works off of the page grid and not the containing element. So, setting an X value of 50 would put the element
				50 pixels from the top of the page.

			Examples:
			(begin code)
				// Show the position of the container element.
				var pos = $$( 'container' ).position();
				alert( pos[0] + ' | ' + pos[1] );

				// Set the Y position to 50 pixels.
				$$( 'container' ).position( null, 50 );
			(end)
		*/
		position: function()
		{

			if ( arguments.length == 0 )
			{

				var offsetLeft = offsetTop = 0;
				var elem = this;

				while ( elem !== null )
				{
					offsetLeft += elem.offsetLeft;
					offsetTop += elem.offsetTop;
					elem = elem.offsetParent;
				}

				return [ offsetLeft, offsetTop ];

			}
			else
			{

				// Get the positioning of this element.
				var positioning = this.getStyle( 'position' );

				// If it's statically positioned, we change it to relative positioning.
				// If it's absolute, we leave it.
                if ( positioning == 'static' )
				{
					positioning = 'relative';
                    this.setStyle( 'position', 'relative' );
                }

                // Try to get the offset value.
                var offset =
				[
                    parseInt( this.getStyle( 'left' ), 10 ),
                    parseInt( this.getStyle( 'top' ), 10 )
                ];

            	// If auto was returned, retrieve the correct offset.
                if ( isNaN( offset[0] ) )
                    offset[0] = (positioning == 'relative') ? 0 : this.offsetLeft;

                // If auto was returned, retrieve the correct offset.
                if ( isNaN( offset[1] ) )
                    offset[1] = (positioning == 'relative') ? 0 : this.offsetTop;

                // Get the page XY position of the element.
                var posXY = this.position();

                // If a new X or Y value was passed in, set it.
                if ( arguments[0] !== null ) this.setStyle( 'left', arguments[0] - posXY[0] + offset[0] + 'px' );
                if ( arguments[1] !== null ) this.setStyle( 'top', arguments[1] - posXY[1] + offset[1] + 'px' );

                return this;

			}

		},


		/*
			Function: scrollOffset()
			Sets/gets the scroll offset of an element.
			If no offset passed in, will return the current offset of the element.

			Syntax:
				*Getting Offset*

				array scrollOffset()

				*Setting Offset*

				object scrollOffset( int X, int Y )

			Parameters:
				*Setting Offset*

				int X - The new X value that you'd like the element's scroll offset to be. Pass in null if you would like the X offset to stay the same.
				int Y - The new Y value that you'd like the element's scroll offset to be. Pass in null if you would like the Y offset to stay the same.

			Returns:
				*Getting Offset*

				Returns an array of the scroll offset, with the first item being the X offset and the second item being the Y offset.

				*Setting Offset*

				Returns the element the scroll offset was set on.

			Examples:
			(begin code)
				// Show the Y offset of the container element.
				alert( $$( 'container' ).position() );

				// Set the X offset to 75 pixels.
				$$( 'container' ).scrollOffset( 75, null );
			(end)
		*/
		scrollOffset: function()
		{

			if ( this.window == window || this == document.body )
			{

				var X = Y = 0;

				if( typeof( window.pageXOffset ) == 'number' )
				{
					X = window.pageXOffset;
					Y = window.pageYOffset;
				}  // Netscape.
				else if( document.body && ( document.body.scrollLeft || document.body.scrollTop ) )
				{
					X = document.body.scrollLeft;
					Y = document.body.scrollTop;
				}  // Standards compliant.
				else if( document.documentElement && ( document.documentElement.scrollLeft || document.documentElement.scrollTop ) )
				{
					X = document.documentElement.scrollLeft;
					Y = document.documentElement.scrollTop;
				}  // IE 6 standards mode.

				return [ X, Y ];

			}
			else if ( arguments.length == 0 )
			{

				return [ this.scrollLeft, this.scrollTop ];

			}
			else
			{

				if ( arguments[0] !== null ) this.scrollLeft = arguments[0];
				if ( arguments[1] !== null ) this.scrollTop = arguments[1];
				return this;

			}

		}

	};

}();

DOMAssistant.attach( Legato_DOM_Library.DOMAssistantPlugIn );
//------------------------------------------------------------------------
// Name: Legato_Form
// Desc: A helper class to help with the PHP Form class.
//------------------------------------------------------------------------

//------------------------------------------------------------------------
// Public Member Functions
//------------------------------------------------------------------------
//------------------------------------------------------------------------
// Name: Legato_Form
// Desc: Class constructor.
//------------------------------------------------------------------------
function Legato_Form( form, options_object )
{

	this.form                  = $$( form );
	this.input_elements        = [];
	this.groups                = [];

	// Store the parameters.
	this.submit_button         = options_object.submit_button;
	this.submit_form           = (options_object.submit_form == null) ? false : true;
	this.redirect_url          = options_object.redirect_url;
	this.request_url           = options_object.request_url;
	this.modified_request_url  = '';
	this.errors                = [];

	this.processing            = false;
	
	// Add on onsubmit event for the form.
	var form = this;
	Legato_Events_Handler.addEvent( this.form, 'onsubmit', function(){ return form.validateForm(); } );

}


//----------------------------------------------------------------------
// Name: validateForm()
// Desc: Start the validation.
//----------------------------------------------------------------------
Legato_Form.prototype.validateForm = function()
{
	
	var errors = false;

	// Are we processing?
	if ( this.processing == true )
		return false;
	else
		this.processing = true;

	// Disable the submit button.
	if ( this.submit_button != null ) 
		this.submit_button.disabled = true;

	// First make sure the form is clean.
	this.cleanupForm();
	
	// If there is a request URL, send the request.
	if ( this.request_url != null )
	{
		
		var query_string = this.getQueryString();

		// Send the request.
		var form = this;
		Legato_RequestManager.makeRequest( this.modified_request_url, function( response ){ form.processResponse( response ); }, query_string );

		// Return. We will continue processing in the processResponse() function.
		return false;

	}
	
	// Finish the validation.
	return this.finishValidation();

};


//----------------------------------------------------------------------
// Name: processResponse()
// Desc: Processes the response from the XHR request.
//----------------------------------------------------------------------
Legato_Form.prototype.processResponse = function( response )
{
	
	var any_errors = false;
	
	// JSON or XML?
	if ( !response.responseXML || response.responseXML.getElementsByTagName( 'errors' ).length == 0 )
	{

		var errors = eval( '(' + response.responseText + ')' );
		
		// Any errors?
		if ( errors.length != 0 )
		{
			
			// Set errors on.
			any_errors = true;
			
			// Loop through each error.
			for ( var id in errors )
			{
				
				// Get the message.
				var message = errors[id];
	
				// Post the error.
				this.postError( id, message );
	
			}  // Next error.
			
		}
		
	}
	else
	{
		
		response = response.responseXML;
		
		// Get the errors.
		var errors = response.getElementsByTagName( 'error' );
		
		// Is there any errors?
		if ( errors.length != 0 )
		{
			
			// Set errors on.
			any_errors = true;
	
			// Loop through each error.
			for ( var i = 0; i < errors.length; i++ )
			{
	
				// Get the error details.
				var id = errors[i].getElementsByTagName( "id" );
	
				if ( id.length == 0 )
					id = null;
				else
					id = id[0].firstChild.data;
	
				// Get the message.
				var message = errors[i].getElementsByTagName( "message" )[0].firstChild.data;
	
				// Post the error.
				this.postError( id, message );
	
			}  // Next error.
			
		}
		
	}
	
	// Any errors?
	if ( !any_errors )
	{
		
		// Finish validation.
		this.finishValidation();

		// Return.
		return;
		
	}

	// Show the generic error message.
	this.postError( null, 'There were errors while processing the form. Please fix them and try submitting the form again.' );

	// Set processing to false.
	this.processing = false;

	// Enable the submit button.
	if ( this.submit_button != null ) 
		this.submit_button.disabled = false;

};


//----------------------------------------------------------------------
// Name: finishValidation()
// Desc: Finishes the validation.
//----------------------------------------------------------------------
Legato_Form.prototype.finishValidation = function()
{

	// Check for a redirect URL and if there is one redirect them.
	if ( this.redirect_url != null )
	{
		window.location = this.redirect_url;
		return true;
	}
	
	// If the submit form flag is set, submit the form.
	if ( this.submit_form )
		return true;
		
	// Set processing to false.
	this.processing = false;

	// Enable the submit button.
	if ( this.submit_button != null ) 
		this.submit_button.disabled = false;
	
	// Don't submit form.
	return false;

};


//----------------------------------------------------------------------
// Name: postError()
// Desc: Posts an error to the form with information about what went
//       wrong and why it went wrong.
// Note: If null is passed in for element_id, the error will be placed
//       at the end of the form.
//----------------------------------------------------------------------
Legato_Form.prototype.postError = function( element_id, error )
{
	
	// Create the error node.
	var error_node = $( document.body ).create( 'p', { className: 'error' }, false, error );
	
	// What type of placement?
	if ( element_id == null )
	{

		// Do we have a submit button?
		if ( this.submit_button != null )
		{
			
			// A group or not?
			if ( $( this.submit_button.parentNode.parentNode ).hasClass( 'group_elements' ) )			
				this.submit_button.parentNode.parentNode.parentNode.insertBefore( error_node, this.submit_button.parentNode.parentNode.parentNode.firstChild );
			else
				this.submit_button.parentNode.insertBefore( error_node, this.submit_button.parentNode.firstChild );
		}
		else
			this.form.addContent( error_node );

	}  // End if general error message.
	else
	{

		var html_element = this.form.cssSelect( '#' + element_id )[0];

		// Group or normal?
		if ( $( html_element.parentNode.parentNode ).hasClass( 'group' ) )
			html_element.parentNode.parentNode.insertBefore( error_node, html_element.parentNode.parentNode.firstChild );
		else
			html_element.parentNode.insertBefore( error_node, html_element.parentNode.firstChild );

	}  // End if normal/group error message.
	
};


//----------------------------------------------------------------------
// Name: cleanupForm()
// Desc: Cleans up the form to make it ready for form validation.
//----------------------------------------------------------------------
Legato_Form.prototype.cleanupForm = function()
{

	// Get all the forms error's.
	var errors = this.form.cssSelect( 'p.error' );

	// Loop through each error.
	// We get the length before hand, because we take elements away from the
	// array in the loop.
	for ( var i = 0; i < errors.length; i++ )
	{

		// Get the element. We retrieve the 0th element because we remove
		// the child below, and the next one will fall in this place.
		var error_element = errors[i];

		// Is this an error element?
		error_element.remove();  // Remove the element.


	}  // Next error node.

};


//----------------------------------------------------------------------
// Name: getQueryString()
// Desc: Concatenates all the managed input elements in to a query
//       string suitable for appending to a URL.
//----------------------------------------------------------------------
Legato_Form.prototype.getQueryString = function()
{

	var values = Array();

	// Loop through each input element in the form.
	for ( var i = 0; i < this.form.elements.length; i++ )
	{

		var element = this.form.elements[i];

		// What type of element?
		switch ( element.type )
		{

		// Simple elements.
		case 'text':
		case 'password':
		case 'file':
		case 'textarea':
		case 'hidden':
		case 'select-one':

			values.push( (element.name + "=" + encodeURIComponent( element.value )) );
			break;

		// Checkboxes and radio buttons.
		case 'checkbox':
		case 'radio':

			// Only add if checked.
			if ( element.checked )
				values.push( (element.name + "=" + encodeURIComponent( element.value )) );

			break;

		// Select multiples.
		case 'select-multiple':

			// Loop through each option.
			for ( var n = 0; n < element.options.length; n++ )
			{

				// Only add if option is selected.
				if ( element.options[n].selected )
					values.push( (element.name + "=" + encodeURIComponent( element.options[n].value )) );

			}  // Next option.

			break;

		}  // End what type of elements.

	}  // Next input element.

	// Put the values together into a string.
	var query_string = values.join( "&" );

	// Get the pieces of the request URL.
	var pieces = this.request_url.match( /(\S*)\?(\S*)/ );

	// If the request_url does not have a ? in it, get rid of the query values from
	// the request URL, and add it to the query string.
	if ( pieces != null )
	{

		this.modified_request_url = pieces[1];
		query_string              = pieces[2] + "&" + query_string;

	}
	else
	{

		this.modified_request_url = this.request_url;

	}

	// Return the query string.
	return query_string;

};

