// Developed by Robert Nyman/DOMAssistant team, code/licensing: http://domassistant.googlecode.com/, documentation: http://www.domassistant.com/documentation, version 2.8.1
var DOMAssistant = function () {
	var HTMLArray = function () {
		// Constructor
	},
	w = window, _$ = w.$, _$$ = w.$$,
	isIE = /*@cc_on!@*/false,
	byTag = !!document.getElementsByTagName("*").length,
	strictElmCreation = function() {
		if (!isIE) return true;
		try {
			document.createElement("<img />");
			return false;
		} catch (e) {
			return true;
		}
	}(),
	sort, 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[-\w]*\s*([~^$*|])?(=\s*([-\w\u00C0-\uFFFF\s.]+|"[^"]*"|'[^']*'))?\]+)*)?((:\w[-\w]*(\((odd|even|\-?\d*n?([-+]\d+)?|[:#]?[-\w\u00C0-\uFFFF.]+|"[^"]*"|'[^']*'|((\w*\.[-\w\u00C0-\uFFFF]+)*)?|(\[#?\w[-\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[-\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)|[*^$]=\s*(""|'')/
	},
	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,
		strictElmCreation : strictElmCreation,
		camel : camel,
		def : def,
		allMethods : [],
		publicMethods : [
			"prev",
			"next",
			"hasChild",
			"cssSelect",
			"elmsByClass",
			"elmsByAttribute",
			"elmsByTag"
		],
		
		harmonize : function () {
			w.$ = _$;
			w.$$ = _$$;
			return this;
		},
		
		initCore : function () {
			this.applyMethod.call(w, "$", this.$);
			this.applyMethod.call(w, "$$", this.$$);
			w.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;
			};
		},
		
		cleanUp : function (elm) {
			var children = elm.all || elm.getElementsByTagName("*");
			for (var i=0, child; (child=children[i++]);) {
				if (child.hasData && child.hasData()) {
					if (child.removeEvent) { child.removeEvent(); }
					child.unstore();
				}
			}
			elm.innerHTML = "";
		},
		
		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 = def(addMethods)? 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, anyTag,
				elm = new HTMLArray(), index = elm.indexOf, prevElm = [], matchingElms = [], cssRules = cssRule.replace(regex.rules, ",").split(","), splitRule = {};
			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 hasAttr (elm, attr) {
				return elm.hasAttribute? elm.hasAttribute(attr) : (elm.attributes && elm.attributes[attr] && !/type/.test(attr))? !!elm.attributes[attr].specified : (elm.getAttribute(attr) !== null);
			}
			function getAttr (elm, attr) {
				var isSpecial = regex.special.test(attr);
				if (isIE && !isSpecial && !hasAttr(elm, attr)) { return undefined; }
				return (isIE || isSpecial)? 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 substrOperator? {
					"^": "^" + attrVal,
					"$": attrVal + "$",
					"*": attrVal,
					"|": "^" + attrVal + "(\\-\\w+)*$",
					"~": "\\b" + attrVal + "\\b"
				}[substrOperator] : (attrVal !== null? "^" + attrVal + "$" : attrVal);
			}
			function notComment(el) {
				return (el || this).tagName !== "!";
			}
			function getTags (tag, context) {
				return byTag? context.getElementsByTagName(tag) : (tag === "*"? context.all : context.all.tags(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) {
						for (var node=el.firstChild; node!==null; node=node.nextSibling) {
							if (node.nodeType === 1 && node.nodeName !== "!" || node.nodeType === 3) { return false; }
						}
						return true;
					},
					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 (notComment(previous) && 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, kTag;
						while ((previous=previousMatch[idx++])) {
							prevParent = previous.parentNode;
							var q = previous.nodeName;
							if (prevParent !== kParent || q !== kTag) {
								if (match.first(previous) && match.last(previous)) {
									matchingElms[matchingElms.length] = previous;
								}
								kParent = prevParent;
								kTag = q;
							}
						}
						break;
					case "nth":
						if (pseudoValue === "n") {
							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;
									prevParent.childElms = prevParent.childElms || {};
									var p = previous.nodeName;
									if (!prevParent.childElms[p]) {
										var childCount = 0;
										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]];
										}
										if (anyTag) { sort++; }
										prevParent.childElms[p] = 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[0] : 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;
			}
			sort = -1;
			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 ((childOrSiblingRef = regex.relation.exec(rule))) {
						var idElm = null, nextWord = cssSelectors[i+1];
						if ((nextTag = regex.tag.exec(nextWord))) {
							nextTag = nextTag[0];
							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[1] : "*",
						id : cssSelector[2],
						allClasses : cssSelector[3],
						allAttr : cssSelector[5],
						allPseudos : cssSelector[10]
					};
					anyTag = (splitRule.tag === "*");
					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 && (anyTag || 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 matchAttr, r = 0, regExpAttributes = [], matchingAttributeElms = [], allAttr = splitRule.allAttr.match(regex.attribs), emptyAttrRegExp = /^[$^]?$/;
						for (var specialStrip = /^\[(selected|readonly)(\s*=.+)?\]$/, q=0, ql=allAttr.length, attributeMatch, attrVal; q<ql; q++) {
							regex.attribs.lastIndex = 0;
							attributeMatch = regex.attribs.exec(allAttr[q].replace(specialStrip, "[$1]"));
							attrVal = attrToRegExp(attributeMatch[4], attributeMatch[2] || null);
							regExpAttributes[q] = [attrVal, attributeMatch[1]];
						}
						while ((current = matchingElms[r++])) {
							for (var s=0, sl=regExpAttributes.length; s<sl; s++) {
								var attributeRE = regExpAttributes[s][0], attributeRegExp = attributeRE? new RegExp(attributeRE) : null, currentAttr = getAttr(current, regExpAttributes[s][1]);
								matchAttr = true;
								if (!attributeRegExp && currentAttr === true) { continue; }
								if (emptyAttrRegExp.test(attributeRE) || (!attributeRegExp && typeof currentAttr !== "string") || (!!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 && (anyTag || index.call(tagBin, splitRule.tag) >= 0 || index.call(tagBin, "*") >= 0))? pushUnique : pushAll)(elm, prevElm);
				tagBin.push(splitRule.tag);
				if (isIE && anyTag) { elm = elm.filter(notComment); }
			}
			return ((elm.length > 1 && cssRules.length > 1) || sort > 0)? 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, ",").split(",");
				function attrToXPath (wrap) {
					var pre = wrap? "[" : "", post = wrap? "]" : "";
					return function (match, p1, p2, p3, p4) {
						p4 = (p4 || "").replace(regex.quoted, "$1");
						if (p1 === p4 && p1 === "readonly") { p3 = null; }
						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::$0").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 "not(child::*) and not(text())";
						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[1] : "*"),
							id: cssSelector[2],
							allClasses: cssSelector[3],
							allAttr: cssSelector[5],
							allPseudos: cssSelector[10],
							tagRelation: cssSelector[20]
						};
						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 && !/-of-type/.test(cssRule))? 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 {
		hasData : function () {
			var uid = this[expando];
			return !!uid && !!data[uid];
		},
		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;
				}
			}
			return this;
		}
	};
}();
DOMAssistant.attach(DOMAssistant.Storage);
DOMAssistant.AJAX = function () {
	var globalXMLHttp = null,
	readyState = 0,
	status = -1,
	statusText = "",
	requestPool = [],
	createAjaxObj = function (url, method, callback, addToContent) {
		var params = null;
		if (/POST/i.test(method)) {
			url = url.split("?");
			params = url[1];
			url = url[0];
		}
		return {
			url : url,
			method : method,
			callback : callback,
			params : params,
			headers : {},
			responseType : "text",
			addToContent : addToContent || false
		};
	};
	return {
		publicMethods : [
			"ajax",
			"get",
			"post",
			"load"
		],
		
		initRequest : function () {
			var XMLHttp = null;
			if (!!window.XMLHttpRequest && !DOMAssistant.isIE) {
				XMLHttp = new XMLHttpRequest();
				DOMAssistant.AJAX.initRequest = function () {
					return requestPool.length? requestPool.pop() : new XMLHttpRequest();
				};
			}
			else if (!!window.ActiveXObject) {
				var XMLHttpMS = ["Msxml2.XMLHTTP.6.0", "Msxml2.XMLHTTP.3.0", "Msxml2.XMLHTTP", "Microsoft.XMLHTTP"];
				for (var i=0; i<XMLHttpMS.length; i++) {
					try {
						XMLHttp = new window.ActiveXObject(XMLHttpMS[i]);
						DOMAssistant.AJAX.initRequest = function () {
							return requestPool.length? requestPool.pop() : new window.ActiveXObject(XMLHttpMS[i]);
						};
						break;
					}
					catch (e) {
						XMLHttp = null;
					}
				}
			}
			return XMLHttp;
		},
		
		ajax : function (ajaxObj) {
			if (!ajaxObj.noParse && ajaxObj.url && /\?/.test(ajaxObj.url) && ajaxObj.method && /POST/i.test(ajaxObj.method)) {
				var url = ajaxObj.url.split("?");
				ajaxObj.url = url[0];
				ajaxObj.params = url[1] + ((url[1].length > 0 && ajaxObj.params)? ("&" + ajaxObj.params) : "");
			}
			return DOMAssistant.AJAX.makeCall.call(this, ajaxObj);
		},
		
		get : function (url, callback, addToContent) {
			return DOMAssistant.AJAX.makeCall.call(this, createAjaxObj(url, "GET", callback, addToContent));
		},
		
		post : function (url, callback) {
			return DOMAssistant.AJAX.makeCall.call(this, createAjaxObj(url, "POST", callback));
		},
		
		load : function (url, addToContent) {
			this.get(url, DOMAssistant.AJAX.replaceWithAJAXContent, addToContent);
		},
		
		makeCall : function (ajaxObj) {
			var XMLHttp = DOMAssistant.AJAX.initRequest();
			if (XMLHttp) {
				globalXMLHttp = XMLHttp;
				(function (elm) {
					var url = ajaxObj.url,
						method = ajaxObj.method || "GET",
						callback = ajaxObj.callback,
						params = ajaxObj.params,
						headers = ajaxObj.headers,
						responseType = ajaxObj.responseType || "text",
						addToContent = ajaxObj.addToContent,
						timeout = ajaxObj.timeout || null,
						ex = ajaxObj.exception,
						timeoutId = null,
						done = false;
					XMLHttp.open(method, url, true);
					XMLHttp.setRequestHeader("AJAX", "true");
					XMLHttp.setRequestHeader("X-Requested-With", "XMLHttpRequest");
					if (method === "POST") {
						XMLHttp.setRequestHeader("Content-type", "application/x-www-form-urlencoded");
						XMLHttp.setRequestHeader("Content-length", params? params.length : 0);
						if (XMLHttp.overrideMimeType) {
							XMLHttp.setRequestHeader("Connection", "close");
						}
					}
					if (responseType === "json") {
						XMLHttp.setRequestHeader("Accept", "application/json, text/javascript, */*");
					}
					for (var i in headers) {
						if (typeof i === "string") {
							XMLHttp.setRequestHeader(i, headers[i]);
						}
					}
					if (typeof callback === "function") {
						XMLHttp.onreadystatechange = function () {
							try {
								if (XMLHttp.readyState === 4 && !done) {
									window.clearTimeout(timeoutId);
									done = true;
									status = XMLHttp.status;
									statusText = XMLHttp.statusText;
									readyState = 4;
									if ((status || location.protocol !== "file:") && (status < 200 || status >= 300)) {
										throw new Error(statusText);
									}
									var response = /xml/i.test(responseType)? XMLHttp.responseXML : XMLHttp.responseText;
									if (/json/i.test(responseType) && !!response) {
										response = (typeof JSON === "object" && typeof JSON.parse === "function")? JSON.parse(response) : eval("(" + response + ")");
									}
									globalXMLHttp = null;
									XMLHttp.onreadystatechange = function () {};
									requestPool.push(XMLHttp);
									callback.call(elm, response, addToContent);
								}
							}
							catch (e) {
								globalXMLHttp = XMLHttp = null;
								if (typeof ex === "function") {
									ex.call(elm, e);
									ex = null;
								}
							}
						};
					}
					XMLHttp.send(params);
					if (timeout) {
						timeoutId = window.setTimeout( function () {
							if (!done) {
								XMLHttp.abort();
								done = true;
								if (typeof ex === "function") {
									readyState = 0;
									status = 408;
									statusText = "Request timeout";
									globalXMLHttp = XMLHttp = null;
									ex.call(elm, new Error(statusText));
									ex = null;
								}
							}
						}, timeout);
					}
				})(this);
			}
			return this;
		},
		
		replaceWithAJAXContent : function (content, add) {
			if (add) {
				this.innerHTML += content;
			}
			else {
				DOMAssistant.cleanUp(this);
				this.innerHTML = content;
			}
		},
		
		getReadyState : function () {
			return (globalXMLHttp && DOMAssistant.def(globalXMLHttp.readyState))? globalXMLHttp.readyState : readyState;
		},
		
		getStatus : function () {
			return status;
		},
		
		getStatusText : function () {
			return statusText;
		}
	};
}();
DOMAssistant.attach(DOMAssistant.AJAX);
DOMAssistant.CSS = function () {
	var def = DOMAssistant.def,
		direct = { display: true };
	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") {
							if (direct[i]) { css[i] = style[i]; }
							styleToSet += ";" + i + ":" + style[i];
						}
					}
				}
				else {
					if (direct[style]) { css[style] = value; }
					styleToSet += ";" + style + ":" + value;
				}
				css.cssText = styleToSet;
			}
			return this;
		},

		getStyle : function (cssRule) {
			var val = "", f;
			cssRule = cssRule.toLowerCase();
			if (window.getComputedStyle) {
				val = document.defaultView.getComputedStyle(this, null).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 D$ = DOMAssistant.$$;
	return {
		init : function () {
			DOMAssistant.setCache(false);
		},

		create : function (name, attr, append, content) {
			var elm = D$(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.strictElmCreation) {
				var setAttr = function (elm, att, val) {
					var attLower = att.toLowerCase();
					switch (attLower) {
						case "name":
						case "type":
						case "multiple":
							return D$(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) {
			DOMAssistant.cleanUp(this);
			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 () {
			DOMAssistant.cleanUp(this);
			if (this.hasData()) {
				if (this.removeEvent) { this.removeEvent(); }
				this.unstore();
			}
			this.parentNode.removeChild(this);
			return null;
		}
	};
}();
DOMAssistant.attach(DOMAssistant.Content);
DOMAssistant.Events = function () {
	var handler,
		key = "_events",
		w3cMode = !!document.addEventListener,
		useCapture = { focus: true, blur: true },
		translate = DOMAssistant.isIE? { focus: "activate", blur: "deactivate", mouseenter: "mouseover", mouseleave: "mouseout" } : { mouseenter: "mouseover", mouseleave: "mouseout" },
		regex = {
			special: /^submit|reset|change|select$/i,
			mouseenterleave: /^mouse(enter|leave)$/i,
			dom: /^DOM/,
			on: /^on/i
		},
		isBubble = function (e) {
			// Test for event bubbling
			e = 'on' + e;
			var el = document.createElement('div');
			el.setAttribute(e, '');
			return (typeof el[e] === 'function');
		},
		special = function (e) {
			return regex.special.test(e) && !isBubble(e);
		},
		fix = function (e) {
			return translate[e] || e;
		},
		createEvent = function (e, type, target) {
			e = e || window.event || {};
			if (e.event) { return e; }
			var event = {
				event: e,
				type: type || e.type,
				bubbles: e.bubbles || true,
				cancelable: e.cancelable || false,
				target: target || e.target || e.srcElement,
				clientX: e.clientX || 0,
				clientY: e.clientY || 0,
				altKey: e.altKey || false,
				ctrlKey: e.ctrlKey || false,
				shiftKey: e.shiftKey || false,
				button: e.button || null,
				timeStamp: +new Date(),
				preventDefault: function() {
					if (e.preventDefault) { e.preventDefault(); }
					this.returnValue = e.returnValue = false;
				},
				stopPropagation: function() {
					if (e.stopPropagation) { e.stopPropagation(); }
					this.cancelBubble = e.cancelBubble = true;
				}
			};
			if (event.target && 3 === event.target.nodeType) { // Safari textnode bug
				event.target = event.target.parentNode;	
			}
			event.currentTarget = event.target;
			event.relatedTarget = e.relatedTarget || (e.fromElement === event.target? e.toElement : e.fromElement) || null;
			var de = document.documentElement, b = document.body;
			event.pageX = DOMAssistant.def(e.pageX)? e.pageX : (event.clientX + (de.scrollLeft || b.scrollLeft) - (de.clientLeft || 0));
			event.pageY = DOMAssistant.def(e.pageY)? e.pageY : (event.clientY + (de.scrollTop || b.scrollTop) - (de.clientTop || 0));
			if ("number" === typeof e.which) {
				event.keyCode = e.keyCode;
				event.charCode = event.which = e.which;
			}
			else if (e.keyCode) {
				event.keyCode = event.charCode = e.keyCode;
			}
			return event;
		};

	return {
		publicMethods : [
			"triggerEvent",
			"addEvent",
			"removeEvent",
			"relayEvent",
			"unrelayEvent",
			"preventDefault",
			"cancelBubble"
		],

		init : function () {
			DOMAssistant.preventDefault = this.preventDefault;
			DOMAssistant.cancelBubble = this.cancelBubble;
			handler = this.handleEvent;
		},

		triggerEvent : function (evt, target, e) {
			var fevt = fix(evt),
				events = this.retrieve(key),
				event = e || createEvent(e, fevt, target || this);
			event.currentTarget = this;
			if (events && events[fevt]) {
				for (var i=0, iL=events[fevt].length; i<iL; i++) {
					if (events[fevt][i].call(this, event) === false) { event.stopPropagation(); }
				}
			}
			else if (typeof this["on" + fevt] === "function") {
				this["on" + fevt].call(this, event);
			}
			var p = DOMAssistant.$$(this.parentNode);
			if (!event.cancelBubble && p && p.nodeType === 1) {
				p.triggerEvent(fevt, target, event);
			}
			return this;
		},

		addEvent : function (evt, func, relay, proxy, selector) {
			var existingEvent,
				fevt = fix(evt),
				uid = fevt + this.retrieve(),
				onevt = "on" + fevt;
			if (!(func.attachedElements && func.attachedElements[uid])) {
				var events = this.retrieve(key) || {};
				if (!events[fevt]) {
					events[fevt] = [];
					existingEvent = this[onevt];
					this[onevt] = null;
				}
				if (typeof this.window === "object") { this.window[onevt] = handler; }
				else if (!events[fevt].length) {
					if (w3cMode) { this.addEventListener(fevt, handler, useCapture[fevt]); }
					else { this[onevt] = handler; }
				}
				if (existingEvent) {
					events[fevt].push(existingEvent);
				}
				if (fevt !== evt) { func.evt = evt; }
				func.relay = relay;
				func.proxy = proxy;
				func.selector = selector;
				func.attachedElements = func.attachedElements || {};
				func.attachedElements[uid] = true;
				events[fevt].push(func);
				this.store(key, events);
			}
			return this;
		},

		handleEvent : function (evt) {
			var currentEvt = (evt && regex.dom.test(evt.type) && w3cMode)? evt : createEvent(evt),
				type = fix(currentEvt.type),
				targ = currentEvt.target,
				relatedTarg = currentEvt.relatedTarget,
				eventColl = this.retrieve(key)[type].slice(0), eventCollLength, eventReturn, oevt;
			if ((eventCollLength = eventColl.length)) {
				for (var i=0; i<eventCollLength; i++) {
					if (typeof eventColl[i] === "function") {
						if ((oevt = eventColl[i].evt) && oevt !== type) {
							currentEvt.type = oevt;
							if (relatedTarg && regex.mouseenterleave.test(oevt)) {
								if (eventColl[i].relay) {
									var elms = eventColl[i].elms || (eventColl[i].elms = this.cssSelect(eventColl[i].selector));
									if (elms.indexOf(targ) < 0 || !DOMAssistant.hasChild.call(relatedTarg, targ)) { continue; }
								}
								else if (this === relatedTarg || this.hasChild(relatedTarg)) { continue; }
							}
						}
						eventReturn = eventColl[i].call(this, currentEvt);
					}
				}
				if (eventReturn === false) { currentEvt.stopPropagation(); }
				return eventReturn;
			}
		},

		removeEvent : function (evt, func, relay, proxy) {
			var uid = (evt = fix(evt)) + this.retrieve(),
				events = this.retrieve(key),
				onevt = "on" + evt;
			if (events && !evt) {
				for (var ev in events) {
					if (events[ev].length) { this.removeEvent(ev); }
				}
				var attr = this.attributes;
				for (var att, j=attr.length; j--;) {
					att = attr[j].nodeName.toLowerCase();
					if (regex.on.test(att) && typeof this[att] === "function") {
						this[att] = null;
					}
				}
			}
			else if (events && events[evt]) {
				var eventColl = events[evt];
				for (var fn, i=eventColl.length; i--;) {
					fn = func || eventColl[i];
					if (eventColl[i] === fn && relay === fn.relay && proxy === fn.proxy) {
						eventColl.splice(i, 1);
						if (!!proxy && fn.selector) {
							this.cssSelect(fn.selector).removeEvent(proxy);
						}
						if (fn.attachedElements) {
							fn.attachedElements[uid] = null;
						}
					}
				}
				if (!events[evt].length) {
					if (w3cMode) { this.removeEventListener(evt, handler, useCapture[evt]); }
					else { this[onevt] = null; }
				}
			}
			else if (this[onevt] && !func && !relay) {
				this[onevt] = null;
			}
			return this;
		},

		relayEvent: function (evt, selector, fn, proxy) {
			if (special(evt)) {
				this.relayEvent("focus", selector, function() {
					DOMAssistant.$$(this).removeEvent(evt).addEvent(evt, function(e) {
						return fn.call(this, createEvent(e));
					});
				}, evt).relayEvent("blur", selector, function() {
					DOMAssistant.$$(this).removeEvent(evt);
				}, evt);
				return this;
			}
			return this.addEvent(evt, function(e) {
				e = createEvent(e);
				var target = e.target, args = arguments, i = 0, elm, elms = this.cssSelect(selector);
				while ((elm = elms[i++])) {
					if ((elm === target || DOMAssistant.hasChild.call(elm, target)) && !elm.disabled) {
						e.currentTarget = elm;
						var retVal = fn.apply(elm, args);
						if (!retVal) { e.preventDefault(); }
						return retVal;
					}
				}
			}, true, proxy, selector);
		},

		unrelayEvent: function (evt) {
			if (special(evt)) {
				return this.removeEvent("focus", null, true, evt).removeEvent("blur", null, true, evt);
			}
			return this.removeEvent(evt, null, true);
		},

		preventDefault : function (evt) {
			if (evt.preventDefault) { evt.preventDefault(); }
			evt.returnValue = false;
		},

		cancelBubble : function (evt) {
			if (evt.stopPropagation) { evt.stopPropagation(); }
			evt.cancelBubble = true;
		}
	};
}();
DOMAssistant.attach(DOMAssistant.Events);
DOMAssistant.DOMLoad = function () {
	var DOMLoaded = false,
	DOMLoadTimer = null,
	functionsToCall = [],
	addedStrings = {},
	errorHandling = null,
	execFunctions = function () {
		for (var i=0, il=functionsToCall.length; i<il; i++) {
			try {
				functionsToCall[i]();
			}
			catch (e) {
				if (errorHandling && typeof errorHandling === "function") {
					errorHandling(e);
				}
			}
		}
		functionsToCall = [];
	},
	DOMHasLoaded = function () {
		if (DOMLoaded) {
			return;
		}
		DOMLoaded = true;
		execFunctions();
	};
	/* Internet Explorer */
	/*@cc_on
	@if (@_win32 || @_win64)
		document.write("<script id=\"ieScriptLoad\" defer src=\"//:\"><\/script>");
		document.getElementById("ieScriptLoad").onreadystatechange = function() {
			if (this.readyState === "complete") {
				DOMHasLoaded();
			}
		};
	@end @*/
	/* Mozilla, Chrome, Opera */
	if (document.addEventListener) {
		document.addEventListener("DOMContentLoaded", DOMHasLoaded, false);
	}
	/* Safari, iCab, Konqueror */
	if (/KHTML|WebKit|iCab/i.test(navigator.userAgent)) {
		DOMLoadTimer = setInterval(function () {
			if (/loaded|complete/i.test(document.readyState)) {
				DOMHasLoaded();
				clearInterval(DOMLoadTimer);
			}
		}, 10);
	}
	/* Other web browsers */
	window.onload = DOMHasLoaded;
	
	return {
		DOMReady : function () {
			for (var i=0, il=arguments.length, funcRef; i<il; i++) {
				funcRef = arguments[i];
				if (!funcRef.DOMReady && !addedStrings[funcRef]) {
					if (typeof funcRef === "string") {
						addedStrings[funcRef] = true;
						funcRef = new Function(funcRef);
					}
					funcRef.DOMReady = true;
					functionsToCall.push(funcRef);
				}
			}
			if (DOMLoaded) {
				execFunctions();
			}
		},
		
		setErrorHandling : function (funcRef) {
			errorHandling = funcRef;
		}
	};
}();
DOMAssistant.DOMReady = DOMAssistant.DOMLoad.DOMReady;