window.efx = (function() {
var _efx = {};
function trace(_pVal) {
	if (window.opera){
		opera.postError(_pVal);
		
	} else if (window.console){
		if (typeof _pVal === "object") {
            console.dir(_pVal);
            
        } else {
            console.log(_pVal);
        }
                
	} else if (window.debugService){
		if (typeof _pVal === "object") {
			window.debugService.inspect("trace", _pVal); 
            
        } else {
            window.debugService.trace(_pVal);
        }
        
	} else {
        alert(_pVal);
	}
}

/*!
 * Sizzle CSS Selector Engine - v1.0
 *  Copyright 2009, The Dojo Foundation
 *  Released under the MIT, BSD, and GPL Licenses.
 *  More information: http://sizzlejs.com/
 */
var _GetElementsBySelector = (function(){

var chunker = /((?:\((?:\([^()]+\)|[^()]+)+\)|\[(?:\[[^[\]]*\]|['"][^'"]*['"]|[^[\]'"]+)+\]|\\.|[^ >+~,(\[\\]+)+|[>+~])(\s*,\s*)?/g,
	done = 0,
	toString = Object.prototype.toString,
	hasDuplicate = false;

var Sizzle = function(selector, context, results, seed) {
	results = results || [];
	var origContext = context = context || document;

	if ( context.nodeType !== 1 && context.nodeType !== 9 ) {
		return [];
	}

	if ( !selector || typeof selector !== "string" ) {
		return results;
	}

	var parts = [], m, set, checkSet, check, mode, extra, prune = true, contextXML = isXML(context);

	// Reset the position of the chunker regexp (start from head)
	chunker.lastIndex = 0;

	while ( (m = chunker.exec(selector)) !== null ) {
		parts.push( m[1] );

		if ( m[2] ) {
			extra = RegExp.rightContext;
			break;
		}
	}

	if ( parts.length > 1 && origPOS.exec( selector ) ) {
		if ( parts.length === 2 && Expr.relative[ parts[0] ] ) {
			set = posProcess( parts[0] + parts[1], context );
		} else {
			set = Expr.relative[ parts[0] ] ?
				[ context ] :
				Sizzle( parts.shift(), context );

			while ( parts.length ) {
				selector = parts.shift();

				if ( Expr.relative[ selector ] )
					selector += parts.shift();

				set = posProcess( selector, set );
			}
		}
	} else {
		// Take a shortcut and set the context if the root selector is an ID
		// (but not if it'll be faster if the inner selector is an ID)
		if ( !seed && parts.length > 1 && context.nodeType === 9 && !contextXML &&
				Expr.match.ID.test(parts[0]) && !Expr.match.ID.test(parts[parts.length - 1]) ) {
			var ret = Sizzle.find( parts.shift(), context, contextXML );
			context = ret.expr ? Sizzle.filter( ret.expr, ret.set )[0] : ret.set[0];
		}

		if ( context ) {
			var ret = seed ?
				{ expr: parts.pop(), set: makeArray(seed) } :
				Sizzle.find( parts.pop(), parts.length === 1 && (parts[0] === "~" || parts[0] === "+") && context.parentNode ? context.parentNode : context, contextXML );
			set = ret.expr ? Sizzle.filter( ret.expr, ret.set ) : ret.set;

			if ( parts.length > 0 ) {
				checkSet = makeArray(set);
			} else {
				prune = false;
			}

			while ( parts.length ) {
				var cur = parts.pop(), pop = cur;

				if ( !Expr.relative[ cur ] ) {
					cur = "";
				} else {
					pop = parts.pop();
				}

				if ( pop == null ) {
					pop = context;
				}

				Expr.relative[ cur ]( checkSet, pop, contextXML );
			}
		} else {
			checkSet = parts = [];
		}
	}

	if ( !checkSet ) {
		checkSet = set;
	}

	if ( !checkSet ) {
		throw "Syntax error, unrecognized expression: " + (cur || selector);
	}

	if ( toString.call(checkSet) === "[object Array]" ) {
		if ( !prune ) {
			results.push.apply( results, checkSet );
		} else if ( context && context.nodeType === 1 ) {
			for ( var i = 0; checkSet[i] != null; i++ ) {
				if ( checkSet[i] && (checkSet[i] === true || checkSet[i].nodeType === 1 && contains(context, checkSet[i])) ) {
					results.push( set[i] );
				}
			}
		} else {
			for ( var i = 0; checkSet[i] != null; i++ ) {
				if ( checkSet[i] && checkSet[i].nodeType === 1 ) {
					results.push( set[i] );
				}
			}
		}
	} else {
		makeArray( checkSet, results );
	}

	if ( extra ) {
		Sizzle( extra, origContext, results, seed );
		Sizzle.uniqueSort( results );
	}

	return results;
};

Sizzle.uniqueSort = function(results){
	if ( sortOrder ) {
		hasDuplicate = false;
		results.sort(sortOrder);

		if ( hasDuplicate ) {
			for ( var i = 1; i < results.length; i++ ) {
				if ( results[i] === results[i-1] ) {
					results.splice(i--, 1);
				}
			}
		}
	}
};

Sizzle.matches = function(expr, set){
	return Sizzle(expr, null, null, set);
};

Sizzle.find = function(expr, context, isXML){
	var set, match;

	if ( !expr ) {
		return [];
	}

	for ( var i = 0, l = Expr.order.length; i < l; i++ ) {
		var type = Expr.order[i], match;

		if ( (match = Expr.match[ type ].exec( expr )) ) {
			var left = RegExp.leftContext;

			if ( left.substr( left.length - 1 ) !== "\\" ) {
				match[1] = (match[1] || "").replace(/\\/g, "");
				set = Expr.find[ type ]( match, context, isXML );
				if ( set != null ) {
					expr = expr.replace( Expr.match[ type ], "" );
					break;
				}
			}
		}
	}

	if ( !set ) {
		set = context.getElementsByTagName("*");
	}

	return {set: set, expr: expr};
};

Sizzle.filter = function(expr, set, inplace, not){
	var old = expr, result = [], curLoop = set, match, anyFound,
		isXMLFilter = set && set[0] && isXML(set[0]);

	while ( expr && set.length ) {
		for ( var type in Expr.filter ) {
			if ( (match = Expr.match[ type ].exec( expr )) != null ) {
				var filter = Expr.filter[ type ], found, item;
				anyFound = false;

				if ( curLoop == result ) {
					result = [];
				}

				if ( Expr.preFilter[ type ] ) {
					match = Expr.preFilter[ type ]( match, curLoop, inplace, result, not, isXMLFilter );

					if ( !match ) {
						anyFound = found = true;
					} else if ( match === true ) {
						continue;
					}
				}

				if ( match ) {
					for ( var i = 0; (item = curLoop[i]) != null; i++ ) {
						if ( item ) {
							found = filter( item, match, i, curLoop );
							var pass = not ^ !!found;

							if ( inplace && found != null ) {
								if ( pass ) {
									anyFound = true;
								} else {
									curLoop[i] = false;
								}
							} else if ( pass ) {
								result.push( item );
								anyFound = true;
							}
						}
					}
				}

				if ( found !== undefined ) {
					if ( !inplace ) {
						curLoop = result;
					}

					expr = expr.replace( Expr.match[ type ], "" );

					if ( !anyFound ) {
						return [];
					}

					break;
				}
			}
		}

		// Improper expression
		if ( expr == old ) {
			if ( anyFound == null ) {
				throw "Syntax error, unrecognized expression: " + expr;
			} else {
				break;
			}
		}

		old = expr;
	}

	return curLoop;
};

var Expr = Sizzle.selectors = {
	order: [ "ID", "NAME", "TAG" ],
	match: {
		ID: /#((?:[\w\u00c0-\uFFFF_-]|\\.)+)/,
		CLASS: /\.((?:[\w\u00c0-\uFFFF_-]|\\.)+)/,
		NAME: /\[name=['"]*((?:[\w\u00c0-\uFFFF_-]|\\.)+)['"]*\]/,
		ATTR: /\[\s*((?:[\w\u00c0-\uFFFF_-]|\\.)+)\s*(?:(\S?=)\s*(['"]*)(.*?)\3|)\s*\]/,
		TAG: /^((?:[\w\u00c0-\uFFFF\*_-]|\\.)+)/,
		CHILD: /:(only|nth|last|first)-child(?:\((even|odd|[\dn+-]*)\))?/,
		POS: /:(nth|eq|gt|lt|first|last|even|odd)(?:\((\d*)\))?(?=[^-]|$)/,
		PSEUDO: /:((?:[\w\u00c0-\uFFFF_-]|\\.)+)(?:\((['"]*)((?:\([^\)]+\)|[^\2\(\)]*)+)\2\))?/
	},
	attrMap: {
		"class": "className",
		"for": "htmlFor"
	},
	attrHandle: {
		href: function(elem){
			return elem.getAttribute("href");
		}
	},
	relative: {
		"+": function(checkSet, part, isXML){
			var isPartStr = typeof part === "string",
				isTag = isPartStr && !/\W/.test(part),
				isPartStrNotTag = isPartStr && !isTag;

			if ( isTag && !isXML ) {
				part = part.toUpperCase();
			}

			for ( var i = 0, l = checkSet.length, elem; i < l; i++ ) {
				if ( (elem = checkSet[i]) ) {
					while ( (elem = elem.previousSibling) && elem.nodeType !== 1 ) {}

					checkSet[i] = isPartStrNotTag || elem && elem.nodeName === part ?
						elem || false :
						elem === part;
				}
			}

			if ( isPartStrNotTag ) {
				Sizzle.filter( part, checkSet, true );
			}
		},
		">": function(checkSet, part, isXML){
			var isPartStr = typeof part === "string";

			if ( isPartStr && !/\W/.test(part) ) {
				part = isXML ? part : part.toUpperCase();

				for ( var i = 0, l = checkSet.length; i < l; i++ ) {
					var elem = checkSet[i];
					if ( elem ) {
						var parent = elem.parentNode;
						checkSet[i] = parent.nodeName === part ? parent : false;
					}
				}
			} else {
				for ( var i = 0, l = checkSet.length; i < l; i++ ) {
					var elem = checkSet[i];
					if ( elem ) {
						checkSet[i] = isPartStr ?
							elem.parentNode :
							elem.parentNode === part;
					}
				}

				if ( isPartStr ) {
					Sizzle.filter( part, checkSet, true );
				}
			}
		},
		"": function(checkSet, part, isXML){
			var doneName = done++, checkFn = dirCheck;

			if ( !part.match(/\W/) ) {
				var nodeCheck = part = isXML ? part : part.toUpperCase();
				checkFn = dirNodeCheck;
			}

			checkFn("parentNode", part, doneName, checkSet, nodeCheck, isXML);
		},
		"~": function(checkSet, part, isXML){
			var doneName = done++, checkFn = dirCheck;

			if ( typeof part === "string" && !part.match(/\W/) ) {
				var nodeCheck = part = isXML ? part : part.toUpperCase();
				checkFn = dirNodeCheck;
			}

			checkFn("previousSibling", part, doneName, checkSet, nodeCheck, isXML);
		}
	},
	find: {
		ID: function(match, context, isXML){
			if ( typeof context.getElementById !== "undefined" && !isXML ) {
				var m = context.getElementById(match[1]);
				return m ? [m] : [];
			}
		},
		NAME: function(match, context, isXML){
			if ( typeof context.getElementsByName !== "undefined" ) {
				var ret = [], results = context.getElementsByName(match[1]);

				for ( var i = 0, l = results.length; i < l; i++ ) {
					if ( results[i].getAttribute("name") === match[1] ) {
						ret.push( results[i] );
					}
				}

				return ret.length === 0 ? null : ret;
			}
		},
		TAG: function(match, context){
			return context.getElementsByTagName(match[1]);
		}
	},
	preFilter: {
		CLASS: function(match, curLoop, inplace, result, not, isXML){
			match = " " + match[1].replace(/\\/g, "") + " ";

			if ( isXML ) {
				return match;
			}

			for ( var i = 0, elem; (elem = curLoop[i]) != null; i++ ) {
				if ( elem ) {
					if ( not ^ (elem.className && (" " + elem.className + " ").indexOf(match) >= 0) ) {
						if ( !inplace )
							result.push( elem );
					} else if ( inplace ) {
						curLoop[i] = false;
					}
				}
			}

			return false;
		},
		ID: function(match){
			return match[1].replace(/\\/g, "");
		},
		TAG: function(match, curLoop){
			for ( var i = 0; curLoop[i] === false; i++ ){}
			return curLoop[i] && isXML(curLoop[i]) ? match[1] : match[1].toUpperCase();
		},
		CHILD: function(match){
			if ( match[1] == "nth" ) {
				// parse equations like 'even', 'odd', '5', '2n', '3n+2', '4n-1', '-n+6'
				var test = /(-?)(\d*)n((?:\+|-)?\d*)/.exec(
					match[2] == "even" && "2n" || match[2] == "odd" && "2n+1" ||
					!/\D/.test( match[2] ) && "0n+" + match[2] || match[2]);

				// calculate the numbers (first)n+(last) including if they are negative
				match[2] = (test[1] + (test[2] || 1)) - 0;
				match[3] = test[3] - 0;
			}

			// TODO: Move to normal caching system
			match[0] = done++;

			return match;
		},
		ATTR: function(match, curLoop, inplace, result, not, isXML){
			var name = match[1].replace(/\\/g, "");

			if ( !isXML && Expr.attrMap[name] ) {
				match[1] = Expr.attrMap[name];
			}

			if ( match[2] === "~=" ) {
				match[4] = " " + match[4] + " ";
			}

			return match;
		},
		PSEUDO: function(match, curLoop, inplace, result, not){
			if ( match[1] === "not" ) {
				// If we're dealing with a complex expression, or a simple one
				if ( match[3].match(chunker).length > 1 || /^\w/.test(match[3]) ) {
					match[3] = Sizzle(match[3], null, null, curLoop);
				} else {
					var ret = Sizzle.filter(match[3], curLoop, inplace, true ^ not);
					if ( !inplace ) {
						result.push.apply( result, ret );
					}
					return false;
				}
			} else if ( Expr.match.POS.test( match[0] ) || Expr.match.CHILD.test( match[0] ) ) {
				return true;
			}

			return match;
		},
		POS: function(match){
			match.unshift( true );
			return match;
		}
	},
	filters: {
		enabled: function(elem){
			return elem.disabled === false && elem.type !== "hidden";
		},
		disabled: function(elem){
			return elem.disabled === true;
		},
		checked: function(elem){
			return elem.checked === true;
		},
		selected: function(elem){
			// Accessing this property makes selected-by-default
			// options in Safari work properly
			elem.parentNode.selectedIndex;
			return elem.selected === true;
		},
		parent: function(elem){
			return !!elem.firstChild;
		},
		empty: function(elem){
			return !elem.firstChild;
		},
		has: function(elem, i, match){
			return !!Sizzle( match[3], elem ).length;
		},
		header: function(elem){
			return /h\d/i.test( elem.nodeName );
		},
		text: function(elem){
			return "text" === elem.type;
		},
		radio: function(elem){
			return "radio" === elem.type;
		},
		checkbox: function(elem){
			return "checkbox" === elem.type;
		},
		file: function(elem){
			return "file" === elem.type;
		},
		password: function(elem){
			return "password" === elem.type;
		},
		submit: function(elem){
			return "submit" === elem.type;
		},
		image: function(elem){
			return "image" === elem.type;
		},
		reset: function(elem){
			return "reset" === elem.type;
		},
		button: function(elem){
			return "button" === elem.type || elem.nodeName.toUpperCase() === "BUTTON";
		},
		input: function(elem){
			return /input|select|textarea|button/i.test(elem.nodeName);
		}
	},
	setFilters: {
		first: function(elem, i){
			return i === 0;
		},
		last: function(elem, i, match, array){
			return i === array.length - 1;
		},
		even: function(elem, i){
			return i % 2 === 0;
		},
		odd: function(elem, i){
			return i % 2 === 1;
		},
		lt: function(elem, i, match){
			return i < match[3] - 0;
		},
		gt: function(elem, i, match){
			return i > match[3] - 0;
		},
		nth: function(elem, i, match){
			return match[3] - 0 == i;
		},
		eq: function(elem, i, match){
			return match[3] - 0 == i;
		}
	},
	filter: {
		PSEUDO: function(elem, match, i, array){
			var name = match[1], filter = Expr.filters[ name ];

			if ( filter ) {
				return filter( elem, i, match, array );
			} else if ( name === "contains" ) {
				return (elem.textContent || elem.innerText || "").indexOf(match[3]) >= 0;
			} else if ( name === "not" ) {
				var not = match[3];

				for ( var i = 0, l = not.length; i < l; i++ ) {
					if ( not[i] === elem ) {
						return false;
					}
				}

				return true;
			}
		},
		CHILD: function(elem, match){
			var type = match[1], node = elem;
			switch (type) {
				case 'only':
				case 'first':
					while (node = node.previousSibling)  {
						if ( node.nodeType === 1 ) return false;
					}
					if ( type == 'first') return true;
					node = elem;
				case 'last':
					while (node = node.nextSibling)  {
						if ( node.nodeType === 1 ) return false;
					}
					return true;
				case 'nth':
					var first = match[2], last = match[3];

					if ( first == 1 && last == 0 ) {
						return true;
					}

					var doneName = match[0],
						parent = elem.parentNode;

					if ( parent && (parent.sizcache !== doneName || !elem.nodeIndex) ) {
						var count = 0;
						for ( node = parent.firstChild; node; node = node.nextSibling ) {
							if ( node.nodeType === 1 ) {
								node.nodeIndex = ++count;
							}
						}
						parent.sizcache = doneName;
					}

					var diff = elem.nodeIndex - last;
					if ( first == 0 ) {
						return diff == 0;
					} else {
						return ( diff % first == 0 && diff / first >= 0 );
					}
			}
		},
		ID: function(elem, match){
			return elem.nodeType === 1 && elem.getAttribute("id") === match;
		},
		TAG: function(elem, match){
			return (match === "*" && elem.nodeType === 1) || elem.nodeName === match;
		},
		CLASS: function(elem, match){
			return (" " + (elem.className || elem.getAttribute("class")) + " ")
				.indexOf( match ) > -1;
		},
		ATTR: function(elem, match){
			var name = match[1],
				result = Expr.attrHandle[ name ] ?
					Expr.attrHandle[ name ]( elem ) :
					elem[ name ] != null ?
						elem[ name ] :
						elem.getAttribute( name ),
				value = result + "",
				type = match[2],
				check = match[4];

			return result == null ?
				type === "!=" :
				type === "=" ?
				value === check :
				type === "*=" ?
				value.indexOf(check) >= 0 :
				type === "~=" ?
				(" " + value + " ").indexOf(check) >= 0 :
				!check ?
				value && result !== false :
				type === "!=" ?
				value != check :
				type === "^=" ?
				value.indexOf(check) === 0 :
				type === "$=" ?
				value.substr(value.length - check.length) === check :
				type === "|=" ?
				value === check || value.substr(0, check.length + 1) === check + "-" :
				false;
		},
		POS: function(elem, match, i, array){
			var name = match[2], filter = Expr.setFilters[ name ];

			if ( filter ) {
				return filter( elem, i, match, array );
			}
		}
	}
};

var origPOS = Expr.match.POS;

for ( var type in Expr.match ) {
	Expr.match[ type ] = new RegExp( Expr.match[ type ].source + /(?![^\[]*\])(?![^\(]*\))/.source );
}

var makeArray = function(array, results) {
	array = Array.prototype.slice.call( array );

	if ( results ) {
		results.push.apply( results, array );
		return results;
	}

	return array;
};

// Perform a simple check to determine if the browser is capable of
// converting a NodeList to an array using builtin methods.
try {
	Array.prototype.slice.call( document.documentElement.childNodes );

// Provide a fallback method if it does not work
} catch(e){
	makeArray = function(array, results) {
		var ret = results || [];

		if ( toString.call(array) === "[object Array]" ) {
			Array.prototype.push.apply( ret, array );
		} else {
			if ( typeof array.length === "number" ) {
				for ( var i = 0, l = array.length; i < l; i++ ) {
					ret.push( array[i] );
				}
			} else {
				for ( var i = 0; array[i]; i++ ) {
					ret.push( array[i] );
				}
			}
		}

		return ret;
	};
}

var sortOrder;

if ( document.documentElement.compareDocumentPosition ) {
	sortOrder = function( a, b ) {
		var ret = a.compareDocumentPosition(b) & 4 ? -1 : a === b ? 0 : 1;
		if ( ret === 0 ) {
			hasDuplicate = true;
		}
		return ret;
	};
} else if ( "sourceIndex" in document.documentElement ) {
	sortOrder = function( a, b ) {
		var ret = a.sourceIndex - b.sourceIndex;
		if ( ret === 0 ) {
			hasDuplicate = true;
		}
		return ret;
	};
} else if ( document.createRange ) {
	sortOrder = function( a, b ) {
		var aRange = a.ownerDocument.createRange(), bRange = b.ownerDocument.createRange();
		aRange.selectNode(a);
		aRange.collapse(true);
		bRange.selectNode(b);
		bRange.collapse(true);
		var ret = aRange.compareBoundaryPoints(Range.START_TO_END, bRange);
		if ( ret === 0 ) {
			hasDuplicate = true;
		}
		return ret;
	};
}

// Check to see if the browser returns elements by name when
// querying by getElementById (and provide a workaround)
(function(){
	// We're going to inject a fake input element with a specified name
	var form = document.createElement("div"),
		id = "script" + (new Date).getTime();
	form.innerHTML = "<a name='" + id + "'/>";

	// Inject it into the root element, check its status, and remove it quickly
	var root = document.documentElement;
	root.insertBefore( form, root.firstChild );

	// The workaround has to do additional checks after a getElementById
	// Which slows things down for other browsers (hence the branching)
	if ( !!document.getElementById( id ) ) {
		Expr.find.ID = function(match, context, isXML){
			if ( typeof context.getElementById !== "undefined" && !isXML ) {
				var m = context.getElementById(match[1]);
				return m ? m.id === match[1] || typeof m.getAttributeNode !== "undefined" && m.getAttributeNode("id").nodeValue === match[1] ? [m] : undefined : [];
			}
		};

		Expr.filter.ID = function(elem, match){
			var node = typeof elem.getAttributeNode !== "undefined" && elem.getAttributeNode("id");
			return elem.nodeType === 1 && node && node.nodeValue === match;
		};
	}

	root.removeChild( form );
})();

(function(){
	// Check to see if the browser returns only elements
	// when doing getElementsByTagName("*")

	// Create a fake element
	var div = document.createElement("div");
	div.appendChild( document.createComment("") );

	// Make sure no comments are found
	if ( div.getElementsByTagName("*").length > 0 ) {
		Expr.find.TAG = function(match, context){
			var results = context.getElementsByTagName(match[1]);

			// Filter out possible comments
			if ( match[1] === "*" ) {
				var tmp = [];

				for ( var i = 0; results[i]; i++ ) {
					if ( results[i].nodeType === 1 ) {
						tmp.push( results[i] );
					}
				}

				results = tmp;
			}

			return results;
		};
	}

	// Check to see if an attribute returns normalized href attributes
	div.innerHTML = "<a href='#'></a>";
	if ( div.firstChild && typeof div.firstChild.getAttribute !== "undefined" &&
			div.firstChild.getAttribute("href") !== "#" ) {
		Expr.attrHandle.href = function(elem){
			return elem.getAttribute("href", 2);
		};
	}
})();

if ( document.querySelectorAll ) (function(){
	var oldSizzle = Sizzle, div = document.createElement("div");
	div.innerHTML = "<p class='TEST'></p>";

	// Safari can't handle uppercase or unicode characters when
	// in quirks mode.
	if ( div.querySelectorAll && div.querySelectorAll(".TEST").length === 0 ) {
		return;
	}

	Sizzle = function(query, context, extra, seed){
		context = context || document;

		// Only use querySelectorAll on non-XML documents
		// (ID selectors don't work in non-HTML documents)
		if ( !seed && context.nodeType === 9 && !isXML(context) ) {
			try {
				return makeArray( context.querySelectorAll(query), extra );
			} catch(e){}
		}

		return oldSizzle(query, context, extra, seed);
	};

	for ( var prop in oldSizzle ) {
		Sizzle[ prop ] = oldSizzle[ prop ];
	}
})();

if ( document.getElementsByClassName && document.documentElement.getElementsByClassName ) (function(){
	var div = document.createElement("div");
	div.innerHTML = "<div class='test e'></div><div class='test'></div>";

	// Opera can't find a second classname (in 9.6)
	if ( div.getElementsByClassName("e").length === 0 )
		return;

	// Safari caches class attributes, doesn't catch changes (in 3.2)
	div.lastChild.className = "e";

	if ( div.getElementsByClassName("e").length === 1 )
		return;

	Expr.order.splice(1, 0, "CLASS");
	Expr.find.CLASS = function(match, context, isXML) {
		if ( typeof context.getElementsByClassName !== "undefined" && !isXML ) {
			return context.getElementsByClassName(match[1]);
		}
	};
})();

function dirNodeCheck( dir, cur, doneName, checkSet, nodeCheck, isXML ) {
	var sibDir = dir == "previousSibling" && !isXML;
	for ( var i = 0, l = checkSet.length; i < l; i++ ) {
		var elem = checkSet[i];
		if ( elem ) {
			if ( sibDir && elem.nodeType === 1 ){
				elem.sizcache = doneName;
				elem.sizset = i;
			}
			elem = elem[dir];
			var match = false;

			while ( elem ) {
				if ( elem.sizcache === doneName ) {
					match = checkSet[elem.sizset];
					break;
				}

				if ( elem.nodeType === 1 && !isXML ){
					elem.sizcache = doneName;
					elem.sizset = i;
				}

				if ( elem.nodeName === cur ) {
					match = elem;
					break;
				}

				elem = elem[dir];
			}

			checkSet[i] = match;
		}
	}
}

function dirCheck( dir, cur, doneName, checkSet, nodeCheck, isXML ) {
	var sibDir = dir == "previousSibling" && !isXML;
	for ( var i = 0, l = checkSet.length; i < l; i++ ) {
		var elem = checkSet[i];
		if ( elem ) {
			if ( sibDir && elem.nodeType === 1 ) {
				elem.sizcache = doneName;
				elem.sizset = i;
			}
			elem = elem[dir];
			var match = false;

			while ( elem ) {
				if ( elem.sizcache === doneName ) {
					match = checkSet[elem.sizset];
					break;
				}

				if ( elem.nodeType === 1 ) {
					if ( !isXML ) {
						elem.sizcache = doneName;
						elem.sizset = i;
					}
					if ( typeof cur !== "string" ) {
						if ( elem === cur ) {
							match = true;
							break;
						}

					} else if ( Sizzle.filter( cur, [elem] ).length > 0 ) {
						match = elem;
						break;
					}
				}

				elem = elem[dir];
			}

			checkSet[i] = match;
		}
	}
}

var contains = document.compareDocumentPosition ?  function(a, b){
	return a.compareDocumentPosition(b) & 16;
} : function(a, b){
	return a !== b && (a.contains ? a.contains(b) : true);
};

var isXML = function(elem){
	return elem.nodeType === 9 && elem.documentElement.nodeName !== "HTML" ||
		!!elem.ownerDocument && elem.ownerDocument.documentElement.nodeName !== "HTML";
};

var posProcess = function(selector, context){
	var tmpSet = [], later = "", match,
		root = context.nodeType ? [context] : context;

	// Position selectors must be done after the filter
	// And so must :not(positional) so we move all PSEUDOs to the end
	while ( (match = Expr.match.PSEUDO.exec( selector )) ) {
		later += match[0];
		selector = selector.replace( Expr.match.PSEUDO, "" );
	}

	selector = Expr.relative[selector] ? selector + "*" : selector;

	for ( var i = 0, l = root.length; i < l; i++ ) {
		Sizzle( selector, root[i], tmpSet );
	}

	return Sizzle.filter( later, tmpSet );
};

// EXPOSE

return Sizzle;

})();

//@requires Sizzle

var _isFn = function(o) { return typeof o == "function"; },
KEY_SPACE = 32,
KEY_ENTER = 13,
KEY_TAB = 9,
KEY_BACKSPACE = 8,
KEY_SHIFT = 16,
KEY_CTRL = 17,
KEY_ALT = 18,
KEY_CAPS_LOCK = 20,
KEY_NUM_LOCK = 144,
KEY_SCROLL_LOCK = 145,
KEY_LEFT = 37,
KEY_UP = 38,
KEY_RIGHT = 39,
KEY_DOWN = 40,
KEY_PAGE_UP = 33,
KEY_PAGE_DOWN = 34,
KEY_HOME = 36,
KEY_END = 35,
KEY_INSERT = 45,
KEY_DELETE = 46,
KEY_ESCAPE = 27,
KEY_PAUSE = 19,
KEY_F1 = 112,
KEY_F2 = 113,
KEY_F3 = 114,
KEY_F4 = 115,
KEY_F5 = 116,
KEY_F6 = 117,
KEY_F7 = 118,
KEY_F8 = 119,
KEY_F9 = 120,
KEY_F10 = 121,
KEY_F11 = 122,
KEY_F12 = 123,
KEY_NUM0 = 96,
KEY_NUM1 = 97,
KEY_NUM2 = 98,
KEY_NUM3 = 99,
KEY_NUM4 = 100,
KEY_NUM5 = 101,
KEY_NUM6 = 102,
KEY_NUM7 = 103,
KEY_NUM8 = 104,
KEY_NUM9 = 105,
KEY_NUM_STAR = 106,
KEY_NUM_SLASH = 111,
KEY_NUM_DOT = 110,

_Class = function() { },
_UNDEFINED,
_htmlNode = document.documentElement,
_bodyNode = null,
_shiftPressed = false,
_tempEfxNodeID = 0,
_ie = !+"\v1",
_ieVersion = _ie && parseFloat(navigator.appVersion.split("MSIE")[1].split(";")[0]),
_ie6 = _ie && _ieVersion < 7,
_ie7 = _ie && (_ieVersion > 6 && _ieVersion < 8),

_argumentsToArray = function(_pArgs, _pFrom) {
	return Array.prototype.slice.call(_pArgs, _pFrom);
};

_Class.create = function(_proto, _static) {
	var _key,

	k = function(_magic) { // call init only if there's no magic cookie
		var $ = this;
		if (_magic != _isFn) {
			$._methods = [];
			$.delegate = function(_pFunction) {
				var a = _argumentsToArray(arguments, 1);
				return function() {
					return _pFunction.apply($, a.concat(_argumentsToArray(arguments, 0)));
				}
			};

			if (_isFn($.init)) $.init.apply($, arguments);
		}
	};

	k.prototype = new this(_isFn); // use our private method as magic cookie

	for (_key in _proto) {
		if (_key == "statics") continue;
		(function(_fn, _sfn) { // create a closure
			k.prototype[_key] = !_isFn(_fn) || !_isFn(_sfn) ? _fn : // add _super method
        function() {
        	this._super = _sfn;
        	return _fn.apply(this, arguments);
        };
		})(_proto[_key], k.prototype[_key]);
	}

	k.prototype.constructor = k;
	k.extend = this.extend || this.create;
	k.mixin = function(_proto) {
		for (_key in _proto) {
			k.prototype[_key] = _proto[_key];
		}
	};

	if (_proto.statics) {
		for (var i in _proto.statics) {
			_static ? k.prototype[i] = _proto.statics[i] : k[i] = _proto.statics[i];
		}
	}

	return _static ? new k() : k;
};

_efx.Class = _Class;

function _registeredClass(_pObj) {
	_pObj = _pObj.split(".");
	var _tmpobj = window, _cp;
	while (_cp = _pObj.shift()) {
		if (!(_tmpobj = _tmpobj[_cp])) return false;
	}

	return true;
}

var _String = _efx.String = _Class.create({
	isNullOrEmpty: function(_str) {
		if (!_str) return true;
		return (_str.toString()).replace(/^\s*|\s*$/g, '') === '';
	},

	trim: function(_str) {
		return _str.replace(/^\s+|\s+$/g, '');
	}
}, true);

var _objCopy = _efx.objectCopy = function() {
	var _deepCopy = function(_pOldObj, _pNewObj) {
		var _obj, i;

		for (i in _pOldObj) {
			if (_pOldObj.hasOwnProperty(i)) {
				_obj = _pOldObj[i];

				if (typeof (_obj) === 'object') {
					if (!_pNewObj[i]) _pNewObj[i] = _obj.prototype ? new _obj.prototype.constructor : {};
					_deepCopy(_obj, _pNewObj[i])

				} else {
					_pNewObj[i] = _obj
				}
			}
		}
	};

	return function() {
		var a = arguments,
		_obj = a[0],
		_ret = _obj.prototype ? new _obj.prototype.constructor : {},
		j = 1;

		_deepCopy(_obj, _ret);

		if (a.length < 2)//0 or 1 arguments
			return _ret;

		for (; j < a.length; j++) {
			_deepCopy(a[j], _ret)
		}

		return _ret;
	}
} ();

// GET ID OF AN ELEMENT
function _getId(_el) {
	var _id = _getAttr("id", _el);
	if (_id === "") _el.setAttribute("id", (_id = "efxElementID" + (_tempEfxNodeID++)));
	return _id;
}

var _Enumerate = _Class.create({
	init: function(_pObj, _pCallback) {
		var i, o, $ = this;
		$._running = true;

		if (!_pObj) {
			throw new Error("Invalid non-enumerable object passed to Enumerate: [" + _pObj + "]");
			return;
		}

		if (_pObj.hasValidData) {//Data source
			_pObj.enumerate(_pCallback);

		} else if (_pObj instanceof Array) {
			i = 0;
			while ($._running && (o = _pObj[i])) _pCallback($, o, i++);

		} else if (_pObj instanceof Object) {//check that it is not null
			for (i in _pObj) {
				if (!$._running) break;
				if (_pObj.hasOwnProperty(i)) _pCallback($, _pObj[i], i);
			}
		}
	},

	exit: function() {
		this._running = false;
	}
});

var _Enumerator = _efx.Enumerator = function(_pObj, _pCallback) {
	return new _Enumerate(_pObj, _pCallback);
};

var _HTMLNode = _Class.create({
	_node: _UNDEFINED,
	_fix: {
		'class': 'className',
		'float': 'cssFloat',
		'for': 'htmlFor',
		'colspan': 'colSpan',
		'rowspan': 'rowSpan'
	},

	_rx1: /:\s*/,
	_rx2: /^\s*([^\s]+)\s*/,
	_rx3: /^\s*([^\s]+)\s*/,

	init: function(_pNodeName, _pAttrs) {
		var $ = this, _nodes;
		if (typeof _pNodeName === "string") {
			if (_pNodeName.indexOf("@") === 0) {//create
				$._node = document.createElement(_pNodeName.substr(1));

			} else {//get ONE from dom...
				_nodes = _GetElementsBySelector(_pNodeName);
				//_nodes = _GetElementsBySelector(_pNodeName, _pContext);
				if (_nodes && _nodes.length) $._node = _nodes[0];
			}

		} else {//passed an HTMLNode as parameter
			$._node = _pNodeName;
		}

		if (_pAttrs) {
			_Enumerator(_pAttrs, function(_pEnum, _pArg, _pIdx) {
				$.attr(_pIdx, _pArg);
			})
		}
	},

	setStyle: function(_pStyle) {
		_pStyle = _pStyle.split(/;/);
		var $ = this, t;

		for (g = 0; g < _pStyle.length; g++) {
			t = _pStyle[g].split($._rx1);
			$._node.style[t[0].replace($._rx2, '$1')] = t[1].replace($._rx3, '$1');
		}

		return $;
	},

	setVisible: function(_pVisible) {
		this._node.style.display = _pVisible ? "block" : "none";
		return this;
	},

	attr: function(_pKey, _pValue) {
		var $ = this, n = $._node;

		if (arguments.length === 2) {
			if (_pKey in $._fix) {
				n[$._fix[_pKey]] = _pValue;

			} else {
				n.setAttribute(_pKey, _pValue);
			}

		} else {
			return n.getAttribute(_pKey) || (n.attributes[_pKey] ? n.attributes[_pKey].nodeValue : _UNDEFINED)
		}
	},

	/**
	true = return element id, if the element does not has one, create it.
	string = set the id of the element
	null = return element id, undefined if none
	*/
	id: function(_pValue) {
		var $ = this, i = "id";
		if (typeof _pValue === "string") {
			$.attr(i, _pValue);

		} else {
			_pValue = $.attr(i);
			if (_pValue === _UNDEFINED) {
				$.attr(i, (_pValue = "efxElementId" + (_tempEfxNodeID++)));
			}

			return _pValue;
		}
	},

	removeChilds: function() {
		var $ = this;
		while ($._node && $._node.firstChild) $._node.removeChild($._node.firstChild);
		return $;
	},

	addChild: function(_pNode) {
		if (_pNode instanceof _HTMLNode) {
			this._node.appendChild(_pNode._node);

		} else {
			this._node.appendChild(_pNode);
		}
		return this;
	},

	appendToDom: function(_pNode) {
		_pNode.appendChild(this._node);
	},

	getNode: function() {
		return this._node;
	},

	addText: function(_pText) {
		this.addChild(document.createTextNode(_pText));
		return this;
	},

	statics: {
		create: function(_pNodeName, _pAttrs) {
			return new _HTMLNode(_pNodeName, _pAttrs);
		},

		list: function(_pSelector, _pContext) {
			var r = [];
			_Enumerator(this.listRaw(_pSelector, _pContext), function(_pEnum, _pArg, _pIdx) {
				r[r.length] = $$(_pArg);
			});

			return r;
		},

		listRaw: function(_pSelector, _pContext) {
			return _GetElementsBySelector(_pSelector, _pContext);
		}
	}
});

var $$ = _efx.$$ = _HTMLNode.create;

/*
add new functions
_HTMLNode.mixin({
	other:function(){
	}
});

new way:
$$("@dl", [{attrs}]).attrs({attrs}).addChild($$("@dt").addText()).addChild($$("@dd").addChild($$("@ul"));
*/
/*
 	Selector 				What it Selects
	a 						a elements
	a span 					span elements descended from a elements
	a > span 				span elements that are direct children of a elements
	a + span 				span elements that are immediate next siblings of a elements
	a ~ span 				span elements that are following siblings of a elements
	a, span 				any span or a element
	a@title 				a elements having title attributes
	a@title='foo' 			a elements whose title attributes equal foo
	a@title^='foo' 			a elements whose title attributes begin with foo
	a@title*='foo' 			a elements whose title attributes contain foo
	a@title$='foo' 			a elements whose title attributes end with foo
	a@title~='foo' 			a elements whose title attributes contain foo in a space-separated list.
	a@title|='foo' 			a elements whose title attributes either exactly match foo or start with foo followed by a hyphen
	a#foo 					a elements whose id equals foo
	a.foo 					a elements whose class attributes equal foo, or contain foo in a space-separated list
	a:target 				a elements whose id matches the URL hash
	#foo or *#foo 			any element with an id that equals foo
	.foo or *.foo 			any element with a class attribute that equals foo, or contains foo in a space-separated list
	.foo.bar 				any element with a class attribute that contains both foo and bar in a space-separated list, order-agnostic
	:target or *:target 	any element whose id matches the URL hash
 */

var _expressions = {
	_leadSpace: new RegExp("^\\s+"),
	_tagName: new RegExp("^([a-z_][a-z0-9_-]*)", "i"),
	_wildCard: new RegExp("^\\*([^=]|$)"),
	_className: new RegExp("^(\\.([a-z0-9_-]+))", "i"),
	_id: new RegExp("^(#([a-z0-9_-]+))", "i"),
	_att: new RegExp("^(@([a-z0-9_-]+))", "i"),
	_matchType: new RegExp("(^\\^=)|(^\\$=)|(^\\*=)|(^~=)|(^\\|=)|(^=)"),
	_spaceQuote: new RegExp("^\\s+['\"]")
};

if (window.Node && Node.prototype && !Node.prototype.contains) {
	Node.prototype.contains = function (_arg) {
		return !!(this.compareDocumentPosition(_arg) & 16);
	}
}

// #############################################################################
// #### SELECTORS ##############################################################
// #############################################################################

	/*
	A CSS-like selector API focusing on matching not traversal:
	- new reg.Selector(selectorString)
	- new reg.Selector(selectorString).matches(someElement)

	For example:
	var sel = new Selector('div#foo > ul.bar > li');
	var item = document.getElementById('myListItem');
	if (sel.matches(item)) { ... }
	*/


	// constructor
function _Selector(_selString) {
	var $ = this;
	$._items = []; // for each comma-separated selector, this array has an item
	var _itms = [], // this will be added to this.items
	_count = 0,
	_origSel = _selString;

	while (_selString.length > 0) {
		if (_count > 100) { throw new Error("failed parsing '"+_origSel+"' stuck at '"+_selString+"'"); }

		// get rid of any leading spaces
		var _leadSpaceChopped = false;
		if (_expressions._leadSpace.test(_selString)) {
			_selString = _selString.replace(_expressions._leadSpace,'');
			_leadSpaceChopped = true;
		}

		// find tag name
		var _tagNameMatch = _expressions._tagName.exec(_selString);

		if (_tagNameMatch) {
			if (_itms.length > 0 && _itms[_itms.length-1]._name==='tag') { _itms.push({_name:'descendant'}); }
			_itms.push({_name:'tag',_tagName:_tagNameMatch[1].toLowerCase()});
			_selString=_selString.substring(_tagNameMatch[1].length);
			_tagNameMatch=_UNDEFINED;
			continue;
		}

		// explicit wildcard selector
		if (_expressions._wildCard.test(_selString)) {
			if (_itms.length > 0 && _itms[_itms.length-1]._name==='tag') { _itms.push({_name:'descendant'}); }
			_itms.push({_name:'tag',_tagName:'*'});
			_selString = _selString.substring(1);
			continue;
		}

		var _classMatch = _expressions._className.exec(_selString),
		_idMatch = _expressions._id.exec(_selString),
		_attMatch = _expressions._att.exec(_selString);

		if (_classMatch || _idMatch || _attMatch) {
			// declare descendant if necessary
			if (_leadSpaceChopped && _itms.length>0 && _itms[_itms.length-1]._name==='tag') { _itms.push({_name:'descendant'}); }
			// create a tag wildcard * if necessary
			if (_itms.length===0 || _itms[_itms.length-1]._name!=='tag') { _itms.push({_name:'tag',_tagName:'*'}); }
			var _lastTag = _itms[_itms.length-1];
			// find class name, like .entry
			if (_classMatch) {
				if (_lastTag._classNames) {
					_lastTag._classNames.push(_classMatch[2]);

				} else {
					_lastTag._classNames = [_classMatch[2]];
				}

				_selString=_selString.substring(_classMatch[1].length);
				_classMatch=null;
				continue;
			}
			// find id, like #content
			if (_idMatch) {
				_lastTag._id=_idMatch[2];
				_selString=_selString.substring(_idMatch[1].length);
				_idMatch=null;
				continue;
			}
			// find attribute selector, like @src
			if (_attMatch) {
				if (!_lastTag._attributes) {
					_lastTag._attributes = [{_name:_attMatch[2]}];
				} else {
					_lastTag._attributes.push({_name:_attMatch[2]});
				}

				_selString=_selString.substring(_attMatch[1].length);
				_attMatch=null;
				continue;
			}
		}

		// find attribute value specifier
		var _mTypeMatch=_expressions._matchType.exec(_selString);
		if (_mTypeMatch) {
			// this will determine how the matching is done
			// (lastTag should still be hanging around)
			if(_lastTag && _lastTag._attributes && !_lastTag._attributes[_lastTag._attributes.length-1]._value){

				var _lastAttribute = _lastTag._attributes[_lastTag._attributes.length-1];
				_lastAttribute._matchType = _mTypeMatch[0];

				_selString=_selString.substring(_lastAttribute._matchType.length);
				if(_selString.charAt(0)!='"'&&_selString.charAt(0)!="'"){
					if(_expressions._spaceQuote.test(_selString)){_selString=_selString.replace(_expressions._leadSpace,'');}
					else{throw new Error(_origSel+" is invalid, single or double quotes required around attribute values");}
				}
				// it is enclosed in quotes, end is closing quote
				var q=_selString.charAt(0);
				var _lastQInd=_selString.indexOf(q,1);
				if(_lastQInd==-1){throw new Error(_origSel+" is invalid, missing closing quote");}
				while(_selString.charAt(_lastQInd-1)=='\\'){
					_lastQInd=_selString.indexOf(q,_lastQInd+1);
					if(_lastQInd==-1){throw new Error(_origSel+" is invalid, missing closing quote");}
				}
				_lastAttribute._value=_selString.substring(1,_lastQInd);
				if      ('~=' == _lastAttribute._matchType) { _lastAttribute._valuePatt = new RegExp("(^|\\s)"+_lastAttribute._value+"($|\\s)"); }
				else if ('|=' == _lastAttribute._matchType) { _lastAttribute._valuePatt = new RegExp("^"+_lastAttribute._value+"($|\\-)"); }
				_selString=_selString.substring(_lastAttribute._value.length+2);// +2 for the quotes
				continue;
			} else {
				throw new Error(_origSel+" is invalid, "+_mTypeMatch[0]+" appeared without preceding attribute identifier");
			}
			_mTypeMatch=null;
		}

		// find child selector
		if (_selString.charAt(0) === '>') {
			_itms.push({_name:'child'});
			_selString=_selString.substring(1);
			continue;
		}
		// find next sibling selector
		if (_selString.charAt(0) === '+') {
			_itms.push({_name:'nextSib'});
			_selString=_selString.substring(1);
			continue;
		}
		// find after sibling selector
		if (_selString.charAt(0) === '~') {
			_itms.push({_name:'followingSib'});
			_selString=_selString.substring(1);
			continue;
		}
		// find the comma separator
		if (_selString.charAt(0) === ',') {
			$._items.push(_itms);
			_itms = [];
			_selString = _selString.substring(1);
			continue;
		}
		_count++;
	}

	$._items.push(_itms);
	$._selectorString=_origSel;
	// do some structural validation
	for (var a=0;a<$._items.length;a++){
		var _itms = $._items[a];
		if (_itms.length===0) { throw new Error("illegal structure: '"+_origSel+"' contains an empty set"); }
		if (_itms[0]._name!=='tag') { throw new Error("illegal structure: '"+_origSel+"' contains a dangling relation"); }
		if (_itms[_itms.length-1]._name!=='tag') {throw new Error("illegal structure: '"+_origSel+"' contains a dangling relation"); }
		for(var b=1;b<_itms.length;b++){
			if(_itms[b]._name!=='tag'&&_itms[b-1]._name!=='tag'){ throw new Error("illegal structure: '"+_origSel+"' contains doubled up relations"); }
		}
	}
}

// returns string suitable for querySelector() and querySelectorAll()
function _toQuerySelectorString(_sel) {
	if (!_sel._qss) {
		var _itemStrings = [];
		for (var i=0; i<_sel._items.length; i++) {
			var _result = '',
			_item = _sel._items[i];

			for (var j=0; j<_item.length; j++) {
				var _des = _item[j];

				if (_des._name==='tag') {
					_result += _des._tagName;

					if (_des._classNames) {
						_result += "." + _des._classNames.join(".");
					}

					if (_des._id) {        _result += '#' + _des._id; }
					if (_des._targeted) {  _result += ':target'; }
					if (_des._attributes) {

						for (var k=0; k<_des._attributes.length; k++) {
							_result += '[' + _des._attributes[k]._name;
							if (_des._attributes[k]._matchType) {
								_result += _des._attributes[k]._matchType;
								_result += '"'+_des._attributes[k]._value.replace(/"/,'\\"')+'"';
							}
							_result += ']';
						}

					}

				} else if (_des._name==='descendant') {
					_result += ' ';
					continue;
				} else if (_des._name==='child') {
					_result += ' > ';
					continue;
				} else if (_des._name==='followingSib') {
					_result += ' ~ ';
					continue;
				} else if (_des._name==='nextSib') {
					_result += ' + ';
					continue;
				}
			}
			_itemStrings.push(_result);
		}
		_sel._qss = _itemStrings.join(', ');
	}
	return _sel._qss;
}

// match against an element
_Selector.prototype.matches = function(_el) {
	var $ = this;
	if (!_el) { throw new Error('no element provided'); }
	if (_el.nodeType !== 1) { throw new Error($._selectorString+' cannot be evaluated against element of type '+_el.nodeType); }

	_commas:for (var a=0;a<$._items.length;a++) { // for each comma-separated selector
		var _tempEl = _el,
		_itms = $._items[a];
		for (var b=_itms.length-1; b>-1; b--) { // loop backwards through the items
			var _itm = _itms[b];

			if (_itm._name === 'tag') {
				if (!_matchIt(_tempEl, _itm)) {
					// these relational selectors require more extensive searching
					if (_tempEl && b < _itms.length-1 && _itms[b+1]._name==='descendant') { _tempEl=_tempEl.parentNode; b++; continue; }
					else if (_tempEl && b < _itms.length-1 && _itms[b+1]._name==='followingSib') { _tempEl=_tempEl.previousSibling; b++; continue; }
					else { continue _commas; } // fail this one
				}
			}

			else if (_itm._name === 'nextSib') { _tempEl = _previousElement(_tempEl); }
			else if (_itm._name === 'followingSib') { _tempEl = _previousElement(_tempEl); }
			else if (_itm._name === 'child') { _tempEl = _tempEl.parentNode; }
			else if (_itm._name === 'descendant') {_tempEl = _tempEl.parentNode; }
		}

		return true;
	}

	return false;
};

_efx.Selector = _Selector;

// subroutine for matches() above
function _matchIt(_el, _itm) {
	// try to falsify as soon as possible
	if (!_el) { return false; }
	if (_itm._tagName!=='*' && _el.nodeName.toLowerCase()!==_itm._tagName) {return false; }
	if (_itm._classNames) {
		 for (var i=0; i<_itm._classNames.length; i++) {
			 if (!_hasClassName(_el, _itm._classNames[i])) {
				return false;
			}
		}
	}

	if (_itm._id && _el.id !== _itm._id) {
		 return false;
	}

	if (_itm._attributes) {
		for (var i=0; i<_itm._attributes.length; i++) {
			var _itmAtt = _itm._attributes[i];
			if (typeof _el.hasAttribute != 'undefined') {
				if (!_el.hasAttribute(_itmAtt._name)) { return false; }
				var _att = _el.getAttribute(_itmAtt._name);

			}else{
				if(_el.nodeType!=1) {return false;}
				var _att = _el.getAttribute(_itmAtt._name, 2);
				if(_itmAtt._name=='class'){_att=_el.className;}
				else if(_itmAtt._name=='for'){_att=_el.htmlFor;}
				if(!_att){return false;}
			}

			if (_itmAtt._value) {
				if (_itmAtt._matchType=='^='){
					if (_att.indexOf(_itmAtt._value)!=0){return false;}
				} else if (_itmAtt._matchType=='*='){
					if (_att.indexOf(_itmAtt._value)==-1){return false;}
				} else if (_itmAtt._matchType=='$='){
					if (_att.indexOf(_itmAtt._value)!=_att.length-_itmAtt._value.length){return false;}
				} else if (_itmAtt._matchType=='='){
					if (_att!=_itmAtt._value){return false;}
				} else if ('|='==_itmAtt._matchType || '~='==_itmAtt._matchType){
					if (!_itmAtt._valuePatt.test(_att)){return false;}
				}else{
					if(!_itmAtt._matchType){throw new Error("illegal structure, parsed selector cannot have null or empty attribute match type");}
					else{throw new Error("illegal structure, parsed selector cannot have '"+_itm._matchType+"' as an attribute match type");}
				}
			}
		}

	}
	return true;
}

// gets the tag names that the selector represents
function _getTagNames(_sel) {
	var _hash = {}; // this avoids dupes
	for (var a=0;a<_sel._items.length;a++){
		_hash[_sel._items[a][_sel._items[a].length-1]._tagName]=null;
	}
	var _result = [];
	for (var _tag in _hash){if(_hash.hasOwnProperty(_tag)){_result.push(_tag);}}
	return _result;
}

// #############################################################################
// #### DOM HELPERS ############################################################
// #############################################################################

/*
A bunch of DOM convenience methods (alias names in braces):

CLASSNAMES
- reg.addClassName(el, cName)..............................{acn}
- reg.getElementsByClassName(cNames[, ctxNode[, tagName]]).{gebcn}
- reg.hasClassName(el, cName)..............................{hcn}
- reg.matchClassName(el, regexp)...........................{mcn}
- reg.removeClassName(el, cName)...........................{rcn}
- reg.switchClassName(el, cName1, cName2)..................{scn}
- reg.toggleClassName(el, cName)...........................{tcn}

SELECTORS
- reg.elementMatchesSelector(el, _selString)................{matches}
- reg.getElementsBySelector(_selString[, ctxNode])..........{gebs}

OTHER
- reg.elementText(el)......................................{elemText}
- reg.getElementById().....................................{gebi}
- reg.getElementsByTagName(tagName[, ctxNode]).............{gebtn}
- reg.getParent(el, _selString)
- reg.innerWrap(el, wrapperEl)
- reg.insertAfter(insertMe, afterThis)
- reg.newElement(tagName[, attObj[, contents]])............{elem}
- reg.nextElement(el)......................................{nextElem}
- reg.outerWrap(el, wrapperEl)
- reg.previousElement(el)..................................{prevElem}
*/

var _clPatts = {}; // cache compiled classname regexps

// TEST FOR CLASS NAME
function _hasClassName(_element, _cName) {
	if (!_clPatts[_cName]) { _clPatts[_cName] = new RegExp("(^|\\s)"+_cName+"($|\\s)"); }
	return _element.className && _clPatts[_cName].test(_element.className);
}

// ADD CLASS NAME
function _addClassName(_element, _cName) {
	if (!_hasClassName(_element, _cName)) {
		_element.className += ' ' + _cName;
	}
}

// REMOVE CLASS NAME
function _removeClassName(_element, _cName) {
	if (!_clPatts[_cName]) { _clPatts[_cName] = new RegExp("(^|\\s+)"+_cName+"($|\\s+)"); }
	if (_element.className) _element.className = _element.className.replace(_clPatts[_cName], ' ');
}

// TOGGLE CLASS NAME
function _toggleClassName(_element, _cName) {
	if (_hasClassName(_element, _cName)) { _removeClassName(_element, _cName); }
	else { _addClassName(_element, _cName); }
}

// FIND PREVIOUS ELEMENT
function _previousElement(_el) {
	var _prev = _el.previousSibling;
	while(_prev && _prev.nodeType!==1){_prev=_prev.previousSibling;}
	return _prev;
}

// INSERT AFTER
function _insertAfter(_insertMe, _afterThis){
	var _beforeThis = _afterThis.nextSibling;
	var _parent = _afterThis.parentNode;
	if (_beforeThis) { _parent.insertBefore(_insertMe, _beforeThis); }
	else { _parent.appendChild(_insertMe); }
}

// GET ELEMENT BY ID
function _getElementById(_id) { return document.getElementById(_id); };

//GET ELEMENT ATTR
function _getAttr(_pAttr, _el){
	return _el.getAttribute(_pAttr) || (_el.attributes[_pAttr] ? _el.attributes[_pAttr].nodeValue : "");
}

// GET ELEMENTS BY TAG NAME
function _getElementsByTagName(_tag, _contextNode) {
	if(!_contextNode){_contextNode=document;}
	return _contextNode.getElementsByTagName(_tag);
}

// GET ELEMENTS BY NAME
function _getElementsByName(_tag) {
	return document.getElementsByName(_tag);
}

// #############################################################################
// #### X-BROWSER EVENTS #######################################################
// #############################################################################

/*
Event attachment and detachment:
- reg.addEvent(elmt,evt,handler,cptr)
- reg.calcelDefault(evt)
- reg.getTarget(evt)
*/

var _memEvents = {},
_aMemInd = 0;
function _rememberEvent(_elmt,_evt,_handle,_cptr,_cleanable){
	var _memInd = _aMemInd++;
	_memEvents[_memInd+""] = {
		_element:   _elmt,
		_event:     _evt,
		_handler:   _handle,
		_capture:   !!_cptr,
		_cleanable: !!_cleanable
	};
	return _memInd;
}


function _cleanup(_all){
	for (var _key in _memEvents) {
		if (!_memEvents.hasOwnProperty(_key)) { continue; }
		if (_all || (_memEvents[_key]._cleanable && !document.documentElement.contains(_memEvents[_key]._element))) {
			_removeEvent(_key);
		}
	}
}

//periodically clean up all cleanable events
window.setInterval(function(){ _cleanup(false); },10000);


function _getKey(_e){
	return _e.charCode || _e.keyCode || _e.which || 0;
}

// get the element on which the event occurred
function _getTarget(_e) {
	if (!_e) { _e = window.event; }
	var _targ = _e.target ||  _e.srcElement;
	if (_targ.nodeType === 3) { _targ = _targ.parentNode; } // safari hack
	return _targ;
}

// get the element on which the event occurred
function _getRelatedTarget(e) {
	if (!e) { e = window.event; }
	var _rTarg = e.relatedTarget;
	if (!_rTarg) {
		if ('mouseover'===e.type) { _rTarg = e.fromElement; }
		if ('mouseout'===e.type) { _rTarg = e.toElement; }
	}
	return _rTarg;
}

// cancel default action
function _cancelDefault(_e) {
	if (_e.preventDefault) { _e.preventDefault(); return; }
	_e.returnValue=false;
}

// cancel bubble
function _cancelBubble(_e) {
	if (_e.stopPropagation) { _e.stopPropagation(); return; }
	_e.cancelBubble=true;
}

function _cancelEvent(e) {
	_cancelDefault(e);
	_cancelBubble(e);
}

// generic event adder, plus memory leak prevention
// returns an int mem that you can use to later remove that event removeEvent(mem)
// cptr defaults false
function _addEvent(_elmt, _evt, _handler, _cptr, _cleanable) {
	if (_elmt.addEventListener) {
		_elmt.addEventListener(_evt,_handler,_cptr);
		return _rememberEvent(_elmt, _evt, _handler, _cptr, _cleanable);

	}else if(_elmt.attachEvent){
		var _actualHandler = function(){_handler.call(_elmt,window.event);};
		_elmt.attachEvent("on"+_evt,_actualHandler);
		return _rememberEvent(_elmt,_evt,_actualHandler,_cptr,_cleanable);
	}
}

/*
function _addEventS(_elmt, _evt, _scope, _handler, _cptr, _cleanable) {
	return _addEvent(_elmt, _evt, _Delegate(_scope, _handler), _cptr, _cleanable);
}
*/

// event remover
function _removeEvent(_memInd) {
	var _key = _memInd+"",
	_eo = _memEvents[_key];
	if (_eo) {
		var _el=_eo._element;
		if(_el.removeEventListener) {
			_el.removeEventListener(_eo._event, _eo._handler, _eo._capture);
			delete _memEvents[_key];
			return true;
		} else if(_el.detachEvent) {
			_el.detachEvent('on'+_eo._event, _eo._handler);
			delete _memEvents[_key];
			return true;
		}
	}
	return false;
}



// fight memory leaks in ie
_addEvent(window,'unload',function(){_cleanup(true)});

_efx.addEvent = _addEvent;
_efx.removeEvent = _removeEvent;


// #############################################################################
// #### ON(DOM)LOAD ACTIONS ####################################################
// #############################################################################

/*
Add actions to run onload:
- reg.preSetup(func)
- reg.setup(_selString, func, firstTimeOnly)
- reg.postSetup(func)

!!! WARNING !!!
On browsers *without* native querySelector() support
reg.setup makes page load time O(MN)
where M is the number of calls to reg.setup()
and N is the number of elements on the page
*/

// these contain lists of things to do
var _preSetupQueue=[];
var _setupQueue={};
var _postSetupQueue=[];

// traverse and act onload
_efx.setup=function(_selector, _setup, _firstTimeOnly){
	_firstTimeOnly = _firstTimeOnly ? true:false;
	var _sqt=_setupQueueByTag,
	_parsedSel = new _efx.Selector(_selector),
	_tagNames=_getTagNames(_parsedSel),
	_regObj={
		_selector:_parsedSel,
		_setup:_setup,
		_ran:false,
		_firstTimeOnly:_firstTimeOnly
	};
	_setupQueue.push(_regObj);
	for(var a=0;a<_tagNames.length;a++){
		var _tagName = _tagNames[a];
		if(!_sqt[_tagName]){_sqt[_tagName]=[_regObj];}
		else{_sqt[_tagName].push(_regObj);}
	}
};

// do this before setup
_efx.preSetup=function(_fn){_preSetupQueue.push(_fn)};
// do this after setup
_efx.postSetup=function(_fn){_postSetupQueue.push(_fn)};

// (re)run setup functions
var _runSetupFunctions = _efx.rerun = function(_el, _noClobber){
	function _runIt(_el, _regObj){
		_regObj._setup.call(_el);
		_regObj._ran=true;
	}

	var _start = new Date().getTime();
	if (typeof _el._clobberable !== 'undefined' && _el._clobberable && _noClobber) { return; }
	var _doc=_el?_el:document;
	var _sqIsEmpty=true;
	for (var _tagName in _setupQueue) {
		if(!_setupQueue.hasOwnProperty(_tagName)) { continue; }
		_sqIsEmpty = false; break;
	}

	if (_el.querySelector) {

		//####################################
		//querySelector() branch
		var _qSelResults = [];

		for (var _tagName in _setupQueue) {
			if(!_setupQueue.hasOwnProperty(_tagName)) { continue; }
			var _regObjArray = _setupQueue[_tagName];
			for (var i=0; i<_regObjArray.length; i++) {
				var _regObj = _regObjArray[i];
				if (_regObj._firstTimeOnly) {
					if (_regObj._ran) { continue; }
					try {
						var _elmt = _el.querySelector(_toQuerySelectorString(_regObj._selector));
						if (_elmt) { _qSelResults.push({_el:_elmt,_regObj:_regObj}); }
					} catch (_ex) {
						trace("querySelector('"+_toQuerySelectorString(_regObj._selector)+"') threw "+ex);
						continue;
					}
				} else {
					try {
						var _elmts = _el.querySelectorAll(_toQuerySelectorString(_regObj._selector));
						for (var j=0; j<_elmts.length; j++) {
							_qSelResults.push({_el:_elmts[j],_regObj:_regObj});
						}
					} catch (ex) {
						trace("querySelectorAll('"+_toQuerySelectorString(_regObj._selector)+"') threw "+ex);
						continue;
					}
				}

			}
		}

		for (var i=0; i<_qSelResults.length; i++) {
			_runIt(_qSelResults[i]._el, _qSelResults[i]._regObj);
		}

	} else if (!_sqIsEmpty) {

		//####################################
		//old branch

		var _elsList=_getElementsByTagName('*',_doc);

		//dump live list to static list
		for (var i=_elsList.length-1, _els=[]; i>=0; i--) {
			_els[i] = _elsList[i];
		}

		var _qSelResults = [];

		// crawl the dom
		for(var a=0,_elmt;_elmt=_els[a++];){
			if (_elmt.nodeType!==1){continue;}//for ie7
			var _lcNodeName=_elmt.nodeName.toLowerCase(),
			_regObjArrayAll=_setupQueue['*'],
			_regObjArrayTag=_setupQueue[_lcNodeName];

			// any wildcards?
			if(_regObjArrayAll){
				for(var b=0;b<_regObjArrayAll.length;b++){
					var _regObj=_regObjArrayAll[b];
					if(_regObj._firstTimeOnly && _regObj._ran){continue;}
					var _matches = _regObj._selector.matches(_elmt);
					if(_matches){ _qSelResults.push({_el:_elmt,_regObj:_regObj}); }
				}
			}

			// any items match this specific tag?
			if(_regObjArrayTag){
				for(var b=0;b<_regObjArrayTag.length;b++){
					var _regObj=_regObjArrayTag[b];
					if(_regObj._firstTimeOnly && _regObj._ran){continue;}
					var _matches = _regObj._selector.matches(_elmt);
					if(_matches){ _qSelResults.push({_el:_elmt,_regObj:_regObj}); }
				}
			}
		}

		for (var i=0; i<_qSelResults.length; i++) {
			_runIt(_qSelResults[i]._el, _qSelResults[i]._regObj);
		}
	}

	_el._clobberable = true;
	var _runtime = new Date().getTime() - _start;
	if(!_efx._setupTime){ _efx._setupTime=_runtime; }
	_efx._lastSetupTime=_runtime;
};

var _loadFuncRan = false, _bodyLoaded = false;
function _loadFunc(_e) {
	if (_loadFuncRan) return;
	_loadFuncRan = _bodyLoaded = true;
	_bodyNode = document.body;

	for(var a=0;a<_preSetupQueue.length;a++){
		_preSetupQueue[a]();
	}

	_runSetupFunctions(document, true);

	for(var a=0;a<_postSetupQueue.length;a++){
		_postSetupQueue[a]();
	}
}

// contents of loadFunc only execute once, this sidesteps user agent sniffing
_addEvent(window, 'load', _loadFunc);
_addEvent(window, 'DOMContentLoaded', _loadFunc);


// #############################################################################
// #### EVENT DELEGATION #######################################################
// #############################################################################

/*
The main purpose of reglib is event delegation:
- reg.click(selString, handler, depth)
- reg.hover(selString, overHandler, outHandler, depth)
- reg.focus(selString, focusHandler, blurHandler, depth)
- reg.key(selString, downHandler, pressHandler, upHandler, depth)
- reg.submit(selString, handler, depth)
- reg.reset(selString, handler, depth)
- reg.change(selString, handler, depth)
- reg.select(selString, handler, depth)


Note: delegated events are active before page load, and remain
active throughout arbitrary rewrites of the DOM.
*/

// these contain the event handling functions
var _clickHandlers = {},
	_mDownHandlers = {},
	_mUpHandlers = {},
	_dblClickHandlers = {},
	_mOverHandlers = {},
	_mOutHandlers = {},
	_focusHandlers = {},
	_blurHandlers = {},
	_keyDownHandlers = {},
	_keyPressHandlers = {},
	_keyUpHandlers = {},
	_submitHandlers = {},
	_resetHandlers = {},
	_changeHandlers = {},
	_selectHandlers = {};


// returns first arg that's a number
function _getDepth(_fargs){
	var _result = null;
	for (var i=2; i<_fargs.length; i++) {
		if (!isNaN(parseInt(_fargs[i]))) {
			_result = _fargs[i];
			break;

		}
	}

	if(_result===null) _result=-1;
	if (_result < -1) throw new Error("bad arg for depth, must be -1 or higher");
	return _result;
}

function _pushFunc(_selStr, _handlerFunc, _depth, _handlers, _hoverFlag) {
    if (!_handlerFunc || typeof _handlerFunc != "function") { return; }

    var _parsedSel = new _efx.Selector(_selStr);

    if (!_handlers[_selStr]) { _handlers[_selStr] = []; }
	
	var _selHandler = {
		_selector:_parsedSel,
		_handle:_handlerFunc,
		_depth:_depth,
		_hoverFlag:_hoverFlag

};

	_handlers[_selStr].push(_selHandler);
}

// click
_efx.onClick=function(_selStr, _clickFunc, _downFunc, _upFunc, _doubleFunc){
	var _depth = _getDepth(arguments);
	_pushFunc(_selStr, _clickFunc,  _depth, _clickHandlers,    false);
	_pushFunc(_selStr, _downFunc,   _depth, _mDownHandlers,    false);
	_pushFunc(_selStr, _upFunc,     _depth, _mUpHandlers,      false);
	_pushFunc(_selStr, _doubleFunc, _depth, _dblClickHandlers, false);
};

_efx.onHover=function(_selStr, _overFunc, _outFunc){
	var _depth = _getDepth(arguments);
	_pushFunc(_selStr, _overFunc, _depth, _mOverHandlers, true);
	_pushFunc(_selStr, _outFunc,  _depth, _mOutHandlers,  true);
};

_efx.onFocus=function(_selStr, _focusFunc, _blurFunc){
	var _depth = _getDepth(arguments);
	_pushFunc(_selStr, _focusFunc, _depth, _focusHandlers, false);
	_pushFunc(_selStr, _blurFunc,  _depth, _blurHandlers,  false);
};


_efx.onKey = function(_selStr, _downFunc, _pressFunc, _upFunc) {
    var _depth = _getDepth(arguments);
    _pushFunc(_selStr, _downFunc, _depth, _keyDownHandlers, false);
    _pushFunc(_selStr, _pressFunc, _depth, _keyPressHandlers, false);
    _pushFunc(_selStr, _upFunc,    _depth, _keyUpHandlers,    false);
};

_efx.onSubmit=function(_selStr, _func) {
	var _depth = _getDepth(arguments);
	_pushFunc(_selStr, _func, _depth, _submitHandlers, false);
};

_efx.onReset=function(_selStr, _func) {
	var _depth = _getDepth(arguments);
	_pushFunc(_selStr, _func, _depth, _resetHandlers, false);
};

_efx.onChange=function(_selStr, _func) {
	var _depth = _getDepth(arguments);
	_pushFunc(_selStr, _func, _depth, _changeHandlers, false);
};

_efx.onSelect=function(_selStr, _func) {
	var _depth = _getDepth(arguments);
	_pushFunc(_selStr, _func, _depth, _selectHandlers, false);
};

// workaround for IE's lack of support for bubbling on form events
// set delegation directly on the element in question by co-opting
// the focus event which is guaranteed to happen first
if (_ie) {
	function _ieSubmitDelegate(e) {
		_delegate(_submitHandlers,e);
		_cancelBubble(e);
	}

	function _ieResetDelegate(e) {
		_delegate(_resetHandlers,e);
		_cancelBubble(e);
	}

	function _ieChangeDelegate(e) {
		_delegate(_changeHandlers,e);
		_cancelBubble(e);
	}

	function _ieSelectDelegate(e) {
		_delegate(_selectHandlers,e);
		_cancelBubble(e);
	}

	_efx.onFocus('form',function(){
		_removeEvent(this._submit_prep);
		this._submit_prep=_addEvent(this,'submit',_ieSubmitDelegate,false,true);
		_removeEvent(this._reset_prep);
		this._reset_prep=_addEvent(this,'reset',_ieResetDelegate,false,true);
	},function(){
		_removeEvent(this._submit_prep);
		_removeEvent(this._reset_prep);
	});
	_efx.onFocus('select,input,textarea',function(){
		_removeEvent(this._change_prep);
		this._change_prep=_addEvent(this,'change',_ieChangeDelegate,false,true);
	},function(){
		_removeEvent(this._change_prep);
	});
	_efx.onFocus('input,textarea',function(){
		_removeEvent(this._select_prep);
		this._select_prep=_addEvent(this,'select',_ieSelectDelegate,false,true);
	},function(){
		_removeEvent(this._select_prep);
	});
}

// the delegator
function _delegate(_selectionHandlers, _event) {
	if (_selectionHandlers) {
		var _targ = _getTarget(_event);
		for (var _sel in _selectionHandlers) {
		    if (!_selectionHandlers.hasOwnProperty(_sel)) { continue; }

			for(var a=0; a<_selectionHandlers[_sel].length; a++) {
				var _selHandler=_selectionHandlers[_sel][a],
				_depth = (_selHandler._depth==-1) ? 100 : _selHandler._depth,
				_el = _targ;

			
				for (var b = -1; b < _depth && _el && _el.nodeType == 1; b++, _el = _el.parentNode) {
					if (_selHandler._selector.matches(_el)) {
						// replicate mouse enter/leave
						if (_selHandler._hoverFlag) {
							var _relTarg = _getRelatedTarget(_event);
							if (_relTarg && (_el.contains(_relTarg) || _el == _relTarg)) {
								break;
							}
						}
						//cambiar por esto para pasar como par�metro el evento y el elemento, y no el elemento como scope...
						//var _retVal = _selHandler._handle(_event, _el);
						var _retVal = _selHandler._handle.call(_el, _event);

						// if they return false from the handler, cancel default
						if (_retVal !== undefined && !_retVal) {
						    //_cancelEvent(_event);
						    _cancelDefault(_event);
						}
						
						break;
					}
				}
			}
		}
	}
}


var _focusEventType, _blurEventType;
if(typeof document.onactivate === 'object'){
	_focusEventType = 'activate';
	_blurEventType = 'deactivate';
}else{
	_focusEventType = 'focus';
	_blurEventType = 'blur';
}


// attach the events
_addEvent(_htmlNode,'click',        function(e){_delegate(_clickHandlers,   e);});
_addEvent(_htmlNode,'mousedown',    function(e){_delegate(_mDownHandlers,   e);});
_addEvent(_htmlNode,'mouseup',      function(e){_delegate(_mUpHandlers,     e);});
_addEvent(_htmlNode,'dblclick',     function(e){_delegate(_dblClickHandlers,e);});
_addEvent(_htmlNode,'keydown',      function(e){if (_getKey(e) === 16 || e.shiftKey) _shiftPressed = true; _delegate(_keyDownHandlers, e);});
_addEvent(_htmlNode,'keypress',     function(e){_delegate(_keyPressHandlers,e);});
_addEvent(_htmlNode,'keyup',        function(e){if (_getKey(e) === 16 || !e.shiftKey) _shiftPressed = false;_delegate(_keyUpHandlers,   e);});
_addEvent(_htmlNode,_focusEventType, function(e){_delegate(_focusHandlers,   e);},true);
_addEvent(_htmlNode,_blurEventType,  function(e){_delegate(_blurHandlers,    e);},true);
_addEvent(_htmlNode,'mouseover',    function(e){_delegate(_mOverHandlers,   e);});
_addEvent(_htmlNode,'mouseout',     function(e){_delegate(_mOutHandlers,    e);});
_addEvent(_htmlNode,'submit',       function(e){_delegate(_submitHandlers,  e);});
_addEvent(_htmlNode,'reset',        function(e){_delegate(_resetHandlers,   e);});
_addEvent(_htmlNode,'change',       function(e){_delegate(_changeHandlers,  e);});
_addEvent(_htmlNode,'select',       function(e){_delegate(_selectHandlers,  e);});


// handy for css
_addClassName(_htmlNode, 'hasJavascript');


/**
 * _createDom("node name", {"attr":"value"}, [["text" | [... more child nodes]])
 */

var _createDom = _efx.createDom = function() {
	var _fix = {
		'class': 'className',
		'float': 'cssFloat',
		'for': 'htmlFor',
		'colspan': 'colSpan',
		'rowspan': 'rowSpan'
	},

	_rx1 = /:\s*/,
	_rx2 = /^\s*([^\s]+)\s*/,
	_rx3 = /^\s*([^\s]+)\s*/,

	_createNode = function() {
		var a = arguments,
		a = a[0] instanceof Array ? a[0] : a,
		i = 0,
		_ret = document.createDocumentFragment(),
		j, e, f, g, o, c, _rootNodes = 0;

		for (; i < a.length; i++) {
			if (a[i + 1] instanceof Object) {
				e = document.createElement(a[i]);
				_rootNodes++;

				_ret.appendChild(e);

				for (j in a[++i]) {//attrs, obj
					if (!a[i].hasOwnProperty(j)) continue;
					j = j.toLowerCase();
					f = _fix[j] || j;

					if (f === "style") {
						var _props = a[i][j].split(/;/);

						for (g = 0; g < _props.length; g++) {
							var t = _props[g].split(_rx1);
							var _prop = t[0].replace(_rx2, '$1');
							var _val = t[1].replace(_rx3, '$1');
							e.style[_prop] = _val;
						}

					} else {
						if (j in _fix) {
							e[f] = a[i][j];

						} else {
							e.setAttribute(f, a[i][j]);
						}
					}
				}

				if (a[i + 1] instanceof Array) {
					e.appendChild(_createNode(a[++i]));
				}

			} else {
				_ret.appendChild(document.createTextNode(a[i]));
			}
		}

		return _rootNodes === 1 ? e : _ret;
	};

	return _createNode;
} ();
//@requires createDom

	/*
	 * 		var json = [
   {'name' : "John", 'surname' : "Smith"},
   {'name' : "Sarra", 'surname' : "Smith"}
];

	a = efx.dom.Document.fromTemplate(json, function(){
   return [
      'ul', { 'class':"MyTableRow" }, [
         'li', { 'class':"MyTableCol1" }, [ this.name ],
         'li', { 'class':"MyTableCol2" }, [ this.surname ]]
   ];
})
	 */


/**
 * Creates HTML from a json object and a template Dom.
 * @param _json The json object
 * @param _tpl The template, in DomTemplate format.
 */
function _fromTemplate(_json, _tpl) {
	var _ret = document.createDocumentFragment(), a, i=0, j;
	_json = _json instanceof Array ? _json : [_json];
	for (; i<_json.length; i++) {
		_ret.appendChild(_createDom(_tpl.call(_json[i])))
	}

	return _ret;
};

_efx.fromTemplate = _fromTemplate;

function _setElementDisplay(_pElement, _pState){
	_pElement.style.display = _pState ? "block":"none";
}
_String.format = function() {
	var _compositeFormattingRegExp = new RegExp(/{(\d+)}|{(\d+):([^\r\n{}]+)}/g);

	return function(_pSource, _pParams) {
		if (!(_pParams instanceof Array)) _pParams = Array.prototype.slice.call(arguments, 1);

		var _fmtArgs,
		_cIdx,
		_cArg,
		_aFmt,
		_theString,
		i,
		_target = _pSource;

		while ((_fmtArgs = _compositeFormattingRegExp.exec(_pSource)) != null) {
			_cArg = _pParams[parseInt(_fmtArgs[1] || _fmtArgs[2])];
			_aFmt = _fmtArgs[3] || "";

			if (_cArg === _UNDEFINED) {
				_theString = "";

			} else {
				if (_cArg.toFormattedString) {
					_theString = _cArg.toFormattedString(_aFmt);

				} else {
					if (_registeredClass("efx.DateTimeTools") && _cArg instanceof Date && _aFmt.length) {//Date
						_theString = _DateTimeTools.format(_cArg, _aFmt);

					} else if (_cArg.format) {
						_theString = _cArg.format(_aFmt);

					} else {
						_theString = _cArg.toString();
					}
				}
			}

			_target = _target.split(_fmtArgs[0]).join(_theString);
		};

		return _target;
	};
} ();

/**
 * @requires createDom
 * @requires fromTemplate
 * @requires setElementDisplay
 * @requires StringFormat
 */

var _TabbedUI = _efx.TabbedUI = _Class.create({
	init: function(_settings) {
		var $ = this;
		$._tabName = "tabs_" + _TabbedUI._counter + "_",
		$._tabArray = [],
		$._lastTabShown = 0;

		$._settings = _objCopy({
			tabText: "Page {0}",
			tabsLocation: "tabsHere",
			tabsHolder: "tabsHolder",
			prevText: "« Previous",
			nextText: "Next »",
			showPrevNext: true
		}, _settings);

		_TabbedUI._instances[$._tabName] = $;
		_efx.postSetup($.delegate($._init));
	},

	_showTab: function(_pTabIndex) {
		var $ = this,
		_ls,
		_ns;

		if ((_ls = $._tabArray[$._lastTabShown]) && _ls._div) {
			_setElementDisplay(_ls._div);
			_removeClassName(_getElementById(_ls._a).parentNode, "selected");
		}

		if (_ns = $._tabArray[_pTabIndex]) {
			if (_ns._div) {
				_setElementDisplay(_ns._div, true);
				_ns = _getElementById(_ns._a);
				_addClassName(_ns.parentNode, "selected");
				_ns.blur();
			}

			$._lastTabShown = _pTabIndex;
		}
	},

	_onclick: function(_pEvent, _pNode) {
		this._showTab(_getAttr("href", _pNode).split("#" + this._tabName).join(""));
	},

	_init: function() {
		var $ = this,
 		_tabHolder = _getElementById($._settings.tabsHolder);
		if (!_tabHolder) return;

		var _tabsDIVs = _getElementsByTagName("div", _tabHolder),
		i = 0,
		_idx = 0,
		_tabsInfo = [],
		_div,
		_name,
		_pnDiv,
		_totalTabs = 0,
		_tabList,
		_settings = $._settings;

		while (_div = _tabsDIVs[_idx++]) {
			if (_div.parentNode === _tabHolder) _totalTabs++;
		}

		_totalTabs--;
		_idx = 0;

		while (_div = _tabsDIVs[_idx++]) {
			if (_div.parentNode === _tabHolder) {
				_name = $._tabName + i;
				$._tabArray[i] = { _div: _div, _a: "tab_" + _name };
				_setElementDisplay(_div);
				_div.setAttribute("id", _name);

				_tabsInfo[i] = {
					_text: _String.format(_settings.tabText, i+1),
					_name: _name
				};

				if (_settings.showPrevNext && (_settings.prevText !== "" || _settings.nextText !== "")) {
					_div.appendChild(_pnDiv = _createDom('div', { "class": "efxTabPrevNext" }));

					if (_settings.prevText !== "") {
						if (i === 0) {
							_pnDiv.appendChild(_createDom("span", { "class": "efxTabPrevDisabled" }, [_settings.prevText]));

						} else {
							_pnDiv.appendChild(
								_createDom('a', {
									'href': "#" + $._tabName + (i - 1),
									'class': 'efxTabPrev',
									'tabIndex': -1
								},
									[_settings.prevText])
							);
						}
					}

					if (_settings.nextText !== "") {
						if (i < _totalTabs) {
							_pnDiv.appendChild(
								_createDom('a', {
									'href': "#" + $._tabName + (i + 1),
									'class': 'efxTabNext',
									'tabIndex': -1
								},
									[_settings.nextText])
							);

						} else {
							_pnDiv.appendChild(_createDom("span", { "class": "efxTabPrevDisabled" }, [_settings.nextText]));
						}
					}
				}

				i++;
			}
		}

		_tabList = _createDom('ul', { 'class': "tabList", "id": $._tabName });
		if (_settings.tabsLocation) {
			_settings.tabsLocation = _getElementById(_settings.tabsLocation);
			_settings.tabsLocation.appendChild(_tabList);

		} else {
			_tabHolder.parentNode.insertBefore(_tabList, _tabHolder);
		}

		_tabList.appendChild(_fromTemplate(_tabsInfo,
			function() {
				return [
     				'li', {/*attrs*/
     			},
     					[
     						"a",
     						{ "href": "#" + this._name, "id": "tab_" + this._name },
     						[this._text]
						]
					]
			}
			)
		);

		$._showTab(parseInt(location.hash.split("#" + $._tabName).join("") || "0"));
		_efx.onClick("#" + $._tabName + " li a, div.efxTabPrevNext a", function(_pEvent) { $._onclick(_pEvent, this) });
	},

	statics: {
		_counter: 0,
		_instances: {},
		getMyTab: function(_pElement) {
			var _tab;
			while (_pElement = _pElement.parentNode) {
				_tab = _getAttr("id", _pElement);
				if (_tab && _tab.indexOf("tabs_") === 0) {
					return _tab;
				}
			}
		},

		showTab: function(_tabName) {
			var _idx = _tabName.lastIndexOf("_"),
			_tabNumber = _tabName.substr(_idx + 1);

			_tabName = _tabName.substr(0, _idx + 1);
			_TabbedUI._instances[_tabName]._showTab(_tabNumber);
		}
	}
});

	
	

var _Point = _Class.create({
	init: function(_pX, _pY) {
		this.x = _pX || 0;
		this.y = _pY || 0;
	}
});
//@requires Point

function _getMousePosition(_pEvent){
	if (_pEvent.pageX || _pEvent.pageY) 	{
		return new _Point(_pEvent.pageX, _pEvent.pageY);
		
	} else if (_pEvent.clientX || _pEvent.clientY) 	{
		return new _Point(
			_pEvent.clientX + _bodyNode.scrollLeft + _htmlNode.scrollLeft,
		 	_pEvent.clientY + _bodyNode.scrollTop + _htmlNode.scrollTop
		 );
	}

	return new _Point();
}
/**
* Usage:
* Elements that will trigger the tooltip must have the attr "rel" with the id of the tooltip element.
* Tooltips must have the class "efx_tooltip" applied, plus an id related to it´s trigger element.
*
* Basic CSS:
* .hasJavascript .efx_tooltip{
* 	display: none;
* 	position: absolute;
* 	z-index: 1000;
* }
*
* @requires getMousePosition
* @requires setElementDisplay
*/

var _ToolTips = _efx.ToolTips = _Class.create({
	init: function(_pSettings) {
		var $ = this;
		$._settings = _objCopy({
			offsetX: 30,
			offsetY: -25,
			tag:"a"
		}, _pSettings);

		_efx.onHover($._settings.tag+"@rel^='tooltip_'",
			function(_pEvent) { $._showTip(_pEvent, this) },
			function(_pEvent) { $._hideTip(_pEvent, this) }
		);
	},

	_moveTip: function(_pEvent, _pNode) {
		var _mp = _getMousePosition(_pEvent), $ = this;
		$._toolTip.style.left = (_mp.x + $._settings.offsetX) + "px";
		$._toolTip.style.top = (_mp.y + $._settings.offsetY) + "px";
	},

	_showTip: function(_pEvent, _pNode) {
		var $ = this;
		$._toolTip = _getElementById(_getAttr("rel", _pNode));
		_setElementDisplay($._toolTip, true);
		$._moveTip(_pEvent);
		$._ttMouseEvent = _addEvent(window, "mousemove", function(_pEvent) { $._moveTip(_pEvent, this) });
	},

	_hideTip: function(_pEvent) {
		var $ = this;
		_removeEvent($._ttMouseEvent);
		_setElementDisplay($._toolTip);
	}
});
/**
* @constructor
* @argument {String} $name The name of the cookie
* @argument {String} $path Setting a path for the cookie allows the current document to share cookie information with other pages within the same domain that is, if the path is set to /thispathname, all pages in /thispathname and all pages in subfolders of /thispathname can access the same cookie information.
* @argument {String} $domain Setting the domain of the cookie allows pages on a domain made up of more than one server to share cookie information.
* @argument {Boolean} $secure The stored cookie information can be accessed only from a secure environment.
*/

var _Cookie = _Class.create({
	init: function(_pName, _pPath, _pDomain, _pSecure) {
		var $ = this;
		$._name = _pName;
		$._path = _pPath ? ";path=" + _pPath : "";
		$._domain = _pDomain ? ";domain=" + _pDomain : "";
		$._secure = _pSecure ? ";secure" : "";
	},
	
	get: function() {
		var $ = this;
		var _cookie = document.cookie;
		var _start = _cookie.indexOf($._name + "=");
		var $len = _start + $._name.length + 1;
		if ((!_start) && ($._name !== _cookie.substring(0, $._name.length))) return;
		if (_start === -1) return;
		var _end = _cookie.indexOf(";", $len);
		if (_end === -1) _end = _cookie.length;
		//			return unescape(_cookie.substring($len, _end));
		return decodeURIComponent(_cookie.substring($len, _end));
	},

	/**
	* @param _pValue The value to store in the cookie
	* @param {Date} _pExpires Setting no expiration date on a cookie causes it to expire when the browser closes. If you set an expiration date, the cookie is saved across browser sessions. If you set an expiration date in the past, the cookie is deleted.
	*/
	set: function(_pValue, _pExpires) {
		var $ = this;
		//document.cookie = $._name + "=" + escape(_pValue) + 
		document.cookie = $._name + "=" + encodeURIComponent(_pValue) +
		((_pExpires) ? ";expires=" + _pExpires.toGMTString() : "") + $._path + $._domain + $._secure;
	},

	destroy: function() {
		var $ = this;
		if ($.get($._name)) document.cookie = $._name + "=" + $._path + $._domain + ";expires=Thu, 01-Jan-70 00:00:01 GMT";
	}
});

var _Size = _Class.create({
	init: function(_pW, _pH) {
		this.w = _pW || 0;
		this.h = _pH || 0;
	}
});

/**
 * @requirs getStyle
 * @requires Size
 */

function _getElementSize(_pElement, _pIncludeBorders) {
	return _pIncludeBorders ?
	//get a node's dimensions (including padding and border)
		new _Size(_pElement.offsetWidth, _pElement.offsetHeight) :
	//get dimensions without border but includes padding
		new _Size(_pElement.clientWidth, _pElement.clientHeight);
}


/* 
function _getElementSize(_pElement, _pIncludeBorders, _pIncludePadding){
	var w = _getStyle(_pElement, "width"), h = _getStyle(_pElement, "height");

	if (_pIncludePadding) {
		w += _getStyle(_pElement, "padding-left") + _getStyle(_pElement, "padding-right");
		h += _getStyle(_pElement, "padding-top") + _getStyle(_pElement, "padding-bottom");
	}

	if (_pIncludeBorders) {
		w += _getStyle(_pElement, "border-left-width") + _getStyle(_pElement, "border-right-width");
		h += _getStyle(_pElement, "border-top-width") + _getStyle(_pElement, "border-bottom-width");
	}
	
	return new _Size(w || _pElement.clientWidth, h || _pElement.clientHeight);
}
*/
_efx.getElementSize = _getElementSize;
//@requires getElementSize

var _FlashObject = _efx.FlashObject = _Class.create({
	init: function(_pUrl, _pVersion, _pW, _pH, _pClass) {
		var $ = this;

		$._url = _pUrl;
		$._width = _pW;
		$._height = _pH;
		$._klass = _pClass || "";
		$._replaced = false;

		$._params = {
			allowScriptAccess: "always"
		};

		$._vars = {};
		$.version = _FlashObject._swfVersion.concat();
		$.validVersion = $._isValid(_pVersion);
	},

	addParam: function(_name, _value) {
		this._params[_name] = _value;
	},

	addVar: function(_name, _value) {
		this._vars[_name] = _value;
	},

	destroy: function(){
		var $ = this;
		$._cleanUp();

		if ($._flashObj) {
			try {
				$._flashObj.parentNode.removeChild($._flashObj);
			} catch (e) {}
		}

		window[$._flashID] = null;
	},

	_cleanUp:function(){
		var $ = this;
		try {
			if ($._flashObj && typeof($._flashObj.CallFunction) === "unknown") { // We only want to do this in IE
				for (k in $._flashObj) {
					try {
						if (typeof($._flashObj[k]) === "function") {
							$._flashObj[k] = null;
						}
					} catch (e) {}
				}
			}

		} catch (e) {};

		// Fix Flashes own cleanup code so if the SWF Movie was removed from the page
		// it doesn't display errors.
		window["__flash__removeCallback"] = function (_instance, _name) {
			try {
				if (_instance) {
					_instance[_name] = null;
				}
			} catch (e) {}
		};
	},



	/*
	setSize: function(_pW, _pH) {
	var $ = this, _obj;
	if ($._replaced) {

	if (document.all && !document.getElementById) {
	(_obj = document.all[$._flashID]).style.pixelWidth = _pW;
	_obj.style.pixelHeight = _pH;

	} else {
	(_obj = document.getElementById($._flashID)).style.width = _pW + 'px';
	_obj.style.height = _pH + 'px';
	}

	} else {
	$._width = _pW;
	$._height = _pH;
	}
	},
	*/
	_getInnerHtml: function() {
		var $ = this;
		var _params = $._params;

		var _vars = new Array();
		for (var i in $._vars) {
			_vars[_vars.length] = i + '=' + encodeURIComponent($._vars[i]);
		}

		_params['FlashVars'] = _vars.join('&');

		var _flashnode = '<object id="' + $._flashID + '" width="' + $._width + '" height="' + $._height + '"';

		if ($._klass.length) _flashnode += ' class="' + $._klass + '"';

		if (_ie) { // PC IE
			_flashnode += ' classid="clsid:D27CDB6E-AE6D-11cf-96B8-444553540000">';
			_params['movie'] = $._url;

		} else { // netscape plugin architecture
			_flashnode += ' type="application/x-shockwave-flash" data="' + $._url + '">';
		}

		for (var i in _params) {
			_flashnode += '<param name="' + i + '" value="' + _params[i] + '" />';
		}

		return _flashnode += '</object>';
	},

	write: function(_pNodeId, _pUseOuterNode) {
		var $ = this;

		if (!$.validVersion) return;

		var _node, _flashID, _nc = _FlashObject._nodeCount++;

		if (typeof _pNodeId === 'string') {// Am I passing a string?
			_node = _getElementById(_pNodeId);
			_flashID = "flash_" + _pNodeId + "_" + _nc;

		} else {
			_node = _pNodeId;
			_flashID = "flash_nn" + _nc;
		}

		if (!_node) return;

		var _temp = _getElementSize(_node);
		$._width = $._width || _temp.w;
		$._height = $._height || _temp.h;

		$._flashID = _flashID;
		$.addVar("flashObject_ID", _flashID);

		var _innerHtml = $._getInnerHtml();

		if (_ie) { // PC IE
			//Create a fake object to hold flash objects cached in memory... fix for IE bugs when using the "Back" button
			if (!window[_flashID]) window[_flashID] = {};

			if (_pUseOuterNode) {
				_node.outerHTML = _innerHtml;

			} else {
				_node.innerHTML = _innerHtml;
			}

			// fixes borked ExternalInterface inside forms on IE...
			var _obj = $._flashObj = _getElementById(_flashID);
			var _wid = window[$._flashID];
			for (var i in _wid) {
				if (typeof (_wid[i]) === "function") {
					_obj[i] = function() {
						return eval(_obj.CallFunction("<invoke name=\"" + i + "\" returntype=\"javascript\">" + __flash__argumentsToXML(arguments, 0) + "</invoke>"));
					}
				}
			}

			window[_flashID] = _obj;

		} else { // netscape plugin architecture
			if (_pUseOuterNode) {
				var _newNode = document.createElement('div');
				_newNode.innerHTML = _innerHtml;
				_node.parentNode.replaceChild(_newNode, _node);

			} else {
				_node.innerHTML = _innerHtml;
			}

			$._flashObj = _getElementById(_flashID);
		}

		return true;
	},

	_createParameter: function(_pNode, _pName, _pValue) {
		var _node = document.createElement("param");
		_node.setAttribute("name", _pName);
		_node.setAttribute("value", _pValue);
		_pNode.appendChild(_node);
	},

	_isValid: function(_pM, _pN, _pR) {
		var $ = this;

		if (_pM instanceof Array) {
			_pR = _pM[2];
			_pN = _pM[1];
			_pM = _pM[0];
		}

		_pM = parseInt(_pM) || 0;
		_pN = parseInt(_pN) || 0;
		_pR = parseInt(_pR) || 0;

		var m = $.version[0];
		var n = $.version[1];

		if (m < _pM) return false;
		if (m > _pM) return true;
		if (n < _pN) return false;
		if (n > _pN) return true;

		if ($.version[2] < _pR) return false;
		return true;
	},

	statics: {
		_nodeCount: 0,
		_getVer: function(_version) {
			return [parseInt(_version[0]) || 0, parseInt(_version[1]) || 0, parseInt(_version[2]) || 0]
		}
	}
});

_FlashObject._swfVersion = function() {
	var _getVer = _FlashObject._getVer,
	        _ver = _getVer('0.0.0'),
	        n = navigator,
	        mt = "application/x-shockwave-flash",
	        d;


	if (n.plugins && n.plugins['Shockwave Flash']) {
		var x = n.plugins['Shockwave Flash'], d = x.description;

		if (x && d && !(n.mimeTypes && n.mimeTypes[mt] && !n.mimeTypes[mt].enabledPlugin)) {
			d = d.replace(/^.*\s+(\S+\s+\S+$)/, "$1");
			return (_getVer(
				        [
					        d.replace(/^(.*)\..*$/, "$1"),
					        d.replace(/^.*\.(.*)\s.*$/, "$1"),
					        /[a-zA-Z]/.test(d) ? parseInt(d.replace(/^.*[a-zA-Z]+(.*)$/, "$1"), 10) : 0
				        ]
			        ));
		}

	} else if (window.ActiveXObject) {
		try {
			if (_axo = new ActiveXObject('ShockwaveFlash.ShockwaveFlash')) {
				if (d = _axo.GetVariable('$version')) {
					return d.split(" ")[1].split(",");
				}
			}

		} catch (e) {
		}
	}

	return _ver;
} ();

if (_ie) {
	_addEvent(window, 'beforeunload', function() {
		__flash_unloadHandler = __flash_savedUnloadHandler = null;
		_addEvent(window, "unload", function() {
			var _objects = document.getElementsByTagName('object');
			var _element, i = 0, _node;

			while (_node = _objects[i++]) {
				_node.style.display = 'none';

				for (var x in _node) {
					if (typeof _node[x] === 'function') {
						_node[x] = null;
					}
				}
			}
		})
	});
};

function _getInnerXhtml(_pNode) {
	if (_pNode === _UNDEFINED) return '';
	if (_pNode.nodeType === _UNDEFINED) return '';

  	var _tagname = _pNode.nodeName.toLowerCase(),
  	_open = '<'+_tagname,
 	_emptytag = (_pNode.nodeName.match(/area|base|basefont|br|col|frame|hr|img|input|isindex|link|meta|param/i)) ? true : false,
	_attrs = _pNode.attributes,
	_attr, _idx = 0;

	while (_attr = _attrs[_idx++]){
  		if (_attr.specified && _attr.value !== "null") _open += ' '+_attr.name.toLowerCase()+'="'+_attr.value+'"';
  	}

	if (_emptytag){
		return 	_open + ' />';
	}

	var _content = '';
	_open += '>';

	var _node, _idx = 0;
	while (_node = _pNode.childNodes[_idx++]){
		if (_node.nodeType===3){
			var $val = _node.nodeValue.replace(/[\t\n\r\f]*/g, '');
			if ($val.replace(/ */g, '') !== '') {
				_content += '<![CDATA['+$val+']]>';
			}

		} else if (_node.nodeType===1){
			_content += this.getInnerXHTML(_node);
		}
	}

	return _open+_content+'</'+_tagname+'>';
}

_efx.getInnerXhtml = _getInnerXhtml;
function _getQueryStringVars(){
	var _obj = {},
	_varsplit = location.search.split('?');
	
	if(_varsplit.length>1){
		var _vars = _varsplit[1].split('&'), _split,
		_element, _idx = 0;
		
		while (_element = _vars[_idx++]){
			_split=_element.split('=');
			_obj[_split[0]] = _split[1];
		}
	}
	
	return _obj;
};
//@requires storage.Cookie
//@requires flash.FlashObject
//@requires getInnerXhtml
//@requires getQueryStringVars


var _Flash = _efx.Flash = _Class.create({
	init: function() {
		var $ = this;

		$._updating = false;
		$._updUseAutoUpdate = true;
		$._updFile = "flash_updater.swf";
		$._updWidth = 215;
		$._updHeight = 138;
		$._updUrlCancel = "";
		$._updUrlFailed = "";
		$._updUrlRedir = "";
		$._items = [];
		$._cookie = new _Cookie('useFlash');

		$._minVersion = [8, 0, 0];
		$._fixTitle = window.location.hash !== '' && _ie7;

		_efx.postSetup(function() {
			$._docTitle = document.title;
			$._replace();
		});
	},

	setMinVersion: function(_pM, _pMn, _pR) {
		if (_pM instanceof Array) {
			this._minVersion = _pM;

		} else {
			this._minVersion = [_pM, _pMn, _pR];
		}
	},

	setAutoUpdate: function(_pUrlCancel, _pUrlFailed, _pUrlRedir, _pMovie, _pWidth, _pHeight) {
		var $ = this;
		if (_pUrlCancel === true) {
			$._updUseAutoUpdate = true

		} else if (_pUrlCancel === false) {
			$._updUseAutoUpdate = false;

		} else {
			$._updFile = _pMovie;
			$._updWidth = _pWidth;
			$._updHeight = _pHeight;
			$._updUrlCancel = _pUrlCancel;
			$._updUrlFailed = _pUrlFailed;
			$._updUrlRedir = _pUrlRedir;
			$._updUseAutoUpdate = true;
		}
	},

	replace: function(_pUserParams, _pNodeId, _pClass) {
		var $ = this;
		if ($._updating) return;

		var _item = {
			_id: _pNodeId,
			_params: _pUserParams,
			_klass: _pClass
		};

		if (_bodyLoaded) {
			$._dwrite(_item);

		} else {
			$._items.push(_item);
		}
	},

	installStatus: function(_pStatus) {
		var $ = this;
		_bodyNode.removeChild(document.getElementById('SWF_container'));

		_htmlNode.style.height = $._htmlStyleHeight;
		_htmlNode.style.overflow = $._htmlStyleOverflow;

		_bodyNode.style.height = $._bodyStyleHeight;
		_bodyNode.style.overflow = $._bodyStyleOverflow;

		if (_pStatus === 'cancelled') {
			var _noflash = confirm('This content requires a more recent version of the Adobe Flash Player.\nDo you want to disable this warning for the whole session?');

			if (_noflash) {
				$._cookie.set('false');
			}

		} else if (_pStatus === 'failed') {
			alert('There was an error downloading the Flash Player update. Please try again later, or visit adobe.com/go/getflashplayer/ to download the latest version of the Flash plugin.');
		}
	},

	_doFixTitle: function() {
		var $ = this;
		if ($._fixTitle) setTimeout(function() { document.title = $._docTitle }, 10);
	},

	_update: function() {
		var $ = this;
		if ($._updating) return;
		
		var _fo = new _FlashObject($._updFile, [6, 0, 65], $._updWidth, $._updHeight);

		if (_fo.validVersion) {
			$._updating = true;
			$._doFixTitle();

			$._htmlStyleHeight = _htmlNode.style.height;
			$._htmlStyleOverflow = _htmlNode.style.overflow;

			$._bodyStyleHeight = _bodyNode.style.height;
			$._bodyStyleOverflow = _bodyNode.style.overflow;

			_bodyNode.style.height = _htmlNode.style.height = '100%';
			_bodyNode.style.overflow = _htmlNode.style.overflow = 'hidden';

			var _container = document.createElement("div");
			_container.setAttribute("id", "SWF_container");

			var _dialog = document.createElement("div");
			_container.appendChild(_dialog)
			;
			_container.style.position = 'absolute';
			_container.style.left = '0';
			_container.style.top = '0';
			_container.style.width = '100%';
			_container.style.height = '100%';
			_container.style.backgroundColor = '#333';
			_container.style.opacity = '0.5';

			_dialog.style.position = 'absolute';
			_dialog.style.left = '50%';
			_dialog.style.top = '50%';
			_dialog.style.marginLeft = '-' + (parseInt(_updWidth) / 2) + 'px';
			_dialog.style.marginTop = '-' + (parseInt(_updHeight) / 2) + 'px';
			_dialog.style.width = _updWidth + 'px';
			_dialog.style.height = _updHeight + 'px';

			document.title = document.title.slice(0, 47) + ' - Flash Player Installation';
			_fo.addParam("bgcolor", "#000");
			_fo.addParam("allowScriptAccess", "always");

			_fo.addVar("urlCancel", _updUrlCancel);
			_fo.addVar("urlFailed", _updUrlFailed);
			_fo.addVar("MMredirectURL", window.location);
			_fo.addVar("MMplayerType", _ie ? 'ActiveX' : 'PlugIn');
			_fo.addVar("MMdoctitle", document.title);

			_fo.write(_dialog);

			_bodyNode.appendChild(_container);

		} else if ($._updUrlRedir !== '') {
			document.location.replace($._updUrlRedir);
		}
	},

	_dwrite: function(_pItem) {
		var $ = this,
		i,
		_userParams = _pItem._params,
		_nodeId = _pItem._id,
		_version = _userParams.version ? _userParams.version.split(".") : $._minVersion,
		_fo = new _FlashObject(_userParams.movie, _version, _userParams.width, _userParams.height, _pItem._klass);

		if (_fo.validVersion) {
			_nodeId = _getElementById(_nodeId);

			if (!_nodeId) return;

			if (_userParams.sendhtml) {
				_fo.addVar("htmlContent", _getInnerXHTML(_nodeId));
			};

			if (_userParams.useBrowserVars) {
				var _bvars = _getQueryStringVars();
				for (i in _bvars) {
					_fo.addVar(i, _bvars[i]);
				}
			};

			for (i in _userParams.params) {
				_fo.addParam(i, _userParams.params[i]);
			};

			for (i in _userParams.vars) {
				_fo.addVar(i, _userParams.vars[i]);
			};

			_fo.write(_nodeId, _userParams.useOuter);
			_addClassName(_nodeId, "hasFlash");

		} else if ($._updUseAutoUpdate) {
			if ($._cookie.get() !== 'false') {
				var _t = setInterval(function() {
					$._update();
					clearInterval(_t);
				}, 250);
			}
		}
	},

	_replace: function() {
		var $ = this,
		_idx = 0, _item;
		while (_item = $._items[_idx++]) $._dwrite(_item);
		$._doFixTitle();
	}

}, true);

/**
 * @requires ui.TabbedUI
 * @requires ui.ToolTips
 * @requires flash.Flash
 */

return _efx;
})();

